|
Making a Venetian blind.
- First we need to make a single slat of a Venetian blind, so make a grid using Model->Get->Primitive->Grid,
and scale it so that it is 12 units long and 1 unit wide.
- Now make the rest of the slats by Model->Duplicate->Repetition of grid1,
specifying 30 or 40 duplicates. Rename null1 which is the parent of all these new
slats control.
- Rename the original slat from grid1 to blind1.
- Next move the slats into their proper positions, Use Model->Display->SoftWishConsole
to generate an expression on each of these slats, by typing:
SWN_do i grid* {
regexp {([a-z]+)([0-9]+)} $i junk alphaJunk num
SW_expression "$i.etrny = blind1.etrny + [expr $num - 1 + 0.75]"
}
where 0.75 is the space we will leave between slats when they are
in the open position. (Remember that you need to type:
package require SWN
SWN_init
before you can use any of the SWN procedures, and
package require SW
before you can use any SW procedures.)
SWN_do takes 3 arguments: a variable name, a pattern, and some code. For
every element in the scene which matches the pattern, it evaluates the code, substituting
each specific element name which matches the pattern. For example, grid2 matches
the pattern grid*, so SW_do will substitute grid2 into the
code, producing:
regexp {([a-z]+)([0-9]+)} grid2 junk alphaJunk num
SW_expression "grid2.etrny = blind1.etrny + [expr $num - 1 + 0.75]"
When evaluated, this generates an expression on grid2. Let's take a
closer look at the regexp line. Regexp takes a regular
expression as its first argument and uses it to split its second argument into parts. In
this case the ([a-z]+) part matches grid and gets put into alphaJunk,
the ([0-9]+) matches the number 2 from grid2 and puts it into num.
Tcl uses expr to evaluate arithmetic expressions, so [expr $num - 1
+ 0.75] returns the number of the grid minus 1 plus 0.75, so in this case it would
return 1.75.

Click for hi-res image.
- Now that the slats are in the right place, delete blind1 (Model->Delete->Selection).
This will delete all the expressions we used to move the slats, which we don't need
anymore.
- We should also name all the slats correctly, which we can do easily using
SWC_rename,
like this: SWN_rename /control/* grid* blind*
This renames all the descendants of the control null whose names begin with grid
to begin with blind instead.

Click for hi-res image.
- Now select the control null and Motion->Savekey->Object->Rotation->All.
- In order to be able to rotate all of the slats by rotating only the control null,
we generate expressions on each of the slats, like this:
SWN_do i blind* { SW_expression "$i.rotx = control.rotx" }

Click for hi-res image.
- Construction of the Venetian blind is now complete. Now that we know what to do, let's
make a Venetian-blind generating procedure so that we never have to figure this out again.
Such a procedure would look like this:
proc makeVenetianBlind { numSlats } {
package require SWN
package require SW
global SAA_Constants
global SWN
SWN_init
SWN_pre
# make the parent null
set nullElem [ SAA_AllocElem ]
SAA_nullCreate $SWN(scene) $nullElem
set name control
SAA_elementSetName $SWN(scene) $nullElem name
# build all the slats, name them blindn, for n=0,1,2... numSlats, and
# parent to control null
for { set i 0 } { $i < $numSlats } { incr i } {
set gridElem [ SAA_AllocElem $numSlats ]
SAA_meshCreateGrid $SWN(scene) 1.0 12.0 1 1 [ access $gridElem $i ]
set name blind$i
SAA_elementSetName $SWN(scene) [ access $gridElem $i ] name
SAA_modelSetParent $SWN(scene) [ access $gridElem $i] $nullElem
}
# move all the slats into position, don't use expressions
SWN_do i blind* {
regexp {([a-z]+)([0-9]+)} $i j1 j2 num
set start [SW_exprValue blind1.etrny]
set position [expr $start + ($num * 0.75)]
set modelID [ SWN_name2element $i ]
SAA_modelGetTranslation $SWN(scene) $modelID $SAA_Constants(SAA_COORDSYS_GLOBAL) x y z
SAA_modelSetTranslation $SWN(scene) $modelID 1 0 $SAA_Constants(SAA_COORDSYS_GLOBAL) $x
$position $z
}
# slave the rotation of all the slats to the first slat
SWN_do i blind* {
regexp {([a-z]+)([0-9]+)} $i j1 j2 num
if {$num != 0 } {
set expression [ SW_expression "$i.rotz=blind0.rotz" ]
SAA_Free $expression
}
}
}
- If we store this code in a file named VenetianBlind.tcl then the next time we need to
make a blind with 100 slats, we can use the SoftWish Console to source this file and then
type:
makeVenetianBlind 100
- To safeguard against accidentally rotating the entire Venetian blind, when we meant to
rotate only the control null we could use SoftWish behavior to attach a
select-me-only script to the control null. SoftWish behaviors cause attached
scripts to be evaluated whenever the object to which the script is attached is modified.
The select-me-only script determines if the object it is attached to is selected, and if
it is, then it deselects everything else in that object's branch. Using SoftWish behaviors
involves 2 steps:
- Attach the script. We do this with Model->Effects->behaviorEdit, which
brings up the behaviorEdit control. We choose the control null and then attach the
select-me-only.tcl script, via the file browser, to the control null .

Click for hi-res image.
Getting the script evaluated. We do this with Model->Effects->behaviorSingleFirst.
The status bar prompts us to pick a model with attached tcl code and we pick the control
null twice. Now the script attached to the control null gets evaluated before
any changes in the scene which affect the null take place.

Click for hi-res image.
- If one wants to be able to select the entire Venetian blind tree and transform it, just Model->Effects->behaviorEdit
again and disable the attached select-me-only script. Note that you can also use
behaviorEdit to edit scripts. Any changes you make are immediately reflected in the
behavior of the object that the script is attached to. Here is what the select-me-only
script looks like:
set rootID [ SWB_getCurrentElement ]
SAA_elementGetSelectionType [ SWB_getScene ] $rootID selType
if { $SAA_unConstants(SAA_SelectionType,$selType) == "SAA_SEL_BRANCH_SELECTED" } {
SAA_selectlistSetModelBranch [ SWB_getScene ] 0 1 $rootID
SAA_selectlistSetElements [ SWB_getScene ] 1 1 $rootID
}
|