File Coverage

blib/lib/Math/Zap/Draw.pm
Criterion Covered Total %
statement 16 18 88.8
branch n/a
condition n/a
subroutine 6 6 100.0
pod n/a
total 22 24 91.6


line stmt bran cond sub pod time code
1            
2             =head1 Draw
3            
4             Draw 3d scene as 2d image with lighting and shadowing to assist the human
5             observer in reconstructing the original 3d scene.
6            
7             PhilipRBrenan@yahoo.com, 2004, Perl License
8            
9            
10             =head2 Synopsis
11            
12             Example t/draw.t
13            
14             #!perl -w
15             #______________________________________________________________________
16             # Test drawing.
17             # philiprbrenan@yahoo.com, 2004, Perl License
18             #______________________________________________________________________
19            
20             use Math::Zap::Draw;
21             use Math::Zap::Cube unit=>'cu';
22             use Math::Zap::Triangle;
23             use Math::Zap::Vector;
24            
25             use Test::Simple tests=>1;
26            
27             #_ Draw _______________________________________________________________
28             # Draw this set of objects.
29             #______________________________________________________________________
30            
31             $l =
32             draw
33             ->from (vector( 10, 10, 10))
34             ->to (vector( 0, 0, 0))
35             ->horizon (vector( 1, 0.5, 0))
36             ->light (vector( 20, 30, -20))
37            
38             ->object(triangle(vector( 0, 0, 0), vector( 8, 0, 0), vector( 0, 8, 0)), 'red')
39             ->object(triangle(vector( 0, 0, 0), vector( 0, 0, 8), vector( 0, 8, 0)), 'green')
40             ->object(triangle(vector( 0, 0, 0), vector(12, 0, 0), vector( 0, 0, 12)) - vector(2.5, 0, 2.5), 'blue')
41             ->object(triangle(vector( 0, 0, 0), vector( 8, 0, 0), vector( 0, -8, 0)), 'pink')
42             ->object(triangle(vector( 0, 0, 0), vector( 0, 0, 8), vector( 0, -8, 0)), 'orange')
43             ->object(cu()*2+vector(3,5,1), 'lightblue')
44            
45             ->print;
46            
47             $L = <<'END';
48             #!perl -w
49             use Math::Zap::Draw;
50             use Math::Zap::Triangle;
51             use Math::Zap::Vector;
52            
53             draw
54             ->from (vector(10, 10, 10))
55             ->to (vector(0, 0, 0))
56             ->horizon (vector(1, 0.5, 0))
57             ->light (vector(20, 30, -20))
58             ->object(triangle(vector(0, 0, 0), vector(8, 0, 0), vector(0, 8, 0)), 'red')
59             ->object(triangle(vector(0, 0, 0), vector(0, 0, 8), vector(0, 8, 0)), 'green')
60             ->object(triangle(vector(-2.5, 0, -2.5), vector(9.5, 0, -2.5), vector(-2.5, 0, 9.5)), 'blue')
61             ->object(triangle(vector(0, 0, 0), vector(8, 0, 0), vector(0, -8, 0)), 'pink')
62             ->object(triangle(vector(0, 0, 0), vector(0, 0, 8), vector(0, -8, 0)), 'orange')
63             ->object(triangle(vector(3, 5, 1), vector(5, 5, 1), vector(3, 7, 1)), 'lightblue')
64             ->object(triangle(vector(5, 7, 1), vector(5, 5, 1), vector(3, 7, 1)), 'lightblue')
65             ->object(triangle(vector(3, 5, 3), vector(5, 5, 3), vector(3, 7, 3)), 'lightblue')
66             ->object(triangle(vector(5, 7, 3), vector(5, 5, 3), vector(3, 7, 3)), 'lightblue')
67             ->object(triangle(vector(3, 5, 1), vector(3, 7, 1), vector(3, 5, 3)), 'lightblue')
68             ->object(triangle(vector(3, 7, 3), vector(3, 7, 1), vector(3, 5, 3)), 'lightblue')
69             ->object(triangle(vector(5, 5, 1), vector(5, 7, 1), vector(5, 5, 3)), 'lightblue')
70             ->object(triangle(vector(5, 7, 3), vector(5, 7, 1), vector(5, 5, 3)), 'lightblue')
71             ->object(triangle(vector(3, 5, 1), vector(3, 5, 3), vector(5, 5, 1)), 'lightblue')
72             ->object(triangle(vector(5, 5, 3), vector(3, 5, 3), vector(5, 5, 1)), 'lightblue')
73             ->object(triangle(vector(3, 7, 1), vector(3, 7, 3), vector(5, 7, 1)), 'lightblue')
74             ->object(triangle(vector(5, 7, 3), vector(3, 7, 3), vector(5, 7, 1)), 'lightblue')
75             ->done;
76             END
77            
78             ok($l eq $L);
79            
80            
81            
82             =head2 Description
83            
84             This package supplies methods to draw a scene, containing three dimensional
85             objects, as a two dimensional image, using lighting and shadowing to assist the
86             human observer in reconstructing the original three dimensional scene.
87            
88             There are many existing packages to perform this important task: this
89             package Math::Zap::Is the only one to make the attempt in Pure Perl. Pending the
90             $VERSION=1.07;
91             power of Petaflop Parallel Perl (when we will be set free from C), this
92             approach is slow. However, it is not so slow as to be completely useless
93             for simple scenes as might be encountered inside, say for instance, beam
94             lines used in high energy particle physics, the owners of which often
95             have large Perl computers.
96            
97             The key advantage of this package is that is open: you can manipulate
98             both the objects to be drawn and the drawing itself all in Pure Perl.
99            
100             =cut
101            
102            
103             package Math::Zap::Draw;
104             $VERSION=1.07;
105 1     1   7067 use Math::Zap::Vector check=>'vectorCheck';
  1         3  
  1         147  
106 1     1   638 use Math::Zap::Vector2;
  1         3  
  1         31  
107 1     1   639 use Math::Zap::Triangle2 newnnc=>'triangle2Newnnc';
  1         3  
  1         34  
108 1     1   871 use Math::Zap::Triangle;
  1         5  
  1         33  
109 1     1   761 use Math::Zap::Color;
  1         4  
  1         48  
110 1     1   1723 use Tk;
  0            
  0            
111             use Carp;
112            
113             use constant debug=>0;
114            
115            
116             =head2 Constructors
117            
118            
119             =head3 draw
120            
121             Constructor
122            
123             =cut
124            
125            
126             sub draw() {bless {}}
127            
128            
129             =head2 Methods
130            
131            
132             =head3 from
133            
134             Set view point
135            
136             =cut
137            
138            
139             sub from($$)
140             {my ($d) = check(@_[0..0]); # Drawing
141             my ($v) = vectorCheck(@_[1..1]); # Vector
142            
143             $d->{from} = $v;
144             $d;
145             }
146            
147            
148             =head3 to
149            
150             Viewing this point
151            
152             =cut
153            
154            
155             sub to($$)
156             {my ($d) = check(@_[0..0]); # Drawing
157             my ($v) = vectorCheck(@_[1..1]); # Vector
158            
159             $d->{to} = $v;
160             $d;
161             }
162            
163            
164             =head3 Horizon
165            
166             Sets the direction of the horizon.
167            
168             =cut
169            
170            
171             sub horizon($$)
172             {my ($d) = check(@_[0..0]); # Drawing
173             my ($v) = vectorCheck(@_[1..1]); # Vector
174            
175             $d->{horizon} = $v;
176             $d;
177             }
178            
179            
180             =head3 light
181            
182             Light source position
183            
184             =cut
185            
186            
187             sub light($$)
188             {my ($d) = check(@_[0..0]); # Drawing
189             my ($v) = vectorCheck(@_[1..1]); # Vector
190            
191             $d->{light} = $v;
192             $d;
193             }
194            
195            
196             =head3 withControls
197            
198             Display a window allowing the user to set to,from,horizon,light
199            
200             =cut
201            
202            
203             sub withControls($)
204             {my ($d) = check(@_[0..0]); # Drawing
205            
206             $d->{withControls} = 1;
207             $d;
208             }
209            
210             =head3 object
211            
212             Draw this object
213            
214             =cut
215            
216            
217             sub object($$$)
218             {my ($d) = check(@_[0..0]); # Drawing
219             my ($o) = @_[1..1]; # Object to be drawn
220             my ($c) = @_[2..2]; # Color of object's surfaces
221            
222             if ($o->can('triangulate'))
223             {push @{$d->{triangles}}, $o->triangulate($c);
224             }
225             else
226             {die "Cannot draw $o";
227             }
228             $d;
229             }
230            
231            
232             =head3 done
233            
234             Draw the complete object list
235            
236             =cut
237            
238            
239             sub done($)
240             {my ($d) = check(@_[0..0]); # Drawing
241             &fission($d);
242             &new($d);
243             }
244            
245            
246             =head2 Methods
247            
248            
249             =head3 print
250            
251             Print the complete object list as a triangles in a reusable manner.
252            
253             =cut
254            
255            
256             sub print($)
257             {my ($d) = check(@_[0..0]); # Drawing
258             my $l = << 'END';
259             #!perl -w
260             use Math::Zap::Draw;
261             use Math::Zap::Triangle;
262             use Math::Zap::Vector;
263            
264             draw
265             END
266             $l .= '->from ('. $d->{from} ->print .")\n";
267             $l .= '->to ('. $d->{to} ->print .")\n";
268             $l .= '->horizon ('. $d->{horizon}->print .")\n";
269             $l .= '->light ('. $d->{light} ->print .")\n";
270            
271             for my $p(@{$d->{triangles}}) # Triangulation
272             {$l .= ' ->object('. $p->{triangle}->print .', \''. $p->{color}. "\')\n";
273             }
274             $l .= "->done;\n";
275             }
276            
277            
278             =head3 check
279            
280             Check its a drawing
281            
282             =cut
283            
284            
285             sub check(@)
286             {if (debug)
287             {for my $t(@_)
288             {confess "$t is not a drawing" unless ref($t) eq __PACKAGE__;
289             }
290             }
291             return (@_)
292             }
293            
294            
295             =head3 is
296            
297             Test its a drawing
298            
299             =cut
300            
301            
302             sub is(@)
303             {for my $t(@_)
304             {return 0 unless ref($t) eq __PACKAGE__;
305             }
306             'draw';
307             }
308            
309            
310             =head3 showFissionFragments
311            
312             Show fission fragments: the objects to be drawn are triangulated
313             where-ever they may intersect. It is useful to see these sub triangles
314             when debugging. See also L.
315            
316             =cut
317            
318            
319             sub showFissionFragments($)
320             {my ($d) = check(@_[0..0]); # Drawing
321            
322             $d->{showFissionFragments} = 1;
323             $d;
324             }
325            
326            
327             =head3 Fission
328            
329             Fission the triangles that intersect. See L
330            
331             =cut
332            
333            
334             sub fission($)
335             {my ($d) = check(@_[0..0]); # Drawing
336             my @P = @{$d->{triangles}}; # Triangles to be fissoned
337             my $tested; # Source triangles already tested
338            
339             #_ Draw ________________________________________________________________
340             # Check each pair of triangles
341             #_______________________________________________________________________
342            
343             L: for(;;)
344             {for (my $i = 0; $i < scalar(@P); ++$i)
345             {my $p = $P[$i];
346             next unless defined($p);
347            
348             #_ Draw ________________________________________________________________
349             # Check against triangle
350             #_______________________________________________________________________
351            
352             for (my $j = $i+1; $j < scalar(@P); ++$j)
353             {my $q = $P[$j];
354             next unless defined($q);
355             my ($t, @t, @T);
356            
357             #_ Draw ________________________________________________________________
358             # Already tested
359             #_______________________________________________________________________
360            
361             next if $tested->{$p->{plane}}{$q->{plane}};
362             $tested->{$p->{plane}}{$q->{plane}} = 1;
363             $tested->{$q->{plane}}{$p->{plane}} = 1;
364             next if $p->{triangle}->parallel($q->{triangle});
365            
366             #_ Draw ________________________________________________________________
367             # Divide intersecting triangles
368             #_______________________________________________________________________
369            
370             @t = $p->{triangle}->divide($q->{triangle});
371             @T = $q->{triangle}->divide($p->{triangle});
372            
373             #_ Draw ________________________________________________________________
374             # Add divisions to list of triangles
375             #_______________________________________________________________________
376            
377             next unless @t > 1 or @T > 1;
378             delete $P[$i];
379             delete $P[$j];
380            
381             push @P, {triangle=>$_, color=>$q->{color}, plane=>$q->{plane}} for(@t);
382             push @P, {triangle=>$_, color=>$p->{color}, plane=>$p->{plane}} for(@T);
383             next L;
384             }
385             }
386             last;
387             }
388            
389             #_ Draw ________________________________________________________________
390             # Update list of triangles to be drawn
391             #_______________________________________________________________________
392            
393             my @p;
394             for my $p(@P)
395             {push @p, $p if defined($p);
396             }
397             $d->{triangles} = [@p];
398             }
399            
400            
401             =head3 new
402            
403             New drawing - not a constructor
404            
405             =cut
406            
407            
408             sub new($)
409             {my ($d) = check(@_[0..0]); # Drawing
410             &newCanvas ($d);
411             &newControl($d);
412             &drawing ($d, 1);
413             MainLoop;
414             }
415            
416            
417             =head3 newCanvas
418            
419             Canvas for drawing
420            
421             =cut
422            
423            
424             sub newCanvas($)
425             {my ($d) = check(@_[0..0]); # Drawing
426             my $m = $d->{MainWindowCanvas}
427             = new MainWindow;
428             my $c = $d->{canvas}
429             = $m->Canvas(-background=>'yellow')->pack(-expand=>1, -fill=>'both');
430            
431             $d->{canvas}{width} = $c->cget(-width=>);
432             $d->{canvas}{height} = $c->cget(-height=>);
433            
434             $c->CanvasBind('' => [$d=>'configure', Ev('w'), Ev('h')]);
435             }
436            
437            
438             =head3 newControl
439            
440             Controls for drawing
441            
442             =cut
443            
444            
445             sub newControl()
446             {my ($d) = check(@_[0..0]); # Drawing
447             my $m = $d->{MainWindowControls} = new MainWindow;
448            
449             my $a11 = $d->{a11} = $m->Label(-text=>'View point');
450             my $a12 = $d->{a12} = $m->Entry(-textvariable=>\$d->{from}->{x});
451             my $a13 = $d->{a13} = $m->Entry(-textvariable=>\$d->{from}->{y});
452             my $a14 = $d->{a14} = $m->Entry(-textvariable=>\$d->{from}->{z});
453             my $a21 = $d->{a21} = $m->Label(-text=>'Looking to');
454             my $a22 = $d->{a22} = $m->Entry(-textvariable=>\$d->{to}->{x});
455             my $a23 = $d->{a23} = $m->Entry(-textvariable=>\$d->{to}->{y});
456             my $a24 = $d->{a24} = $m->Entry(-textvariable=>\$d->{to}->{z});
457             my $a31 = $d->{a31} = $m->Label(-text=>'Horizontal');
458             my $a32 = $d->{a32} = $m->Entry(-textvariable=>\$d->{horizon}->{x});
459             my $a33 = $d->{a33} = $m->Entry(-textvariable=>\$d->{horizon}->{y});
460             my $a34 = $d->{a34} = $m->Entry(-textvariable=>\$d->{horizon}->{z});
461             my $a41 = $d->{a41} = $m->Label(-text=>'Lit from');
462             my $a42 = $d->{a42} = $m->Entry(-textvariable=>\$d->{light}->{x});
463             my $a43 = $d->{a43} = $m->Entry(-textvariable=>\$d->{light}->{y});
464             my $a44 = $d->{a44} = $m->Entry(-textvariable=>\$d->{light}->{z});
465             my $a51 = $d->{a51} = $m->Button(-text=>'Redraw', -command=>sub{&drawing($d, 1)});
466             my $a52 = $d->{a52} = $m->Button(-text=>'In');
467             my $a53 = $d->{a53} = $m->Button(-text=>'Out');
468             my $a54 = $d->{a54} = $m->Button(-text=>'Quit', -command=>sub{exit(0)});
469            
470             $a11->grid($a12, $a13, $a14);
471             $a21->grid($a22, $a23, $a24);
472             $a31->grid($a32, $a33, $a34);
473             $a41->grid($a42, $a43, $a44);
474             $a51->grid($a52, $a53, $a54);
475             }
476            
477            
478             =head3 Configure
479            
480             Configuration of canvas has been changed
481            
482             =cut
483            
484            
485             sub configure
486             {my ($d) = check(@_[0..0]); # Drawing
487             my $c = $d->{canvas};
488             $d->{canvas}{width} = $_[1];
489             $d->{canvas}{height} = $_[2];
490             &drawing($d, 0);
491             }
492            
493            
494             =head3 drawing
495            
496             New drawing of objects
497            
498             =cut
499            
500            
501             sub drawing($$)
502             {my ($d) = check(@_[0..0]); # Drawing
503             my $zorder = shift; # Re-sort of zorder required?
504            
505             #_ Draw ________________________________________________________________
506             # Locate background
507             #_______________________________________________________________________
508            
509             my $from = $d->{from}; # View point
510             my $lt = $d->{light}; # Light
511             my $to = $d->{to}; # View towards
512             my $hz = $d->{horizon}; # Horizon
513            
514             my $v = (($from-$to) x $hz)->norm; # Vertical in background plane
515             my $h = ($v x ($from-$to))->norm; # Horizontal in background plane
516             my $B = triangle($to, $to+$h, $to+$v); # Background plane
517             $d->{background} = $B;
518            
519             &zorder($d) if $zorder; # Partially order triangles from view point
520             $d->{canvas}->delete('all'); # Clear canvas
521            
522             #_ Draw ________________________________________________________________
523             # Dimensions of projected image
524             #_______________________________________________________________________
525            
526             my ($mx, $Mx, $my, $My);
527             for my $D(@{$d->{triangles}})
528             {my $t = $B->project($D->{triangle}, $from); # Project onto background
529             $D->{project} = $t; # Optimization - record for reuse
530            
531             my ($ax, $ay) = ($t->a->x, $t->a->y);
532             my ($bx, $by) = ($t->b->x, $t->b->y);
533             my ($cx, $cy) = ($t->c->x, $t->c->y);
534            
535             $mx = $ax if !defined($mx) or $mx > $ax;
536             $mx = $bx if !defined($mx) or $mx > $bx;
537             $mx = $cx if !defined($mx) or $mx > $cx;
538             $Mx = $ax if !defined($Mx) or $Mx < $ax;
539             $Mx = $bx if !defined($Mx) or $Mx < $bx;
540             $Mx = $cx if !defined($Mx) or $Mx < $cx;
541            
542             $my = $ay if !defined($my) or $my > $ay;
543             $my = $by if !defined($my) or $my > $by;
544             $my = $cy if !defined($my) or $my > $cy;
545             $My = $ay if !defined($My) or $My < $ay;
546             $My = $by if !defined($My) or $My < $by;
547             $My = $cy if !defined($My) or $My < $cy;
548             }
549            
550             my $cw = $d->{canvas}{width};
551             my $ch = $d->{canvas}{height};
552            
553             my $sx = int($d->{canvas}{width} /($Mx-$mx));
554             my $sy = int($d->{canvas}{height}/($My-$my));
555             my $s = $d->{canvas}{scale} = ($sx < $sy ? $sx : $sy);
556            
557             my $dx = $d->{canvas}{dx} = -$mx * $s + ($cw - $s * ($Mx-$mx)) / 2;
558             my $dy = $d->{canvas}{dy} = $My * $s + ($ch - $s * ($My-$my)) / 2;
559            
560             #_ Draw ________________________________________________________________
561             # Draw each triangle
562             #_______________________________________________________________________
563            
564             for my $D(@{$d->{triangles}})
565             {my $T = $D->{triangle};
566             my $color = $D->{color};
567             my $p = $D->{plane};
568             my $t = $D->{project};
569            
570             # Coordinates of triangle to be drawn
571             my @a = ($dx+$t->a->x*$s, $dy-$t->a->y*$s,
572             $dx+$t->b->x*$s, $dy-$t->b->y*$s,
573             $dx+$t->c->x*$s, $dy-$t->c->y*$s,
574             );
575             push @a, -outline=>'black' if defined($d->{showFissionFragments});
576            
577             #_ Draw ________________________________________________________________
578             # Side towards/away from the light
579             #_______________________________________________________________________
580            
581             my $fb = $T->frontInBehindZ($from, $lt);
582            
583             if (!defined($fb) or $fb < 0) # Towards light
584             {push @a, -fill=>$color;
585             $d->{canvas}->createPolygon(@a);
586             &shadows($d, $D);
587             }
588             else # Away from light
589             {$d->{canvas}->createPolygon(@a, -fill=>color($color)->dark);
590             }
591             }
592             }
593            
594            
595             =head3 shadows
596            
597             Shadows from a point of illumination
598            
599             =cut
600            
601            
602             sub shadows($$)
603             {my ($d) = check(@_[0..0]); # Drawing
604             my ($p) = (@_[1..1]); # Current triangle to be drawn
605             my $from = $d->{from}; # View point
606             my $to = $d->{to}; # Look towards
607             my $light = $d->{light}; # Position of light
608             my $back = $d->{background}; # Background
609             my $c = $d->{canvas}; # Canvas
610             my $dx = $d->{canvas}{dx}; # Canvas center x
611             my $dy = $d->{canvas}{dy}; # Canvas center y
612             my $s = $d->{canvas}{scale}; # Scale factor
613            
614             #_ Draw ________________________________________________________________
615             # Shadow each triangle
616             #_______________________________________________________________________
617            
618             my @s;
619             for my $q(@{$d->{triangles}})
620             {next if $p == $q; # Do not shadow self
621             next if $p->{plane} == $q->{plane}; # Do not shadow stuff in same plane
622             my $t = $p->{triangle}; # Shadowed triangle
623             my $T = $q->{triangle}; # Shadowing triangle
624             # next if $t->frontInBehindZ($from, $light) > 0; # Check that plane view point and light
625            
626             my $b = $t->project($T, $light); # Project Shadowing triangle onto shadowed triangle
627             my $d = triangle2Newnnc # Shadow in shadowed plane coordinates
628             (vector2($b->a->x, $b->a->y),
629             vector2($b->b->x, $b->b->y),
630             vector2($b->c->x, $b->c->y)
631             );
632             my $D = triangle2Newnnc # Shadowed plane
633             (vector2(0,0),
634             vector2(1,0),
635             vector2(0,1)
636             );
637             return if $d->narrow(); # Projected shadow too narrow?
638             return if $D->narrow(); # Shadowed triangle too narrow?
639            
640             my @r = $d->ring($D); # Ring of common points
641             if (scalar(@r) > 2) # Less than two - small intersection
642             {my @a;
643             for my $r(@r) # Points of intersection current/shadowing triangle
644             {my $sr = $t->convertPlaneToSpace($r); # Convert intersection to space coords
645             last if $T->frontInBehind($light, $sr) == 1; # $t gives back of shadowing plane
646             my $sb = $back->intersectionInPlane($from, $sr); # Project from view point onto background
647             push @a, $dx+$sb->x*$s, $dy-$sb->y*$s; # Save coordinates
648             }
649            
650             #_ Draw ________________________________________________________________
651             # Draw shadow
652             #_______________________________________________________________________
653            
654             push @a, -outline=>color($p->{color})->dark, -fill=>color($p->{color})->dark;
655             $c->createPolygon(@a);
656             }
657             }
658             }
659            
660            
661             =head4 zorder
662            
663             Z-order: order the fission triangles from the back ground to the point
664             of view:
665            
666             Compare each triangle with every other, recording for each triangle
667             which triangles are behind it.
668            
669             Place all triangles with no triangles behind them with at the start of
670             the order.
671            
672             Reprocess the remainder until none left (success) or a cycle is detected
673             (bad algorithm).
674            
675             The two triangles to be compared are projected on to the background: if
676             their projections have no points in common they are unordered, otherwise
677             use the distance to each triangle from the view point towards the common
678             point as a measure of which is first.
679            
680             fission() guarantees that no two triangles intersect, this algorithm
681             should correctly order each pair of triangles.
682            
683             =cut
684            
685            
686             sub zorder($)
687             {my ($d) = check(@_[0..0]); # Drawing
688            
689             my $from = $d->{from}; # View point
690             my $back = $d->{background}; # Background
691             my @P = @{$d->{triangles}}; # Triangles to be drawn
692            
693             #_ Draw ________________________________________________________________
694             # Filter for useful triangles
695             #_______________________________________________________________________
696            
697             my @o;
698             for(my $ip = 0; $ip < @P; ++$ip)
699             {my $t = $P[$ip]{triangle};
700             # next unless $t->area > .1; # Ignore small triangles
701             # next if $t->narrow(0);
702            
703             $o{$ip} = {};
704             push @o, $ip;
705             }
706            
707             #_ Draw ________________________________________________________________
708             # Relationship
709             #_______________________________________________________________________
710            
711             for my $ip(@o)
712             {my $t = $P[$ip]{triangle};
713            
714             for my $jp(@o)
715             {next unless $ip < $jp;
716             my $T = $P[$jp]{triangle};
717             my $i = $back->project($t, $from);
718             my $I = $back->project($T, $from);
719            
720             my $i2 = triangle2Newnnc(vector2($i->a->x, $i->a->y), vector2($i->b->x, $i->b->y), vector2($i->c->x, $i->c->y));
721             my $I2 = triangle2Newnnc(vector2($I->a->x, $I->a->y), vector2($I->b->x, $I->b->y), vector2($I->c->x, $I->c->y));
722             # next if $i2->narrow(0);
723             # next if $I2->narrow(0);
724            
725             my @c = $i2->pointsInCommon($I2);
726             next unless scalar(@c);
727            
728             for my $c(@c)
729             {my $C = $back->convertPlaneToSpace($c);
730             my $d = $t->distanceToPlaneAlongLine($from, $C);
731             my $D = $T->distanceToPlaneAlongLine($from, $C);
732             next if abs($d-$D) < 0.1; # Points to close in space to disambiguate
733            
734             $o{$ip}{$jp} = 1 if $d < $D; # Assumes order does not matter for coplanar triangles
735             $o{$jp}{$ip} = 1 if $d > $D; # Assumes order does not matter for coplanar triangles
736             last;
737             }
738             }
739             }
740            
741             #_ Draw ________________________________________________________________
742             # Order by relationship
743             #_______________________________________________________________________
744            
745             my @p;
746             for(;;)
747             {my $n = 0;
748             for my $i(sort(keys(%o)))
749             {unless (keys(%{$o{$i}}))
750             {push @p, $P[$i];
751             delete $o{$i};
752             ++$n;
753             for my $j(keys(%o))
754             {delete $o{$j}{$i};
755             }
756             }
757             }
758             last unless $n;
759             }
760             keys(%o) == 0 or warn "Cycle present??";
761             $d->{triangles} = [@p];
762             }
763            
764            
765             =head2 Exports
766            
767             Export L
768            
769             =cut
770            
771            
772             use Math::Zap::Exports qw(
773             draw ()
774             );
775            
776             #_ Draw ________________________________________________________________
777             # Package loaded successfully
778             #_______________________________________________________________________
779            
780             1;
781            
782            
783            
784             =head2 Credits
785            
786             =head3 Author
787            
788             philiprbrenan@yahoo.com
789            
790             =head3 Copyright
791            
792             philiprbrenan@yahoo.com, 2004
793            
794             =head3 License
795            
796             Perl License.
797            
798            
799             =cut