Initial import.
[Faustine.git] / interpretor / faust-0.9.47mr3 / compiler / draw / schema / seqSchema.cpp
1 /************************************************************************
2 ************************************************************************
3 FAUST compiler
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.
10
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.
15
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 ************************************************************************/
21
22
23 #include "seqSchema.h"
24 #include <iostream>
25 #include <assert.h>
26
27 using namespace std;
28
29 enum {kHorDir, kUpDir, kDownDir}; ///< directions of connections
30
31 static double computeHorzGap(schema* a, schema* b);
32 static int direction(const point& a, const point& b);
33
34
35 //----------------------------INTERFACE--------------------------------
36
37 /**
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.
42 */
43 schema * makeSeqSchema (schema* s1, schema* s2)
44 {
45 unsigned int o = s1->outputs();
46 unsigned int i = s2->inputs();
47
48 schema* a = (o < i) ? makeParSchema(s1, makeCableSchema(i-o)) : s1;
49 schema* b = (o > i) ? makeParSchema(s2, makeCableSchema(o-i)) : s2;
50
51 return new seqSchema(a, b, computeHorzGap(a,b));
52 }
53
54
55
56 //-----------------------IMPLEMENTATION------------------------------
57
58 /**
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)
61 */
62 seqSchema::seqSchema (schema* s1, schema* s2, double hgap)
63 : schema( s1->inputs(),
64 s2->outputs(),
65 s1->width() + hgap + s2->width(),
66 max(s1->height(), s2->height()) ),
67 fSchema1(s1),
68 fSchema2(s2),
69 fHorzGap(hgap)
70 {
71 assert(s1->outputs() == s2->inputs());
72 }
73
74
75 //-----------------------placement------------------------------
76
77
78 /**
79 * Place the two components horizontally with enough space
80 * for the connections
81 */
82 void seqSchema::place(double ox, double oy, int orientation)
83 {
84 beginPlace(ox, oy, orientation);
85
86 double y1 = max(0.0, 0.5*(fSchema2->height() - fSchema1->height()));
87 double y2 = max(0.0, 0.5*(fSchema1->height() - fSchema2->height()));
88
89 if (orientation == kLeftRight) {
90 fSchema1->place(ox, oy+y1, orientation);
91 fSchema2->place(ox+fSchema1->width()+fHorzGap, oy+y2, orientation);
92 } else {
93 fSchema2->place(ox, oy+y2, orientation);
94 fSchema1->place(ox+fSchema2->width()+fHorzGap, oy+y1, orientation);
95 }
96 endPlace();
97 }
98
99
100 /**
101 * The input points are the input points of the first component
102 */
103 point seqSchema::inputPoint(unsigned int i) const
104 {
105 return fSchema1->inputPoint(i);
106 }
107
108
109 /**
110 * The output points are the output points of the second component
111 */
112 point seqSchema::outputPoint(unsigned int i) const
113 {
114 return fSchema2->outputPoint(i);
115 }
116
117
118
119 //--------------------------drawing------------------------------
120
121
122 /**
123 * Draw the two components as well as the internal wires
124 */
125 void seqSchema::draw(device& dev)
126 {
127 assert(placed());
128 assert(fSchema1->outputs() == fSchema2->inputs());
129
130 fSchema1->draw(dev);
131 fSchema2->draw(dev);
132 //drawInternalWires(dev);
133 }
134
135 /**
136 * Draw the two components as well as the internal wires
137 */
138 void seqSchema::collectTraits(collector& c)
139 {
140 assert(placed());
141 assert(fSchema1->outputs() == fSchema2->inputs());
142
143 fSchema1->collectTraits(c);
144 fSchema2->collectTraits(c);
145 collectInternalWires(c);
146 }
147
148
149 /**
150 * Draw the internal wires aligning the vertical segments in
151 * a symetric way when possible.
152 */
153
154 void seqSchema::drawInternalWires(device& dev)
155 {
156 assert (fSchema1->outputs() == fSchema2->inputs());
157
158 const int N = fSchema1->outputs();
159 double dx = 0;
160 double mx = 0;
161 int dir =-1;
162
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);
168
169 int d = direction(src,dst);
170 if (d != dir) {
171 // compute attributes of new direction
172 switch (d) {
173 case kUpDir : mx = 0; dx = dWire; break;
174 case kDownDir : mx = fHorzGap; dx = -dWire; break;
175 default : mx = 0; dx = 0; break;
176 }
177 dir = d;
178 } else {
179 // move in same direction
180 mx = mx +dx;
181 }
182 if (src.y == dst.y) {
183 // draw straight cable
184 dev.trait(src.x, src.y, dst.x, dst.y);
185 } else {
186 // draw zizag cable
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);
190 }
191
192 }
193 } else {
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);
198
199 int d = direction(src,dst);
200 if (d != dir) {
201 // compute attributes of new direction
202 switch (d) {
203 case kUpDir : mx = -fHorzGap; dx = dWire; break;
204 case kDownDir : mx = 0; dx = -dWire; break;
205 default : mx = 0; dx = 0; break;
206 }
207 dir = d;
208 } else {
209 // move in same direction
210 mx = mx +dx;
211 }
212 if (src.y == dst.y) {
213 // draw straight cable
214 dev.trait(src.x, src.y, dst.x, dst.y);
215 } else {
216 // draw zizag cable
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);
220 }
221
222 }
223 }
224 }
225
226
227
228 /**
229 * Draw the internal wires aligning the vertical segments in
230 * a symetric way when possible.
231 */
232
233 void seqSchema::collectInternalWires(collector& c)
234 {
235 assert (fSchema1->outputs() == fSchema2->inputs());
236
237 const int N = fSchema1->outputs();
238 double dx = 0;
239 double mx = 0;
240 int dir =-1;
241
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);
247
248 int d = direction(src,dst);
249 if (d != dir) {
250 // compute attributes of new direction
251 switch (d) {
252 case kUpDir : mx = 0; dx = dWire; break;
253 case kDownDir : mx = fHorzGap; dx = -dWire; break;
254 default : mx = 0; dx = 0; break;
255 }
256 dir = d;
257 } else {
258 // move in same direction
259 mx = mx +dx;
260 }
261 if (src.y == dst.y) {
262 // draw straight cable
263 c.addTrait(trait(point(src.x, src.y), point(dst.x, dst.y)));
264 } else {
265 // draw zizag cable
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)));
269 }
270
271 }
272 } else {
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);
277
278 int d = direction(src,dst);
279 if (d != dir) {
280 // compute attributes of new direction
281 switch (d) {
282 case kUpDir : mx = -fHorzGap; dx = dWire; break;
283 case kDownDir : mx = 0; dx = -dWire; break;
284 default : mx = 0; dx = 0; break;
285 }
286 dir = d;
287 } else {
288 // move in same direction
289 mx = mx +dx;
290 }
291 if (src.y == dst.y) {
292 // draw straight cable
293 c.addTrait(trait(point(src.x, src.y), point(dst.x, dst.y)));
294 } else {
295 // draw zizag cable
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)));
299 }
300
301 }
302 }
303 }
304
305 //--------------------------helpers------------------------------
306
307
308
309 /**
310 * Compute the direction of a connection. Note that
311 * Y axis goes from top to bottom
312 */
313 static int direction(const point& a, const point& b)
314 {
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
318 }
319
320 /**
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
323 * direction.
324 */
325 static double computeHorzGap(schema* a, schema* b)
326 {
327 assert(a->outputs() == b->inputs());
328
329 if (a->outputs() == 0) {
330 return 0;
331 } else {
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;
334
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);
340
341 // init current group direction and size
342 int gdir = direction(a->outputPoint(0), b->inputPoint(0));
343 int gsize = 1;
344
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));
348 if (d == gdir) {
349 gsize++;
350 } else {
351 if (gsize > MaxGroupSize[gdir]) MaxGroupSize[gdir]=gsize;
352 gsize = 1;
353 gdir = d;
354 }
355 }
356
357 // update for last group
358 if (gsize > MaxGroupSize[gdir]) MaxGroupSize[gdir]=gsize;
359
360 // the gap required for the connections
361 return dWire * max(MaxGroupSize[kUpDir],MaxGroupSize[kDownDir]);
362 }
363 }