--- /dev/null
+/************************************************************************
+ ************************************************************************
+ 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.
+ */
+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)
+ */
+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
+ */
+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
+ */
+point seqSchema::inputPoint(unsigned int i) const
+{
+ return fSchema1->inputPoint(i);
+}
+
+
+/**
+ * The output points are the output points of the second component
+ */
+point seqSchema::outputPoint(unsigned int i) const
+{
+ return fSchema2->outputPoint(i);
+}
+
+
+
+//--------------------------drawing------------------------------
+
+
+/**
+ * Draw the two components as well as the internal wires
+ */
+void seqSchema::draw(device& dev)
+{
+ assert(placed());
+ assert(fSchema1->outputs() == fSchema2->inputs());
+
+ fSchema1->draw(dev);
+ fSchema2->draw(dev);
+ //drawInternalWires(dev);
+}
+
+/**
+ * Draw the two components as well as the internal wires
+ */
+void seqSchema::collectTraits(collector& c)
+{
+ assert(placed());
+ assert(fSchema1->outputs() == fSchema2->inputs());
+
+ fSchema1->collectTraits(c);
+ fSchema2->collectTraits(c);
+ collectInternalWires(c);
+}
+
+
+/**
+ * Draw the internal wires aligning the vertical segments in
+ * a symetric way when possible.
+ */
+
+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);
+ }
+
+ }
+ }
+}
+
+
+
+/**
+ * Draw the internal wires aligning the vertical segments in
+ * a symetric way when possible.
+ */
+
+void seqSchema::collectInternalWires(collector& c)
+{
+ 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
+ c.addTrait(trait(point(src.x, src.y), point(dst.x, dst.y)));
+ } else {
+ // draw zizag cable
+ c.addTrait(trait(point(src.x, src.y), point(src.x+mx, src.y)));
+ c.addTrait(trait(point(src.x+mx, src.y), point(src.x+mx, dst.y)));
+ c.addTrait(trait(point(src.x+mx, dst.y), point(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
+ c.addTrait(trait(point(src.x, src.y), point(dst.x, dst.y)));
+ } else {
+ // draw zizag cable
+ c.addTrait(trait(point(src.x, src.y), point(src.x+mx, src.y)));
+ c.addTrait(trait(point(src.x+mx, src.y), point(src.x+mx, dst.y)));
+ c.addTrait(trait(point(src.x+mx, dst.y), point(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]);
+ }
+}