Project

General

Profile

Statistics
| Branch: | Revision:

root / src / tkenv / arrow.cc @ 3e29b8a0

History | View | Annotate | Download (12.7 KB)

1
//==========================================================================
2
//  ARROW.CC - part of
3
//
4
//                     OMNeT++/OMNEST
5
//
6
//  Implementation of
7
//   connection arrow positioning in module drawing
8
//
9
//==========================================================================
10

    
11
/*--------------------------------------------------------------*
12
  Copyright (C) 1992-2008 Andras Varga
13
  Copyright (C) 2006-2009 OpenSim Ltd.
14

15
  This file is distributed WITHOUT ANY WARRANTY. See the file
16
  `license' for details on this and other legal matters.
17
*--------------------------------------------------------------*/
18

    
19

    
20
#include <stdio.h>
21
#include <string.h>
22
#include <stdlib.h>
23
#include <math.h>
24

    
25
#include "arrow.h"
26
#include "tkutil.h"
27

    
28
USING_NAMESPACE
29

    
30
static void clip_line_to_rect(
31
            double& x1, double& y1, double x2, double y2,
32
            double rx1, double ry1, double rx2, double ry2)
33
{
34
   // line: (x1,y1) to (x2,y2)
35
   // rectangle: rx1,ry1,rx2,ry2
36
   //
37
   // adjust (x1,y1) end of the line so that it fits on the rectangle boundary
38
   // if (x2,y2) is inside the rectangle, do nothing
39

    
40
   int p1_inside = (x1>=rx1 && x1<=rx2 && y1>=ry1 && y1<=ry2);
41
   int p2_inside = (x2>=rx1 && x2<=rx2 && y2>=ry1 && y2<=ry2);
42

    
43
   // we'll clip the line to two edges of the rect: y=cy (horiz) and x=cx (vert)
44
   double cx,cy;
45

    
46
   if (p1_inside && p2_inside)
47
   {
48
      cx = x1<x2 ? rx1 : rx2;
49
      cy = y1<y2 ? ry1 : ry2;
50
   }
51
   else if (p1_inside)
52
   {
53
      cx = x1<x2 ? rx2 : rx1;
54
      cy = y1<y2 ? ry2 : ry1;
55
   }
56
   else if (p2_inside)
57
   {
58
      cx = x1<x2 ? rx1 : rx2;
59
      cy = y1<y2 ? ry1 : ry2;
60
   }
61
   else
62
   {
63
      cx = x1<x2 ? rx2 : rx1;
64
      cy = y1<y2 ? ry2 : ry1;
65
   }
66

    
67
   // first, deal with the special cases: line is vert or horiz
68
   if (x1==x2)
69
   {
70
      if (x1<rx1) x1=rx1;
71
      if (x1>rx2) x1=rx2;
72
      if (x1!=rx1 && x1!=rx2) y1=cy;
73
      return;
74
   }
75

    
76
   if (y1==y2)
77
   {
78
      if (y1<ry1) y1=ry1;
79
      if (y1>ry2) y1=ry2;
80
      if (y1!=ry1 && y1!=ry2) x1=cx;
81
      return;
82
   }
83

    
84
   // write the line into y=ax+b form
85
   double a = (y2-y1)/(x2-x1);
86
   double b = y1 - a*x1;
87

    
88
   double xx,yy;
89

    
90
   // clip to the y=cy horizontal edge of the rect
91
   xx = (cy-b)/a;  //==> (xx,cy)
92

    
93
   // clip to the x=cx vertical edge of the rect
94
   yy = a*cx+b;  //==> (cx,yy)
95

    
96
   if (xx>=rx1 && xx<=rx2) {
97
         x1 = xx;
98
         y1 = cy;
99
   } else if (yy>=ry1 && yy<=ry2) {
100
         x1 = cx;
101
         y1 = yy;
102
   } else {
103
         x1 = cx;
104
         y1 = cy;
105
   }
106
   // default: rect and line don't have any common point :-(
107
   // leave line unchanged
108
}
109

    
110

    
111
int arrowcoords(Tcl_Interp *interp, int argc, const char **argv)
112
{
113
      if (argc!=18) {
114
          printf("arrowcoords called with wrong number of args: %d\n", argc-1);
115
          for (int i=1; i<argc; i++)
116
          {
117
              if (i==1) printf(" src:");
118
              if (i==5) printf(" dest:");
119
              if (i==9) printf(" srcgateindex/size:");
120
              if (i==11) printf(" destgateindex/size:");
121
              if (i==13) printf(" mode:");
122
              if (i==14) printf(" anchorpoints:");
123
              printf(" <%s>", argv[i]);
124
          }
125
          printf("\n");
126
          Tcl_SetResult(interp, TCLCONST("17 args expected"), TCL_STATIC);
127
          return TCL_ERROR;
128
      }
129

    
130
      // args 1..4: coords of source module rectangle
131
      double src_x1 = atof(argv[1]),
132
             src_y1 = atof(argv[2]),
133
             src_x2 = atof(argv[3]),
134
             src_y2 = atof(argv[4]);
135

    
136
      // args 5..8: coords of destination module rectangle
137
      double dest_x1 = atof(argv[5]),
138
             dest_y1 = atof(argv[6]),
139
             dest_x2 = atof(argv[7]),
140
             dest_y2 = atof(argv[8]);
141

    
142
      // args 9..12: gate indices and gate vector sizes
143
      int    src_i = atoi(argv[9]),
144
             src_n = atoi(argv[10]);
145
      int    dest_i = atoi(argv[11]),
146
             dest_n = atoi(argv[12]);
147

    
148
      // arg 13: mode: a(uto), m(anual), n, e, w, s (default is 'a')
149
      int    mode = argv[13][0]=='-' ? 'a' : argv[13][0];
150

    
151
      // args 14..17: destination anchoring (default value 50%)
152
      double src_anch_dx =  argv[14][0]=='-' ? 50 : atof(argv[14]),
153
             src_anch_dy =  argv[15][0]=='-' ? 50 : atof(argv[15]);
154
      double dest_anch_dx = argv[16][0]=='-' ? 50 : atof(argv[16]),
155
             dest_anch_dy = argv[17][0]=='-' ? 50 : atof(argv[17]);
156

    
157
      double src_x, src_y, dest_x, dest_y;
158

    
159
      // error checks
160
      if (!strchr("amnews",mode)) {
161
          Tcl_SetResult(interp, TCLCONST("mode must be one of (a,m,n,e,w,s)"), TCL_STATIC);
162
          return TCL_ERROR;
163
      }
164

    
165
      // see if the two rects are the same, one is in the other etc.
166
      int same_rect = 0;
167
      int src_within_dest = 0;
168
      int dest_within_src = 0;
169
      int overlapping_rects = 0;
170

    
171
      if (src_x1==dest_x1 && src_x2==dest_x2 && src_y1==dest_y1 && src_y2==dest_y2)
172
         same_rect = 1;
173
      else if (src_x1<=dest_x1 && src_x2>=dest_x2 && src_y1<=dest_y1 && src_y2>=dest_y2)
174
         dest_within_src = 1;
175
      else if (src_x1>=dest_x1 && src_x2<=dest_x2 && src_y1>=dest_y1 && src_y2<=dest_y2)
176
         src_within_dest = 1;
177
      else if (std::max(src_x1,dest_x1)<std::min(src_x2,dest_x2) && std::max(src_y1,dest_y1)<std::min(src_y2,dest_y2))
178
        overlapping_rects = 1;
179

    
180
      // some useful values...
181
      double src_width = src_x2-src_x1,
182
             src_height = src_y2-src_y1,
183
             dest_width = dest_x2-dest_x1,
184
             dest_height = dest_y2-dest_y1;
185

    
186
      // prepare for mode 'm'
187
      if (mode!='m') src_anch_dx=src_anch_dy=dest_anch_dx=dest_anch_dy=50;
188
      src_x = src_x1 + src_anch_dx*src_width/100;
189
      src_y = src_y1 + src_anch_dy*src_height/100;
190
      dest_x = dest_x1 + dest_anch_dx*dest_width/100;
191
      dest_y = dest_y1 + dest_anch_dy*dest_height/100;
192

    
193
      double factor = 1;  // factor to set where the anchor point can run. 0: runs on the border, 1:runs on the middle line
194

    
195
      double src_delta = factor * std::min(src_width, src_height) / 2.0;
196
      double dest_delta = factor * std::min(dest_width, dest_height) / 2.0;
197

    
198
      double src_wire_x1 = src_x1 + src_delta;
199
      double src_wire_y1 = src_y1 + src_delta;
200
      double src_wire_x2 = src_x2 - src_delta;
201
      double src_wire_y2 = src_y2 - src_delta;
202
      double dest_wire_x1 = dest_x1 + dest_delta;
203
      double dest_wire_y1 = dest_y1 + dest_delta;
204
      double dest_wire_x2 = dest_x2 - dest_delta;
205
      double dest_wire_y2 = dest_y2 - dest_delta;
206

    
207
      // do all rectangle relations one-by-one
208
      if (same_rect)
209
      {
210
         // a - height>width: E or W
211
         //     otherwise:    N or S
212
         if (mode=='a')
213
             mode = (src_height>src_width) ? 'e' : 'n';
214

    
215
         //  E,W - connection points E or W. Vert shift by gate indices
216
         //  N,S - connection points N or S. Horiz shift by gate indices
217
         switch (mode)
218
         {
219
           case 'n':
220
           case 's':
221
             src_x = dest_x = src_x1 + (src_i+1) * src_width / (src_n+1);
222
             src_y = src_y1; dest_y = src_y2;
223
             break;
224
           case 'e':
225
           case 'w':
226
             src_x = src_x1; dest_x = src_x2;
227
             src_y = dest_y = src_y1 + (src_i+1) * src_height / (src_n+1);
228
             break;
229
           case 'm':
230
             clip_line_to_rect(src_x,src_y,dest_x,dest_y, src_x1,src_y1,src_x2,src_y2);
231
             clip_line_to_rect(dest_x,dest_y,src_x,src_y, dest_x1,dest_y1,dest_x2,dest_y2);
232
             break;
233
         }
234
      }
235
      else if (src_within_dest)
236
      {
237
         // a - N,E,S,W (to nearest border,calculated from side)
238
         if (mode=='a')
239
         {
240
             double top =     src_y1 - dest_y1,
241
                    bottom =  dest_y2 - src_y2,
242
                    left =    src_x1 - dest_x1,
243
                    right =   dest_x2 - src_x2;
244
             if (top<=bottom && top<=left && top<=right)
245
                  mode = 'n';
246
             else if (right<=bottom && right<=left && right<=top)
247
                  mode = 'e';
248
             else if (left<=bottom && left<=right && left<=top)
249
                  mode = 'w';
250
             else if (bottom<=left && bottom<=right && bottom<=top)
251
                  mode = 's';
252
         }
253

    
254
         //  E,W - connection points E or W. Vert shift by gate indices
255
         //  N,S - connection points N or S. Horiz shift by gate indices
256
         switch (mode)
257
         {
258
           case 'n':
259
             src_x = dest_x = src_x1 + (src_i+1) * src_width / (src_n+1);
260
             src_y = src_y1; dest_y = dest_y1;
261
             break;
262
           case 's':
263
             src_x = dest_x = src_x1 + (src_i+1) * src_width / (src_n+1);
264
             src_y = src_y2; dest_y = dest_y2;
265
             break;
266
           case 'e':
267
             src_x = src_x2; dest_x = dest_x2;
268
             src_y = dest_y = src_y1 + (src_i+1) * src_height / (src_n+1);
269
             break;
270
           case 'w':
271
             src_x = src_x1; dest_x = dest_x1;
272
             src_y = dest_y = src_y1 + (src_i+1) * src_height / (src_n+1);
273
             break;
274
           case 'm':
275
             clip_line_to_rect(src_x,src_y,dest_x,dest_y, src_x1,src_y1,src_x2,src_y2);
276
             clip_line_to_rect(dest_x,dest_y,src_x,src_y, dest_x1,dest_y1,dest_x2,dest_y2);
277
             break;
278
         }
279
      }
280
      else if (dest_within_src)
281
      {
282
         // a - N,E,S,W (to nearest border,calculated from side)
283
         if (mode=='a')
284
         {
285
             double top =     dest_y1 - src_y1,
286
                    bottom =  src_y2 - dest_y2,
287
                    left =    dest_x1 - src_x1,
288
                    right =   src_x2 - dest_x2;
289
             if (top<=bottom && top<=left && top<=right)
290
                  mode = 'n';
291
             else if (right<=bottom && right<=left && right<=top)
292
                  mode = 'e';
293
             else if (left<=bottom && left<=right && left<=top)
294
                  mode = 'w';
295
             else if (bottom<=left && bottom<=right && bottom<=top)
296
                  mode = 's';
297
         }
298

    
299
         //  E,W - connection points E or W. Vert shift by gate indices
300
         //  N,S - connection points N or S. Horiz shift by gate indices
301
         switch (mode)
302
         {
303
           case 'n':
304
             src_x = dest_x = dest_x1 + (dest_i+1) * dest_width / (dest_n+1);
305
             src_y = src_y1; dest_y = dest_y1;
306
             break;
307
           case 's':
308
             src_x = dest_x = dest_x1 + (dest_i+1) * dest_width / (dest_n+1);
309
             src_y = src_y2; dest_y = dest_y2;
310
             break;
311
           case 'e':
312
             src_x = src_x2; dest_x = dest_x2;
313
             src_y = dest_y = dest_y1 + (dest_i+1) * dest_height / (dest_n+1);
314
             break;
315
           case 'w':
316
             src_x = src_x1; dest_x = dest_x1;
317
             src_y = dest_y = dest_y1 + (dest_i+1) * dest_height / (dest_n+1);
318
             break;
319
           case 'm':
320
             clip_line_to_rect(src_x,src_y,dest_x,dest_y, src_x1,src_y1,src_x2,src_y2);
321
             clip_line_to_rect(dest_x,dest_y,src_x,src_y, dest_x1,dest_y1,dest_x2,dest_y2);
322
             break;
323
         }
324
      }
325
      else // disjoint (or partially overlapping) rectangles
326
      {
327
         //  a - E,W if one module's y range is within y range of other module
328
         //      N,S if one module's x range is within x range of other module
329
         //      otherwise M mode with (50%,50%) (50%,50%)
330

    
331
         if (mode!='m')
332
         {
333
             // horizontal coordinates
334
             if (src_wire_x2 <= dest_wire_x1)
335
                 { src_x = src_wire_x2; dest_x = dest_wire_x1; }
336
             if (dest_wire_x2 <= src_wire_x1)
337
                 { src_x = src_wire_x1; dest_x = dest_wire_x2; }
338
             double overlap_x1 = std::max(src_wire_x1, dest_wire_x1);
339
             double overlap_x2 = std::min(src_wire_x2, dest_wire_x2);
340
             if (overlap_x1 <= overlap_x2)
341
               src_x = dest_x = overlap_x1 + (src_i+1)*(overlap_x2 - overlap_x1)/(src_n+1);
342

    
343
             // vertical coordinates
344
             if (src_wire_y2 <= dest_wire_y1)
345
                 { src_y = src_wire_y2; dest_y = dest_wire_y1; }
346
             if (dest_wire_y2 <= src_wire_y1)
347
                 { src_y = src_wire_y1; dest_y = dest_wire_y2; }
348
             double overlap_y1 = std::max(src_wire_y1, dest_wire_y1);
349
             double overlap_y2 = std::min(src_wire_y2, dest_wire_y2);
350
             if (overlap_y1 <= overlap_y2)
351
               src_y = dest_y = overlap_y1 + (src_i+1)*(overlap_y2 - overlap_y1)/(src_n+1);
352

    
353
         }
354

    
355
         // clip the line to the bounding rectangles if they are not overlapping
356
         if (!overlapping_rects)
357
         {
358
             double src_x_tmp = src_x;
359
             double src_y_tmp = src_y;
360
             clip_line_to_rect(src_x,src_y,dest_x,dest_y, src_x1,src_y1,src_x2,src_y2);
361
             clip_line_to_rect(dest_x,dest_y,src_x_tmp,src_y_tmp, dest_x1,dest_y1,dest_x2,dest_y2);
362
         }
363
      }
364

    
365
      char buf[60];
366
      sprintf(buf, "%g %g %g %g", src_x, src_y, dest_x, dest_y);
367
      Tcl_SetResult(interp, buf, TCL_VOLATILE);
368
      return TCL_OK;
369
}
370