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