1
////////////////////////////////////////////////////////////////////////////////
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).
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.
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.
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
21
////////////////////////////////////////////////////////////////////////////////
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;
40
const TypeName LineEnd::typeNames[] = {
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"},
61
LineEnd::LineEnd(Type t) {
65
void LineEnd::SetType(Type t) {
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;
77
else if (type == WHITE_DIAMOND || type == BLACK_DIAMOND) {
78
length = DIAMOND_LENGTH;
79
width = DIAMOND_WIDTH;
81
else if (type == OPEN_ARROW || type == DOUBLE_OPEN_ARROW) {
82
length = OPEN_ARROW_LENGTH;
83
width = OPEN_ARROW_WIDTH;
85
else if (type == FILLED_ARROW || type == DOUBLE_FILLED_ARROW) {
86
length = width = FILLED_ARROW_LENGTH;
94
void LineEnd::SetLength(int l) {
96
if (type==WHITE_CIRCLE || type == BLACK_CIRCLE)
100
void LineEnd::SetWidth(int w) {
104
void LineEnd::Type2String(LineEnd::Type l, string *s) {
105
if (1 <= (int)l && (int)l <= NUMTYPES)
106
*s = typeNames[l-1].name;
111
LineEnd::Type LineEnd::String2Type(const string *s) {
112
const TypeName *n = &typeNames[0];
113
while (n->code != 0) {
115
return (Type) n->code;
121
void LineEnd::Draw(Grafport *g, const DPoint *from,
122
const DPoint *to, DPoint *newEnd) {
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);
147
error("%s, line %d: this end type is forgotten\n",
152
void LineEnd::DrawArrowHead(Grafport *g, const DPoint *from,
153
const DPoint *to, bool filled, int count,
156
DrawFilledArrowHead(g, from, to, newEnd);
158
DrawOpenArrowHead(g, from, to, newEnd);
160
DrawExtraArrowHead(g, from, to, filled, newEnd);
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);
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);
178
g->FillPolygon(points, 4);
179
// if (!g->DrawsXor())
180
// g->DrawPolygon(points, 4);
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.
191
// values for rotation needed to get from canonical line to target.
192
double sin_theta, cos_theta;
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;
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
202
// final arrowhead points transformed to match the line
203
double result1_x, result1_y, result2_x, result2_y, result3_x, result3_y;
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);
210
// bail out now if its zero length (since direction is not determined)
214
// compute canonical arrow head points (as if on a line on x axis) //
218
// +-----------2-------0----- x axis ---> //
222
// arrowhead is draw as a 4 point polygon (with pt0 at the tip) //
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;
231
// sin and cos of rotation to get canonical from x axis to target
232
sin_theta = dy / len;
233
cos_theta = dx / len;
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;
243
// set the arrow head polygon
245
points[1] = DPoint(result1_x, result1_y);
246
points[2] = DPoint(result2_x, result2_y);
247
points[3] = DPoint(result3_x, result3_y);
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);
262
pt1.x -= dx; pt1.y -= dy;
263
pt2.x -= dx; pt2.y -= dy;
266
DrawFilledArrowHead(g, &pt1, &pt2, newEnd);
268
DrawOpenArrowHead(g, &pt1, &pt2, newEnd);
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);
279
g->FillPolygon(points, 3);
281
points[3] = points[0];
282
g->DrawOpenPolygon(points, 4);
284
double dx = (points[1].x - newEnd->x) * 0.5;
285
double dy = (points[1].y - newEnd->y) * 0.5;
291
void LineEnd::DrawDiamond(Grafport *g,
292
const DPoint *from, const DPoint *to, bool filled,
294
DPoint *points = new DPoint[5];
297
CalcDiamond(from, to, length, width, points);
300
g->FillPolygon(points, 4);
302
points[4] = points[0];
303
g->DrawOpenPolygon(points, 4);
309
void LineEnd::DrawCircle(Grafport *g, const DPoint *from,
310
const DPoint *to, bool filled, DPoint *newEnd) {
312
double dx = fabs(to->x - from->x);
313
double dy = fabs(to->y - from->y);
316
alpha = atan(double(dy)/double(dx));
319
dx = width * cos(alpha) * 0.5;
320
dy = width * sin(alpha) * 0.5;
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;
328
g->FillEllipse(p.x-width/2, p.y-width/2,
330
if (!filled || !g->DrawsXor())
331
g->DrawEllipse(p.x-width/2, p.y-width/2,
336
void LineEnd::CalcDiamond(const DPoint *from, const DPoint *to,
337
int len, int wd, DPoint *points) {
341
dx = fabs(to->x - from->x);
342
dy = fabs(to->y - from->y);
344
alpha = atan(dy / dx);
345
beta = M_PI_2 - alpha;
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;
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;
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;
373
void LineEnd::CalcDirectionDiamond(const DPoint *from, const DPoint *to,
375
int len, int wd, DPoint *points) {
380
dx = fabs(to->x - from->x);
381
dy = fabs(to->y - from->y);
383
alpha = atan(dy / dx);
384
beta = M_PI_2 - alpha;
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;
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;
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;
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;
409
if (from->x == to->x) {
411
too.y = (from->y < to->y) ? apos->y + y : apos->y + y;
413
too.x = (from->x < to->x) ? apos->x + x : apos->x + dx;
415
if (from->y == to->y)
418
too.y = (from->y < to->y) ? apos->y + y : apos->y - y;
420
points[0].x = too.x + x;
421
points[0].y = too.y - y;
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;
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;