1 unit
2  interactive_polygon_ ;
3 
4 INTERFACE
5 
6 {$I agg_mode.inc }
7 
8 uses
9  Math ,
10  agg_basics ,
11  agg_conv_stroke ,
12  agg_ellipse ,
13  agg_vertex_source ;
14 
15 { TYPES DEFINITION }
16 type
17  simple_polygon_vertex_source = object(vertex_source )
18    m_polygon : double_ptr;
19 
20    m_num_points ,
21    m_vertex     : unsigned;
22 
23    m_roundoff ,
24    m_close    : boolean;
25 
26    constructor Construct(polygon : double_ptr; np : unsigned; roundoff : boolean = false; close : boolean = true );
27 
28    procedure close_(f : boolean );
_closenull29    function  _close : boolean;
30 
31    procedure rewind(path_id : unsigned ); virtual;
vertexnull32    function  vertex(x ,y : double_ptr ) : unsigned; virtual;
33 
34   end;
35 
36  interactive_polygon = object(vertex_source )
37    m_polygon    : double_ptr;
38    m_num_points : unsigned;
39 
40    m_node ,
41    m_edge : int;
42 
43    m_vs : simple_polygon_vertex_source;
44 
45    m_stroke  : conv_stroke;
46    m_ellipse : ellipse;
47 
48    m_point_radius : double;
49    m_status       : unsigned;
50 
51    m_dx ,
52    m_dy : double;
53 
54    constructor Construct(np : unsigned; point_radius : double );
55    destructor  Destruct; virtual;
56 
num_pointsnull57    function  num_points : unsigned;
58 
xnnull59    function  xn(n : unsigned ) : double;
ynnull60    function  yn(n : unsigned ) : double;
61 
xn_ptrnull62    function  xn_ptr(n : unsigned ) : double_ptr;
yn_ptrnull63    function  yn_ptr(n : unsigned ) : double_ptr;
64 
polygonnull65    function  polygon : double_ptr;
66 
_nodenull67    function  _node : int;
68    procedure node_(n : int );
69 
_closenull70    function  _close : boolean;
71    procedure close_(f : boolean );
72 
73    procedure rewind(path_id : unsigned ); virtual;
vertexnull74    function  vertex(x ,y : double_ptr ) : unsigned; virtual;
75 
on_mouse_movenull76    function  on_mouse_move(x ,y : double ) : boolean;
77 
on_mouse_button_downnull78    function  on_mouse_button_down(x ,y : double ) : boolean;
on_mouse_button_upnull79    function  on_mouse_button_up(x ,y : double ) : boolean;
80 
check_edgenull81    function  check_edge(i : unsigned; x ,y : double ) : boolean;
82 
point_in_polygonnull83    function  point_in_polygon(tx ,ty : double ) : boolean;
84 
85   end;
86 
87 { GLOBAL PROCEDURES }
88 
89 
90 IMPLEMENTATION
91 { LOCAL VARIABLES & CONSTANTS }
92 { UNIT IMPLEMENTATION }
93 { CONSTRUCT }
94 constructor simple_polygon_vertex_source.Construct;
95 begin
96  inherited Construct;
97 
98  m_polygon   :=polygon;
99  m_num_points:=np;
100 
101  m_vertex  :=0;
102  m_roundoff:=roundoff;
103  m_close   :=close;
104 
105 end;
106 
107 { CLOSE_ }
108 procedure simple_polygon_vertex_source.close_;
109 begin
110  m_close:=f;
111 
112 end;
113 
114 { _CLOSE }
simple_polygon_vertex_source._closenull115 function simple_polygon_vertex_source._close;
116 begin
117  result:=m_close;
118 
119 end;
120 
121 { REWIND }
122 procedure simple_polygon_vertex_source.rewind;
123 begin
124  m_vertex:=0;
125 
126 end;
127 
128 { VERTEX }
simple_polygon_vertex_source.vertexnull129 function simple_polygon_vertex_source.vertex;
130 begin
131  if m_vertex > m_num_points then
132   begin
133    result:=path_cmd_stop;
134 
135    exit;
136 
137   end;
138 
139  if m_vertex = m_num_points then
140   begin
141    inc(m_vertex );
142 
143    if m_close then
144     result:=path_cmd_end_poly or path_flags_close
145    else
146     result:=path_cmd_end_poly or 0;
147 
148    exit;
149 
150   end;
151 
152  x^:=double_ptr(ptrcomp(m_polygon ) + (m_vertex * 2 ) * sizeof(double ) )^;
153  y^:=double_ptr(ptrcomp(m_polygon ) + (m_vertex * 2 + 1 ) * sizeof(double ) )^;
154 
155  if m_roundoff then
156   begin
157    x^:=Floor(x^ ) + 0.5;
158    y^:=Floor(y^ ) + 0.5;
159 
160   end;
161 
162  inc(m_vertex );
163 
164  if m_vertex = 1 then
165   result:=path_cmd_move_to
166  else
167   result:=path_cmd_line_to;
168 
169 end;
170 
171 { CONSTRUCT }
172 constructor interactive_polygon.Construct;
173 begin
174  inherited Construct;
175 
176  agg_getmem(pointer(m_polygon ) ,np * 2 * sizeof(double ) );
177 
178  m_num_points:=np;
179 
180  m_node:=-1;
181  m_edge:=-1;
182 
183  m_vs.Construct    (m_polygon ,m_num_points ,false );
184  m_stroke.Construct(@m_vs );
185  m_ellipse.Construct;
186 
187  m_point_radius:=point_radius;
188  m_status      :=0;
189 
190  m_dx:=0.0;
191  m_dy:=0.0;
192 
193  m_stroke.width_(1.0 );
194 
195 end;
196 
197 { DESTRUCT }
198 destructor interactive_polygon.Destruct;
199 begin
200  agg_freemem(pointer(m_polygon ) ,m_num_points * 2 * sizeof(double ) );
201 
202  m_stroke.Destruct;
203 
204 end;
205 
206 { NUM_POINTS }
interactive_polygon.num_pointsnull207 function interactive_polygon.num_points;
208 begin
209  result:=m_num_points;
210 
211 end;
212 
213 { XN }
interactive_polygon.xnnull214 function interactive_polygon.xn;
215 begin
216  result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 ) * sizeof(double ) )^;
217 
218 end;
219 
220 { YN }
interactive_polygon.ynnull221 function interactive_polygon.yn;
222 begin
223  result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 + 1 ) * sizeof(double ) )^;
224 
225 end;
226 
227 { XN_PTR }
interactive_polygon.xn_ptrnull228 function interactive_polygon.xn_ptr;
229 begin
230  result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 ) * sizeof(double ) );
231 
232 end;
233 
234 { YN_PTR }
interactive_polygon.yn_ptrnull235 function interactive_polygon.yn_ptr;
236 begin
237  result:=double_ptr(ptrcomp(m_polygon ) + (n * 2 + 1 ) * sizeof(double ) );
238 
239 end;
240 
241 { POLYGON }
interactive_polygon.polygonnull242 function interactive_polygon.polygon;
243 begin
244  result:=m_polygon;
245 
246 end;
247 
248 { _NODE }
interactive_polygon._nodenull249 function interactive_polygon._node;
250 begin
251  result:=m_node;
252 
253 end;
254 
255 { NODE_ }
256 procedure interactive_polygon.node_;
257 begin
258  m_node:=n;
259 
260 end;
261 
262 { _CLOSE }
interactive_polygon._closenull263 function interactive_polygon._close;
264 begin
265  result:=m_vs._close;
266 
267 end;
268 
269 { CLOSE_ }
270 procedure interactive_polygon.close_;
271 begin
272  m_vs.close_(f );
273 
274 end;
275 
276 { REWIND }
277 procedure interactive_polygon.rewind;
278 begin
279  m_status:=0;
280 
281  m_stroke.rewind(0 );
282 
283 end;
284 
285 { VERTEX }
interactive_polygon.vertexnull286 function interactive_polygon.vertex;
287 var
288  r : double;
289 
290  cmd : unsigned;
291 
292 begin
293  cmd:=path_cmd_stop;
294  r  :=m_point_radius;
295 
296  if m_status = 0 then
297   begin
298    cmd:=m_stroke.vertex(x ,y );
299 
300    if not is_stop(cmd ) then
301     begin
302      result:=cmd;
303 
304      exit;
305 
306     end;
307 
308    if (m_node >= 0 ) and
309       (m_node = int(m_status ) ) then
310     r:=r * 1.2;
311 
312    m_ellipse.init(xn(m_status ) ,yn(m_status ) ,r ,r ,32 );
313 
314    inc(m_status );
315 
316   end;
317 
318  cmd:=m_ellipse.vertex(x ,y );
319 
320  if not is_stop(cmd ) then
321   begin
322    result:=cmd;
323 
324    exit;
325 
326   end;
327 
328  if m_status >= m_num_points then
329   begin
330    result:=path_cmd_stop;
331 
332    exit;
333 
334   end;
335 
336  if (m_node >= 0 ) and
337     (m_node = int(m_status ) ) then
338   r:=r * 1.2;
339 
340  m_ellipse.init(xn(m_status ) ,yn(m_status ) ,r ,r ,32 );
341 
342  inc(m_status );
343 
344  result:=m_ellipse.vertex(x ,y );
345 
346 end;
347 
348 { ON_MOUSE_MOVE }
interactive_polygon.on_mouse_movenull349 function interactive_polygon.on_mouse_move;
350 var
351  ret : boolean;
352 
353  i ,n1 ,n2 : unsigned;
354 
355  dx ,dy : double;
356 
357 begin
358  ret:=false;
359 
360  if m_node = int(m_num_points ) then
361   begin
362    dx:=x - m_dx;
363    dy:=y - m_dy;
364 
365    for i:=0 to m_num_points - 1 do
366     begin
367      xn_ptr(i )^:=xn_ptr(i )^ + dx;
368      yn_ptr(i )^:=yn_ptr(i )^ + dy;
369 
370     end;
371 
372    m_dx:=x;
373    m_dy:=y;
374 
375    ret:=true;
376 
377   end
378  else
379   if m_edge >= 0 then
380    begin
381     n1:=m_edge;
382     n2:=(n1 + m_num_points - 1 ) mod m_num_points;
383 
384     dx:=x - m_dx;
385     dy:=y - m_dy;
386 
387     xn_ptr(n1 )^:=xn_ptr(n1 )^ + dx;
388     yn_ptr(n1 )^:=yn_ptr(n1 )^ + dy;
389     xn_ptr(n2 )^:=xn_ptr(n2 )^ + dx;
390     yn_ptr(n2 )^:=yn_ptr(n2 )^ + dy;
391 
392     m_dx:=x;
393     m_dy:=y;
394 
395     ret:=true;
396 
397    end
398   else
399    if m_node >= 0 then
400     begin
401      xn_ptr(m_node )^:=x - m_dx;
402      yn_ptr(m_node )^:=y - m_dy;
403 
404      ret:=true;
405 
406     end;
407 
408  result:=ret;
409 
410 end;
411 
412 { ON_MOUSE_BUTTON_DOWN }
interactive_polygon.on_mouse_button_downnull413 function interactive_polygon.on_mouse_button_down;
414 var
415  i : unsigned;
416 
417  ret : boolean;
418 
419 begin
420  ret:=false;
421 
422  m_node:=-1;
423  m_edge:=-1;
424 
425  for i:=0 to m_num_points - 1 do
426   if Sqrt((x - xn(i ) ) * (x - xn(i ) ) + (y - yn(i ) ) * (y - yn(i ) ) ) < m_point_radius then
427    begin
428     m_dx:=x - xn(i );
429     m_dy:=y - yn(i );
430 
431     m_node:=int(i );
432 
433     ret:=true;
434 
435     break;
436 
437    end;
438 
439  if not ret then
440   for i:=0 to m_num_points - 1 do
441    if check_edge(i ,x ,y ) then
442     begin
443      m_dx:=x;
444      m_dy:=y;
445 
446      m_edge:=int(i );
447 
448      ret:=true;
449 
450      break;
451 
452     end;
453 
454  if not ret then
455   if point_in_polygon(x ,y ) then
456    begin
457     m_dx:=x;
458     m_dy:=y;
459 
460     m_node:=int(m_num_points );
461 
462     ret:=true;
463 
464    end;
465 
466  result:=ret;
467 
468 end;
469 
470 { ON_MOUSE_BUTTON_UP }
interactive_polygon.on_mouse_button_upnull471 function interactive_polygon.on_mouse_button_up;
472 var
473  ret : boolean;
474 
475 begin
476  ret:=(m_node >= 0 ) or (m_edge >= 0 );
477 
478  m_node:=-1;
479  m_edge:=-1;
480  result:=ret;
481 
482 end;
483 
484 { CHECK_EDGE }
interactive_polygon.check_edgenull485 function interactive_polygon.check_edge;
486 var
487  ret : boolean;
488 
489  n1 ,n2 : unsigned;
490 
491  x1 ,y1 ,x2 ,y2 ,dx ,dy ,x3 ,y3 ,x4 ,y4 ,den ,u1 ,xi ,yi : double;
492 
493 begin
494  ret:=false;
495 
496  n1:= i;
497  n2:= (i + m_num_points - 1 ) mod m_num_points;
498 
499  x1:=xn(n1 );
500  y1:=yn(n1 );
501  x2:=xn(n2 );
502  y2:=yn(n2 );
503 
504  dx:=x2 - x1;
505  dy:=y2 - y1;
506 
507  if Sqrt(dx * dx + dy * dy ) > 0.0000001 then
508   begin
509    x3:=x;
510    y3:=y;
511    x4:=x3 - dy;
512    y4:=y3 + dx;
513 
514    den:=(y4 - y3 ) * (x2 - x1 ) - (x4 - x3 ) * (y2 - y1 );
515    u1 :=((x4 - x3 ) * (y1 - y3 ) - (y4 - y3 ) * (x1 - x3 ) ) / den;
516 
517    xi:=x1 + u1 * (x2 - x1);
518    yi:=y1 + u1 * (y2 - y1);
519 
520    dx:=xi - x;
521    dy:=yi - y;
522 
523    if (u1 > 0.0 ) and
524       (u1 < 1.0 ) and
525       (Sqrt(dx * dx + dy * dy ) <= m_point_radius ) then
526     ret:=true;
527 
528   end;
529 
530  result:=ret;
531 
532 end;
533 
534 { POINT_IN_POLYGON }
535 //======= Crossings Multiply algorithm of InsideTest ========================
536 //
537 // By Eric Haines, 3D/Eye Inc, erich@eye.com
538 //
539 // This version is usually somewhat faster than the original published in
540 // Graphics Gems IV; by turning the division for testing the X axis crossing
541 // into a tricky multiplication test this part of the test became faster,
542 // which had the additional effect of making the test for "both to left or
543 // both to right" a bit slower for triangles than simply computing the
544 // intersection each time.  The main increase is in triangle testing speed,
545 // which was about 15% faster; all other polygon complexities were pretty much
546 // the same as before.  On machines where division is very expensive (not the
547 // case on the HP 9000 series on which I tested) this test should be much
548 // faster overall than the old code.  Your mileage may (in fact, will) vary,
549 // depending on the machine and the test data, but in general I believe this
550 // code is both shorter and faster.  This test was inspired by unpublished
551 // Graphics Gems submitted by Joseph Samosky and Mark Haigh-Hutchinson.
552 // Related work by Samosky is in:
553 //
554 // Samosky, Joseph, "SectionView: A system for interactively specifying and
555 // visualizing sections through three-dimensional medical image data",
556 // M.S. Thesis, Department of Electrical Engineering and Computer Science,
557 // Massachusetts Institute of Technology, 1993.
558 //
559 // Shoot a test ray along +X axis.  The strategy is to compare vertex Y values
560 // to the testing point's Y and quickly discard edges which are entirely to one
561 // side of the test ray.  Note that CONVEX and WINDING code can be added as
562 // for the CrossingsTest() code; it is left out here for clarity.
563 //
564 // Input 2D polygon _pgon_ with _numverts_ number of vertices and test point
565 // _point_, returns 1 if inside, 0 if outside.
566 function interactive_polygon.point_in_polygon;
567 var
568  j ,k : unsigned;
569 
570  yflag0 ,yflag1 ,inside_flag : int;
571 
572  vtx0 ,vty0 ,vtx1 ,vty1 : double;
573 
574 begin
575  if m_num_points < 3 then
576   begin
577    result:=false;
578 
579    exit;
580 
581   end;
582 
583  vtx0:=xn(m_num_points - 1 );
584  vty0:=yn(m_num_points - 1 );
585 
586 // get test bit for above/below X axis
587  yflag0:=int(vty0 >= ty );
588 
589  vtx1:=xn(0 );
590  vty1:=yn(0 );
591 
592  inside_flag:=0;
593 
594  for j:=1 to m_num_points do
595   begin
596    yflag1:=int(vty1 >= ty );
597 
598   // Check if endpoints straddle (are on opposite sides) of X axis
599   // (i.e. the Y's differ); if so, +X ray could intersect this edge.
600   // The old test also checked whether the endpoints are both to the
601   // right or to the left of the test point.  However, given the faster
602   // intersection point computation used below, this test was found to
603   // be a break-even proposition for most polygons and a loser for
604   // triangles (where 50% or more of the edges which survive this test
605   // will cross quadrants and so have to have the X intersection computed
606   // anyway).  I credit Joseph Samosky with inspiring me to try dropping
607   // the "both left or both right" part of my code.
608    if yflag0 <> yflag1 then
609    // Check intersection of pgon segment with +X ray.
610    // Note if >= point's X; if so, the ray hits it.
611    // The division operation is avoided for the ">=" test by checking
612    // the sign of the first vertex wrto the test point; idea inspired
613    // by Joseph Samosky's and Mark Haigh-Hutchinson's different
614    // polygon inclusion tests.
615     if int((vty1 - ty ) * (vtx0 - vtx1 ) >= (vtx1 - tx ) * (vty0 - vty1 ) ) = yflag1 then
616      inside_flag:=inside_flag xor 1;
617 
618   // Move to the next pair of vertices, retaining info as possible.
619    yflag0:=yflag1;
620 
621    vtx0:=vtx1;
622    vty0:=vty1;
623 
624    if j >= m_num_points then
625     k:=j - m_num_points
626    else
627     k:=j;
628 
629    vtx1:=xn(k );
630    vty1:=yn(k );
631 
632   end;
633 
634  result:=inside_flag <> 0;
635 
636 end;
637 
638 END.
639