1 /*
2  * curves.cc -- polygons, ellipses, circular arcs, splines
3  *
4  * This file is part of ePiX, a C++ library for creating high-quality
5  * figures in LaTeX
6  *
7  * Version 1.2.6
8  * Last Change: January 31, 2009
9  */
10 
11 /*
12  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
13  * Andrew D. Hwang <rot 13 nujnat at zngupf dot ubylpebff dot rqh>
14  * Department of Mathematics and Computer Science
15  * College of the Holy Cross
16  * Worcester, MA, 01610-2395, USA
17  */
18 
19 /*
20  * ePiX is free software; you can redistribute it and/or modify it
21  * under the terms of the GNU General Public License as published by
22  * the Free Software Foundation; either version 2 of the License, or
23  * (at your option) any later version.
24  *
25  * ePiX is distributed in the hope that it will be useful, but WITHOUT
26  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
27  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
28  * License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with ePiX; if not, write to the Free Software Foundation, Inc.,
32  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33  */
34 
35 #include <cmath>
36 
37 #include "constants.h"
38 #include "triples.h"
39 #include "errors.h"
40 
41 #include "functions.h"
42 #include "pairs.h"
43 
44 #include "camera.h"
45 
46 #include "active_screen.h"
47 #include "screen.h"
48 
49 #include "paint_style.h"
50 
51 #include "state.h"
52 #include "domain.h"
53 
54 #include "arrow_data.h"
55 #include "path.h"
56 #include "picture.h"
57 
58 #include "spline.h"
59 #include "spline_data.h"
60 #include "curves.h"
61 
62 namespace ePiX {
63 
64   // Simple geometric objects
65 
66   // Lines take a stretch factor, roughly in percent
line(const P & tail,const P & head,double expand)67   void line(const P& tail, const P& head, double expand)
68   {
69     unsigned int num_pts(cam().is_linear() ? 1 : EPIX_NUM_PTS);
70 
71     path data(tail, head, expand, num_pts);
72     data.draw();
73   }
74 
line(const P & tail,const P & head,double expand,unsigned int num_pts)75   void line(const P& tail, const P& head, double expand,
76 	    unsigned int num_pts)
77   {
78     if (!cam().is_linear())
79       num_pts = (unsigned int) max(num_pts, EPIX_NUM_PTS);
80 
81     path data(tail, head, expand, num_pts);
82     data.draw();
83   }
84 
85   // Line(p1, p2) -- draw uncropped portion of long line through p1, p2
Line(const P & arg1,const P & arg2)86   void Line(const P& arg1, const P& arg2)
87   {
88     P dir(arg2-arg1);
89     const double denom(norm(dir));
90 
91     if (EPIX_EPSILON < denom)
92       {
93 	// CAUTION: Magic numbers
94 	dir *= 1/denom;
95 	line(arg1-100*dir, arg1+100*dir, 0, cam().is_linear() ? 1 : 400);
96       }
97   } // end of Line
98 
99 
100   // point-slope form
Line(const P & tail,double slope)101   void Line(const P& tail, double slope)
102   {
103     Line(tail, tail+P(1, slope, 0));
104   }
105 
106 
triangle(const P & p1,const P & p2,const P & p3)107   void triangle(const P& p1, const P& p2, const P& p3)
108   {
109     path data;
110     if (cam().is_linear())
111       data.pt(p1).pt(p2).pt(p3);
112     else
113       {
114 	// Magic number 60
115 	const unsigned int N(60);
116 	const double dt(1.0/N);
117 
118 	const P step12(dt*(p2-p1));
119 	const P step23(dt*(p3-p2));
120 	const P step31(dt*(p1-p3));
121 
122 	for (unsigned int i=0; i<N; ++i)
123 	  data.pt(p1 + i*step12);
124 
125 	for (unsigned int i=0; i<N; ++i)
126 	  data.pt(p2 + i*step23);
127 
128 	for (unsigned int i=0; i<N; ++i)
129 	  data.pt(p3 + i*step31);
130       }
131 
132     data.close().fill(the_paint_style().fill_flag());
133     data.draw();
134   }
135 
quad(const P & p1,const P & p2,const P & p3,const P & p4)136   void quad(const P& p1, const P& p2, const P& p3, const P& p4)
137   {
138     path data;
139     if (cam().is_linear())
140       data.pt(p1).pt(p2).pt(p3).pt(p4);
141     else
142       {
143 	// Magic number 60 -> quad has 240 pts, is printed in one segment
144 	const unsigned int N(60);
145 	const double dt(1.0/N);
146 
147 	const P step12(dt*(p2-p1));
148 	const P step23(dt*(p3-p2));
149 	const P step34(dt*(p4-p3));
150 	const P step41(dt*(p1-p4));
151 
152 	for (unsigned int i=0; i<N; ++i)
153 	  data.pt(p1 + i*step12);
154 
155 	for (unsigned int i=0; i<N; ++i)
156 	  data.pt(p2 + i*step23);
157 
158 	for (unsigned int i=0; i<N; ++i)
159 	  data.pt(p3 + i*step34);
160 
161 	for (unsigned int i=0; i<N; ++i)
162 	  data.pt(p4 + i*step41);
163       }
164 
165     data.close().fill(the_paint_style().fill_flag());
166     data.draw();
167   }
168 
169   // Draw coordinate rectangle with opposite corners as given. Arguments
170   // must lie is a plane parallel to a coordinate plane, but not on a
171   // line parallel to a coordinate axis.
172 
rect(const P & p1,const P & p2)173   void rect(const P& p1, const P& p2)
174   {
175     P diagonal(p2 - p1);
176     P jump;
177     int perp_count(0);
178 
179     // count coordinate axes perp to diagonal and flag normal
180     if (fabs(diagonal|E_1) < EPIX_EPSILON)
181       {
182 	++perp_count;
183 	jump = E_2&(diagonal);
184       }
185     if (fabs(diagonal|E_2) < EPIX_EPSILON)
186       {
187 	++perp_count;
188 	jump = E_3&(diagonal);
189       }
190     if (fabs(diagonal|E_3) < EPIX_EPSILON)
191       {
192 	++perp_count;
193 	jump = E_1&(diagonal);
194       }
195 
196     quad(p1, p1+jump, p2, p2-jump);
197   } // end rect
198 
dart(const P & tail,const P & head)199   void dart(const P& tail, const P& head)
200   {
201     arrow(tail, head, 0.5);
202   }
203 
aarrow(const P & tail,const P & head,double scale)204   void aarrow(const P& tail, const P& head, double scale)
205   {
206     P midpt(0.5*(tail+head));
207     arrow(midpt, tail, scale);
208     arrow(midpt, head, scale);
209   }
210 
ellipse(const P & center,const P & axis1,const P & axis2,double t_min,double t_max,unsigned int num_pts)211   void ellipse(const P& center, const P& axis1, const P& axis2,
212 	       double t_min, double t_max, unsigned int num_pts)
213   {
214     path data(center, axis1, axis2, t_min, t_max, num_pts);
215 
216 
217     if (min(fabs(t_max-t_min)/full_turn(), 1) == 1)
218       {
219 	data.close();
220 	if (the_paint_style().fill_flag())
221 	  data.fill();
222       }
223     data.draw();
224   }
225 
ellipse(const P & center,const P & axis1,const P & axis2,double t_min,double t_max)226   void ellipse(const P& center, const P& axis1, const P& axis2,
227 	       double t_min, double t_max)
228   {
229     ellipse(center, axis1, axis2, t_min, t_max, EPIX_NUM_PTS);
230   }
231 
232 
ellipse(const P & center,const P & axis1,const P & axis2)233   void ellipse(const P& center, const P& axis1, const P& axis2)
234   {
235     ellipse(center, axis1, axis2, 0, full_turn());
236   }
237 
238 
ellipse_arc(const P & center,const P & axis1,const P & axis2,double t_min,double t_max)239   void ellipse_arc(const P& center, const P& axis1, const P& axis2,
240 		   double t_min, double t_max)
241   {
242     ellipse(center, axis1, axis2, t_min, t_max);
243   }
244 
245 
arrow(const P & tail,const P & head,double scale)246   void arrow(const P& tail, const P& head, double scale)
247   {
248     std::vector<P> shaft(2);
249     shaft.at(0) = tail;
250     shaft.at(1) = head;
251 
252     arrow_data data(shaft, tail, head, scale);
253     data.draw();
254   }
255 
ellipse(const P & center,const P & radius)256   void ellipse(const P& center, const P& radius)
257   {
258     ellipse(center, radius.x1()*E_1, radius.x2()*E_2);
259   }
260 
261   // Standard half-ellipse functions
ellipse_left(const P & center,const P & radius)262   void ellipse_left (const P& center, const P& radius)
263   {
264     ellipse(center, radius.x1()*E_1, radius.x2()*E_2,
265 	    0.25*full_turn(), 0.75*full_turn());
266   }
267 
ellipse_right(const P & center,const P & radius)268   void ellipse_right (const P& center, const P& radius)
269   {
270     ellipse(center, radius.x1()*E_1, radius.x2()*E_2,
271 	    -0.25*full_turn(), 0.25*full_turn());
272   }
273 
ellipse_top(const P & center,const P & radius)274   void ellipse_top (const P& center, const P& radius)
275   {
276     ellipse(center, radius.x1()*E_1, radius.x2()*E_2, 0, 0.5*full_turn());
277   }
278 
ellipse_bottom(const P & center,const P & radius)279   void ellipse_bottom (const P& center, const P& radius)
280   {
281     ellipse(center, radius.x1()*E_1, radius.x2()*E_2, -0.5*full_turn(), 0);
282   }
283 
arc(const P & center,double r,double start,double finish)284   void arc(const P& center, double r,
285 	   double start,  double finish)
286   {
287     ellipse(center, r*E_1, r*E_2, start, finish);
288   }
289 
arrow(const P & center,const P & axis1,const P & axis2,double t_min,double t_max,double scale)290   void arrow(const P& center, const P& axis1, const P& axis2,
291 	     double t_min, double t_max, double scale)
292   {
293     // EPIX_NUM_PTS pts = one full turn; scale accordingly
294     double frac(fabs(t_max-t_min)/full_turn());
295     unsigned int num_pts((unsigned int) max(2, ceil(frac*EPIX_NUM_PTS)));
296 
297     const double dt((t_max - t_min)/num_pts);
298 
299     std::vector<P> shaft(num_pts+1);
300 
301     for (unsigned int i=0; i <= num_pts; ++i)
302       {
303 	double t(t_min + i*dt);
304 	shaft.at(i) = center + ((Cos(t)*axis1)+(Sin(t)*axis2));
305       }
306 
307     arrow_data data(shaft, shaft.at(num_pts-1), shaft.at(num_pts), scale);
308     data.draw();
309   }
310 
311   // circular arcs parallel to (x,y)-plane
312 
arc_arrow(const P & center,double r,double start,double finish,double scale)313   void arc_arrow(const P& center, double r,
314 		 double start, double finish, double scale)
315   {
316     arrow(center, r*E_1, r*E_2, start, finish, scale);
317   }
318 
319 
320   // quadratic spline
spline(const P & p1,const P & p2,const P & p3,unsigned int num_pts)321   void spline(const P& p1, const P& p2, const P& p3, unsigned int num_pts)
322   {
323     path data(p1, p2, p3, num_pts);
324     data.draw();
325   }
326 
spline(const P & p1,const P & p2,const P & p3)327   void spline(const P& p1, const P& p2, const P& p3)
328   {
329     spline(p1, p2, p3, EPIX_NUM_PTS);
330   }
331 
arrow(const P & p1,const P & p2,const P & p3,double scale)332   void arrow(const P& p1, const P& p2, const P& p3, double scale)
333   {
334     const unsigned int num_pts(EPIX_NUM_PTS);
335     const double dt(1.0/num_pts);
336     std::vector<P> shaft(num_pts+1);
337 
338     for (unsigned int i=0; i <= num_pts; ++i)
339       shaft.at(i) = spl_pt(p1, p2, p3, i*dt);
340 
341     arrow_data data(shaft, shaft.at(num_pts-1), shaft.at(num_pts), scale);
342     data.draw();
343   }
344 
345   // cubic spline
spline(const P & p1,const P & p2,const P & p3,const P & p4,unsigned int num_pts)346   void spline(const P& p1, const P& p2,
347 	      const P& p3, const P& p4, unsigned int num_pts)
348   {
349     path data(p1, p2, p3, p4, num_pts);
350     data.draw();
351   }
352 
spline(const P & p1,const P & p2,const P & p3,const P & p4)353   void spline(const P& p1, const P& p2, const P& p3, const P& p4)
354   {
355     spline(p1, p2, p3, p4, EPIX_NUM_PTS);
356   }
357 
358   // natural spline through points
spline(const std::vector<P> & data,unsigned int num_pts)359   void spline(const std::vector<P>& data, unsigned int num_pts)
360   {
361     n_spline tmp(data, data.at(0) == data.at(data.size()-1));
362     path trace(tmp.data(num_pts));
363 
364     trace.draw();
365   }
366 
arrow(const P & p1,const P & p2,const P & p3,const P & p4,double scale)367   void arrow(const P& p1, const P& p2, const P& p3, const P& p4, double scale)
368   {
369     const unsigned int num_pts(EPIX_NUM_PTS);
370     const double dt(1.0/num_pts);
371     std::vector<P> shaft(num_pts+1);
372 
373     for (unsigned int i=0; i <= num_pts; ++i)
374       shaft.at(i) = spl_pt(p1, p2, p3, p4, i*dt);
375 
376     arrow_data data(shaft, shaft.at(num_pts-1), shaft.at(num_pts), scale);
377     data.draw();
378   }
379 
380 
381   // n1 x n2 Cartesian grid, where coarse = (n1, n2)
grid(const P & p1,const P & p2,mesh coarse,mesh fine)382   void grid(const P& p1, const P& p2, mesh coarse, mesh fine)
383   {
384     P diagonal(p2 - p1);
385     P jump1, jump2; // sides of grid
386 
387     int perp_count(0);
388 
389     int N1(coarse.n1());
390     int N2(coarse.n2());
391 
392     // count coordinate axes diagonal is perp to and flag normal
393     if (fabs(diagonal|E_1) < EPIX_EPSILON)
394       {
395 	++perp_count;
396 	jump1 = E_2&diagonal;
397 	jump2 = E_3&diagonal;
398 
399       }
400     if (fabs(diagonal|E_2) < EPIX_EPSILON)
401       {
402 	++perp_count;
403 	jump1 = E_3&diagonal;
404 	jump2 = E_1&diagonal;
405       }
406     if (fabs(diagonal|E_3) < EPIX_EPSILON)
407       {
408 	++perp_count;
409 	jump1 = E_1&diagonal;
410 	jump2 = E_2&diagonal;
411       }
412 
413     if (perp_count != 1)
414       epix_warning("Ignoring degenerate coordinate grid");
415 
416     else
417       {
418 	// grid line spacing
419 	P grid_step1((1.0/N1)*jump1);
420 	P grid_step2((1.0/N2)*jump2);
421 
422 	// makes grid subject to filling
423 	rect(p1, p1 + jump1 + jump2);
424 
425 	for (int i=1; i < N1; ++i)
426 	  line(p1+i*grid_step1, p1+i*grid_step1+jump2, 0, fine.n2());
427 
428 	for (int j=1; j < N2; ++j)
429 	  line(p1+j*grid_step2, p1+j*grid_step2+jump1, 0, fine.n1());
430       }
431   }
432 
433   // Grids that fill bounding_box with default camera
grid(const P & p1,const P & p2,unsigned int n1,unsigned int n2)434   void grid(const P& p1, const P& p2, unsigned int n1, unsigned int n2)
435   {
436     grid(p1, p2, mesh(n1, n2), mesh(1,1));
437   }
438 
grid(unsigned int n1,unsigned int n2)439   void grid(unsigned int n1, unsigned int n2)
440   {
441     grid(active_screen()->bl(), active_screen()->tr(), n1, n2);
442   }
443 
444 
445   // polar grid with specified radius, mesh (rings and sectors), and resolution
polar_grid(double radius,mesh coarse,mesh fine)446   void polar_grid(double radius, mesh coarse, mesh fine)
447   {
448     for (int i=1; i <= coarse.n1(); ++i)
449       ellipse(P(0,0,0),
450 	      (i*radius/coarse.n1())*E_1, (i*radius/coarse.n1())*E_2,
451 	      0, full_turn(), fine.n2());
452 
453     for (int j=0; j < coarse.n2(); ++j)
454       line(P(0,0,0), polar(radius, j*(full_turn())/coarse.n2()),
455 	   0, 2*fine.n1());
456   }
457 
polar_grid(double radius,unsigned int n1,unsigned int n2)458   void polar_grid(double radius, unsigned int n1, unsigned int n2)
459   {
460     polar_grid(radius, mesh(n1,n2), mesh(n1,EPIX_NUM_PTS));
461   }
462 
463 
464   // logarithmic grids
465 
466   // local helpers
grid_lines1_log(double x_lo,double x_hi,double y_lo,double y_hi,unsigned int segs,unsigned int base)467   void grid_lines1_log(double x_lo, double x_hi, double y_lo, double y_hi,
468 		       unsigned int segs, unsigned int base)
469   {
470     if (segs == 0)
471       return;
472 
473     const double dx((x_hi-x_lo)/segs); // "major grid" steps
474     const double denom(log(base));  // "minor"/log grid scale factor
475 
476     for (unsigned int i=0; i < segs; ++i)
477       for (unsigned int j=1; j<base; ++j)
478 	{
479 	  double x_tmp(x_lo + dx*(i+log(j)/denom));
480 
481 	  line(P(x_tmp, y_lo), P(x_tmp, y_hi));
482 	}
483 
484     line(P(x_hi,y_lo), P(x_hi, y_hi)); // draw rightmost line manually
485   }
486 
grid_lines2_log(double x_lo,double x_hi,double y_lo,double y_hi,unsigned int segs,unsigned int base)487   void grid_lines2_log(double x_lo, double x_hi, double y_lo, double y_hi,
488 		       unsigned int segs, unsigned int base)
489   {
490     if (segs == 0)
491       return;
492 
493     const double dy((y_hi-y_lo)/segs);
494     const double denom(log(base));
495 
496     for (unsigned int i=0; i < segs; ++i)
497       for (unsigned int j=1; j<base; ++j)
498 	{
499 	  double y_tmp(y_lo + dy*(i+log(j)/denom));
500 
501 	  line(P(x_lo, y_tmp), P(x_hi, y_tmp));
502 	}
503 
504     line(P(x_hi,y_lo), P(x_hi, y_hi));
505   }
506 
507   // global functions
log_grid(const P & p1,const P & p2,unsigned int segs1,unsigned int segs2,unsigned int base1,unsigned int base2)508   void log_grid(const P& p1, const P& p2,
509 		unsigned int segs1, unsigned int segs2,
510 		unsigned int base1, unsigned int base2)
511   {
512     grid_lines1_log(min(p1.x1(), p2.x1()), max(p1.x1(), p2.x1()),
513 		    min(p1.x2(), p2.x2()), max(p1.x2(), p2.x2()),
514 		    segs1, base1);
515 
516     grid_lines2_log(min(p1.x1(), p2.x1()), max(p1.x1(), p2.x1()),
517 		    min(p1.x2(), p2.x2()), max(p1.x2(), p2.x2()),
518 		    segs2, base2);
519   }
520 
log1_grid(const P & p1,const P & p2,unsigned int segs1,unsigned int segs2,unsigned int base1)521   void log1_grid(const P& p1, const P& p2,
522 		 unsigned int segs1, unsigned int segs2,
523 		 unsigned int base1)
524   {
525     grid_lines1_log(min(p1.x1(), p2.x1()), max(p1.x1(), p2.x1()),
526 		    min(p1.x2(), p2.x2()), max(p1.x2(), p2.x2()),
527 		    segs1, base1);
528 
529     grid_lines2_log(min(p1.x1(), p2.x1()), max(p1.x1(), p2.x1()),
530 		    min(p1.x2(), p2.x2()), max(p1.x2(), p2.x2()),
531 		    segs2, 2);
532   }
533 
log2_grid(const P & p1,const P & p2,unsigned int segs1,unsigned int segs2,unsigned int base2)534   void log2_grid(const P& p1, const P& p2,
535 		 unsigned int segs1, unsigned int segs2,
536 		 unsigned int base2)
537   {
538     grid_lines1_log(min(p1.x1(), p2.x1()),
539 		    max(p1.x1(), p2.x1()),
540 		    min(p1.x2(), p2.x2()),
541 		    max(p1.x2(), p2.x2()),
542 		    segs1, 2);
543 
544     grid_lines2_log(min(p1.x1(), p2.x1()),
545 		    max(p1.x1(), p2.x1()),
546 		    min(p1.x2(), p2.x2()),
547 		    max(p1.x2(), p2.x2()),
548 		    segs2, base2);
549   }
550 
551 
552   // grids fill current bounding box
log_grid(unsigned int segs1,unsigned int segs2,unsigned int base1,unsigned int base2)553   void log_grid(unsigned int segs1, unsigned int segs2,
554 		unsigned int base1, unsigned int base2)
555   {
556     grid_lines1_log(active_screen()->h_min(), active_screen()->h_max(),
557 		    active_screen()->v_min(), active_screen()->v_max(),
558 		    segs1, base1);
559 
560     grid_lines2_log(active_screen()->h_min(), active_screen()->h_max(),
561 		    active_screen()->v_min(), active_screen()->v_max(),
562 		    segs2, base2);
563   }
564 
log1_grid(unsigned int segs1,unsigned int segs2,unsigned int base1)565   void log1_grid(unsigned int segs1, unsigned int segs2,
566 		 unsigned int base1)
567   {
568     grid_lines1_log(active_screen()->h_min(), active_screen()->h_max(),
569 		    active_screen()->v_min(), active_screen()->v_max(),
570 		    segs1, base1);
571 
572     grid_lines2_log(active_screen()->h_min(), active_screen()->h_max(),
573 		    active_screen()->v_min(), active_screen()->v_max(),
574 		    segs2, 2);
575   }
576 
log2_grid(unsigned int segs1,unsigned int segs2,unsigned int base2)577   void log2_grid(unsigned int segs1, unsigned int segs2,
578 		 unsigned int base2)
579   {
580     grid_lines1_log(active_screen()->h_min(), active_screen()->h_max(),
581 		    active_screen()->v_min(), active_screen()->v_max(),
582 		    segs1, 2);
583 
584     grid_lines2_log(active_screen()->h_min(), active_screen()->h_max(),
585 		    active_screen()->v_min(), active_screen()->v_max(),
586 		    segs2, base2);
587   }
588 
589 
590   // fractal generation
591   //
592   // The basic recursion unit is a piecewise-linear path whose segments
593   // are parallel to spokes on a wheel, labelled modulo <spokes>.
594   // Recursively up to <depth>, each segment is replaced by a copy of the
595   // recursion unit, scaled and rotated in order to join p to q.
596   //
597   // Kludge: pre_seed[0] = spokes, pre_seed[1] = length of seed;
598   //
599   // Sample data for _/\_ standard Koch snowflake:
600   // const int seed[] = {6, 4, 0, 1, -1, 0};
601 
jump(int spokes,int length,const std::vector<int> & seed)602   P jump(int spokes, int length, const std::vector<int>& seed)
603   {
604     P sum(P(0,0));
605 
606     for (int i=0; i< length; ++i)
607       sum += cis(seed.at(i)*(full_turn())/spokes);
608 
609     return sum;
610   }
611 
fractal(const P & p,const P & q,const int depth,const int * pre_seed)612   void fractal(const P& p, const P& q, const int depth, const int *pre_seed)
613   {
614     int spokes(pre_seed[0]);
615     int seed_length(pre_seed[1]);
616     std::vector<int> seed(seed_length);
617 
618     // extract seed from pre_seed
619     for (int i=0; i<seed_length; ++i)
620       seed.at(i) = pre_seed[i+2];
621 
622     // Unit-length steps in <seed> sequence add up to <scale>
623     P scale(jump(spokes, seed_length, seed));
624 
625     // Number of points in final fractal
626     int length(1+(int)pow(seed_length, depth));
627     std::vector<int> dir(length); // stepping information
628     std::vector<P> data(length);  // vertices
629 
630     // dir[] starts out [0, 1, -1, 0, ..., 0] (seed_length^depth entries)
631     // then take repeated "Kronecker sum" with seed = [0, 1, -1, 0]
632 
633     for(int i=0; i<seed_length; ++i)
634       dir.at(i) = seed.at(i);
635 
636     for(int i=1; i<depth; ++i) // recursively fill dir array
637       for(int j=0; j < pow(seed_length,i); ++j)
638 	for(int k=seed_length-1; 0 < k; --k)
639 	  dir.at(k*(int)pow(seed_length,i) + j) = dir.at(j) + seed.at(k);
640 
641     P curr(p);
642     // 10/09/06: -depth -> 1-depth
643     double radius(pow(norm(scale), 1-depth));
644 
645     for(int i=0; i<length; ++i)
646       {
647 	data.at(i) = curr;
648 	// increment between successive points as a pair
649 	pair temp((polar(radius, dir.at(i)*(full_turn())/spokes))*pair(q-p));
650 
651 	// complex arithmetic
652 	temp /= pair(scale); // homothety to join p to q
653 
654 	curr += P(temp.x1(), temp.x2());
655       }
656 
657     path fractal(data, false, false);
658     fractal.draw();
659   }
660 
661 } // end of namespace
662