Advanced Projects in Active Worlds

revised May 2006
Google site search

ContentsIntroductionBasicIntermediateAdvancedFuturePolicyInfrastructure

1   Introduction        

Active worlds is an important, perhaps a paramount, vehicle in which to learn and do exercises in a very wide variety of disciplines. In particular building objects requires a good knowledge of mathematics and trigonometry in particular. This presumes a reasonable ability with algebra and geometry from the outset.

Active Worlds also provides a new medium for communication using 3-D environments. This relates not only to architecture, but also to organise simple spaces for meetings, to direct pedestrian traffic, attract attention and so on. These are important social skills for holding meetings, gatherings, hui and conferences. The way that space is organised determines the good functioning of activities performed in that space. Knowing how to lay out space is as important as knowing how to lay out a page of information.

Active Worlds allows us to learn how to use texture in our environment. To design textile patterns and see them in use in an environment. This is a useful way to learn how to "dress" space and make it interesting for its users without it being cluttered, claustrophobic, or too barren. This is specially important in instituional situations such as schools which are usually deserts of interior design that make students feel as though school is an alien environment. Students can learn in virtual reality, the elements of design which they can use to make their home and work environment pleasant using simple resources.

Active Worlds provides a medium to explore ideas in sculpture without the expense of using real materials.

Historical arenas can be explored that may already exist - or they may be designed and built.

Active Worlds is itself an entertainment medium which may well develop to be the major entertainment medium of the 21st century. This entertainment medium will require a large number of people with special skills, similar to what we call the movie industry today. Y12-13 students can learn the basic principles of how these environments work:

Senior students may then wish to pursue some of these aspects in detail.

These are just a few of the obvious ways that Active Worlds can be used in a learning context. More will develop as the medium develops. Learning the concepts of Active Worlds is a tough learning curve and should be approached in a slow steady and rigorous manner from as early as is practical. Simply learning to navigate in 3 dimensions is a skill that requires some learning to start with. Here is a suggested lesson plan for learning about Active Worlds. The first items in the list including textures and animations are covered in the introduction to Active Worlds.

  1. Introduction 1 (Y7)- download the browser - install and explore awgate.

    Explain the following terms - avatar, object, facer, coordinate system in AW, navigation skills, walking, flying, passing thru walls, chatting in public and private, etiquette.

  2. Introduction 2 (Y5) - explore aw - look at the way buildings are constructed, at the way object commands are used, note structures and effects that are interesting and others that do not work well.
  3. Building 1 (Y7) - visit awgate - look at object yards - build simple objects in the play fields
  4. Design and Build (Y10) - design and build a structure or environment in a special building world. By this stage schools should be able to provide a world for students to play in. It is inexpensive and parents will probably willingly fund a world for a school to run on the school's server.
  5. Create textures and animations (Y10)
  6. Special Design and Build Project (Y11) - design a special project which uses the web, chat and VR together.
  7. Creating a world - Develop the previous project or start a new one. Develop idea, style, function and construct. This is a major class project and could be done for (Y11). An example may be set design.
  8. Building objects (Y11-12)- this is where mathematics comes into play. Tcl or other scripting languages will be required to create the object structures so prerequisites are Y11 trigonometry, Tcl, algebra, geometry. However, some will want to start building objects at Y7 or earlier, so trigonometry has to be available to learn at that level.
  9. Building avatars (Y12)- requires the use of specialist programs, knowledge of anatomy, makeup, posture and expression. It also includes textile and clothing design.
  10. Sequencing avatars (Y12-13) - requires knowledge of movement associated with theatre and dance, posture and expression. This requires the use of specialised software.
There are a number of scenarios for projects using AW. These scenarios mirror the exercises and projects described in the Intermediate section for webpages: individual, class, and school based webpages. However the amount of work required to do these exercises is of an order of 10 times what is required for a webpage project. However if the medium is used with effective restraint the rewards can be good.

One problem with building in AW is the problem of ownership. Dreamland Park allows you to build in the Tourist world for nothing. If you reserve your space properly this is an OK solution. You can supply warp address (similar to a URL) which takes anyone directly to your spot in VR space. You can reserve quite a chunk of space in the Tourist world so that you dont have to rub elbows with neighbours and you can feel like you have a whole world to yourself. Tourist is not a huge world (P40) but has plenty of space where you can build and not see your neighbours. The problem here is managing cleaningup. Only the tourist (name,password) who creates objects can delete them. Allowing kids free acces to the Tourist world could create a huge rubbish zone of mess in Tourist world and Dreamland Park may not be impressed.

The second way is for the school to pay for a citizenship. The school can then issue secondary passwords to students so they can build in their own space. This process is called "Shared Privileges". The school or teacher then has the power to "renovate" its space when those students have finished whatever they were doing in that space. This is in a sense a better solution as it does not pollute the Tourist world, with buildings created by kids on a whim who cannot be relied on to go back and clean up their mess. Annual membership costs $40 US per year. As a citizen working with shared privileges, all building will be done under one authority code, and so someone can go through and clean up the mess. It is possible parents may provide citizenship to their children as presents if the children show sufficient interest in this medium. It is quite as fascinating as any game.

The third and probably best way to manage this is to buy a small world. The most important factor here is the number of people it will have in it at the same time. You can theoretically have a whole school class together in virtual reality together. Each student would have to be on a computer and connected. If this is something you want to be able to do then you need a world that permits that many people in it at a time. At Dreamland Park the CX1 Commercial is probably the best option. At $320 US for the first year and $128 US each following year, it is a comparative package but with more flexibility that the DLP20 - 20 user Personal World Server which costs 70+190+40=$300 US for the first year and 190+40=$230 US each following year. The $40 extra for the personal makes up the difference between the two offerrings of a free citizenship. CX1 provides 2 while DLP20 provides 1. 2 citizenships are required to manage a world where there are many active users. One citizenship is reserved for secure activities, the other is shared out to people who want to build.

Management of a World is simpler than managing in someone else's world because you have the power to wipe the whole world and start again from any position you wish. A world can be saved and wiped and restored later. So there could be a number of worlds built and held on your computer. You can load a particular build of the world into it for whatever specific purpose is required.

However owning and managing a world is a big investment in time and energy. The monetary cost of the world is negligable by comparison.

1.1   Student AW Site        

The simplest exercise is for students to build their own place and compare results. There is no way that you can assess the result except to detrermine that the student knows how to go through the motions, which is a small challenge in itself. In the medium of AW, small is beautiful. There is a limit to how far you can see in a world, roughly about 40 m. Objects which are conceptually larger than this simply cant be seen in their entireity. Also there is a limit to how densely you can pack space with objects. A class could establish a space, build a few streets and allocate plots to build on of a reasonably small size, say 40 m square. Ask them to build something that uses at least 40 and no more than 100 objects. Assessment could be simply that this request has been performed, without any judgement whatsoever on what the student has built. This is an exercise in virtual Lego, very like a game. Students can wander around other worlds and get their own ideas for what kind of place to build. When describing the exercise do not use any words that might prejudice or influence the students as to the kind of structure that they might build. Home, house, building, structure are all words that should not be used. In fact the less said on this subject the better.

Extra marks could be given if the student works out how to create a sign that takes you to the students own homepage or elsewhere, and for implementing other AW "animation" features which make the project more integrated and active.

1.2   Class or Project AW Site        

A more advanced project is for the class to create its own place. This could be used as the gateway to sets of webpages which the class has produced as projects through the year. The place could be used as the Gateway to an ongoing project which the School is working on, a Healthy Community Project, or Arts and Sporting Events.

1.3   School or School Project World        

The next step is for a school to create its presence in AW. It may wish to do this simply by occupying a space in one of the major Worlds, or it may wish to buy an AW server and create its own world. This last step would be a big investment in time and energy and require a major project to justify such a hi-tech front-end. It is a project which the school would have to be dedicated to permanently, or pass on to another school after a few years. It would also have to be a project which needed to employ all the features that AW offers, social congregation, imaginative creative artistic and unique qualities, integrating with many websites, and possibly exploring new techniques in VR design.

Because AW has a social component, there comes a need to create gatherings, meetings or events. Such events take time and care to organise. A large amount of time can be spent in this activity. The activity may be worth it and be very rewarding or it may be a rod for some trachers back. Examine the pros and cons of event oriented projects in AW very carefully in the earliest planning stages.

1.4   Mathematical Applications in AW        

Perhaps the most compelling reason get an Active World is that it allows you to create your own objects and place them in your world. Creating objects requires a good familiarity with three dimensional coordinate geometry and trigonometry at the very least, that is to just understand the basics. If you want to start creating interesting objects you will soon find yourself delving into multivariable calculus, parametric functions, differentiation and integration. Much of this is University level mathematics, but Active Worlds makes the application of this material so accessable that school students are going to discover it for themsleves in any case.

After discovering and using the basic kinds of 3D objects such as blocks, columns, cones and spheres students will move on to try to build objects made from combinations of these objects. To do this the various components must be related to each other in space, turned and positioned and grouped. This is exactly the problem that vector spaces and linear algebra was created to deal with. With AW a rather dry mathematical theory comes to life.

The next step will be to describe extended objects such as a column which changes its diameter along its length. Another kind of object is the extrusion of a cross section around a circular path or arc. These objects require some indepth practical application of trigonometry.

A generalisation of these structures is to take a path which may or may not be closed and extrude it along a path in 1 dimension, which is a generalisation of the extruded column example. Or to extrude it in a path described over 2 dimensions, which is a generalistion of the arc example. And finally extrude the object over a path in 3 dimensions. This is the area of multivariable calculus. The length of a path is its integral, the differential of the path in each direction describes its angular change in space and the length of the segments required to model it.

You need to have your own world in order to put into it new objects which other visitors can then see.

Creating a library of Tcl functions which perform these tasks is a worthwhile exercise for mathematical high-achievers.

2   Topics in Active Worlds        

Having built, created new textures, and created interactions in Active Worlds the next step is to create new objects, and movements and so on. To do this you need your own world. A small world costs about the same as a computer game, and, although not cheap (i.e. free), it is a good investment.

Once you have your world you can populate it with your creations. The following topics introduce objects of different kinds, increasing in complexity.

Describing VR objects is done in AW using the Renderware (.rwx) format developed by Criterion.

2.1   Resources        

Making RWX Models for Active Worlds
The 3D RWX Webring

2.2   Facers        

The simplest object in Active Worlds is the facer. This is like a flat piece of card that always faces towards, no matter which angle you approach it from. It is a rectangle of any dimension and usually has a transparent texture applied to the visible side (the other side is always turned away from you). It is used to display natural objects such as flowers, greenery, small animals, but could also be used creatively as large screens. You often need to make your own facers because you usually need exact dimensions for the image that you wish to put on the facer. However the transparency of the facer does allow for some latitude. You can make a standard set of facers of suitable proportions for the objects you wish to displace to supplement the limited number facers supplied in the AW library. You may also want special lighting qualities for a group of facers. The texture is usually applied when the facer is placed in the world to avoid needing a vast library of facers.

Exercise - Write a Tcl procedure that generates the .rwx file for a facer with name width heigth, style, opacity surface and tag.

Answer

# Make facer objects of style 2 - opaque=1 tag 200

proc writefacer {name width height args} {
    array set arg { tag 200 opacity 1 surface ".5 .3 0"}
    array get arg $args
    set f [open $name.rwx w]
    puts $f "ModelBegin
 ClumpBegin
  AxisAlignment ZOrientY
  TextureModes NULL
   Vertex [expr -$width/2.0] 0  0  UV 0 1
   Vertex [expr $width/2.0] 0 0 UV 1 1
   Vertex [expr $width/2.0] $height 0 UV 1 0
   Vertex [expr -$width/2.0] $height 0 UV 0 0
   Surface $arg(surface)       #ambience, diffusion, shininess
   Color 1 1 1       # red, green, blue
   Opacity $arg(opacity)
   Quad 1 2 3 4 tag $arg(tag)
 ClumpEnd
ModelEnd"
close $f
}

#examples
writefacer fc2s6_18 .06 .18
writefacer fc2s6_6 .06 .06
writefacer fc2s6_9 .06 .09
writefacer fc2s6_12 .06 .12

writefacer fc2m1_1 .1 .1
writefacer fc2m1_2 .1 .2
writefacer fc2m1_15 .1 .15
writefacer fc2m1_3 .1 .3

2.3   Box        

The simplest 3-D object is the box or rectangular block. You quite often want a box of particular size and shape and sometimes the way the texture is mapped onto the block is important. Boxes typically only have a texture mapped onto their outside, but sometimes you want to be inside a box that has textures on it. For instance you can make a small space feel very large by mapping a nightsky starscape onto its interior. Sometimes boxes need a special mapping to make them fit in with other objects.

Exercise - Write a Tcl procedure that outputs a .rwx file for a box with name, height, width, depth and texture density.

Answer

#########################
#
# box_create - create a rectalinear solid
# parameters - name - name of the object to be produced
#                    w - width of the box
#                    h - height of the box
#                    d - depth of the box
#                    density - texture density - unit is texture per linear 10 metres - usually 5 or 10
# Change this so that if the depth=0 just the two faces are done

proc box { name w h d density args} {
    array set arg {tag 100 opacity 1 surface ".5 .3 0" }
    array set arg $args

    set f [open $name.rwx w]
    puts $f  "# copyright 2001 edgeplay@webscool.org
ModelBegin
Surface $arg(surface)
Color .535 .53 .525
ClumpBegin

# Origin is at the centre of the base of the box
# Start vertices at the top left corner

    # Front Face
    Vertex [expr -$w*0.5] $h [expr $d*0.5] UV 0 0    
    Vertex [expr $w*0.5] $h [expr $d*0.5] UV [expr $w*$density] 0
    Vertex [expr $w*0.5] 0 [expr $d*0.5] UV [expr $w*$density] [expr $h*$density ]    
    Vertex [expr -$w*0.5] 0 [expr $d*0.5] UV 0 [expr $h*$density]  
    # Right side
    Vertex [expr $w*0.5] $h [expr $d*0.5] UV 0 0
    Vertex [expr $w*0.5] $h [expr -$d*0.5] UV [expr $d*$density ]  0 
    Vertex [expr $w*0.5] 0 [expr -$d*0.5] UV [expr $d*$density ]  [ expr $h*$density ]    
    Vertex [expr $w*0.5] 0 [expr $d*0.5] UV 0 [expr $h*$density ]    
    # Back Face
    Vertex [expr $w*0.5] $h [expr -$d*0.5] UV 0  0 
    Vertex [expr -$w*0.5] $h [expr -$d*0.5] UV [expr $w*$density ]  0    
    Vertex [expr -$w*0.5] 0 [expr -$d*0.5] UV [expr $w*$density ] [ expr $h*$density ]  
    Vertex [expr $w*0.5] 0 [expr -$d*0.5] UV 0  [ expr $h*$density ]    
    # Left Face
    Vertex [expr -$w*0.5] $h [expr -$d*0.5] UV 0  0    
    Vertex [expr -$w*0.5] $h [expr $d*0.5] UV [expr $d*$density] 0  
    Vertex [expr -$w*0.5] 0 [expr $d*0.5] UV [expr $d*$density ] [ expr $h*$density ]  
    Vertex [expr -$w*0.5] 0 [expr -$d*0.5] UV 0 [expr $h*$density]     
    # Top
    Vertex [expr -$w*0.5] $h [expr -$d*0.5] UV 0  0
    Vertex [expr $w*0.5] $h [expr -$d*0.5] UV [expr $w*$density]   0       
    Vertex [expr $w*0.5] $h [expr $d*0.5] UV [expr $w*$density] [expr $d*$density]   
    Vertex [expr -$w*0.5] $h [expr $d*0.5] UV 0 [expr $d*$density] 
    # Base
    Vertex [expr -$w*0.5] 0 [expr $d*0.5] UV 0 0  
    Vertex [expr $w*0.5] 0 [expr $d*0.5] UV [expr $w*$density] 0
    Vertex [expr $w*0.5] 0 [expr -$d*0.5] UV [expr $w*$density]  [ expr $d*$density ] 
    Vertex [expr -$w*0.5] 0 [expr -$d*0.5] UV 0 [expr $d*$density]  
    TextureModes lit
    Texture stone1
    Quad  4 3 2 1   tag $arg(tag)    # front
    Quad  8 7 6 5  tag $arg(tag)    # right side
    Quad  12 11 10 9  tag $arg(tag)    # back
    Quad  16 15 14 13  tag $arg(tag)    # left
    Quad  20 19 18 17 tag $arg(tag)   # top
    Quad  24 23 22 21 tag $arg(tag)   # base "

  puts $f "ClumpEnd
ModelEnd"
    close $f

}

box_create slab0s540 .05 .02 .4 10
box_create slab0s510 .05 .02 .1 10
box_create slab0s520 .05 .02 .2 10

2.4   Column        

The column is the simplest trigonometric object. Care needs to be taken with the way that texture maps around the cylinder. Try not to have a break in the texture anywhere around the column. Care needs to be given to the texture mapping on the cap at each end. The trigonometry for this object is fairly basic but nevertheless requires some care. Notice some special cases of the column. With 4 facets the column is a special class of box (with at least 1 square face) - notice that this produces a different texture mapping.With 3 facets the column is a prism.

Exercise - Write a Tcl procedure that outputs a .rwx file for a cylinder (column) with name, width, height, angle, number of facets around, texture density and any useful additional arguments.

Answers

set tcl_precision 6

###############################3
#
# col_create
#
################################

proc col_create { name w h angle facets density args} {
    
    set pi [expr acos(-1)]
    array set arg { surface ".5 .3 0" opacity 1 }
    array set arg $args
    set f [open $name.rwx w]
    puts $f  "#column - copyright 2001 edgeplay@eroxxx.co.nz
ModelBegin
Surface $arg(surface)
Color .535 .53 .525
Opacity $arg(opacity)
ClumpBegin
    Rotate  0 0 1  $angle

# Origin is at the centre of the axis of the column
# Start vertices at the top left corner
"
    for { set j 0} { $j < $facets } { incr j } {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr $h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV 0 0
    Vertex [expr $w*.5*sin(($j+1)*2*$pi/$facets)]  [expr $h*.5] [expr $w*.5*cos(($j+1)*2*$pi/$facets)] UV [expr $density * $w *  0.5 * sin(2*$pi/$facets)/cos($pi/$facets) ] 0
    Vertex [expr $w*.5*sin(($j+1)*2*$pi/$facets)] [expr -$h*.5] [expr $w*.5*cos(($j+1)*2*$pi/$facets)] UV [expr $density * $w * 0.5 * sin(2*$pi/$facets)/cos($pi/$facets)] [expr $h*$density]
    Vertex [expr $w*.5*sin($j*2*$pi/$facets)]  [expr -$h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV 0 [expr $h*$density]"
    }
    puts $f "    # Caps"
    for { set j 0} { $j < $facets } { incr j } {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr $h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV [expr $w*.5*sin($j*2*$pi/$facets)*$density] [expr $w*.5*cos($j*2*$pi/$facets)*$density] "
    }
    for { set j 0} { $j < $facets } { incr j } {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr -$h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV [expr $w*.5*sin($j*2*$pi/$facets)*$density] [expr $w*.5*cos($j*2*$pi/$facets)*$density] "
    }
    puts $f "    TextureModes lit
    Texture stone1"
    for { set j 0} { $j < $facets } { incr j } {
        set j4 [expr $j*4]
        puts $f "    Quad [expr $j4 + 4] [expr $j4 + 3] [expr $j4 + 2] [expr $j4 + 1]"
    }
    set str "    Polygon $facets"
    for { set j 0} { $j < $facets } { incr j } {
        append str " [expr 4*$facets  + 1 + $j]"
    }
    puts $f "$str   # Top Cap"
    set str "    Polygon $facets"
    for { set j 0} { $j < $facets } { incr j } {
        append str " [expr 6*$facets - $j]"
    }
    puts $f "$str   # Bottom Cap"


  puts $f "ClumpEnd
ModelEnd"
    close $f

}

#col_create col0m146c .1 .4 90 6 10
#floor for tower

#col_create coltower [expr (.2 + (.4*sin(45)) - 0.1)*2] .02 90 8 5
#col_create col0m880 .8 .2 90 8 10

#### col2 is a column with the radius to the inside faces and the faces tangential
#  the end texture is centred at the origin of the cap - 
#    this means the texture aligns into the centre of the faces
#

proc col2_create { name w h angle facets density} {
    global erox pi
    
    set f [open $name.rwx w]
    puts $f  "# Column created by edgeplay - copyright 2001 edgeplay@eroxxx.co.nz
ModelBegin
Surface .5 .3 0
Color .535 .53 .525
ClumpBegin
    Rotate 0 0 1 $angle"

# Origin is at the centre of the axis of the column
# Start vertices at the top left corner
    set w [expr $w/cos($pi/$facets) ]
    for { set j 0.5} { $j < $facets } { set j [ expr $j +1]} {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr $h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV 0 0
    Vertex [expr $w*.5*sin(($j+1)*2*$pi/$facets)]  [expr $h*.5] [expr $w*.5*cos(($j+1)*2*$pi/$facets)] UV [expr $density * $w *  0.5 * sin(2*$pi/$facets)/cos($pi/$facets) ] 0
    Vertex [expr $w*.5*sin(($j+1)*2*$pi/$facets)] [expr -$h*.5] [expr $w*.5*cos(($j+1)*2*$pi/$facets)] UV [expr $density * $w * 0.5 * sin(2*$pi/$facets)/cos($pi/$facets)] [expr $h*$density]
    Vertex [expr $w*.5*sin($j*2*$pi/$facets)]  [expr -$h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV 0 [expr $h*$density]"
    }
    puts $f "    # Caps"
    for { set j 0.5} { $j < $facets } { set j [expr $j + 1 ]} {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr $h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV [expr $w*.5*sin($j*2*$pi/$facets)*$density] [expr $w*.5*cos($j*2*$pi/$facets)*$density] "
    }
    for { set j 0.5} { $j < $facets } { set j [expr $j + 1] } {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr -$h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV [expr $w*.5*sin($j*2*$pi/$facets)*$density] [expr $w*.5*cos($j*2*$pi/$facets)*$density] "
    }
    puts $f "    TextureModes lit
    Texture stone1"
    for { set j 0} { $j < $facets } { incr j } {
        set j4 [expr $j*4]
        puts $f "    Quad [expr $j4 + 4] [expr $j4 + 3] [expr $j4 + 2] [expr $j4 + 1]"
    }
    set str "    Polygon $facets"
    for { set j 0} { $j < $facets } { incr j } {
        append str " [expr 4*$facets  + 1 + $j]"
    }
    puts $f "$str   # Top Cap"
    set str "    Polygon $facets"
    for { set j 0} { $j < $facets } { incr j } {
        append str " [expr 6*$facets - $j]"
    }
    puts $f "$str   # Bottom Cap"


  puts $f "ClumpEnd
ModelEnd"
    close $f

}

#col2_create col2m880a .8 .2 90 8 10
#col2_create coltower3 [ expr 2*(.19 + (.4*sin($pi/4)))] .02 90 8 5
#col2_create col23 [ expr 2*(.1 + (.2*sin($pi/4)))] .055 90 8 20
#col_create col3 [ expr 2*(.1 + (.2*sin($pi/4)))] .055 90 8 20
#col_create neon2 .03 4 90 6 10
#col_create neon3 .04 4 90 6 10
col2_create pinkfloor [expr ((.15 * sin(5*$pi/12)/sin($pi/12)) +0.005)*2 ] .02 0 12 5

#### neon is a column usullay with small width and long height - as in a rod.
# It has special lighting features which make it look like a neon light when 1 is placed inside another.
#  Neons have Ambient 1 1 1 
# Surface  = 1 1 1 tho it is not clear if the reflective quality is any use when ambient is set to 1 1 1
# They are designed to be used with a narrow white one nested inside a slightly larger coloured one
# They are usually just coloured - but may also have animated textures of changing color
#  
# As dais = they can be used as lit floors and also ceiling lights
#

proc neon { name w h angle facets density opacity} {
    global erox pi
    
    set f [open $name.rwx w]
    puts $f  "# Neon Column created by edgeplay - copyright 2001 edgeplay@eroxxx.co.nz
ModelBegin
LightSampling Vertex
Surface 1 1 0
Color .5 .5 .5
Ambient 1 1 1
Opacity  $opacity
ClumpBegin
    Rotate 0 0 1 $angle"

# Origin is at the centre of the axis of the column
# Start vertices at the top left corner
    set w [expr $w/cos($pi/$facets) ]
    for { set j 0.5} { $j < $facets } { set j [ expr $j +1]} {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr $h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV 0 0
    Vertex [expr $w*.5*sin(($j+1)*2*$pi/$facets)]  [expr $h*.5] [expr $w*.5*cos(($j+1)*2*$pi/$facets)] UV [expr $density * $w *  0.5 * sin(2*$pi/$facets)/cos($pi/$facets) ] 0
    Vertex [expr $w*.5*sin(($j+1)*2*$pi/$facets)] [expr -$h*.5] [expr $w*.5*cos(($j+1)*2*$pi/$facets)] UV [expr $density * $w * 0.5 * sin(2*$pi/$facets)/cos($pi/$facets)] [expr $h*$density]
    Vertex [expr $w*.5*sin($j*2*$pi/$facets)]  [expr -$h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV 0 [expr $h*$density]"
    }
    puts $f "    # Caps"
    for { set j 0.5} { $j < $facets } { set j [expr $j + 1 ]} {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr $h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV [expr $w*.5*sin($j*2*$pi/$facets)*$density] [expr $w*.5*cos($j*2*$pi/$facets)*$density] "
    }
    for { set j 0.5} { $j < $facets } { set j [expr $j + 1] } {
        puts $f "    Vertex  [expr $w*.5*sin($j*2*$pi/$facets)]   [expr -$h*.5] [expr $w*.5*cos($j*2*$pi/$facets)] UV [expr $w*.5*sin($j*2*$pi/$facets)*$density] [expr $w*.5*cos($j*2*$pi/$facets)*$density] "
    }
    puts $f "    TextureModes lit
    Texture stone1"
    for { set j 0} { $j < $facets } { incr j } {
        set j4 [expr $j*4]
        puts $f "    Quad [expr $j4 + 4] [expr $j4 + 3] [expr $j4 + 2] [expr $j4 + 1]"
    }
    set str "    Polygon $facets"
    for { set j 0} { $j < $facets } { incr j } {
        append str " [expr 4*$facets  + 1 + $j]"
    }
    puts $f "$str   # Top Cap"
    set str "    Polygon $facets"
    for { set j 0} { $j < $facets } { incr j } {
        append str " [expr 6*$facets - $j]"
    }
    puts $f "$str   # Bottom Cap"


  puts $f "ClumpEnd
ModelEnd"
    close $f

}

foreach i { 00 15 30 45 60 75 90 } {
    neon neon0${i}bm1 .01 .1 $i 4 10 1
    neon neon1${i}bm1 .02 .1 $i  4 10 .7
    neon neon0${i}bm2 .01 .2 $i 4 10 1
    neon neon1${i}bm2 .02 .2 $i  4 10 .7
}
neon neon000bm4 .01 .4 00 4 10 1
neon neon100bm4 .02 .4 00 4 10 .7

2.5   Arch        

You dont have to use AW's arch models to realise that they are not well designed. In particular a cheap nasty method is used to map the textures onto the inside of the arch. It costs nothing more to do this properly, just a little thought and some trigonometry. You can see from the answer that reasonably simple objects become a good exercise in practical trigonometry.

Exercise - Write a Tcl procedure that outputs a .rwx file for a Roman arch with name, width, height, depth, arc width, arc height, number of facets to the arch and texture density.

Answer

set pi [expr acos(-1)]

# Roman Arch -  Create a Roman arch - width height depth for the whole model
#            aw ah  are arch width and height  - this is the "hole" in the centre-base
#            facets is the number of segments in the arch ranging from 1=flat upwards 
#            6 is the AW norm -  12 is the useful maximum which looks close enough to round



proc arch_create { name w h d aw ah facets density args} {
    array set arg {surface ".5 .3 0" opacity 1

  global pi
    
    set f [open $name.rwx w]
    puts $f  "# copyright 2001 edgeplay@eroxxx.co.nz
ModelBegin
Surface .5 .3 0
Color .535 .53 .525
Opacity $arg(opacity)
ClumpBegin"

# Origin is at the centre of the base of the box
# Start vertices at the top left corner

    puts $f "    \# Front
    Vertex [expr $aw*.5] $ah [expr $d*.5]    UV [expr ($w+$aw)*.5*$density] [expr ($h-$ah)*$density]   \# 1
    Vertex [expr $aw*.5] 0 [expr $d*.5]    UV [expr ($w+$aw)*.5*$density] [expr $h*$density]
    Vertex [expr $w*.5] 0 [expr $d*.5]    UV [expr $w*$density] [expr $h*$density]   
    Vertex [expr $w*.5] $h [expr $d*.5]    UV [expr $w*$density] 0
    Vertex [expr -$w*.5] $h [expr $d*.5]    UV 0 0   
    Vertex [expr -$w*.5] 0 [expr $d*.5]    UV 0 [expr $h*$density]
    Vertex [expr -$aw*.5] 0 [expr $d*.5]    UV [expr ($w-$aw)*.5*$density] [expr $h*$density]   \# 7"
  
    for { set j 0 } { $j < $facets } { incr j } {
        set i [expr $j*180/$facets]
        puts $f "    Vertex [expr -$aw*(cos($i*$pi/180))*.5] [expr $ah + $aw* sin($i*$pi/180)*.5] [expr $d*.5] UV [expr ($w - $aw*cos($i*$pi/180))*.5*$density] [expr ($h-$ah - $aw*.5*sin($i*$pi/180))*$density]"
    }
    puts $f "     # Right side
    Vertex [expr $w*0.5] $h [expr $d*0.5] UV 0 0  # [expr 8 + $facets]
    Vertex [expr $w*0.5] $h [expr -$d*0.5] UV [expr $d*$density ]  0 
    Vertex [expr $w*0.5] 0 [expr -$d*0.5] UV [expr $d*$density ]  [ expr $h*$density ]    
    Vertex [expr $w*0.5] 0 [expr $d*0.5] UV 0 [expr $h*$density ] "

    puts $f "    \# Back
    Vertex [expr -$aw*.5] $ah [expr -$d*.5] UV [expr ($w+$aw)*.5*$density] [expr ($h-$ah)*$density]  #  [expr 12+$facets]
    Vertex [expr  -$aw*.5] 0 [expr -$d*.5] UV [expr ($w+$aw)*.5*$density] [expr $h*$density]
    Vertex [expr -$w*.5] 0 [expr -$d*.5] UV [expr $w*$density] [expr $h*$density]    
    Vertex [expr -$w*.5] $h [expr -$d*.5] UV [expr $w*$density] 0
    Vertex [expr $w*.5] $h [expr -$d*.5] UV  0 0    
    Vertex [expr $w*.5] 0 [expr -$d*.5] UV 0 [expr $h*$density]
    Vertex [expr $aw*.5] 0 [expr -$d*.5] UV [expr ($w-$aw)*.5*$density] [expr $h*$density]    # [expr 18+$facets]"
 
    for { set j 0} { $j < $facets } { incr j } {
        set i [expr $j*180/$facets]
        puts $f "    Vertex [expr $aw*(cos($i*$pi/180))*.5] [expr $ah  + $aw* sin($i*$pi/180)*.5] [expr -$d*.5] UV [expr ($w*.5-$aw*.5*cos($i*$pi/180))*$density] [expr ($h-$ah- $aw*.5*sin($i*$pi/180))*$density]"
    }
    
    puts $f "    # Left Face
    Vertex [expr -$w*0.5] $h [expr -$d*0.5] UV 0  0    # [expr 19 + (2*$facets)]
    Vertex [expr -$w*0.5] $h [expr $d*0.5] UV [expr $d*$density] 0  
    Vertex [expr -$w*0.5] 0 [expr $d*0.5] UV [expr $d*$density ] [ expr $h*$density ]  
    Vertex [expr -$w*0.5] 0 [expr -$d*0.5] UV 0 [expr $h*$density] 
    # Top
    Vertex [expr -$w*0.5] $h [expr -$d*0.5] UV 0  0 # [expr 23 + (2*$facets)]
    Vertex [expr $w*0.5] $h [expr -$d*0.5] UV [expr $w*$density]   0       
    Vertex [expr $w*0.5] $h [expr $d*0.5] UV [expr $w*$density] [expr $d*$density]   
    Vertex [expr -$w*0.5] $h [expr $d*0.5] UV 0 [expr $d*$density] 
    # Base
    Vertex [expr -$w*0.5] 0 [expr $d*0.5] UV 0 0     # Base Left [expr 27 + (2*$facets)]
    Vertex [expr -$aw*0.5] 0 [expr $d*0.5] UV [expr ($w-$aw)*.5*$density] 0
    Vertex [expr -$aw*0.5] 0 [expr -$d*0.5] UV [expr ($w-$aw)*.5*$density]  [ expr $d*$density ] 
    Vertex [expr -$w*0.5] 0 [expr -$d*0.5] UV 0 [expr $d*$density]  
    Vertex [expr $w*0.5] 0 [expr $d*0.5] UV 0 0     # Base Right  [expr 31 + (2*$facets)]
    Vertex [expr $aw*0.5] 0 [expr $d*0.5] UV [expr ($w-$aw)*.5*$density] 0
    Vertex [expr $aw*0.5] 0 [expr -$d*0.5] UV [expr ($w-$aw)*.5*$density]  [ expr $d*$density ] 
    Vertex [expr $w*0.5] 0 [expr -$d*0.5] UV 0 [expr $d*$density]  
    # Inside Arch Sides - need to match texture to bottom of front face
    Vertex [expr -$aw*.5] $ah [expr $d*.5] UV 0 [expr ($h - $ah)*$density]    # Inside Left Face  # [expr 35 + $facets*2]
    Vertex [expr -$aw*.5] $ah [expr -$d*.5] UV [expr $d*$density] [expr ($h - $ah)*$density]
    Vertex [expr -$aw*.5] 0 [expr -$d*.5] UV [expr $d*$density] [expr $h*$density]
    Vertex [expr -$aw*.5] 0 [expr $d*.5] UV 0 [expr $h*$density]
    Vertex [expr $aw*.5] $ah [expr $d*.5] UV 0 [expr ($h - $ah)*$density]    # Inside Right Face # [expr 39 + $facets*2]
    Vertex [expr $aw*.5] $ah [expr -$d*.5] UV [expr $d*$density] [expr ($h - $ah)*$density]    
    Vertex [expr $aw*.5] 0 [expr -$d*.5] UV [expr $d*$density] [expr $h*$density]
    Vertex [expr $aw*.5] 0 [expr $d*.5] UV 0 [expr $h*$density]"

    for {set j 0} {$j <= $facets} { incr j} {
         set i [expr $j*180/$facets]
         puts $f "    Vertex [expr -$aw*cos($j*$pi/$facets)*.5] [expr $ah  + $aw* sin($j*$pi/$facets)*.5]  [expr $d*.5] UV 0 [expr ($h-$ah+ $j*($aw*.5*sin($pi/$facets)/cos($pi/(2*$facets))))*$density]
    Vertex [expr -$aw*cos($j*$pi/$facets)*.5] [expr $ah  + $aw* sin($j*$pi/$facets)*.5] [expr -$d*.5] UV [expr $d*$density] [expr ($h - $ah+ ($j*$aw*.5*sin($pi/$facets)/cos($pi/(2*$facets))))*$density]"
    }
    
    set u [ expr $w + $h + $ah+ ($w-$aw)*.5 + ($facets*$aw*.5*sin($pi/$facets)/cos($pi/(2*$facets))) ]
    puts $f "    Vertex [expr $aw*.5] $ah [expr $d*.5] UV [expr $d*$density] [expr $u*$density]
    Vertex [expr $aw*.5] $ah [expr -$d*.5] UV 0 [expr $u*$density]
    Vertex [expr $aw*.5] 0 [expr $d*.5] UV [expr $d*10] [expr ($u + $ah)*$density]
    Vertex [expr $aw*.5] 0 [expr -$d*.5] UV 0 [expr ($u+ $ah)*$density]
    TextureModes lit
    Texture stone1"
     set str "    Polygon [expr 7 + $facets] 1 2 3 4 5 6 7"
     for { set j 0 } { $j < $facets } { incr j } {
         append str " [expr 8 + $j]"
     }
    puts $f "$str   # Front
    Quad [expr 11 + $facets] [expr 10 + $facets] [expr 9+$facets] [expr 8+$facets]   # Right Face"
    set str "    Polygon [expr 7 + $facets] [expr 12 + $facets] [expr 13 + $facets] [expr 14 + $facets] [expr 15 + $facets] [expr 16 + $facets] [expr 17 + $facets] [expr 18 + $facets]"
    for { set j 0 } { $j < $facets } { incr j } {
        append str " [expr 19+ $facets + $j ]"
    }
    puts $f "$str   # Back
    Quad [ expr 22 + $facets*2] [ expr 21 + $facets*2] [ expr 20 + $facets*2] [ expr 19 + $facets*2]   # Left Face
    Quad [expr 26+ $facets*2] [expr 25 + $facets*2] [expr 24 + $facets*2] [expr 23 + $facets*2]   # Top
    Quad [expr 30 + $facets*2] [ expr 29 + $facets*2] [expr 28 + $facets*2] [ expr 27 + $facets*2]   # Base Left
    Quad  [expr 34 + $facets*2] [expr 33 + $facets*2] [ expr 32 + $facets*2] [expr 31 + $facets*2 ]   # Base Right
    Quad [expr 38 + $facets*2] [expr 37 + $facets*2] [expr 36 + $facets*2] [expr 35 + $facets*2]    # Left Inside Face
    Quad [expr 39 + $facets*2] [expr 40 + $facets*2] [expr 41 + $facets*2] [expr 42 + $facets*2]    # Right Inside Face
    # Arch"

    for {set i 0} { $i < $facets} { incr i } {
         puts $f "    Quad [expr 43 + $facets*2 + $i*2 ] [expr 44 + $facets*2 + $i*2 ] [expr 46 + $facets*2 + $i*2] [expr 45 + $facets*2 + $i*2 ] "
    }

  puts $f "ClumpEnd
ModelEnd"
    close $f

}

arch_create ar8m44122 .4 .4 .1 .2 .2 8 10
arch_create ar8m34122 .3 .4 .1 .2 .25 8 10
arch_create ar8m12121 .15 .2 .1 .1 .125 8 10
arch_create ar8m22111 .2 .2 .1 .1 .1 8 10
arch_create arch4 .2 .2 .1 .095 .1 8 20.5
arch_create ar8m34122a .3 .4 .1 .2 .25 8 20

2.6   Arc        

Start with a semicircular arc with a rectangular profile, like the rim of a carriage wheel. Arcs are usually of a slender shape so that breaks in texture mapping are not obvious, but an even texture density over the whole arc should be achieved. If the arc is extended into a full circle you have a section of pipe.

Exercise 1 Write a Tcl procedure that outputs a .rwx file for a square sectioned semicircular arc, with name, width of the section, radius of the arc, depth of the section , facet density of the arc, texture density and opacity.

2 Generalise for any portion of a circle.

3 generalise for a 'circular' cross section of any number of facets.

Answer 1

set tcl_precision 6

# neonarc creates an arc to be used as a neon light
# the arc uses a square profile
# w is the width of the band - usually close to d the depth (or thickness)
# radius is the radius of the semicircle - which determines height and width of the whole object

proc neonarc { name  w radius d  facets density opacity} {
    array set arg {surface ".5 .3 0" opacity 1}
    array set arg $args
    set pi [expr acos(-1)]
    
    set f [open $name.rwx w]
    puts $f  "# Neon Arc - copyright 2001 edgeplay@eroxxx.co.nz
ModelBegin
LightSampling Vertex
Surface $arg(surface)
Color .5 .5 .5
Ambient 1 1 1
Opacity  $arg(opacity)
ClumpBegin"

# Origin is at the centre base of the arc (arch)
# Enumerate the vertices - there is no useful UV mapping
    set w2 [expr $w/2 ]
    set d2 [expr $d/2]
    for { set j 0} { $j <= $facets } { incr j} {
        set ja [expr $j*$pi/$facets]
        puts $f "    Vertex  [expr ($radius-$w2)*cos($ja)]  [expr ($radius-$w2)*sin($ja)] $d2    
    Vertex  [expr ($radius+$w2)*cos($ja)]  [expr ($radius+$w2)*sin($ja)] $d2 
    Vertex  [expr ($radius-$w2)*cos($ja)]  [expr ($radius-$w2)*sin($ja)] [expr -$d2]
    Vertex  [expr ($radius+$w2)*cos($ja)]  [expr ($radius+$w2)*sin($ja)] [expr -$d2]"    
    }
  
    puts $f "    TextureModes lit
    Texture stone1"
    
    # the front face is the first of each 4 followed by the 2nd of each 4 in reverse
    set str "    Polygon [expr ($facets+1)*2]"
    for { set j $facets} { $j >= 0 } { incr j -1} {
        append str " [expr ($j*4) +1]"
    }
    for { set j 0} { $j <= $facets } { incr j } {
        append str " [expr ($j*4) +2]"
    }
    puts $f $str
    # back face is similar
    set str "    Polygon [expr ($facets+1)*2]"
    for { set j $facets} { $j >= 0 } { incr j  -1} {
        append str " [expr ($j*4) +4]"
    }
    for { set j 0} { $j <= $facets } { incr j } {
        append str " [expr ($j*4) +3]"
    }
    puts $f $str
    # do the inside and outside quads for each facet
    for { set j 0} { $j < $facets } { incr j } {
        puts $f "     Quad [expr ($j*4)+1] [expr ($j*4) +5] [expr ($j*4) + 7] [expr ($j*4) + 3]
    Quad [expr ($j*4) +2] [expr ($j*4) +4] [expr ($j*4) + 8] [expr ($j*4) +6]"
    }

  puts $f "ClumpEnd
ModelEnd"
    close $f

}

neonarc nearc2 .01 .38 .01 6 10 1
neonarc nearc1 .02 .38 .02 6 10 .7

neonarc ring12  .25 [expr ((.15 * sin(5*$pi/12)/sin($pi/12)) -0.12) ] .02 6 5 1

3   More Advanced Shapes        

From here on the mathematics start to get a little more complicated. We use notions such as the extrusion of a linear path along a second path. The simplest examples produce shapes like guttering or corrugated iron. These are the extrusion of a fairly simple linear path along a straight line.

A hemispherical dome is created by extruding a quarter circle around in a circle. This can be generalised to more intricate paths extruded in a circle to make onion domes, and fancy columns.

This is where the notion of shapes described by parametric functions comes into play. This is the realm of multivariable calculus and partial differentials, but in this case we have immediate and real application which moves this subject from the purely theoretical level used at Universities to a practical level which much younger people can come to grips with. For this reason the language of multivariable calculus is not used, nor of differential calculus. Instead we talk about paths, closed paths, points, scale, surfaces and differences. This keeps the language specific to the application.

Exercise 1 - Write a procedure that displays a 2D path in a canvas. Include the canvas name on the canvas so it is easy to destroy, display the axis and some kind of scale, and scale the canvas to display the path well. Allow the canvas dimensions to be set. For the path use a structure of a list of 2D points. Return the canvas name so that you can easily add more paths.

Exercise 2 - Exercise 1 will have included some basic procedures which are often used when handling mulitivariable objects. If you look at the Tcllib in the ActiveState distribution you see the ::math::linearalgebra library. You may wish to make use of these procedures as they use the obvious list of lists structure. However you may decide that you can do better. Write the following library procedures:

[pathorigin] displaces a path with respect to a point of origin. Typically the origin may be the first point in the path, in which case this point value gets subtracted from all the other points and the first point of the path is {0 0 ...}. This is also known as a constant transform, all the points are transformed (shifted) by a constatnt point. Handle the special case where a single value is given for the point, meaning that a uniform vector wit this value is implied i.e. {value value ...}

A second is a [pathscale] procedure which scales each axis by a certain proportion. This means mulitplying each point in the path by the scale point. This is known as a scalar product, and with respect to each point as a dot product. In the usual case we want all the axis to be scaled by the same amount. so if a single value is provided again expand this out to {scale scale ...}

Within these two procedures are the procedures which determine the dimension of a path, that is the list length of the points [pathdim], and the procedure which expands a value into a uniform point or vector for a path [pointuniform].

Finally there is the procedure which combines [pathorigin] and [pathscale], [pathLinearT] which performs the scaling and then the origin shift.

In order to add some extra power to these procedures, permit path, scale and origin to be expressions which are evaluated in their namespace using [uplevel].

Example of [pathdisplay]

#######################
#
# pathdisplay - display a  2-d path in a canvas widget .c
#

proc pathdisplay {path args} {

    array set arg {size 200 scalecolor #8080f0 }
    array set arg $args

    global windowcnt
    if { ![ info exists windowcnt] } { set windowcnt 0}
    incr windowcnt
    set c .c$windowcnt
    pack [ canvas $c -height $arg(size) -width $arg(size) ]
    $c create text {0 0} -anchor nw -fill $arg(scalecolor) -text $c

    # get a bounding cube for the path
    set max 1
    foreach i $path {
        if { abs([lindex $i 0]) > $max } { set max [expr abs([lindex $i 0]) ] }
        if { abs([lindex $i 1]) > $max } { set max [expr abs([lindex $i 1]) ] }
    }
    set max [expr $max*1.1]
    set scale { {-1 0} {1 0} {1 1} {0 1} {0 -1} }
    
    # scale the path and the scale to fit the window - move origin to center of window
    set factor [expr $arg(size)*.45/$max ]
    set size2 [expr -$arg(size)/2.0]
    $c create line [join [pathLinearT $scale $factor $size2 ]] -fill $arg(scalecolor)
    $c create text [expr $factor -$size2] [ expr -$size2 ] -anchor se  -fill $arg(scalecolor) -text x
    $c create text [ expr -$size2 ] [expr $factor -$size2]  -anchor se  -fill $arg(scalecolor) -text y
    $c create line [join [pathLinearT $path $factor $size2 ]] -fill #000000
    return $c
}

#################
#
#    pathLinearT
#

proc pathLinearT {path scale origin} {
    return [pathorigin [pathscale $path $scale] $origin]
}

##################
#
#   pathorigin - moves a path in relation to an origin
#   pathorigin path {x0 y0 ...} - will make {x0 y0 ...} the origin of the path
#   in particular if (x0 y0 ...} is the initial point of the path - that gets moved to {0 0 ...}
#   in other words {x0 y0 ...} gets subtracted from every point in the path
#   if origin is a single point 
#             then it is expanded to a uniform vector to match the size of the points in path
#   Note that the values in the path and origin can be expressions
#

proc pathorigin {path origin} {
    if { [llength $origin] == 1 } {
        set origin [pointuniform $origin [pathdim $path]]
    }
    set str {}
    foreach i $path {
        set point {}
        for { set j 0 } { $j < [llength $origin]} { incr j } {
            set k1 [lindex $i $j]
            set k2 [lindex $origin $j]
            lappend point [uplevel "expr $k1 - $k2"]
        }
        lappend str $point
    }
    return $str
}

#########################
#
#   pathscale - scale each dimension of the path
#   this is similar to [pathorigin] except it simply multiplies instead of subtracts
#   Note that the values in path and scale can be expressions
#

proc pathscale {path scale} {
    if { [ llength $scale] == 1 } {
        set scale [pointuniform $scale [pathdim $path]]
    }
    set str {}
    foreach i $path {
        set point {}
        for { set j 0 } { $j < [llength $scale]} { incr j } {
            set k1 [lindex $i $j]
            set k2 [lindex $scale $j]
            lappend point [uplevel "expr $k1 * $k2"]
        }
        lappend str $point
    }
    return $str
}

######################
#
#   pathdim - returns the dimension of path
#             that is the number of elements in the points
#

proc pathdim path {
    return [llength [ lindex $path 0]] 
}

######################
#
#   pointuniform  - for a single value and dimension returns the uniform point (vector)
#

proc pointuniform {value dimension} {
    return [string repeat "$value " $dimension]
}

#example

pathdisplay  {{-2 1} {-2 -1} {2 -1 0} {2 1} {0 0 } {-2 1} {2 1}} 

Exercise 3 - Write a Tcl procedure [pathlist] that creates a path of points from a parametric function list in any number of dimensions.
    parameters:
  • p0 the initial parametric value
  • pend - the final parametric value
  • segments - the number of segments in the path list
  • a list of functions which take one paremeter %p
    returns:
  • a list of coordinate points of the path - each point is a list of real numbers
test out your procedure on this expression:
pathdisplay [pathlist 0 $pi 6 {sin(%p) cos(%p)}]

Answer:

######################
#
#  pathlist - creates a path of points from a parametric function list in any number of dimensions
#              (  NOTE - this procedure uses [lambda] and the lamba version of [unknown]  ) 
#
# parameters:
#        p0 - the initial parametric value
#        pend - the final parametric value
#        points - the number of segments in the path list
#        function_list - a list of functions which take 1 parameter - 
#           until [lambda] is used - use parameter named "%p" e.g.  [ pathlist 0 1 10 {%p*sin(%p)} ]
#
# returns:
#        a list of coordinate points of the path - each point is a list of real numbers
#
######################

proc pathlist { p0 pend segments function_list } {
    regsub -all -- %p $function_list {$p} f_list
    set str {}
    set pincr [expr (double($pend) - $p0)/$segments ]
    set rel "<"
    if { $pincr < 0 } { set rel ">" }
    for { set p $p0 } { [expr ( $p - $pend ) $rel $pincr /2.0 ] } { set p [expr $p + $pincr] } {
        set point {}
        foreach f $f_list {
            lappend point [ eval expr $f ]
        }
        lappend str $point
    }
    return $str
}

source [file join [file dirname [info script]] pathdisplay.tcl]

set pi [expr acos(-1)]
pathdisplay [pathlist 0 $pi 6 {sin(%p) cos(%p)}]

Exercise 4 - We often want to make shapes which are extruded in a circle. One basic procedure we need to do this is to be able to turn a 2D path in its plane. Write a Tcl procedure [pathturn] {path angle} that turns a 2D path about the current origin. Test it out on the following expression:
pathturn [pathlist 0 $pi 8 {sin(%p) cos(-%p)}] 30

Answer:

######################
#
# path turn - turns a path about its current origin - applies ONLY to 2-d paths at present - has yet to be generalised
# any point  p in the path can be made the origin by using [pathorigin [lindex $path $p] $path]
# the angle a is in degrees

proc pathturn {path angle} {
    set str {}
    set pi [expr acos(-1)]
    set ar [expr {$angle*$pi/180}]
    foreach i $path {
        foreach {x y} $i {
            lappend str [list [expr $x*cos($ar)- $y*sin($ar)] [expr $x*sin($ar) +$y*cos($ar)]]
        }
     }
    return $str
}

# Examples
source [file join [file dirname [info script]] pathlist.tcl]
set pi [expr acos(-1)]

pathdisplay [pathturn [pathlist 0 $pi 8 {sin(%p) cos(-%p)}] 30] ] 

Exercise 5 - Make [pathdisplay] a little more general so it can display various lines. Do this by adding a -canvas option so you can supply the window name of the canvas. Also provide a -fill option for the line colour.

Exercise 6 - We often want to make sheet-like objects formed by a path being extruded along a line. Write a Tcl procedure that extrudes a 2D path into a 3D sheet along the third (z) axis

Exercize 7 - As we work with more complex 3D shapes we want to be able to see them from various angles. Write a Tcl procedure [pathdisplay3D] which displays a 3D path on an axis system that can be rotated an all three axis by sliders which range from 0 to 2pi.

Exercise 8 - Write a Tcl procedure that extrudes a path in a straight line on an additional axis for legntgh s.

Exercise 9 - Write a Tcl procedure that extrudes a quarter circle through 360 degrees to form a hemisphere.

Exercise 10 - Exercise 9 is a special case of a more general process of extruding any path on 360 degrees circular path on either the (x z) or (y z) axis, the initasl point may or may not be at the origin (0 0). Write the Tcl procedure for this.

Exercise 11 - Exercise 10 is a special case of a more general case of extruding a 2D path along any path defined by functional parameters or a path list. test this out witb this expression:
pathextrude [pathlist 0 2*$pi 12 {sin(%p) cos(%p)}]

Exercise 12 - Exercise 11 can do with some refinement. If you extrude a circle along a 360 degree circle to reate a hoop, then the circle collapses into a line at the 90 degree point. Address this by rotating the section so it always lies on the axis

©2000 - 2006 WEBSCOOL This page last updated 30 May 2006. All rights reserved - including copying or distribution of any portion of this document in any form or on any medium without authorisation. For more regarding the copyright.