Three-Dimensional Mathematica Graphics (Chapter 6.4)

Extract from Mathematica Graphics: An Intensive Tutorial

by Tom Wickham-Jones

This HTML document is based on Mathematica Graphics: An Intensive Tutorial by Tom Wickham-Jones. It was adapted by Martin Kraus for non-commercial use.

Mathematica and MathLink are registered trademarks, and MathReader, MathSource and 3-Script are trademarks of Wolfram Research, Inc.

All other product names mentioned are trademarks of their producers.

Copyright 1992 by Wolfram Research, Inc.

All rights reserved. No part of this document may be reproduced, stored in a retrieval system, or transmitted, in any form or by any means, electronic, mechanical, photocopying, recording or otherwise, without the prior written permission of the copyright holder.

6.4 Examples of Graphics Programming

Having seen how to construct functions for built-in commands we can go further and build our own graphics commands. Commands such as Plot evaluate their input function over a range and construct a collection of graphics primitives. We could do something very similar. Of course it would not be so useful to reproduce the actual built-in commands. However there are many other tasks to be tackled by writing in the Mathematica programming language. Here we shall look at some possibilities.

6.4.1 Iterative Maps

The first idea comes from iteration. If one repeatedly applies a function, the values returned in some cases converge to a fixed point and in others to seemingly random and complex behavior. For example, consider a function such as 4x(1 - x).

Floating-point numbers are used to ensure real arithmetic. In[1]:= fun[x_] := 4. x (1. - x)
This is the value for an argument of .25. In[2]:= fun[0.25]

Out[2]= 0.75
This gives me four iterations of fun. In[3]:= fun[fun[fun[fun[0.25]]]]

Out[3]= 0.75
Of course Mathematica gives a much neater way to apply this function repeatedly. In[4]:= Nest[fun, 0.25, 10]

Out[4]= 0.75
NestList keeps the intermediate values. In[5]:= NestList[fun, 0.25, 10]

Out[5]= {0.25, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.75}
Now we can change the starting point and we see the function does not converge anymore. In[6]:= NestList[fun, 0.24, 10]

Out[6]= {0.24, 0.7296, 0.789135, 0.665603, 0.890303, 0.390655, 0.952175, 0.182151, 0.595887, 0.963222, 0.1417}

Very nice, but I want to see a graphical representation of this. To do this I shall write some code. The function is being applied to some point, and the value which results is used as the next starting point. We are thus interacting with the line <x,x> and we are interested in the two lines <x,x> and <x,f[x]>.

These are the two lines we are interested in. In[7]:= plot = ParametricPlot3D[{{0, x, x}, {0, x, fun[x]}}, {x, 0, 1}]
Out[7]= - Graphics3D -
This function collects all the steps which we have carried out. It takes two arguments and returns a Graphics3D object. In[8]:= Iterate[st_, n_] :=
res = NestList[fun, st, n];
res = Flatten[Transpose[{res, res}]];
res = Partition[res, 2, 1];
res = Transpose[Prepend[Transpose[res], Table[i / n, {i, 0, 2 n}]]];

In[9]:= Iterate[.4, 20]

Out[9]= - Graphics3D -
This combines the result. In[10]:= Show[plot, %, ViewPoint -> {100, 0, 0}]
Out[10]= - Graphics3D -

In a few lines of code we have a useful way to explore these iterated procedures. This function Iterate could be extended to take the iterated function as an argument and also to generate all the plots automatically.

6.4.2 A New "Primitive"

Mathematica graphics provide many primitives for plotting. However there may be some primitive which you desire but which is not present. Instead of saying "if only they had..." why not actually write it. Here we shall look at an example of building a new primitive out of existing ones to draw arrows in three dimensions.

This defines some options for our three-dimensional arrow. In[11]:= Options[Arrow3D] = {HeadLength -> 0.3, HeadNormal -> {0, 0, 1}, HeadWidth -> 0.5};
This definition of Arrow3D shows the standard way to use options in Mathematica. In[12]:= Arrow3D[a_, b_, opts___] :=
Module[{abLength = N[Sqrt[(b - a) . (b - a)]], abUnit, headPerp, headPerpLength, headLength, headNormal, headWidth},
{headLength, headNormal, headWidth} = {HeadLength, HeadNormal, HeadWidth} /. {opts} /. Options[Arrow3D];
abUnit = (b - a) / abLength;
headPerp = Cross[abUnit, N[headNormal]];
headPerp = headPerp / Sqrt[N[headPerp.headPerp]];
{Line[{a, b - abUnit * headLength}], Polygon[{b, b - abUnit * headLength + headPerp * headWidth / 2 * headLength, b - abUnit * headLength, b - abUnit * headLength - headPerp * headWidth / 2 * headLength}]}];

Arrow3D evaluates to a list of graphics primitives. In[13]:= Arrow3D[{0, 0, 0}, {1, 1, 1}]

Out[13]= {Line[{{0, 0, 0}, {0.826795, 0.826795, 0.826795}}], Polygon[{{1, 1, 1}, {0.879828, 0.773762, 0.826795}, {0.826795, 0.826795, 0.826795}, {0.773762, 0.879828, 0.826795}}]}
Now we can use Arrow3D in Graphics3D objects like other primitives. In[14]:= Show[Graphics3D[{ Arrow3D[{0, 0, 0}, {1, 0, 0}], Arrow3D[{1, 0, 0}, {1, 1, 1}], Arrow3D[{0, 0, 0}, {1, 1, 1}]}]]
Out[14]= - Graphics3D -

no further pages back to table of contents