//############################################################################
//
// LaserBoy !!!
//
// by James Lehman
// Extra Stimulus Inc.
// james@akrobiz.com
//
// began: October 2003
//
// Copyright 2003 to 2026 James Lehman.
// This source is distributed under the terms of the GNU General Public License.
//
// LaserBoy_vertex.hpp is part of LaserBoy.
//
// LaserBoy is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// LaserBoy is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with LaserBoy. If not, see <http://www.gnu.org/licenses/>.
//
//############################################################################
#ifndef __LASERBOY_3D_VERTEX_DEFINITIONS__
#define __LASERBOY_3D_VERTEX_DEFINITIONS__

//############################################################################
#include "LaserBoy_real_vertex.hpp"
#include "LaserBoy_wav.hpp"

//############################################################################
class LaserBoy_vertex : public LaserBoy_3D_short, public LaserBoy_color
{
public:
    LaserBoy_vertex(const short&   _x = 0,
                    const short&   _y = 0,
                    const short&   _z = 0,
                    const u_char&  _r = 255,
                    const u_char&  _g = 0,
                    const u_char&  _b = 0,
                    const u_char&  _k = LASERBOY_BLANKING_BIT,
                    const u_char&  _c = 0 // default palette red index
                   )
                 : LaserBoy_3D_short (_x, _y, _z),
                   LaserBoy_color    (_r, _g, _b),
                   k                 (_k        ),
                   c                 (_c        )
    {}
    //------------------------------------------------------------------------
    LaserBoy_vertex(const LaserBoy_vertex& p)
                 : LaserBoy_3D_short (p.x, p.y, p.z),
                   LaserBoy_color    (p.r, p.g, p.b),
                   k                 (p.k          ),
                   c                 (p.c          )
    {}
    //------------------------------------------------------------------------
    LaserBoy_vertex(const LaserBoy_real_vertex& rv) // cast to LaserBoy_vertex from LaserBoy_real_vertex
                 : LaserBoy_3D_short ((short)round(rv.x),
                                      (short)round(rv.y),
                                      (short)round(rv.z)
                                     ),
                   LaserBoy_color    (rv.r, rv.g, rv.b),
                   k                 (rv.k),
                   c                 (rv.c)
     {
         if(x == -32768) x = -32767;
         if(y == -32768) y = -32767;
         if(z == -32768) z = -32767;
     }
    //------------------------------------------------------------------------
    LaserBoy_vertex(const LaserBoy_3D_short& s,
                    const LaserBoy_color&    rgb,
                    const u_char&            _k = LASERBOY_BLANKING_BIT,
                    const u_char&            _c = 0
                   )
                 : LaserBoy_3D_short (s  ),
                   LaserBoy_color    (rgb),
                   k                 (_k ),
                   c                 (_c )
    {}
    //------------------------------------------------------------------------
    LaserBoy_vertex(const LaserBoy_3D_short& s,
                    const u_char&            _c
                   )
                 : LaserBoy_3D_short (s            ),
                   LaserBoy_color    (255, 255, 255),
                   k                 (LASERBOY_BLANKING_BIT),
                   c                 (_c           )
    {}
    //------------------------------------------------------------------------
    LaserBoy_vertex(const LaserBoy_3D_short& s)
                 : LaserBoy_3D_short (s            ),
                   LaserBoy_color    (255, 255, 255),
                   k                 (LASERBOY_BLANKING_BIT),
                   c                 (0            )
    {}
    //------------------------------------------------------------------------
    LaserBoy_vertex(const LaserBoy_color& rgb)
                 : LaserBoy_3D_short (0, 0, 0),
                   LaserBoy_color    (rgb    ),
                   k                 (LASERBOY_BLANKING_BIT),
                   c                 (0      )
    {}
    //------------------------------------------------------------------------
   ~LaserBoy_vertex()
    {}
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator = (const LaserBoy_vertex& p)
    {
        x = p.x;
        y = p.y;
        z = p.z;
        r = p.r;
        g = p.g;
        b = p.b;
        k = p.k;
        c = p.c;
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator = (const LaserBoy_3D_short& s) // only assign the coordinates
    {
        x = s.x;
        y = s.y;
        z = s.z;
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator = (const LaserBoy_3D_double& d) // only assign the coordinates
    {
        x = (short)round(d.x);
        y = (short)round(d.y);
        z = (short)round(d.z);
        if(x == -32768) x = -32767;
        if(y == -32768) y = -32767;
        if(z == -32768) z = -32767;
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator = (const LaserBoy_color& c) // only assign the colors
    {
        r = c.r;
        g = c.g;
        b = c.b;
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator = (const LaserBoy_real_vertex& rv)
    {
        x = (short)round(rv.x);
        y = (short)round(rv.y);
        z = (short)round(rv.z);
        r = rv.r;
        g = rv.g;
        b = rv.b;
        k = rv.k;
        c = rv.c;
        if(x == -32768) x = -32767;
        if(y == -32768) y = -32767;
        if(z == -32768) z = -32767;
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex  operator + (const LaserBoy_3D_short& s) const
    {
        int X, Y, Z;
        LaserBoy_vertex sum(*this);
        X = x + s.x;
        Y = y + s.y;
        Z = z + s.z;
        if(X >= LASERBOY_MAX_SHORT)
            sum.x = LASERBOY_MAX_SHORT;
        else if(X <= LASERBOY_MIN_SHORT)
            sum.x = LASERBOY_MIN_SHORT;
        else
            sum.x = X;
        if(Y >= LASERBOY_MAX_SHORT)
            sum.y = LASERBOY_MAX_SHORT;
        else if(Y <= LASERBOY_MIN_SHORT)
            sum.y = LASERBOY_MIN_SHORT;
        else
            sum.y = Y;
        if(Z >= LASERBOY_MAX_SHORT)
            sum.z = LASERBOY_MAX_SHORT;
        else if(Z <= LASERBOY_MIN_SHORT)
            sum.z = LASERBOY_MIN_SHORT;
        else
            sum.z = Z;
        return sum;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator += (const LaserBoy_3D_short& s)
    {
        x += s.x;
        y += s.y;
        z += s.z;
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex  operator - (const LaserBoy_3D_short& s) const
    {
        LaserBoy_vertex diff(*this);
        diff.x -= s.x;
        diff.y -= s.y;
        diff.z -= s.z;
        return diff;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator -= (const LaserBoy_3D_short& s)
    {
        x -= s.x;
        y -= s.y;
        z -= s.z;
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex operator *  (const LaserBoy_3D_short& s) const
    {
        LaserBoy_vertex product(*this);
        product.x = (short)(product.x * s.x);
        product.y = (short)(product.y * s.y);
        product.z = (short)(product.z * s.z);
        return product;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator *= (const LaserBoy_3D_short& s)
    {
        x = (short)(x * s.x);
        y = (short)(y * s.y);
        z = (short)(z * s.z);
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_real_vertex operator * (const LaserBoy_3D_double& f) const
    {
        LaserBoy_real_vertex product;
        product = as_real_vertex();
        product.x *= f.x;
        product.y *= f.y;
        product.z *= f.z;
        return product;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& operator *= (const LaserBoy_3D_double& f)
    {
        x = (short)(x * f.x);
        y = (short)(y * f.y);
        z = (short)(z * f.z);
        return *this;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex blend(const LaserBoy_vertex& v, double ratio)
    {
        LaserBoy_vertex blended;
        blended.unblank();
        blended.x = (short) (((1.0 - ratio) * x) + (ratio * v.x) + 0.5);
        blended.y = (short) (((1.0 - ratio) * y) + (ratio * v.y) + 0.5);
        blended.z = (short) (((1.0 - ratio) * z) + (ratio * v.z) + 0.5);
        blended.r = (u_char)(((1.0 - ratio) * r) + (ratio * v.r) + 0.5);
        blended.g = (u_char)(((1.0 - ratio) * g) + (ratio * v.g) + 0.5);
        blended.b = (u_char)(((1.0 - ratio) * b) + (ratio * v.b) + 0.5);
        return blended;
    }
    //------------------------------------------------------------------------
    bool operator == (const LaserBoy_vertex& p) const
    {
        if(    (x != p.x)
            || (y != p.y)
            || (z != p.z)
            || (r != p.r)
            || (g != p.g)
            || (b != p.b)
            || ((k & LASERBOY_BLANKING_BIT) != (p.k & LASERBOY_BLANKING_BIT))
            || (c != p.c)
          )
            return false;
        return true;
    }
    //------------------------------------------------------------------------
    bool operator != (const LaserBoy_vertex& p) const
    {
        if(    (x == p.x)
            && (y == p.y)
            && (z == p.z)
            && (r == p.r)
            && (g == p.g)
            && (b == p.b)
            && ((k & LASERBOY_BLANKING_BIT) == (p.k & LASERBOY_BLANKING_BIT))
            && (c == p.c)
          )
            return false;
        return true;
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex  operator - ()
    {
        LaserBoy_vertex negative;
        negative.x = -x;
        negative.y = -y;
        negative.z = -z;
        negative.r = r;
        negative.g = g;
        negative.b = b;
        negative.k = k;
        negative.c = c;
        return negative;
    }
    //------------------------------------------------------------------------
    bool is_equal_2D(const LaserBoy_vertex& p) const
    {
        if(    (x != p.x)
            || (y != p.y)
            || (r != p.r)
            || (g != p.g)
            || (b != p.b)
            || ((k & LASERBOY_BLANKING_BIT) != (p.k & LASERBOY_BLANKING_BIT))
            || (c != p.c)
          )
            return false;
        return true;
    }
    //------------------------------------------------------------------------
    bool from_ifstream_ild(std::ifstream& in, char format)
    {
        if(in.good())
        {
            char p, q;
            //----------------------------------------------------------------
            in.get(p);
            in.get(q);
            x = ((p & 0x00ff) << 8 | (q & 0x00ff));
            if(x == -32768) x = -32767;
            //----------------------------------------------------------------
            in.get(p);
            in.get(q);
            y = ((p & 0x00ff) << 8 | (q & 0x00ff));
            if(y == -32768) y = -32767;
            //----------------------------------------------------------------
            if(    format == LASERBOY_3D_FRAME
                || format == LASERBOY_3D_FRAME_RGB
              )
            {
                in.get(p);
                in.get(q);
                z = ((p & 0x00ff) << 8 | (q & 0x00ff));
                if(z == -32768) z = -32767;
            }
            //----------------------------------------------------------------
            k = (u_char)in.get();
            if(!(k & LASERBOY_BLANKING_BIT)) // it's not blank
                k = 0x00; // lit is 0x00!
            //----------------------------------------------------------------
            if(    format == LASERBOY_3D_FRAME
                || format == LASERBOY_2D_FRAME
              )
                c = (u_char)in.get();
            else if(    format == LASERBOY_3D_FRAME_RGB
                     || format == LASERBOY_2D_FRAME_RGB
                   )
            {
                b = (u_char)in.get();
                g = (u_char)in.get();
                r = (u_char)in.get();
            }
            //----------------------------------------------------------------
        }
        return in.good();
    }
    //------------------------------------------------------------------------
    void to_ofstream_ild(std::ofstream& out, char format, bool last_vertex) const
    {
        char k_ = k;
        if(k_ != LASERBOY_BLANKING_BIT)
            k_ = 0x00;
        k_ = k | ((last_vertex) ? (LASERBOY_LAST_VERTEX) : (0x00));
        //--------------------------------------------------------------------
        switch(format)
        {
            case LASERBOY_3D_FRAME:
                     out.put((char)((x & 0xff00) >> 8));
                     out.put((char) (x & 0x00ff)      );
                     out.put((char)((y & 0xff00) >> 8));
                     out.put((char) (y & 0x00ff)      );
                     out.put((char)((z & 0xff00) >> 8));
                     out.put((char) (z & 0x00ff)      );
                     out.put((char)k_);
                     out.put((char)c);
                     break;
            //----------------------------------------------------------------
            case LASERBOY_2D_FRAME:
                     out.put((char)((x & 0xff00) >> 8));
                     out.put((char) (x & 0x00ff)      );
                     out.put((char)((y & 0xff00) >> 8));
                     out.put((char) (y & 0x00ff)      );
                     out.put((char)k_);
                     out.put((char)c);
                     break;
            //----------------------------------------------------------------
            case LASERBOY_3D_FRAME_RGB:
                     out.put((char)((x & 0xff00) >> 8));
                     out.put((char) (x & 0x00ff)      );
                     out.put((char)((y & 0xff00) >> 8));
                     out.put((char) (y & 0x00ff)      );
                     out.put((char)((z & 0xff00) >> 8));
                     out.put((char) (z & 0x00ff)      );
                     out.put((char)k_);
                     out.put((char)b);
                     out.put((char)g);
                     out.put((char)r);
                     break;
            //----------------------------------------------------------------
            case LASERBOY_2D_FRAME_RGB:
                     out.put((char)((x & 0xff00) >> 8));
                     out.put((char) (x & 0x00ff)      );
                     out.put((char)((y & 0xff00) >> 8));
                     out.put((char) (y & 0x00ff)      );
                     out.put((char)k_);
                     out.put((char)b);
                     out.put((char)g);
                     out.put((char)r);
                     break;
        }
        return;
    }
    //------------------------------------------------------------------------
    bool from_ifstream_txt(std::ifstream& in,
                           const u_int&   group_type,
                           const u_int&   element_type,
                           int&           line_number
                          )
    {
        bool    got_data  = false;
        int     next_char = '\0';
        double  number;
        clear_to_token(in, next_char, line_number);
        if(    isdigit(next_char)
            || next_char == '.'
            || next_char == '-'
            || next_char == '+'
          )
        {
            if(group_type == LASERBOY_COLOR_TABLE)
            {
                clear();  // this vertex
                if(element_type == LASERBOY_RGB)
                {
                    if(get_next_number(in, number, line_number))
                    {
                        got_data = true;
                        if(number == -1) // it's blank
                        {
                            blank();
                            r = g = b = c = 0;
                        }
                        else
                        {
                            unblank();
                            r = (u_char)number;
                            if(get_next_number(in, number, line_number))
                            {
                                g = (u_char)number;
                                if(get_next_number(in, number, line_number))
                                    b = (u_char)number;
                            }
                            c = 0;
                        }
                    }
                } // end if(element_type == LASERBOY_RGB)
                else if(element_type == LASERBOY_HEX)
                {
                    if(get_next_hex(in, number, line_number))
                    {
                        got_data = true;
                        if(number == -1) // it's blank
                        {
                            blank();
                            r = g = b = c = 0;
                        }
                        else
                        {
                            unblank();
                            r = (((int)number) & 0x00ff0000) >> 16;
                            g = (((int)number) & 0x0000ff00) >>  8;
                            b = (((int)number) & 0x000000ff)      ;
                            c = 0;
                        }
                    }
                } // end else if(element_type == LASERBOY_HEX)
            } // end if(group_type == LASERBOY_COLOR_TABLE)
            //.......................................
            else // It is not a table. It's a frame!
            {
                if(get_next_number(in, number, line_number)) // 1
                {
                    got_data = true;
                    if(element_type == LASERBOY_UNIT)
                        x = (short)(number * LASERBOY_MAX_SHORT);
                    else
                        x = (short)(number);
                    if(get_next_number(in, number, line_number)) // 2
                    {
                        if(element_type == LASERBOY_UNIT)
                            y = (short)(number * LASERBOY_MAX_SHORT);
                        else
                            y = (short)(number);
                        // x and y are always the first 2 values.
                        //.................
                        // if there is a z
                        if(    group_type == LASERBOY_3D_FRAME_RGB
                            || group_type == LASERBOY_3D_FRAME_HEX
                            || group_type == LASERBOY_3D_FRAME_PALETTE
                            || group_type == LASERBOY_3D_FRAME_TABLE
                          )
                        {
                            if(get_next_number(in, number, line_number))
                            {
                                if(element_type == LASERBOY_UNIT)
                                    z = (short)(number * LASERBOY_MAX_SHORT);
                                else
                                    z = (short)(number);
                            }
                            else
                                return in.good();
                        }
                        else
                            z = 0;
                        //.................
                        // color values
                        // if there is an r g b
                        if(    (    (group_type == LASERBOY_3D_FRAME_RGB)
                                 || (group_type == LASERBOY_2D_FRAME_RGB)
                               )
                            && (get_next_number(in, number, line_number))
                          )
                        {
                            if(number == -1) // it's blank
                            {
                                blank();
                                r = g = b = c = 0;
                            }
                            else
                            {
                                unblank();
                                r = (u_char)number;
                                if(get_next_number(in, number, line_number))
                                {
                                    g = (u_char)number;
                                    if(get_next_number(in, number, line_number))
                                        b = (u_char)number;
                                }
                                c = 0;
                            }
                        }
                        //.................
                        // if there is a hex
                        else if(    (    (group_type == LASERBOY_3D_FRAME_HEX)
                                      || (group_type == LASERBOY_2D_FRAME_HEX)
                                    )
                                 && (get_next_hex(in, number, line_number))
                               )
                        {
                            if(number == -1) // it's blank
                            {
                                blank();
                                r = g = b = c = 0;
                            }
                            else
                            {
                                unblank();
                                r = (((int)number) & 0x00ff0000) >> 16;
                                g = (((int)number) & 0x0000ff00) >>  8;
                                b = (((int)number) & 0x000000ff)      ;
                                c = 0;
                            }
                        }
                        //.................
                        // else if there is c (color index to palette)
                        else if(    (    (group_type == LASERBOY_3D_FRAME_PALETTE)
                                      || (group_type == LASERBOY_2D_FRAME_PALETTE)
                                    )
                                 && (get_next_number(in, number, line_number))
                               )
                        {
                            if(number == -1) // it's blank
                            {
                                blank();
                                r = g = b = c = 0;
                            }
                            else
                            {
                                unblank();
                                c = (u_char)number;
                            }
                        }
                    } // end if(get_next_number(in, number, line_number)) // 2
                } // end if(get_next_number(in, number, line_number)) // 1
            } // end else of if(group_type == LASERBOY_COLOR_TABLE)
        }
        return (in.good() && got_data);
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex& blank  () { k |=  LASERBOY_BLANKING_BIT;  return *this; }
    LaserBoy_vertex& unblank() { k &= ~LASERBOY_BLANKING_BIT;  return *this; }
    //------------------------------------------------------------------------
    bool is_blank() const
    {
        return (bool)(k & LASERBOY_BLANKING_BIT);
    }
    //------------------------------------------------------------------------
    bool is_lit() const
    {
        return !is_blank();
    }
    //------------------------------------------------------------------------
    bool  is_black(int black_level) const
    {
        return (    ((r + g + b) <= black_level)
                 && is_lit()
               );
    }
    //------------------------------------------------------------------------
    bool  is_color(int black_level) const
    {
        return (    ((r + g + b) > black_level)
                 && is_lit()
               );
    }
    //------------------------------------------------------------------------
    bool  is_dark(int black_level) const
    {
        return (    ((r + g + b) <= black_level)
                 || is_blank()
               );
    }
    //------------------------------------------------------------------------
    void  clear()
    {
        x = 0;
        y = 0;
        z = 0;
        r = 0;
        g = 0;
        b = 0;
        k = LASERBOY_BLANKING_BIT;
        c = 0;
    }
    //------------------------------------------------------------------------
    LaserBoy_3D_short    as_3D_short   () const
    {
        return (LaserBoy_3D_short) (*this);
    }
    LaserBoy_color       as_color() const
    {
        return (LaserBoy_color)(*this);
    }
    LaserBoy_3D_double   as_3D_double  () const
    {
        return LaserBoy_3D_double  (x, y, z);
    }
    LaserBoy_real_vertex as_real_vertex() const
    {
        return LaserBoy_real_vertex(x, y, z, r, g, b, k, c);
    }
    //------------------------------------------------------------------------
    int color_of(int black_level) const
    {
        if(is_black(black_level))
            return 0;
        return   is_lit()
               ? (int)(   (r    << RED_BIT_SHIFT  )
                        | (g    << GREEN_BIT_SHIFT)
                        | (b    << BLUE_BIT_SHIFT )
                        | (0x00 << ALPHA_BIT_SHIFT)
                      )
               : -1; // is blank
    }
    //------------------------------------------------------------------------
    LaserBoy_vertex bit_masked(const u_int signal_bit_mask[7])
    {
        LaserBoy_vertex masked = *this;
        masked.x &=  short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_X]];
        masked.y &=  short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_Y]];
        masked.z &=  short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_Z]];
        masked.r &= (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_R]] >> 7);
        masked.g &= (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_G]] >> 7);
        masked.b &= (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_B]] >> 7);
        return masked;
    }


    //------------------------------------------------------------------------
    LaserBoy_3D_short bit_masked_position(const u_int signal_bit_mask[7]) const
    {
        LaserBoy_3D_short masked = *this;
        masked.x &= short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_X]];
        masked.y &= short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_Y]];
        masked.z &= short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_Z]];
        return masked;
    }
    //------------------------------------------------------------------------
    LaserBoy_color bit_masked_color(const u_int signal_bit_mask[7]) const
    {
        LaserBoy_color masked = *this;
        masked.r &= (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_R]] >> 7);
        masked.g &= (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_G]] >> 7);
        masked.b &= (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_B]] >> 7);
        return masked;
    }
    //------------------------------------------------------------------------
    void to_fstream_wav(std::fstream&          out,
                         LaserBoy_wav_header&  header,
                         const u_int            signal_bit_mask[7],
                         const bool&            end_of_frame,
                         const bool&            unique_frame,
                         const bool&            inverted = false
                        ) const
    {
        u_char rr    = 0x00,
               gg    = 0x00,
               bb    = 0x00;
        short  s16le = 0x0000;
        u_int  i;
        //--------------------------------------------------------------------
        if(is_color(0))
        {
            rr =    (header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_R)
                  ? (header.color_rescale_r[r])
                  : (r);
            gg =    (header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_G)
                  ? (header.color_rescale_g[g])
                  : (g);
            bb =    (header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_B)
                  ? (header.color_rescale_b[b])
                  : (b);
        }
        //--------------------------------------------------------------------
        for(i = 0; i < header.num_channels; i++)
        {
            switch((int)abs(header.signal_id[i]))
            {
                case LASERBOY_SIGNAL_X_POSITION:
                    s16le = x & short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_X]];
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_Y_POSITION:
                    s16le = y & short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_Y]];
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_Z_POSITION:
                    s16le = z & short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_Z]];
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_RED_ANALOG:
                    s16le = ((rr & (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_R]] >> 7)) << 7);
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_GREEN_ANALOG:
                    s16le = ((gg & (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_G]] >> 7)) << 7);
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_BLUE_ANALOG:
                    s16le = ((bb & (short_bit_mask[signal_bit_mask[LASERBOY_CHANNEL_B]] >> 7)) << 7);
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_MONO_TTL:
                    if(is_dark(0))
                        s16le = 0x0000;
                    else if(header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_I)
                        s16le = header.color_rescale_i[255];
                    else
                        s16le = LASERBOY_MAX_COLOR_SHORT;
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_MONO_OR_ANALOG:
                    if(header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_I)
                        s16le = header.color_rescale_i[rr | gg | bb];
                    else
                        s16le = (rr | gg | bb) << 7;
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_MONO_WEIGHTED_ANALOG:
                    if(header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_I)
                        s16le = header.color_rescale_i[((76 * rr) + (150 * gg) + (28 * bb)) / 254];
                    else
                        s16le = (((76 * rr) + (150 * gg) + (28 * bb)) / 254) << 7;
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_MONO_AVG_ANALOG:
                    if(header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_I)
                        s16le = header.color_rescale_i[(rr + gg + bb) / 3];
                    else
                        s16le = ((rr + gg + bb) / 3) << 7;
                    break;
                //------------------------------------------------------------
                case LASERBOY_SIGNAL_MONO_O_SCOPE:
                    if(header.LaserBoy_wav_mode & LASERBOY_COLOR_RESCALE_I)
                        s16le = header.color_rescale_i[(((76 * rr) + (150 * gg) + (28 * bb)) / 254)] -  - LASERBOY_MAX_COLOR_SHORT;
                    else
                        s16le = ((((76 * rr) + (150 * gg) + (28 * bb)) / 254) << 7) - LASERBOY_MAX_COLOR_SHORT;
                    break;
                //------------------------------------------------------------
                case LASERBOY_NO_SIGNAL:
                default:
                    s16le = 0x0000;
                    break;
            } // end switch((int)abs(header.signal_id[i]))
            //----------------------------------------------------------------
            if(end_of_frame && header.LSB_tag[i] == LASERBOY_LSB_END_OF_FRAME)
                s16le |= end_of_frame;
            if(unique_frame && header.LSB_tag[i] == LASERBOY_LSB_UNIQUE_FRAME)
                s16le |= unique_frame;
            //----------------------------------------------------------------
            if(inverted)
                s16le = -s16le;
            //----------------------------------------------------------------
            out.put( s16le & 0x00ff      );
            out.put((s16le & 0xff00) >> 8);
        } // end for(i = 0; i < header.num_channels; i++)
        header.num_samples++;
        return;
    } // end to_fstream_wav()
    //------------------------------------------------------------------------
    //------------------------------------------------------------------------
    // class data
    u_char  k, // blanking byte
            c; // color index into palette
};

//############################################################################
#endif

//############################################################################
//////////////////////////////////////////////////////////////////////////////
//############################################################################
