Logo Search packages:      
Sourcecode: faust version File versions  Download package

seqSchema.cpp

/************************************************************************
 ************************************************************************
    FAUST compiler
      Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
    ---------------------------------------------------------------------
    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 ************************************************************************
 ************************************************************************/


#include "seqSchema.h"
#include <iostream>
#include <assert.h>

using namespace std;

enum {kHorDir, kUpDir, kDownDir}; ///< directions of connections

static double computeHorzGap(schema* a, schema* b);
static int direction(const point& a, const point& b);


//----------------------------INTERFACE--------------------------------

/**
 * Make a sequential schema. May add cables to ensure the internal
 * connections are between the same number of outputs and inputs.
 * Compute an horizontal gap based on the number of upward and
 * downward connections.
 */
00043 schema * makeSeqSchema (schema* s1, schema* s2)
{
      unsigned int o = s1->outputs();
      unsigned int i = s2->inputs();

      schema* a = (o < i) ? makeParSchema(s1, makeCableSchema(i-o)) : s1;
      schema* b = (o > i) ? makeParSchema(s2, makeCableSchema(o-i)) : s2;

      return new seqSchema(a, b, computeHorzGap(a,b));
}



//-----------------------IMPLEMENTATION------------------------------

/**
 * Constructor for a sequential schema (s1:s2). The components s1 and s2
 * are supposed to be "compatible" (s1 : n->m and s2 : m->q)
 */
00062 seqSchema::seqSchema (schema* s1, schema* s2, double hgap)
      :     schema(     s1->inputs(),
                        s2->outputs(),
                        s1->width() + hgap + s2->width(),
                        max(s1->height(), s2->height()) ),
            fSchema1(s1),
            fSchema2(s2),
            fHorzGap(hgap)
{
      assert(s1->outputs() == s2->inputs());
}


//-----------------------placement------------------------------


/**
 * Place the two components horizontally with enough space
 * for the connections
 */
00082 void seqSchema::place(double ox, double oy, int orientation)
{
      beginPlace(ox, oy, orientation);

      double y1 = max(0.0, 0.5*(fSchema2->height() - fSchema1->height()));
      double y2 = max(0.0, 0.5*(fSchema1->height() - fSchema2->height()));

      if (orientation == kLeftRight) {
            fSchema1->place(ox, oy+y1, orientation);
            fSchema2->place(ox+fSchema1->width()+fHorzGap, oy+y2, orientation);
      } else {
            fSchema2->place(ox, oy+y2, orientation);
            fSchema1->place(ox+fSchema2->width()+fHorzGap, oy+y1, orientation);
      }
      endPlace();
}


/**
 * The input points are the input points of the first component
 */
00103 point seqSchema::inputPoint(unsigned int i) const
{
      return fSchema1->inputPoint(i);
}


/**
 * The output points are the output points of the second component
 */
00112 point seqSchema::outputPoint(unsigned int i) const
{
      return fSchema2->outputPoint(i);
}



//--------------------------drawing------------------------------


/**
 * Draw the two components as well as the internal wires
 */
00125 void seqSchema::draw(device& dev)
{
      assert(placed());
      assert(fSchema1->outputs() == fSchema2->inputs());

      fSchema1->draw(dev);
      fSchema2->draw(dev);
      drawInternalWires(dev);
}


/**
 * Draw the internal wires aligning the vertical segments in
 * a symetric way when possible.
 */

00141 void seqSchema::drawInternalWires(device& dev)
{
      assert (fSchema1->outputs() == fSchema2->inputs());

      const int   N     = fSchema1->outputs();
      double            dx    = 0;
      double            mx    = 0;
      int               dir   =-1;

      if (orientation() == kLeftRight) {
            // draw left right cables
            for (int i=0; i<N; i++) {
                  point src = fSchema1->outputPoint(i);
                  point dst = fSchema2->inputPoint(i);
                  int d = direction(src,dst);
                  if (d != dir) {
                        // compute attributes of new direction
                        switch (d) {
                              case kUpDir       : mx = 0; dx = dWire; break;
                              case kDownDir     : mx = fHorzGap; dx = -dWire; break;
                              default                 : mx = 0; dx = 0; break;
                        }
                        dir = d;
                  } else {
                        // move in same direction
                        mx = mx +dx;
                  }
                  if (src.y == dst.y) {
                        // draw straight cable
                        dev.trait(src.x, src.y, dst.x, dst.y);
                  } else {
                        // draw zizag cable
                        dev.trait(src.x, src.y, src.x+mx, src.y);
                        dev.trait(src.x+mx, src.y, src.x+mx, dst.y);
                        dev.trait(src.x+mx, dst.y, dst.x, dst.y);
                  }
            }
      } else {
            // draw right left cables
            for (int i=0; i<N; i++) {
                  point src = fSchema1->outputPoint(i);
                  point dst = fSchema2->inputPoint(i);
                  int d = direction(src,dst);
                  if (d != dir) {
                        // compute attributes of new direction
                        switch (d) {
                              case kUpDir       : mx = -fHorzGap; dx = dWire; break;
                              case kDownDir     : mx = 0; dx = -dWire; break;
                              default                 : mx = 0; dx = 0; break;
                        }
                        dir = d;
                  } else {
                        // move in same direction
                        mx = mx +dx;
                  }
                  if (src.y == dst.y) {
                        // draw straight cable
                        dev.trait(src.x, src.y, dst.x, dst.y);
                  } else {
                        // draw zizag cable
                        dev.trait(src.x, src.y, src.x+mx, src.y);
                        dev.trait(src.x+mx, src.y, src.x+mx, dst.y);
                        dev.trait(src.x+mx, dst.y, dst.x, dst.y);
                  }
            }
      }
}


//--------------------------helpers------------------------------



/**
 * Compute the direction of a connection. Note that
 * Y axis goes from top to bottom
 */
static int direction(const point& a, const point& b)
{
      if (a.y > b.y) return kUpDir;             // upward connections
      if (a.y < b.y) return kDownDir;     // downward connection
      return kHorDir;                                 // horizontal connections
}

/**
 * Compute the horizontal gap needed to draw the internal wires.
 * It depends on the largest group of connections that go in the same
 * direction.
 */
static double computeHorzGap(schema* a, schema* b)
{
      assert(a->outputs() == b->inputs());

      if (a->outputs() == 0) {
            return 0;
      } else {
            // store here the size of the largest group for each direction
            int   MaxGroupSize[3]; for(int i=0; i<3; i++) MaxGroupSize[i]=0;

            // place a and b to have valid connection points
            double ya = max(0.0, 0.5*(b->height() - a->height()));
            double yb = max(0.0, 0.5*(a->height() - b->height()));
            a->place(0,ya,kLeftRight);
            b->place(0,yb,kLeftRight);

            // init current group direction and size
            int   gdir  = direction(a->outputPoint(0), b->inputPoint(0));
            int   gsize       = 1;

            // analyze direction of remaining points
            for (unsigned int i=1; i<a->outputs(); i++) {
                  int d = direction(a->outputPoint(i), b->inputPoint(i));
                  if (d == gdir) {
                        gsize++;
                  } else {
                        if (gsize > MaxGroupSize[gdir])  MaxGroupSize[gdir]=gsize;
                        gsize = 1;
                        gdir = d;
                  }
            }

            // update for last group
            if (gsize > MaxGroupSize[gdir])  MaxGroupSize[gdir]=gsize;

            // the gap required for the connections
            return dWire * max(MaxGroupSize[kUpDir],MaxGroupSize[kDownDir]);
      }
}

Generated by  Doxygen 1.6.0   Back to index