1
2% Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
3% All rights reserved.
4%
5% Redistribution and use in source and binary forms, with or without
6% modification, are permitted provided that the following conditions are
7% met:
8%
9%     - Redistributions of source code must retain the above copyright
10%       notice, this list of conditions and the following disclaimer.
11%
12%     - Redistributions in binary form must reproduce the above copyright
13%       notice, this list of conditions and the following disclaimer in
14%       the documentation and/or other materials provided with the
15%       distribution.
16%
17%     - Neither the name of The Numerical ALgorithms Group Ltd. nor the
18%       names of its contributors may be used to endorse or promote products
19%       derived from this software without specific prior written permission.
20%
21% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22% IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23% TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
24% PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
25% OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26% EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES-- LOSS OF USE, DATA, OR
28% PROFITS-- OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29% LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30% NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31% SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33% *********************************************************************
34\head{chapter}{Programs for FriCAS Images}{ugAppGraphics}
35% *********************************************************************
36%
37This appendix contains the \Language{} programs used to generate
38the images in the \Gallery{} color insert of this book.
39All these input files are included
40with the \Language{} system.
41To produce the images
42on page 6 of the \Gallery{} insert, for example, issue the command:
43\begin{verbatim}
44)read images6
45\end{verbatim}
46
47These images were produced on an IBM RS/6000 model 530 with a
48standard color graphics adapter.  The smooth shaded images
49were made from X Window System screen dumps.
50The remaining images were produced with \Language{}-generated
51PostScript output.  The images were reproduced from slides made on an Agfa
52ChromaScript PostScript interpreter with a Matrix Instruments QCR camera.
53
54% *********************************************************************
55\head{section}{images1.input}{ugFimagesOne}
56% *********************************************************************
57
58\begin{xmpLinesPlain}
59)read tknot                                              -- Read torus knot program.
60
61torusKnot(15,17, 0.1, 6, 700)                            -- {\bf A (15,17) torus knot.}
62\end{xmpLinesPlain}
63\index{torus knot}
64
65\newpage
66% *********************************************************************
67\head{section}{images2.input}{ugFimagesTwo}
68% *********************************************************************
69
70These images illustrate how Newton's method converges when computing the
71\index{Newton iteration}
72complex cube roots of 2.   Each point in the \smath{(x,y)}-plane represents the
73complex number \smath{x + iy,} which is given as a starting point for Newton's
74method.  The poles in these images represent bad starting values.
75The flat areas are the regions of convergence to the three roots.
76
77\begin{xmpLinesPlain}
78)read newton                                             -- Read the programs from
79)read vectors                                            -- \quad{}Chapter 10.
80f := newtonStep(x^3 - 2)                                 -- Create a Newton's iteration
81                                                         -- \quad{}function for $x^3 = 2$.
82\end{xmpLinesPlain}
83
84The function \texht{$f^n$}{f^n} computes $n$ steps of Newton's method.
85
86\begin{xmpLinesNoResetPlain}
87clipValue := 4                                           -- Clip values with magnitude $> 4$.
88drawComplexVectorField(f^3, -3..3, -3..3)                -- The vector field for $f^3$
89drawComplex(f^3, -3..3, -3..3)                           -- The surface for $f^3$
90drawComplex(f^4, -3..3, -3..3)                           -- The surface for $f^4$
91\end{xmpLinesNoResetPlain}
92
93% *********************************************************************
94\head{section}{images3.input}{ugFimagesThree}
95% *********************************************************************
96
97\begin{xmpLinesPlain}
98)r tknot
99for i in 0..4 repeat torusKnot(2, 2 + i/4, 0.5, 25, 250)
100\end{xmpLinesPlain}
101
102% *********************************************************************
103\head{section}{images5.input}{ugFimagesFive}
104% *********************************************************************
105
106The parameterization of the Etruscan Venus is due to George Frances.
107\index{Etruscan Venus}
108
109\begin{xmpLinesPlain}
110venus(a,r,steps) ==
111  surf := (u:DFLOAT, v:DFLOAT): Point DFLOAT +->
112    cv := cos(v)
113    sv := sin(v)
114    cu := cos(u)
115    su := sin(u)
116    x := r * cos(2*u) * cv + sv * cu
117    y := r * sin(2*u) * cv - sv * su
118    z := a * cv
119    point [x,y,z]
120  draw(surf, 0..%pi, -%pi..%pi, var1Steps==steps,
121       var2Steps==steps, title == "Etruscan Venus")
122
123venus(5/2, 13/10, 50)                                    -- \textbf{The Etruscan Venus}
124\end{xmpLinesPlain}
125
126The Figure-8 Klein Bottle
127\index{Klein bottle}
128parameterization is from
129``Differential Geometry and Computer Graphics'' by Thomas Banchoff,
130in {\it Perspectives in Mathematics,} Anniversary of Oberwolfasch 1984,
131Birkh\"{a}user-Verlag, Basel, pp. 43-60.
132
133\begin{xmpLinesNoResetPlain}
134klein(x,y) ==
135  cx := cos(x)
136  cy := cos(y)
137  sx := sin(x)
138  sy := sin(y)
139  sx2 := sin(x/2)
140  cx2 := cos(x/2)
141  sq2 := sqrt(2.0@DFLOAT)
142  point [cx * (cx2 * (sq2 + cy) + (sx2 * sy * cy)), _
143         sx * (cx2 * (sq2 + cy) + (sx2 * sy * cy)), _
144         -sx2 * (sq2 + cy) + cx2 * sy * cy]
145
146draw(klein, 0..4*%pi, 0..2*%pi, var1Steps==50,           -- \textbf{Figure-8 Klein bottle}
147     var2Steps==50,title=="Figure Eight Klein Bottle")
148\end{xmpLinesNoResetPlain}
149
150The next two images are examples of generalized tubes.
151
152\begin{xmpLinesNoResetPlain}
153)read ntube
154rotateBy(p, theta) ==                                    -- Rotate a point $p$ by
155  c := cos(theta)                                        -- \quad{}$\theta$ around the origin.
156  s := sin(theta)
157  point [p.1*c - p.2*s, p.1*s + p.2*c]
158
159bcircle t ==                                             -- A circle in three-space.
160  point [3*cos t, 3*sin t, 0]
161
162twist(u, t) ==                                           -- An ellipse that twists
163  theta := 4*t                                           -- \quad{}around four times as
164  p := point [sin u, cos(u)/2]                           -- \quad{}\spad{t} revolves once.
165  rotateBy(p, theta)
166
167ntubeDrawOpt(bcircle, twist, 0..2*%pi, 0..2*%pi,         -- \bf{Twisted Torus}
168             var1Steps == 70, var2Steps == 250)
169
170twist2(u, t) ==                                          -- Create a twisting circle.
171  theta := t
172  p := point [sin u, cos(u)]
173  rotateBy(p, theta)
174
175cf(u,v) == sin(21*u)                                     -- Color function with \spad{21} stripes.
176
177ntubeDrawOpt(bcircle, twist2, 0..2*%pi, 0..2*%pi,        -- \textbf{Striped Torus}
178  colorFunction == cf, var1Steps == 168,
179  var2Steps == 126)
180\end{xmpLinesNoResetPlain}
181
182% *********************************************************************
183\head{section}{images6.input}{ugFimagesSix}
184% *********************************************************************
185
186\begin{xmpLinesPlain}
187gam(x,y) ==                                              -- The height and color are the
188  g := Gamma complex(x,y)                                -- \quad{}real and argument parts
189  point [x,y,max(min(real g, 4), -4), argument g]        -- \quad{}of the Gamma function,
190                                                         -- \quad{}respectively.
191
192draw(gam, -%pi..%pi, -%pi..%pi,                          -- \textbf{The Gamma Function}
193     title == "Gamma(x + %i*y)", _
194     var1Steps == 100, var2Steps == 100)
195
196b(x,y) == Beta(x,y)
197
198draw(b, -3.1..3, -3.1 .. 3, title == "Beta(x,y)")        -- \textbf{The Beta Function}
199
200atf(x,y) ==
201  a := atan complex(x,y)
202  point [x,y,real a, argument a]
203
204draw(atf, -3.0..%pi, -3.0..%pi)                          -- \textbf{The Arctangent function}
205\end{xmpLinesPlain}
206\index{function!Gamma}
207\index{function!Euler Beta}
208\index{Euler!Beta function}
209
210
211% *********************************************************************
212\head{section}{images7.input}{ugFimagesSeven}
213% *********************************************************************
214
215First we look at the conformal
216\index{conformal map}
217map \texht{$z \mapsto z + 1/z$}{z +-> z + 1/z}.
218
219\begin{xmpLinesPlain}
220)read conformal                                          -- Read program for drawing
221                                                         -- \quad{}conformal maps.
222
223f z == z                                                 -- The coordinate grid for the
224                                                         -- \quad{}complex plane.
225conformalDraw(f, -2..2, -2..2, 9, 9, "cartesian")        -- \textbf{Mapping 1: Source}
226
227f z == z + 1/z                                           -- The map $z \mapsto z + 1/z$
228
229conformalDraw(f, -2..2, -2..2, 9, 9, "cartesian")        -- \bf{Mapping 1: Target}
230\end{xmpLinesPlain}
231
232The map \texht{$z \mapsto -(z+1)/(z-1)$}{z +-> -(z+1)/(z-1)} maps
233the unit disk to the right half-plane, as shown
234\index{Riemann!sphere}
235on the Riemann sphere.
236
237\begin{xmpLinesNoResetPlain}
238f z == z                                                 -- The unit disk.
239
240riemannConformalDraw(f,0.1..0.99,0..2*%pi,7,11,"polar")  -- \textbf{Mapping 2: Source}
241
242f z == -(z+1)/(z-1)                                      -- The map $x\mapsto -(z+1)/(z-1)$.
243riemannConformalDraw(f,0.1..0.99,0..2*%pi,7,11,"polar")  -- \textbf{Mapping 2: Target}
244
245riemannSphereDraw(-4..4, -4..4, 7, 7, "cartesian")       -- \textbf{Riemann Sphere Mapping}
246\end{xmpLinesNoResetPlain}
247
248% *********************************************************************
249\head{section}{images8.input}{ugFimagesEight}
250% *********************************************************************
251
252\begin{xmpLinesPlain}
253)read dhtri
254)read tetra
255drawPyramid 4                                            -- \textbf{Sierpinsky's Tetrahedron}
256
257\index{Sierpinsky's Tetrahedron}
258)read antoine
259drawRings 2                                              -- \textbf{Antoine's Necklace}
260
261\index{Antoine's Necklace}
262)read scherk
263drawScherk(3,3)                                          -- \textbf{Scherk's Minimal Surface}
264
265\index{Scherk's minimal surface}
266)read ribbonsNew
267drawRibbons([x^i for i in 1..5], x=-1..1, y=0..2)        -- \bf{Ribbon Plot}
268\end{xmpLinesPlain}
269
270
271%\input{gallery/conformal.htex}
272% *********************************************************************
273\head{section}{conformal.input}{ugFconformal}
274% *********************************************************************
275%
276The functions in this section draw conformal maps both on the
277\index{conformal map}
278plane and on the Riemann sphere.
279\index{Riemann!sphere}
280
281%   -- Compile, don't interpret functions.
282%\xmpLine{)set fun comp on}{}
283\begin{xmpLinesPlain}
284C := Complex DoubleFloat                                 -- Complex Numbers
285S := Segment DoubleFloat                                 -- Draw ranges
286R3 := Point DFLOAT                                       -- Points in 3-space
287
288\end{xmpLinesPlain}
289
290\userfun{conformalDraw}{\it (f, rRange, tRange, rSteps, tSteps, coord)}
291draws the image of the coordinate grid under {\it f} in the complex plane.
292The grid may be given in either polar or Cartesian coordinates.
293Argument {\it f} is the function to draw;
294{\it rRange} is the range of the radius (in polar) or real (in Cartesian);
295{\it tRange} is the range of \texht{$\theta$}{\theta} (in polar) or imaginary (in Cartesian);
296{\it tSteps, rSteps}, are the number of intervals in the {\it r} and
297\texht{$\theta$}{\theta} directions; and
298{\it coord} is the coordinate system to use (either {\tt "polar"} or
299{\tt "cartesian"}).
300
301\begin{xmpLinesNoResetPlain}
302conformalDraw: (C -> C, S, S, PI, PI, String) -> VIEW3D
303conformalDraw(f,rRange,tRange,rSteps,tSteps,coord) ==
304  transformC :=                                          -- Function for changing an \smath{(x,y)}
305    coord = "polar" => polar2Complex                     -- \quad{}pair into a complex number.
306    cartesian2Complex
307  cm := makeConformalMap(f, transformC)
308  sp := createThreeSpace()                               -- Create a fresh space.
309  adaptGrid(sp, cm, rRange, tRange, rSteps, tSteps)      -- Plot the coordinate lines.
310  makeViewport3D(sp, "Conformal Map")                    -- Draw the image.
311\end{xmpLinesNoResetPlain}
312
313\userfun{riemannConformalDraw}{\it (f, rRange, tRange, rSteps, tSteps, coord)}
314draws the image of the coordinate grid under {\it f} on the Riemann sphere.
315The grid may be given in either polar or Cartesian coordinates.
316Its arguments are the same as those for \userfun{conformalDraw}.
317\begin{xmpLinesNoResetPlain}
318riemannConformalDraw:(C->C,S,S,PI,PI,String)->VIEW3D
319riemannConformalDraw(f, rRange, tRange,
320                     rSteps, tSteps, coord) ==
321  transformC :=                                          -- Function for changing an \smath{(x,y)}
322    coord = "polar" => polar2Complex                     -- \quad{}pair into a complex number.
323    cartesian2Complex
324  sp := createThreeSpace()                               -- Create a fresh space.
325  cm := makeRiemannConformalMap(f, transformC)
326  adaptGrid(sp, cm, rRange, tRange, rSteps, tSteps)      -- Plot the coordinate lines.
327  curve(sp,[point [0,0,2.0@DFLOAT,0],point [0,0,2.0@DFLOAT,0]]) -- Add an invisible point at
328  makeViewport3D(sp,"Map on the Riemann Sphere")         -- \quad{}the north pole for scaling.
329
330adaptGrid(sp, f, uRange, vRange,  uSteps, vSteps) ==     -- Plot the coordinate grid
331  delU := (high(uRange) - low(uRange))/uSteps            -- \quad{}using adaptive plotting for
332  delV := (high(vRange) - low(vRange))/vSteps            -- \quad{}coordinate lines, and draw
333  uSteps := uSteps + 1; vSteps := vSteps + 1             -- \quad{}tubes around the lines.
334  u := low uRange
335  for i in 1..uSteps repeat                              -- Draw coordinate lines in the \spad{v}
336    c := curryLeft(f,u)                                  -- \quad{}direction; curve \spad{c} fixes the
337    cf := (t:DFLOAT):DFLOAT +-> 0                        -- \quad{}current value of \spad{u}.
338    makeObject(c,vRange::SEG Float,colorFunction==cf,    -- Draw the \spad{v} coordinate line.
339      space == sp, tubeRadius == .02, tubePoints == 6)
340    u := u + delU
341  v := low vRange
342  for i in 1..vSteps repeat                              -- Draw coordinate lines in the \spad{u}
343    c := curryRight(f,v)                                 -- \quad{}direction; curve \spad{c} fixes the
344    cf := (t:DFLOAT):DFLOAT +-> 1                        -- \quad{}current value of \spad{v}.
345    makeObject(c,uRange::SEG Float,colorFunction==cf,    -- Draw the \spad{u} coordinate line.
346      space == sp, tubeRadius == .02, tubePoints == 6)
347    v := v + delV
348  void()
349
350riemannTransform(z) ==                                   -- Map a point in the complex
351  r := sqrt norm z                                       -- \quad{}plane to the Riemann sphere.
352  cosTheta := (real z)/r
353  sinTheta := (imag z)/r
354  cp := 4*r/(4+r^2)
355  sp := sqrt(1-cp*cp)
356  if r>2 then sp := -sp
357  point [cosTheta*cp, sinTheta*cp, -sp + 1]
358
359cartesian2Complex(r:DFLOAT, i:DFLOAT):C ==               -- Convert Cartesian coordinates to
360  complex(r, i)                                          -- \quad{}complex Cartesian form.
361
362polar2Complex(r:DFLOAT, th:DFLOAT):C ==                  -- Convert polar coordinates to
363  complex(r*cos(th), r*sin(th))                          -- \quad{}complex Cartesian form.
364
365makeConformalMap(f, transformC) ==                       -- Convert complex function \spad{f} to a
366  (u:DFLOAT,v:DFLOAT):R3 +->                             -- \quad{}mapping: (\spadtype{DFLOAT},\spadtype{DFLOAT}) $\to$ \pspadtype{R3}
367    z := f transformC(u, v)                              -- \quad{}in the complex plane.
368    point [real z, imag z, 0.0@DFLOAT]
369
370makeRiemannConformalMap(f, transformC) ==                -- Convert a complex function \spad{f} to a
371  (u:DFLOAT, v:DFLOAT):R3 +->                            -- \quad{}mapping: (\spadtype{DFLOAT},\spadtype{DFLOAT}) $\to$ \spadtype{R3}
372    riemannTransform f transformC(u, v)                  -- \quad{}on the Riemann sphere.
373
374riemannSphereDraw: (S, S, PI, PI, String) -> VIEW3D      -- Draw a picture of the mapping
375riemannSphereDraw(rRange,tRange,rSteps,tSteps,coord) ==  -- \quad{}of the complex plane to
376  transformC :=                                          -- \quad{}the Riemann sphere.
377    coord = "polar" => polar2Complex
378    cartesian2Complex
379  grid := (u:DFLOAT, v:DFLOAT): R3 +->                   -- Coordinate grid function.
380    z1 := transformC(u, v)
381    point [real z1, imag z1, 0]
382  sp := createThreeSpace()                               -- Create a fresh space.
383  adaptGrid(sp, grid, rRange, tRange, rSteps, tSteps)    -- Draw the flat grid.
384  connectingLines(sp,grid,rRange,tRange,rSteps,tSteps)
385  makeObject(riemannSphere,0..2*%pi,0..%pi,space==sp)    -- Draw the sphere.
386  f := (z:C):C +-> z
387  cm := makeRiemannConformalMap(f, transformC)
388  adaptGrid(sp, cm, rRange, tRange, rSteps, tSteps)      -- Draw the sphere grid.
389  makeViewport3D(sp, "Riemann Sphere")
390
391connectingLines(sp,f,uRange,vRange,uSteps,vSteps) ==     -- Draw the lines that connect
392  delU := (high(uRange) - low(uRange))/uSteps            -- \quad{}the points in the complex
393  delV := (high(vRange) - low(vRange))/vSteps            -- \quad{}plane to the north pole
394  uSteps := uSteps + 1; vSteps := vSteps + 1             -- \quad{}of the Riemann sphere.
395  u := low uRange
396  for i in 1..uSteps repeat                              -- For each u.
397    v := low vRange
398    for j in 1..vSteps repeat                            -- For each v.
399      p1 := f(u,v)
400      p2 := riemannTransform complex(p1.1, p1.2)         -- Project p1 onto the sphere.
401      fun := lineFromTo(p1,p2)                           -- Create a line function.
402      cf := (t:DFLOAT):DFLOAT +-> 3
403      makeObject(fun, 0..1,space==sp,tubePoints==4,      -- Draw the connecting line.
404                 tubeRadius==0.01,colorFunction==cf)
405      v := v + delV
406    u := u + delU
407  void()
408
409riemannSphere(u,v) ==                                    -- A sphere sitting on the
410  sv := sin(v)                                           -- \quad{}complex plane, with radius 1.
411  0.99@DFLOAT*(point [cos(u)*sv,sin(u)*sv,cos(v),0.0@DFLOAT])+
412    point [0.0@DFLOAT, 0.0@DFLOAT, 1.0@DFLOAT, 4.0@DFLOAT]
413
414lineFromTo(p1, p2) ==                                    -- Create a line function
415  d := p2 - p1                                           -- \quad{}that goes from p1 to p2
416  (t:DFLOAT):Point DFLOAT +->
417    p1 + t*d
418\end{xmpLinesNoResetPlain}
419
420%\input{gallery/tknot.htex}
421% *********************************************************************
422\head{section}{tknot.input}{ugFtknot}
423% *********************************************************************
424%
425Create a $(p,q)$ torus-knot with radius $r$ around the curve.
426The formula was derived by Larry Lambe.
427
428\begin{xmpLinesPlain}
429)read ntube
430torusKnot: (DFLOAT, DFLOAT, DFLOAT, PI, PI) -> VIEW3D
431torusKnot(p, q ,r, uSteps, tSteps) ==
432  knot := (t:DFLOAT):Point DFLOAT +->                    -- Function for the torus knot.
433    fac := 4/(2.2@DFLOAT-sin(q*t))
434    fac * point [cos(p*t), sin(p*t), cos(q*t)]
435  circle := (u:DFLOAT, t:DFLOAT): Point DFLOAT +->       -- The cross section.
436    r * point [cos u, sin u]
437  ntubeDrawOpt(knot, circle, 0..2*%pi, 0..2*%pi,         -- Draw the circle around the knot.
438               var1Steps == uSteps, var2Steps == tSteps)
439
440\end{xmpLinesPlain}
441
442%\input{gallery/ntube.htex}
443% *********************************************************************
444\head{section}{ntube.input}{ugFntube}
445% *********************************************************************
446%
447The functions in this file create generalized tubes (also known as generalized
448cylinders).
449These functions draw a 2-d curve in the normal
450planes around a 3-d curve.
451
452\begin{xmpLinesPlain}
453R3 := Point DFLOAT                                       -- Points in 3-Space
454R2 := Point DFLOAT                                       -- Points in 2-Space
455S := Segment Float                                       -- Draw ranges
456                                                         -- Introduce types for functions for:
457ThreeCurve := DFLOAT -> R3                               -- \quad{}---the space curve function
458TwoCurve := (DFLOAT, DFLOAT) -> R2                       -- \quad{}---the plane curve function
459Surface := (DFLOAT, DFLOAT) -> R3                        -- \quad{}---the surface function
460                                                         -- Frenet frames define a
461FrenetFrame :=                                           -- \quad{}coordinate system around a
462   Record(value:R3,tangent:R3,normal:R3,binormal:R3)     -- \quad{}point on a space curve.
463frame: FrenetFrame                                       -- The current Frenet frame
464                                                         -- \quad{}for a point on a curve.
465\end{xmpLinesPlain}
466
467\userfun{ntubeDraw}{\it (spaceCurve, planeCurve,}
468$u_0 .. u_1,$ $t_0 .. t_1)$
469draws {\it planeCurve} in the normal planes of {\it spaceCurve.}
470The parameter $u_0 .. u_1$ specifies
471the parameter range for {\it planeCurve}
472and $t_0 .. t_1$ specifies the parameter range for {\it spaceCurve}.
473Additionally, the plane curve function takes
474a second parameter: the current parameter of {\it spaceCurve}.
475This allows the plane curve to change shape
476as it goes around the space curve.
477See \spadref{ugFimagesFive} for an example of this.
478%
479\begin{xmpLinesNoResetPlain}
480ntubeDraw: (ThreeCurve,TwoCurve,S,S) -> VIEW3D
481ntubeDraw(spaceCurve,planeCurve,uRange,tRange) ==
482  ntubeDrawOpt(spaceCurve, planeCurve, uRange, _
483               tRange, []$List DROPT)
484
485ntubeDrawOpt: (ThreeCurve,TwoCurve,S,S,List DROPT)
486    -> VIEW3D
487ntubeDrawOpt(spaceCurve,planeCurve,uRange,tRange,l) ==   -- This function is similar
488                                                         -- \quad{}to \userfun{ntubeDraw}, but takes
489  delT:DFLOAT := (high(tRange) - low(tRange))/10000      -- \quad{}optional parameters that it
490  oldT:DFLOAT := low(tRange) - 1                         -- \quad{}passes to the \spadfun{draw} command.
491  fun := ngeneralTube(spaceCurve,planeCurve,delT,oldT)
492  draw(fun, uRange, tRange, l)
493
494\end{xmpLinesNoResetPlain}
495
496\userfun{nfrenetFrame}{\it (c, t, delT)}
497numerically computes the Frenet frame
498about the curve {\it c} at {\it t}.
499Parameter {\it delT} is a small number used to
500compute derivatives.
501\begin{xmpLinesNoResetPlain}
502nfrenetFrame(c, t, delT) ==
503  f0 := c(t)
504  f1 := c(t+delT)
505  t0 := f1 - f0                                          -- The tangent.
506  n0 := f1 + f0
507  b := cross(t0, n0)                                     -- The binormal.
508  n := cross(b,t0)                                       -- The normal.
509  ln := length n
510  lb := length b
511  ln = 0 or lb = 0 =>
512      error "Frenet Frame not well defined"
513  n := (1/ln)*n                                          -- Make into unit length vectors.
514  b := (1/lb)*b
515  [f0, t0, n, b]$FrenetFrame
516\end{xmpLinesNoResetPlain}
517
518\userfun{ngeneralTube}{\it (spaceCurve, planeCurve,}{\it  delT, oltT)}
519creates a function that can be passed to the system \spadfun{draw} command.
520The function is a parameterized surface for the general tube
521around {\it spaceCurve}.  {\it delT} is a small number used to compute
522derivatives. {\it oldT} is used to hold the current value of the
523{\it t} parameter for {\it spaceCurve.}  This is an efficiency measure
524to ensure that frames are only computed once for each value of {\it t}.
525\begin{xmpLinesNoResetPlain}
526ngeneralTube: (ThreeCurve, TwoCurve, DFLOAT, DFLOAT) -> Surface
527ngeneralTube(spaceCurve, planeCurve, delT, oldT) ==
528  free frame                                             -- Indicate that \spad{frame} is global.
529  (v:DFLOAT, t: DFLOAT): R3 +->
530    if (t ~= oldT) then                                  -- If not already computed,
531      frame := nfrenetFrame(spaceCurve, t, delT)         -- \quad{}compute new frame.
532      oldT := t
533    p := planeCurve(v, t)
534    frame.value + p.1*frame.normal + p.2*frame.binormal  -- Project \spad{p} into the normal plane.
535\end{xmpLinesNoResetPlain}
536
537%\input{gallery/dhtri.htex}
538% *********************************************************************
539\head{section}{dhtri.input}{ugFdhtri}
540% *********************************************************************
541%
542Create affine transformations (DH matrices) that transform
543a given triangle into another.
544
545\begin{xmpLinesPlain}
546tri2tri: (List Point DFLOAT, List Point DFLOAT) -> DHMATRIX(DFLOAT)
547                                                         -- Compute a \spadtype{DHMATRIX} that
548tri2tri(t1, t2) ==                                       -- \quad{}transforms \spad{t1} to \spad{t2,} where
549  n1 := triangleNormal(t1)                               -- \quad{}\spad{t1} and \spad{t2} are the vertices
550  n2 := triangleNormal(t2)                               -- \quad{}of two triangles in 3-space.
551  tet2tet(concat(t1, n1), concat(t2, n2))
552
553tet2tet: (List Point DFLOAT, List Point DFLOAT) -> DHMATRIX(DFLOAT)
554                                                         -- Compute a \spadtype{DHMATRIX} that
555tet2tet(t1, t2) ==                                       -- \quad{}transforms \spad{t1} to \spad{t2,}
556  m1 := makeColumnMatrix t1                              -- \quad{}where \spad{t1} and \spad{t2} are the
557  m2 := makeColumnMatrix t2                              -- \quad{}vertices of two tetrahedrons
558  m2 * inverse(m1)                                       -- \quad{}in 3-space.
559
560makeColumnMatrix(t) ==                                   -- Put the vertices of a tetra-
561  m := new(4,4,0)$DHMATRIX(DFLOAT)                       -- \quad{}hedron into matrix form.
562  for x in t for i in 1..repeat
563    for j in 1..3 repeat
564      m(j,i) := x.j
565    m(4,i) := 1
566  m
567
568triangleNormal(t) ==                                     -- Compute a vector normal to
569  a := triangleArea t                                    -- \quad{}the given triangle, whose
570  p1 := t.2 - t.1                                        -- \quad{}length is the square root
571  p2 := t.3 - t.2                                        -- \quad{}of the area of the triangle.
572  c := cross(p1, p2)
573  len := length(c)
574  len = 0 => error "degenerate triangle!"
575  c := (1/len)*c
576  t.1 + sqrt(a) * c
577
578triangleArea t ==                                        -- Compute the area of a
579  a := length(t.2 - t.1)                                 -- \quad{}triangle using Heron's
580  b := length(t.3 - t.2)                                 -- \quad{}formula.
581  c := length(t.1 - t.3)
582  s := (a+b+c)/2
583  sqrt(s*(s-a)*(s-b)*(s-c))
584\end{xmpLinesPlain}
585
586
587% *********************************************************************
588\head{section}{tetra.input}{ugFtetra}
589% *********************************************************************
590%
591%\input{gallery/tetra.htex}
592%\outdent{Sierpinsky's Tetrahedron}
593
594\begin{xmpLinesPlain}
595)set expose add con DenavitHartenbergMatrix              -- Bring DH matrices into the
596                                                         -- \quad{}environment.
597x1:DFLOAT := sqrt(2.0@DFLOAT/3.0@DFLOAT)                 -- Set up the coordinates of the
598x2:DFLOAT := sqrt(3.0@DFLOAT)/6                          -- \quad{}corners of the tetrahedron.
599
600z := 0.0@DFLOAT
601h := 0.5@DFLOAT
602
603p1 := point [-h, -x2, z]                                 -- Some needed points.
604p2 := point [h, -x2, z]
605p3 := point [z, 2*x2, z]
606p4 := point [z, z, x1]
607
608baseTriangle  := [p2, p1, p3]                            -- The base of the tetrahedron.
609
610mt := [h*(p2+p1), h*(p1+p3), h*(p3+p2)]                  -- The ``middle triangle'' inscribed
611                                                         -- \quad{}in the base of the tetrahedron.
612bt1 := [mt.1, p1, mt.2]                                  -- The bases of the triangles of
613bt2 := [p2, mt.1, mt.3]                                  -- \quad{}the subdivided tetrahedron.
614bt3 := [mt.2, p3, mt.3]
615bt4 := [h*(p2+p4), h*(p1+p4), h*(p3+p4)]
616
617tt1 := tri2tri(baseTriangle, bt1)                        -- Create the transformations
618tt2 := tri2tri(baseTriangle, bt2)                        -- \quad{}that bring the base of the
619tt3 := tri2tri(baseTriangle, bt3)                        -- \quad{}tetrahedron to the bases of
620tt4 := tri2tri(baseTriangle, bt4)                        -- \quad{}the subdivided tetrahedron.
621
622drawPyramid(n) ==                                        -- Draw a Sierpinsky tetrahedron
623  s := createThreeSpace()                                -- \quad{}with \spad{n} levels of recursive
624  dh := rotatex(0.0@DFLOAT)                              -- \quad{}subdivision.
625  drawPyramidInner(s, n, dh)
626  makeViewport3D(s, "Sierpinsky Tetrahedron")
627
628drawPyramidInner(s, n, dh) ==                            -- Recursively draw a Sierpinsky
629  n = 0 => makeTetrahedron(s, dh, n)                     -- \quad{}tetrahedron.
630  drawPyramidInner(s, n-1, dh * tt1)                     -- Draw the 4 recursive pyramids.
631  drawPyramidInner(s, n-1, dh * tt2)
632  drawPyramidInner(s, n-1, dh * tt3)
633  drawPyramidInner(s, n-1, dh * tt4)
634
635makeTetrahedron(sp, dh, color) ==                        -- Draw a tetrahedron into the
636  w1 := dh*p1                                            -- \quad{}given space with the given
637  w2 := dh*p2                                            -- \quad{}color, transforming it by
638  w3 := dh*p3                                            -- \quad{}the given DH matrix.
639  w4 := dh*p4
640  polygon(sp, [w1, w2, w4])
641  polygon(sp, [w1, w3, w4])
642  polygon(sp, [w2, w3, w4])
643  void()
644\end{xmpLinesPlain}
645\index{Sierpinsky's Tetrahedron}
646
647
648%\input{gallery/antoine.htex}
649% *********************************************************************
650\head{section}{antoine.input}{ugFantoine}
651% *********************************************************************
652%
653Draw Antoine's Necklace.
654\index{Antoine's Necklace}
655Thank you to Matthew Grayson at IBM's T.J Watson Research Center for the idea.
656
657\begin{xmpLinesPlain}
658)set expose add con DenavitHartenbergMatrix              -- Bring DH matrices into
659                                                         -- \quad{}the environment.
660torusRot: DHMATRIX(DFLOAT)                               -- The current transformation for
661                                                         -- \quad{}drawing a sub ring.
662
663drawRings(n) ==                                          -- Draw Antoine's Necklace with \spad{n}
664  s := createThreeSpace()                                -- \quad{}levels of recursive subdivision.
665  dh:DHMATRIX(DFLOAT) := identity()                      -- \quad{}The number of subrings is $10^n$.
666  drawRingsInner(s, n, dh)                               -- Do the real work.
667  makeViewport3D(s, "Antoine's Necklace")
668
669\end{xmpLinesPlain}
670
671In order to draw Antoine rings, we take one ring, scale it down to
672a smaller size, rotate it around its central axis, translate it
673to the edge of the larger ring and rotate it around the edge to
674a point corresponding to its count (there are 10 positions around
675the edge of the larger ring). For each of these new rings we
676recursively perform the operations, each ring becoming 10 smaller
677rings. Notice how the \spadtype{DHMATRIX} operations are used to build up
678the proper matrix composing all these transformations.
679
680\begin{xmpLinesNoResetPlain}
681F ==> DFLOAT
682drawRingsInner(s, n, dh) ==                              -- Recursively draw Antoine's
683  n = 0 =>                                               -- \quad{}Necklace.
684    drawRing(s, dh)
685    void()
686  t := 0.0@F                                             -- Angle around ring.
687  p := 0.0@F                                             -- Angle of subring from plane.
688  tr := 1.0@F                                            -- Amount to translate subring.
689  inc := 0.1@F                                           -- The translation increment.
690  for i in 1..10 repeat                                  -- Subdivide into 10 linked rings.
691    tr := tr + inc
692    inc := -inc
693    dh' := dh*rotatez(t)*translate(tr,0.0@F,0.0@F)*      -- Transform ring in center
694           rotatey(p)*scale(0.35@F, 0.48@F, 0.4@F)       -- \quad{}to a link.
695    drawRingsInner(s, n-1, dh')
696    t := t + 36.0@F
697    p := p + 90.0@F
698  void()
699
700drawRing(s, dh) ==                                       -- Draw a single ring into
701  free torusRot                                          -- \quad{}the given subspace,
702  torusRot := dh                                         -- \quad{}transformed by the given
703  makeObject(torus, 0..2*%pi, 0..2*%pi, var1Steps == 6,  -- \quad{}\spadtype{DHMATRIX}.
704             space == s, var2Steps == 15)
705
706torus(u ,v) ==                                           -- Parameterization of a torus,
707  cu := cos(u)/6                                         -- \quad{}transformed by the
708  torusRot*point [(1+cu)*cos(v),(1+cu)*sin(v),(sin u)/6] -- \quad{}\spadtype{DHMATRIX} in \spad{torusRot.}
709\end{xmpLinesNoResetPlain}
710
711%\input{gallery/scherk.htex}
712% *********************************************************************
713\head{section}{scherk.input}{ugFscherk}
714% *********************************************************************
715%
716
717Scherk's minimal surface, defined by:
718\index{Scherk's minimal surface}
719\texht{$e^z \cos(x) = \cos(y)$}{\spad{exp(z) * cos(x) = cos(y)}}.
720See: {\it A Comprehensive Introduction to Differential Geometry,} Vol. 3,
721by Michael Spivak, Publish Or Perish, Berkeley, 1979, pp. 249-252.
722
723\begin{xmpLinesPlain}
724(xOffset, yOffset):DFLOAT                                -- Offsets for a single piece
725                                                         -- \quad{}of Scherk's minimal surface.
726
727drawScherk(m,n) ==                                       -- Draw Scherk's minimal surface
728  free xOffset, yOffset                                  -- \quad{}on an \spad{m} by \spad{n} patch.
729  space := createThreeSpace()
730  for i in 0..m-1 repeat
731    xOffset := i*%pi
732    for j in 0 .. n-1 repeat
733      rem(i+j, 2) = 0 => 'iter                           -- Draw only odd patches.
734      yOffset := j*%pi
735      drawOneScherk(space)                               -- Draw a patch.
736  makeViewport3D(space, "Scherk's Minimal Surface")
737
738scherk1(u,v) ==                                          -- The first patch that makes
739  x := cos(u)/exp(v)                                     -- \quad{}up a single piece of
740  point [xOffset + acos(x), yOffset + u, v, abs(v)]      -- \quad{}Scherk's minimal surface.
741
742scherk2(u,v) ==                                          -- The second patch.
743  x := cos(u)/exp(v)
744  point [xOffset - acos(x), yOffset + u, v, abs(v)]
745
746scherk3(u,v) ==                                          -- The third patch.
747  x := exp(v) * cos(u)
748  point [xOffset + u, yOffset + acos(x), v, abs(v)]
749
750scherk4(u,v) ==                                          -- The fourth patch.
751  x := exp(v) * cos(u)
752  point [xOffset + u, yOffset - acos(x), v, abs(v)]
753
754drawOneScherk(s) ==                                      -- Draw the surface by
755  makeObject(scherk1,-%pi/2..%pi/2,0..%pi/2,space==s,    -- \quad{}breaking it into four
756             var1Steps == 28, var2Steps == 28)           -- \quad{}patches and then drawing
757  makeObject(scherk2,-%pi/2..%pi/2,0..%pi/2,space==s,    -- \quad{}the patches.
758             var1Steps == 28, var2Steps == 28)
759  makeObject(scherk3,-%pi/2..%pi/2,-%pi/2..0,space==s,
760             var1Steps == 28, var2Steps == 28)
761  makeObject(scherk4,-%pi/2..%pi/2,-%pi/2..0,space==s,
762             var1Steps == 28, var2Steps == 28)
763  void()
764\end{xmpLinesPlain}
765