~ubuntu-branches/ubuntu/intrepid/tcm/intrepid

« back to all changes in this revision

Viewing changes to src/ui/lineend.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2003-07-03 20:08:21 UTC
  • Revision ID: james.westby@ubuntu.com-20030703200821-se4xtqx25e5miczi
Tags: upstream-2.20
ImportĀ upstreamĀ versionĀ 2.20

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
////////////////////////////////////////////////////////////////////////////////
 
2
//
 
3
// This file is part of Toolkit for Conceptual Modeling (TCM).
 
4
// (c) copyright 1998, Vrije Universiteit Amsterdam.
 
5
// Author: Frank Dehne (frank@cs.vu.nl).
 
6
//
 
7
// TCM is free software; you can redistribute it and/or modify
 
8
// it under the terms of the GNU General Public License as published by
 
9
// the Free Software Foundation; either version 2 of the License, or
 
10
// (at your option) any later version.
 
11
//
 
12
// TCM is distributed in the hope that it will be useful,
 
13
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
15
// GNU General Public License for more details.
 
16
//
 
17
// You should have received a copy of the GNU General Public License
 
18
// along with TCM; if not, write to the Free Software
 
19
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 
20
// 02111-1307, USA.
 
21
////////////////////////////////////////////////////////////////////////////////
 
22
#include "lineend.h"
 
23
#include "lstring.h"
 
24
#include "util.h"
 
25
#include "grafport.h"
 
26
#include <math.h>
 
27
#include <stdlib.h>
 
28
 
 
29
const int LineEnd::CIRCLE_WIDTH = 12;
 
30
const int LineEnd::TRIANGLE_LENGTH = 14;
 
31
const int LineEnd::TRIANGLE_WIDTH = 14;
 
32
const int LineEnd::DIAMOND_LENGTH = 20;
 
33
const int LineEnd::DIAMOND_WIDTH = 14;
 
34
const int LineEnd::OPEN_ARROW_LENGTH = 8;
 
35
const int LineEnd::OPEN_ARROW_WIDTH = 10;
 
36
const int LineEnd::FILLED_ARROW_LENGTH = 10;
 
37
const int LineEnd::FILLED_ARROW_ANGLE = 90;
 
38
const double LineEnd::FILLED_ARROW_INSET = 0.60;
 
39
 
 
40
const TypeName LineEnd::typeNames[] = {
 
41
        {EMPTY, "Empty"},
 
42
        {OPEN_ARROW, "OpenArrow"},
 
43
        {FILLED_ARROW, "FilledArrow"},
 
44
        {DOUBLE_OPEN_ARROW, "DoubleOpenArrow"},
 
45
        {DOUBLE_FILLED_ARROW, "DoubleFilledArrow"},
 
46
        {WHITE_CIRCLE, "WhiteCircle"},
 
47
        {BLACK_CIRCLE, "BlackCircle"},
 
48
        {WHITE_DIAMOND, "WhiteDiamond"},
 
49
        {BLACK_DIAMOND, "BlackDiamond"},
 
50
        {WHITE_TRIANGLE, "WhiteTriangle"},
 
51
        {BLACK_TRIANGLE, "BlackTriangle"},
 
52
        {0, ""}
 
53
};
 
54
 
 
55
LineEnd::LineEnd() {
 
56
        type = EMPTY;
 
57
        length = 0;
 
58
        width = 0;
 
59
}
 
60
 
 
61
LineEnd::LineEnd(Type t) {
 
62
        SetType(t);
 
63
}
 
64
 
 
65
void LineEnd::SetType(Type t) {
 
66
        type = t;
 
67
        SetSizes();
 
68
}
 
69
 
 
70
void LineEnd::SetSizes() {
 
71
        if (type == WHITE_CIRCLE || type == BLACK_CIRCLE)
 
72
                length = width = CIRCLE_WIDTH;
 
73
        else if (type == WHITE_TRIANGLE || type == BLACK_TRIANGLE) {
 
74
                length = TRIANGLE_LENGTH;
 
75
                width = TRIANGLE_WIDTH;
 
76
        }
 
77
        else if (type == WHITE_DIAMOND || type == BLACK_DIAMOND) {
 
78
                length = DIAMOND_LENGTH;
 
79
                width = DIAMOND_WIDTH;
 
80
        }
 
81
        else if (type == OPEN_ARROW || type == DOUBLE_OPEN_ARROW) {
 
82
                length = OPEN_ARROW_LENGTH;
 
83
                width = OPEN_ARROW_WIDTH;
 
84
        }
 
85
        else if (type == FILLED_ARROW || type == DOUBLE_FILLED_ARROW) {
 
86
                length = width = FILLED_ARROW_LENGTH;
 
87
        }
 
88
        else {
 
89
                length = 0;
 
90
                width = 0;
 
91
        }
 
92
}
 
93
 
 
94
void LineEnd::SetLength(int l) {
 
95
        length=l;
 
96
        if (type==WHITE_CIRCLE || type == BLACK_CIRCLE)
 
97
                width = l;
 
98
}
 
99
                
 
100
void LineEnd::SetWidth(int w) {
 
101
        width = w;
 
102
}
 
103
 
 
104
void LineEnd::Type2String(LineEnd::Type l, string *s) {
 
105
        if (1 <= (int)l && (int)l <= NUMTYPES)
 
106
                *s = typeNames[l-1].name;
 
107
        else
 
108
                *s = "Empty";
 
109
}
 
110
 
 
111
LineEnd::Type LineEnd::String2Type(const string *s) {
 
112
        const TypeName *n = &typeNames[0];
 
113
        while (n->code != 0) {
 
114
                if (*s %= n->name)
 
115
                        return (Type) n->code;
 
116
                n++;
 
117
        }
 
118
        return EMPTY;
 
119
}
 
120
 
 
121
void LineEnd::Draw(Grafport *g, const DPoint *from, 
 
122
                const DPoint *to, DPoint *newEnd) {
 
123
        *newEnd = *to;
 
124
        if (type == EMPTY)
 
125
                ;
 
126
        else if (type == WHITE_CIRCLE)
 
127
                DrawCircle(g, from, to, False, newEnd);
 
128
        else if (type == BLACK_CIRCLE)
 
129
                DrawCircle(g, from, to, True, newEnd);
 
130
        else if (type == WHITE_TRIANGLE)
 
131
                DrawTriangle(g, from, to, False, newEnd);
 
132
        else if (type == BLACK_TRIANGLE)
 
133
                DrawTriangle(g, from, to, True, newEnd);
 
134
        else if (type == WHITE_DIAMOND)
 
135
                DrawDiamond(g, from, to, False, newEnd);
 
136
        else if (type == BLACK_DIAMOND)
 
137
                DrawDiamond(g, from, to, True, newEnd);
 
138
        else if (type == OPEN_ARROW)
 
139
                DrawArrowHead(g, from, to, False, 1, newEnd);
 
140
        else if (type == DOUBLE_OPEN_ARROW)
 
141
                DrawArrowHead(g, from, to, False, 2, newEnd);
 
142
        else if (type == FILLED_ARROW)
 
143
                DrawArrowHead(g, from, to, True, 1, newEnd);
 
144
        else if (type == DOUBLE_FILLED_ARROW)
 
145
                DrawArrowHead(g, from, to, True, 2, newEnd);
 
146
        else {
 
147
                error("%s, line %d: this end type is forgotten\n",
 
148
                        __FILE__, __LINE__);
 
149
        }
 
150
}
 
151
 
 
152
void LineEnd::DrawArrowHead(Grafport *g, const DPoint *from, 
 
153
                const DPoint *to, bool filled, int count,
 
154
                DPoint *newEnd) {
 
155
        if (filled)
 
156
                DrawFilledArrowHead(g, from, to, newEnd);
 
157
        else
 
158
                DrawOpenArrowHead(g, from, to, newEnd);
 
159
        if (count > 1)
 
160
                DrawExtraArrowHead(g, from, to, filled, newEnd);
 
161
}
 
162
 
 
163
void LineEnd::DrawOpenArrowHead(Grafport *g, 
 
164
                const DPoint *from, const DPoint *to, DPoint *newEnd) {
 
165
        DPoint *points = new DPoint[4];
 
166
        CalcDiamond(from, to, length, width, points);
 
167
        g->DrawLine(points[0].x, points[0].y, points[1].x, points[1].y);
 
168
        g->DrawLine(points[2].x, points[2].y, points[1].x, points[1].y);
 
169
        *newEnd = points[1];
 
170
        delete points;
 
171
}
 
172
 
 
173
void LineEnd::DrawFilledArrowHead(Grafport *g, 
 
174
                const DPoint *from, const DPoint *to, DPoint *newEnd) {
 
175
        DPoint *points = new DPoint[4];
 
176
        CalcFilledArrowHead(from, to, length, points);
 
177
 
 
178
        g->FillPolygon(points, 4);
 
179
//      if (!g->DrawsXor())                                             
 
180
//              g->DrawPolygon(points, 4);                              
 
181
        *newEnd = points[2];
 
182
        delete points;
 
183
}
 
184
 
 
185
 
 
186
void LineEnd::CalcFilledArrowHead(const DPoint *from, const DPoint *to,
 
187
                                int ln, DPoint *points) {
 
188
        // length of the target line we are drawing arrowhead over.
 
189
        double dx, dy, len; 
 
190
 
 
191
        // values for rotation needed to get from canonical line to target.
 
192
        double sin_theta, cos_theta;
 
193
        
 
194
        // coordinates and values for canonical arrowhead placed on x axis
 
195
        double pt1_x, pt1_y, pt2_x, pt2_y, pt3_x, pt3_y;
 
196
 
 
197
        double ah_len = (double)ln;
 
198
        double ah_angle = M_PI_4 * ((double)FILLED_ARROW_ANGLE)/180.0; // == 22.5 degrees
 
199
        // cos 22.5ļæ½ = 1.0 / sqrt((4.0 - 2.0 * M_SQRT2))
 
200
        // == 0.92387953251128675612818319
 
201
 
 
202
        // final arrowhead points transformed to match the line
 
203
        double result1_x, result1_y, result2_x, result2_y, result3_x, result3_y;
 
204
 
 
205
        // figure out the length of the target line
 
206
        dx = to->x - from->x;
 
207
        dy = to->y - from->y;
 
208
        len = sqrt(dx*dx+dy*dy);
 
209
 
 
210
        // bail out now if its zero length (since direction is not determined)
 
211
        if (len == 0) 
 
212
                return;
 
213
 
 
214
        // compute canonical arrow head points (as if on a line on x axis)  //
 
215
        //                                                                  //
 
216
        //             1                                                    // 
 
217
        //              \                                                   //
 
218
        //   +-----------2-------0----- x axis --->                         //
 
219
        //              /                                                   //
 
220
        //             3                                                    //
 
221
        //                                                                  //
 
222
        // arrowhead is draw as a 4 point polygon (with pt0 at the tip)     //
 
223
 
 
224
        pt1_x = len - ah_len*cos(ah_angle);
 
225
        pt1_y = ah_len*sin(ah_angle);
 
226
        pt2_x = len - (len-pt1_x)*FILLED_ARROW_INSET;
 
227
        pt2_y = 0;
 
228
        pt3_x = pt1_x;
 
229
        pt3_y = -pt1_y;
 
230
        
 
231
        // sin and cos of rotation to get canonical from x axis to target
 
232
        sin_theta = dy / len;
 
233
        cos_theta = dx / len;
 
234
        
 
235
        // rotate and translate to get our final points
 
236
        result1_x = pt1_x*cos_theta - pt1_y*sin_theta + from->x;
 
237
        result1_y = pt1_x*sin_theta + pt1_y*cos_theta + from->y;
 
238
        result2_x = pt2_x*cos_theta - pt2_y*sin_theta + from->x;
 
239
        result2_y = pt2_x*sin_theta + pt2_y*cos_theta + from->y;
 
240
        result3_x = pt3_x*cos_theta - pt3_y*sin_theta + from->x;
 
241
        result3_y = pt3_x*sin_theta + pt3_y*cos_theta + from->y;
 
242
 
 
243
        // set the arrow head polygon
 
244
        points[0] = *to;
 
245
        points[1] = DPoint(result1_x, result1_y);
 
246
        points[2] = DPoint(result2_x, result2_y);
 
247
        points[3] = DPoint(result3_x, result3_y);
 
248
}
 
249
 
 
250
 
 
251
void LineEnd::DrawExtraArrowHead(Grafport *g, 
 
252
                const DPoint *from, const DPoint *to,
 
253
                        bool filled, DPoint *newEnd) {
 
254
        DPoint pt1 = *from, pt2 = *to;
 
255
        double dx = to->x - from->x;
 
256
        double dy = to->y - from->y;
 
257
        double len = sqrt(dx * dx + dy * dy);
 
258
        if ( len ) {
 
259
                len = length / len;
 
260
                dx *= len;
 
261
                dy *= len;
 
262
                pt1.x -= dx; pt1.y -= dy;
 
263
                pt2.x -= dx; pt2.y -= dy;
 
264
        }
 
265
        if (filled)
 
266
                DrawFilledArrowHead(g, &pt1, &pt2, newEnd);
 
267
        else
 
268
                DrawOpenArrowHead(g, &pt1, &pt2, newEnd);
 
269
}
 
270
 
 
271
 
 
272
void LineEnd::DrawTriangle(Grafport *g,
 
273
                const DPoint *from, const DPoint *to,
 
274
                bool filled, DPoint *newEnd) {
 
275
        DPoint *points = new DPoint[4];
 
276
        CalcDiamond(from, to, length, width, points);
 
277
        *newEnd = points[3];
 
278
        if (filled)
 
279
                g->FillPolygon(points, 3);
 
280
        else {
 
281
                points[3] = points[0];
 
282
                g->DrawOpenPolygon(points, 4);
 
283
        }
 
284
        double dx = (points[1].x - newEnd->x) * 0.5;
 
285
        double dy = (points[1].y - newEnd->y) * 0.5;
 
286
        newEnd->x += dx;
 
287
        newEnd->y += dy;
 
288
        delete points;
 
289
}
 
290
 
 
291
void LineEnd::DrawDiamond(Grafport *g,
 
292
                const DPoint *from, const DPoint *to, bool filled,
 
293
                DPoint *newEnd) {
 
294
        DPoint *points = new DPoint[5];
 
295
        int l = length;
 
296
        length = length/2;
 
297
        CalcDiamond(from, to, length, width, points);
 
298
        length = l;
 
299
        if (filled)
 
300
                g->FillPolygon(points, 4);
 
301
        else {
 
302
                points[4] = points[0];
 
303
                g->DrawOpenPolygon(points, 4);
 
304
        }
 
305
        *newEnd = points[3];
 
306
        delete points;
 
307
}
 
308
 
 
309
void LineEnd::DrawCircle(Grafport *g, const DPoint *from,
 
310
                const DPoint *to, bool filled, DPoint *newEnd) {
 
311
        DPoint pt2 = *to;
 
312
        double dx = fabs(to->x - from->x);
 
313
        double dy = fabs(to->y - from->y);
 
314
        double alpha;
 
315
        if (dx != 0)
 
316
                alpha = atan(double(dy)/double(dx));
 
317
        else
 
318
                alpha = M_PI_2;
 
319
        dx = width * cos(alpha) * 0.5;
 
320
        dy = width * sin(alpha) * 0.5;
 
321
        DPoint p = pt2;
 
322
        *newEnd = pt2;
 
323
        p.x += (from->x < to->x) ? -dx : dx;
 
324
        p.y += (from->y < to->y) ? -dy : dy;
 
325
        newEnd->x += (from->x < to->x) ? -2*dx : 2*dx;
 
326
        newEnd->y += (from->y < to->y) ? -2*dy : 2*dy;
 
327
        if (filled)
 
328
                g->FillEllipse(p.x-width/2, p.y-width/2,
 
329
                        width, width);
 
330
        if (!filled || !g->DrawsXor())
 
331
                g->DrawEllipse(p.x-width/2, p.y-width/2,
 
332
                        width, width);
 
333
}
 
334
 
 
335
 
 
336
void LineEnd::CalcDiamond(const DPoint *from, const DPoint *to,
 
337
                                int len, int wd, DPoint *points) {
 
338
        double alpha, beta;
 
339
        double x, y, dx, dy;
 
340
        DPoint too;
 
341
        dx = fabs(to->x - from->x);
 
342
        dy = fabs(to->y - from->y);
 
343
        if (dx != 0) {
 
344
                alpha = atan(dy / dx);
 
345
                beta = M_PI_2 - alpha;
 
346
        } else {
 
347
                alpha = M_PI_2;
 
348
                beta = 0;
 
349
        }
 
350
        // determine length of side
 
351
        dx = len * cos(alpha); 
 
352
        dy = len * sin(alpha);
 
353
        // calculation for corner point of diamond
 
354
        x = wd * cos(beta) * 0.5;
 
355
        y = wd * sin(beta) * 0.5;
 
356
        // for different orientations
 
357
        x = (from->x < to->x) ? x : -x;
 
358
        y = (from->y < to->y) ? y : -y;
 
359
        // midpoint of arrow
 
360
        too.x = (from->x < to->x) ? to->x - dx : to->x + dx;
 
361
        too.y = (from->y < to->y) ? to->y - dy : to->y + dy;
 
362
        points[0].x = too.x + x;
 
363
        points[0].y = too.y - y;
 
364
        points[1].x = to->x;
 
365
        points[1].y = to->y;
 
366
        points[2].x = too.x - x,
 
367
        points[2].y = too.y + y;
 
368
        points[3].x = (from->x < to->x) ? too.x - dx : too.x + dx;
 
369
        points[3].y = (from->y < to->y) ? too.y - dy : too.y + dy;
 
370
}
 
371
 
 
372
 
 
373
void LineEnd::CalcDirectionDiamond(const DPoint *from, const DPoint *to,
 
374
                                const DPoint *apos,
 
375
                                int len, int wd, DPoint *points) {
 
376
        double alpha, beta;
 
377
        double x, y, dx, dy;
 
378
        DPoint too;
 
379
 
 
380
        dx = fabs(to->x - from->x);
 
381
        dy = fabs(to->y - from->y);
 
382
        if (dx != 0) {
 
383
                alpha = atan(dy / dx);
 
384
                beta = M_PI_2 - alpha;
 
385
        } else {
 
386
                alpha = M_PI_2;
 
387
                beta = 0;
 
388
        }
 
389
        // determine length of side
 
390
        dx = len * cos(alpha);
 
391
        dy = len * sin(alpha);
 
392
        // calculation for corner point of diamond
 
393
        x = wd * cos(beta) * 0.5;
 
394
        y = wd * sin(beta) * 0.5;
 
395
        // for different orientations
 
396
        x = (from->x < to->x) ? x : -x;
 
397
        y = (from->y < to->y) ? y : -y;
 
398
        // midpoint of arrow
 
399
        too.x = (from->x < to->x) ? to->x - dx : to->x + dx;
 
400
        too.y = (from->y < to->y) ? to->y - dy : to->y + dy;
 
401
 
 
402
 
 
403
        too.x = (from->x < to->x) ? apos->x + dx : apos->x + dx;
 
404
        too.y = (from->y < to->y) ? apos->y + dy : apos->y + dy;
 
405
 
 
406
        too.x = (from->x < to->x) ? apos->x + x : apos->x + x;
 
407
        too.y = (from->y < to->y) ? apos->y + y : apos->y + y;
 
408
 
 
409
        if (from->x == to->x) {
 
410
                too.x = apos->x;
 
411
                too.y = (from->y < to->y) ? apos->y + y : apos->y + y;
 
412
        } else
 
413
                too.x = (from->x < to->x) ? apos->x + x : apos->x + dx;
 
414
 
 
415
        if (from->y == to->y)
 
416
                too.y = apos->y;
 
417
        else
 
418
                too.y = (from->y < to->y) ? apos->y + y : apos->y - y;
 
419
 
 
420
        points[0].x = too.x + x;
 
421
        points[0].y = too.y - y;
 
422
 
 
423
        points[1].x = (from->x < to->x) ? too.x + dx : too.x - dx;
 
424
        points[1].y = (from->y < to->y) ? too.y + dy : too.y - dy;
 
425
 
 
426
        points[2].x = too.x - x,
 
427
        points[2].y = too.y + y;
 
428
        points[3].x = (from->x < to->x) ? too.x - dx : too.x + dx;
 
429
        points[3].y = (from->y < to->y) ? too.y - dy : too.y + dy;
 
430
}