1 /************************************************************************
2 ************************************************************************
4 Copyright (C) 2003-2004 GRAME, Centre National de Creation Musicale
5 ---------------------------------------------------------------------
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 ************************************************************************
20 ************************************************************************/
23 #include "seqSchema.h"
29 enum {kHorDir
, kUpDir
, kDownDir
}; ///< directions of connections
31 static double computeHorzGap(schema
* a
, schema
* b
);
32 static int direction(const point
& a
, const point
& b
);
35 //----------------------------INTERFACE--------------------------------
38 * Make a sequential schema. May add cables to ensure the internal
39 * connections are between the same number of outputs and inputs.
40 * Compute an horizontal gap based on the number of upward and
41 * downward connections.
43 schema
* makeSeqSchema (schema
* s1
, schema
* s2
)
45 unsigned int o
= s1
->outputs();
46 unsigned int i
= s2
->inputs();
48 schema
* a
= (o
< i
) ? makeParSchema(s1
, makeCableSchema(i
-o
)) : s1
;
49 schema
* b
= (o
> i
) ? makeParSchema(s2
, makeCableSchema(o
-i
)) : s2
;
51 return new seqSchema(a
, b
, computeHorzGap(a
,b
));
56 //-----------------------IMPLEMENTATION------------------------------
59 * Constructor for a sequential schema (s1:s2). The components s1 and s2
60 * are supposed to be "compatible" (s1 : n->m and s2 : m->q)
62 seqSchema::seqSchema (schema
* s1
, schema
* s2
, double hgap
)
63 : schema( s1
->inputs(),
65 s1
->width() + hgap
+ s2
->width(),
66 max(s1
->height(), s2
->height()) ),
71 assert(s1
->outputs() == s2
->inputs());
75 //-----------------------placement------------------------------
79 * Place the two components horizontally with enough space
82 void seqSchema::place(double ox
, double oy
, int orientation
)
84 beginPlace(ox
, oy
, orientation
);
86 double y1
= max(0.0, 0.5*(fSchema2
->height() - fSchema1
->height()));
87 double y2
= max(0.0, 0.5*(fSchema1
->height() - fSchema2
->height()));
89 if (orientation
== kLeftRight
) {
90 fSchema1
->place(ox
, oy
+y1
, orientation
);
91 fSchema2
->place(ox
+fSchema1
->width()+fHorzGap
, oy
+y2
, orientation
);
93 fSchema2
->place(ox
, oy
+y2
, orientation
);
94 fSchema1
->place(ox
+fSchema2
->width()+fHorzGap
, oy
+y1
, orientation
);
101 * The input points are the input points of the first component
103 point
seqSchema::inputPoint(unsigned int i
) const
105 return fSchema1
->inputPoint(i
);
110 * The output points are the output points of the second component
112 point
seqSchema::outputPoint(unsigned int i
) const
114 return fSchema2
->outputPoint(i
);
119 //--------------------------drawing------------------------------
123 * Draw the two components as well as the internal wires
125 void seqSchema::draw(device
& dev
)
128 assert(fSchema1
->outputs() == fSchema2
->inputs());
132 //drawInternalWires(dev);
136 * Draw the two components as well as the internal wires
138 void seqSchema::collectTraits(collector
& c
)
141 assert(fSchema1
->outputs() == fSchema2
->inputs());
143 fSchema1
->collectTraits(c
);
144 fSchema2
->collectTraits(c
);
145 collectInternalWires(c
);
150 * Draw the internal wires aligning the vertical segments in
151 * a symetric way when possible.
154 void seqSchema::drawInternalWires(device
& dev
)
156 assert (fSchema1
->outputs() == fSchema2
->inputs());
158 const int N
= fSchema1
->outputs();
163 if (orientation() == kLeftRight
) {
164 // draw left right cables
165 for (int i
=0; i
<N
; i
++) {
166 point src
= fSchema1
->outputPoint(i
);
167 point dst
= fSchema2
->inputPoint(i
);
169 int d
= direction(src
,dst
);
171 // compute attributes of new direction
173 case kUpDir
: mx
= 0; dx
= dWire
; break;
174 case kDownDir
: mx
= fHorzGap
; dx
= -dWire
; break;
175 default : mx
= 0; dx
= 0; break;
179 // move in same direction
182 if (src
.y
== dst
.y
) {
183 // draw straight cable
184 dev
.trait(src
.x
, src
.y
, dst
.x
, dst
.y
);
187 dev
.trait(src
.x
, src
.y
, src
.x
+mx
, src
.y
);
188 dev
.trait(src
.x
+mx
, src
.y
, src
.x
+mx
, dst
.y
);
189 dev
.trait(src
.x
+mx
, dst
.y
, dst
.x
, dst
.y
);
194 // draw right left cables
195 for (int i
=0; i
<N
; i
++) {
196 point src
= fSchema1
->outputPoint(i
);
197 point dst
= fSchema2
->inputPoint(i
);
199 int d
= direction(src
,dst
);
201 // compute attributes of new direction
203 case kUpDir
: mx
= -fHorzGap
; dx
= dWire
; break;
204 case kDownDir
: mx
= 0; dx
= -dWire
; break;
205 default : mx
= 0; dx
= 0; break;
209 // move in same direction
212 if (src
.y
== dst
.y
) {
213 // draw straight cable
214 dev
.trait(src
.x
, src
.y
, dst
.x
, dst
.y
);
217 dev
.trait(src
.x
, src
.y
, src
.x
+mx
, src
.y
);
218 dev
.trait(src
.x
+mx
, src
.y
, src
.x
+mx
, dst
.y
);
219 dev
.trait(src
.x
+mx
, dst
.y
, dst
.x
, dst
.y
);
229 * Draw the internal wires aligning the vertical segments in
230 * a symetric way when possible.
233 void seqSchema::collectInternalWires(collector
& c
)
235 assert (fSchema1
->outputs() == fSchema2
->inputs());
237 const int N
= fSchema1
->outputs();
242 if (orientation() == kLeftRight
) {
243 // draw left right cables
244 for (int i
=0; i
<N
; i
++) {
245 point src
= fSchema1
->outputPoint(i
);
246 point dst
= fSchema2
->inputPoint(i
);
248 int d
= direction(src
,dst
);
250 // compute attributes of new direction
252 case kUpDir
: mx
= 0; dx
= dWire
; break;
253 case kDownDir
: mx
= fHorzGap
; dx
= -dWire
; break;
254 default : mx
= 0; dx
= 0; break;
258 // move in same direction
261 if (src
.y
== dst
.y
) {
262 // draw straight cable
263 c
.addTrait(trait(point(src
.x
, src
.y
), point(dst
.x
, dst
.y
)));
266 c
.addTrait(trait(point(src
.x
, src
.y
), point(src
.x
+mx
, src
.y
)));
267 c
.addTrait(trait(point(src
.x
+mx
, src
.y
), point(src
.x
+mx
, dst
.y
)));
268 c
.addTrait(trait(point(src
.x
+mx
, dst
.y
), point(dst
.x
, dst
.y
)));
273 // draw right left cables
274 for (int i
=0; i
<N
; i
++) {
275 point src
= fSchema1
->outputPoint(i
);
276 point dst
= fSchema2
->inputPoint(i
);
278 int d
= direction(src
,dst
);
280 // compute attributes of new direction
282 case kUpDir
: mx
= -fHorzGap
; dx
= dWire
; break;
283 case kDownDir
: mx
= 0; dx
= -dWire
; break;
284 default : mx
= 0; dx
= 0; break;
288 // move in same direction
291 if (src
.y
== dst
.y
) {
292 // draw straight cable
293 c
.addTrait(trait(point(src
.x
, src
.y
), point(dst
.x
, dst
.y
)));
296 c
.addTrait(trait(point(src
.x
, src
.y
), point(src
.x
+mx
, src
.y
)));
297 c
.addTrait(trait(point(src
.x
+mx
, src
.y
), point(src
.x
+mx
, dst
.y
)));
298 c
.addTrait(trait(point(src
.x
+mx
, dst
.y
), point(dst
.x
, dst
.y
)));
305 //--------------------------helpers------------------------------
310 * Compute the direction of a connection. Note that
311 * Y axis goes from top to bottom
313 static int direction(const point
& a
, const point
& b
)
315 if (a
.y
> b
.y
) return kUpDir
; // upward connections
316 if (a
.y
< b
.y
) return kDownDir
; // downward connection
317 return kHorDir
; // horizontal connections
321 * Compute the horizontal gap needed to draw the internal wires.
322 * It depends on the largest group of connections that go in the same
325 static double computeHorzGap(schema
* a
, schema
* b
)
327 assert(a
->outputs() == b
->inputs());
329 if (a
->outputs() == 0) {
332 // store here the size of the largest group for each direction
333 int MaxGroupSize
[3]; for(int i
=0; i
<3; i
++) MaxGroupSize
[i
]=0;
335 // place a and b to have valid connection points
336 double ya
= max(0.0, 0.5*(b
->height() - a
->height()));
337 double yb
= max(0.0, 0.5*(a
->height() - b
->height()));
338 a
->place(0,ya
,kLeftRight
);
339 b
->place(0,yb
,kLeftRight
);
341 // init current group direction and size
342 int gdir
= direction(a
->outputPoint(0), b
->inputPoint(0));
345 // analyze direction of remaining points
346 for (unsigned int i
=1; i
<a
->outputs(); i
++) {
347 int d
= direction(a
->outputPoint(i
), b
->inputPoint(i
));
351 if (gsize
> MaxGroupSize
[gdir
]) MaxGroupSize
[gdir
]=gsize
;
357 // update for last group
358 if (gsize
> MaxGroupSize
[gdir
]) MaxGroupSize
[gdir
]=gsize
;
360 // the gap required for the connections
361 return dWire
* max(MaxGroupSize
[kUpDir
],MaxGroupSize
[kDownDir
]);