Listing 3: Compiling graphical primitives to send an accelerator card


class LineArray
{
  BView& my_view;
  BPoint curr_point;
  rgb_color curr_color;
  
  class LineParm // A compilation "unit" describing one line segment
  {
    BPoint from, to;
    rgb_color color;
  public:
    LineParm(void) {} // this is an incomplete constructor: careful!
    LineParm(const BPoint _from, const BPoint _to, rgb_color _color)
        : from(_from), to(_to), color(_color) {}
    BPoint q_from(void) const { return from; }
    BPoint q_to(void) const   { return to; }
    void doAddLine(BView& view) const
        { view.AddLine(from,to,color); }
    void offset_by(const BPoint pt) { from += pt; to += pt; }
    friend ostream& operator << (ostream& os, LineParm& line_parm);
  };
  
// An array of line segments...
  enum {allocation_quantum =5};
  LineParm * current_path;
  int path_length;
  int allocated;
  LineParm path_preallocated[5];
  
  void append_path(const BPoint from, const BPoint to);
  
public:

  // "Compilation" classes
  class line_to
  {
    friend class LineArray;
    BPoint to_point;
  public:
    line_to(const BPoint& pt) : to_point(pt) {}
    line_to(const float x, const float y) : to_point(x,y) {}
  };
  
  class rline_to
  {
    friend class LineArray;
    BPoint to_point;
  public:
    rline_to(const BPoint& pt) : to_point(pt) {}
    rline_to(const float x, const float y) : to_point(x,y) {}
  };

  class line_from_to
  {
    friend class LineArray;
    BPoint from_point;
    BPoint to_point;
  public:
    line_from_to(const BPoint& _from, const BPoint& _to)
        : from_point(_from), to_point(_to) {}
   };
   
   
  // inline constructor/destructor
  LineArray(BView& view) : my_view(view), curr_point(0,0),
    curr_color(view.HighColor()),
    current_path(path_preallocated),
    allocated(
      sizeof(path_preallocated)/sizeof(path_preallocated[0])),
    path_length(0)
      {}
  ~LineArray(void) { if( current_path != path_preallocated )
                       delete current_path; }
  // Setting the current point of a path  
  LineArray& operator << (const BPoint point)
    { curr_point = point; return *this;}
  // draw an outline of a rect
  LineArray& operator << (const BRect point);  
  // Adding line segments to the path
  LineArray& operator << (const line_to& lineto)
    { append_path(curr_point, lineto.to_point);
      curr_point = lineto.to_point; return *this;}
  LineArray& operator << (const rline_to& rlineto);
  LineArray& operator << (const line_from_to& lineft)
    { append_path(lineft.from_point, lineft.to_point);
      curr_point = lineft.to_point; return *this; }

  void close_path(void);    // Connect curr_point to the first pt
  void stroke(void);        // Stroke the path
  // Manipulator procedures...

  typedef LineArray& (*LineArrayManip)(LineArray&);
  LineArray& operator << (const LineArrayManip manip)
    { return manip(*this); }
  friend LineArray& endl(LineArray& larray)
    { larray.stroke(); return larray; }        
  // Dealing with the whole path
  void offset_by(const BPoint pt);
  
  // Print the whole path (in a human-readable form)
  friend ostream& operator << (ostream& os, LineArray& line_array);
};

// Add a new segment to the path
void LineArray::append_path(const BPoint from, const BPoint to)
{
  if( ++path_length > allocated )
  {
    LineParm * old_path = current_path;
    current_path = new LineParm[allocated =
      path_length+allocation_quantum];
    cout << "reallocating path to " << allocated << " elems" << endl;
    memcpy((void*)current_path,(void*)old_path,
           sizeof(current_path[0])*(path_length-1));
    if( old_path != path_preallocated )
      delete old_path;
  }
  current_path[path_length-1] = LineParm(from,to,curr_color);
}

LineArray& LineArray::operator << (const rline_to& rlineto)
{
 BPoint to = curr_point + rlineto.to_point;
 append_path(curr_point, to);
 curr_point = to;
 return *this;
}

// Connect the curr_point to the first pt
void LineArray::close_path(void)
{
  assert( path_length > 0 );
  if( path_length == 1 )        // Nothing to close
    return;                
  append_path(curr_point,current_path[0].q_from());
}

// Stroke the path
// This is the only place where BeginLineArray() etc. are
// called. And they're called in the right sequence!
void LineArray::stroke(void)
{
  assert( path_length > 0 );
  my_view.BeginLineArray(path_length);
  for(register int i=0; i<path_length; i++)
     current_path[i].doAddLine(my_view);
  my_view.EndLineArray();
}

// Apply the simplest transform to the path
void LineArray::offset_by(const BPoint pt)
{
  assert( path_length > 0 );
  for(register int i=0; i<path_length; i++)
     current_path[i].offset_by(pt);
  curr_point += pt;
}

// Print the whole path (in a human-readable form)

ostream& operator << (ostream& os, LineArray::LineParm& line_parm)
{ return os << "line segment: from " << line_parm.from << " to " 
            << line_parm.to << endl; }

ostream& operator << (ostream& os, LineArray& line_array)
{
  if( line_array.path_length == 0 )
   return os << "empty path" << endl;
   
  os << "Line path of " << line_array.path_length
     << " line segments" << endl;
  for(register int i=0; i<line_array.path_length; i++)
     os << line_array.current_path[i];
  return os << endl;
}

//------------------------------------------------------------------
//             Here's how everything is used

static void test_line_array(void)
{
  BView view;
  LineArray lines(view);
  lines << BPoint(10,10);
  lines << LineArray::line_to(10,20);
  lines << LineArray::rline_to(10,0);
  lines << LineArray::rline_to(0,-10);
  lines.close_path();        // connect to the first point

  lines.stroke();
                  // Add more segments to the old path...
  lines << BPoint(20,20) << LineArray::rline_to(-10,-10);
  lines << LineArray::line_from_to(BPoint(10,20),BPoint(20,10));
  lines.offset_by(BPoint(100,100));
  lines.stroke();
  
  cout << "The path last stroked was " << lines << endl; 
  
}

static void test_line_array1(void)
{
  BView view;
  cout << "\n\ntesting an one-liner " << endl;
  LineArray(view) << LineArray::line_from_to(BPoint(0,0),
                                             BPoint(20,10)) << endl;
}
//End of File