PDA

View Full Version : Need help improving "branch" script


Malkalypse
12-24-2008, 08:02 PM
Hello, I managed to get this script working. I tried posting in a previous thread when it was still in early development, but was not able to get any help at that time as the file was rather long, and much of it was not documented. Here I have a script that works within limitations, and have commented thoroughly (excessively?)

The script is intended for easily creating organic branching shapes (i.e. tree branches, roots, nerves, and blood vessels) from splines already created by the user.

http://www.versatileartist.com/temp/branch3.jpg

I would like to expand this scripts capabilities, first by removing the limitations stated in the header. In other words, I would like to be able to branch splines of any index number whether higher or lower, cross them at places other than the start, and be able to have multiple branches starting at the same point.

Any suggestions for implementing these changes would be appreciated, as well as any other suggestions for improving the script.

I have a file for testing purposes as well, found here: www.versatileartist.com/temp/splines02.max (http://www.versatileartist.com/temp/splines02.max)

The script as it stands is below:


-- Spline Brancher
-- splineBrancher.ms
-- Version 0.5 (12/24/08)
-- by Kevin Mackey (kmackey2001@hotmail.com)
-- The purpose of this script is to take a branching spline shape and convert it into a mesh
-- Right now the script only works correctly if:
-- 1) The "child" splines are of a higher index number than their "parent" splines
-- 2) Only one "child" spline branches off from any given knot on the "parent" spline
-- 3) The splines do not cross at any point



global theShape
global w
knotArray = #() -- array for knot positions, sorted by spline
splineArray = #() -- array for generated splines
meshArray = #() -- array for generated meshes
crossPoints = #(#(),#(),#()) -- array for overlapping points
-- crossPoints[1]: position
-- crossPoints[2]: 1st point
-- crossPoints[3]: 2nd point



fn makeBranches theShape theWidth =
(
-- For each spline in theShape
for spl = 1 to (numSplines theShape) do
(
--refineSegment theShape spl 1 0.05 -- add a knot near the start of the spline (optional)
knotArray[spl] = #() -- add a new sub-array
-- for each knot in the spline, put the positions into the sub-array
for knt = 1 to (numKnots theShape spl) do knotArray[spl][knt] = getKnotPoint theShape spl knt
)
updateShape theShape
--format "knotArray: %\n" knotArray



-- This loop uses copies to divide the shape into its component splines,
-- then copies the splines and converts the new copies into meshes
for spl = 1 to (numSplines theShape) do -- For each spline in the shape
(
theSpline = copy theShape -- create a copy of the shape
theSpline.name = theShape.name + "_" + spl as string + "_Spline"
splineArray[spl] = theSpline -- place the copied shape into splineArray
select theSpline -- select the copy
max modify mode -- set command panel to "modify"
subObjectLevel = 3 -- set sub-object level to spline
setSplineSelection theSpline #(spl) -- choose spline spl (corresponds to the iteration)
max select invert -- invert the selection
max delete -- delete all splines except for spline spl
subObjectLevel = 0 -- return to object level
theMesh = copy theSpline -- create a copy of the new spline
theMesh.name = theShape.name + "_" + spl as string + "_Mesh"
meshArray[spl] = theMesh -- place the copied spline into meshArray
theMesh.render_displayRenderMesh = true -- set the copied spline to renderable in display
theMesh.render_sides = 4 -- set the number of sides to 4
theMesh.render_thickness = w -- set the thickness to w
theMesh.render_angle = 45 -- set the angle to 45
convertToMesh theMesh -- convert the renderable spline into a mesh
)
--format "splineArray = %\n" splineArray



-- This loop checks the position of each knot in every spline
-- against each knot in every higher-value spline.
-- If the positions match, they are added to an array.
--format "knotArray = %\n" knotArray
--format "knotArray.count = %\n" knotArray.count
for i = 1 to knotArray.count do
(
for j = 1 to knotArray[i].count do
(
for k = (i + 1) to knotArray.count do
--for k = 1 to knotArray.count do
(
for l = 1 to knotArray[k].count do
(
if knotArray[i][j] == knotArray[k][l] do
(
--if k != i do
--(
append crossPoints[1] knotArray[i][j] -- the position of the knots
append crossPoints[2] #(i,j) -- the index of the first knot
append crossPoints[3] #(k,l) -- the index of the second knot
--)
)
) -- end l loop
) -- end k loop
) -- end j loop
) -- end i loop
--format "knotArray: %\n" knotArray
--format "splineArray: %\n" splineArray
--format "crossPoints %\n" crossPoints
-- create an array to hold the original crossPoints values
crossSplines = deepCopy crossPoints



-- This loop... does a bunch of stuff, really
for n = 1 to crossPoints[1].count do -- For every point of intersection "n"
(
for x = 1 to crossPoints[1].count do -- check against every point of intersection "x"
(
--format "(n: %, x: %)...\n\n" n x
--format "crossPoints[2]: %\n" crossPoints[2]
--format "crossSplines[2]: %\n" crossSplines[2]
-- This is a bit tricky:
-- If the first spline in crossPoints intersection "x"
-- matches the second spline in crossPoints intersection "n"
if crossPoints[2][x][1] == crossPoints[3][n][1] do
(
--format "meshArray[crossPoints[2][x][1]]: %\n" meshArray[crossPoints[2][x][1]]
--format "meshArray[crossPoints[2][n][1]]: %\n\n" meshArray[crossPoints[2][n][1]

-- Change the meshArray value corresponding to the first spline in crossPoints intersection "x"
-- to the meshArray value corresponding to the first spline in crossPoints intersection "n"
meshArray[crossPoints[2][x][1]] = meshArray[crossPoints[2][n][1]]
crossPoints[2][x][1] = crossPoints[2][n][1] -- Update crossPoints value to reflect changes
)
)
pNum = crossPoints[2][n][1] -- spline of the 1st knot in the crossPoints intersection
cNum = crossPoints[3][n][1] -- spline of the 2nd knot in the crossPoints intersection

pNumS = crossSplines[2][n][1] -- original spline of 1st knot
cNumS = crossSplines[3][n][1] -- original spline of 2nd knot

pSpline = execute ("$" + theShape.name + "_" + pNumS as string + "_Spline") -- "parent spline"
cSpline = execute ("$" + theShape.name + "_" + cNumS as string + "_Spline") -- "child spline"
pMesh = execute ("$" + theShape.name + "_" + pNum as string + "_Mesh") -- "parent mesh"
cMesh = execute ("$" + theShape.name + "_" + cNum as string + "_Mesh") -- "child mesh"



-- These lines remove the starting vertices (1-4) from the "child mesh"
convertToPoly cMesh -- change the "child mesh" to an Editable Poly
select cMesh -- select the "child mesh"
subObjectLevel = 1 -- set sub-object level to vertex
polyOp.setVertSelection cMesh #{1..4} -- select vertices 1 to 4
max delete -- delete vertices
subObjectLevel = 0 -- return to object level



-- These lines create "branch faces" along the "parent mesh" at the intersection point
segLengths = getSegLengths theShape pNumS -- create an array for segment lengths
addLength = 0 -- create a variable with a value of 0
for seg = 1 to (crossSplines[2][n][2] - 1) do -- For each spline segment up to the intersection
(
tmp = segLengths[seg] -- temporary value from the segment length
addLength = addLength + tmp -- add up the segment length values
)
myConstraint = path_Constraint() -- create a path constraint
myPoint = point() -- create point "myPoint"
myPoint.pos.controller = myConstraint -- set myPoint to follow the path constraint
myConstraint.path = pSpline -- set the path to the "parent spline"
myConstraint.follow = true -- set the path constraint to follow the path
myConstraint.axis = 2 -- set the path axis to "y"
myConstraint.percent = addLength * 100 -- place myPoint at the intersection
myBox = box height:(w) length:(w * 3) width:(w * 3) -- create a box
centerPivot myBox -- center myBox's pivot
myBox.transform = myPoint.transform -- set myBox's transform to match myPoint
myBox.name = "Box_" + pNum as string + "_" + cNum as string
convertToMesh myBox -- convert myBox into a mesh
delete myPoint -- delete myPoint
boolObj.createBooleanObject pMesh -- convert "parent mesh" to a boolean
boolObj.setOperandB pMesh myBox 4 5 -- subtract myBox from "parent mesh"
convertToPoly pMesh -- convert "parent mesh" to a mesh
select pMesh -- select "parent mesh"
subObjectLevel = 4 -- set sub-object level to polygon
max delete -- delete selected polygons
myBorders = polyOp.getOpenEdges pMesh -- define a selection set as all open borders
myBorders = myBorders - #{1..4} -- remove vertices 1 to 4 from the selection set
polyOp.setEdgeSelection pMesh myBorders -- set edges to selection
pMesh.bridge selLevel:#Border -- bridge the selected borders



-- These lines determine which of the "branch faces" should be used for bridging the meshes together
theTarget = getKnotPoint cSpline 1 2 -- second knot of "child spline"
branchFaces = polyOp.getFaceSelection pMesh -- selected faces
branchFaces = branchFaces as array -- selected faces as an array
midFaces = #() -- array for center points of branch faces
distArray = #() -- array for distances to target
for f = 1 to branchFaces.count do -- For each branch face
(
midFaces[f] = polyOp.getFaceCenter pMesh branchFaces[f] -- add center position to midFaces array
distArray[f] = distance theTarget midFaces[f] -- add distance to distArray
)
minDist = aMin distArray -- get the shortest distance
for n = 1 to distArray.count do -- For each value in distArray
(
if distArray[n] == minDist do -- if it matches minDist
(
polyOp.setFaceSelection pMesh branchFaces[n] -- select the corresponding face
max delete -- delete the face
)
)



-- These lines attach the "parent mesh" and "child mesh"
-- and bridge them together at the appropriate "branch face"
polyOp.attach pMesh cMesh -- attach the "parent mesh" and "child mesh"
myBorders = polyOp.getOpenEdges pMesh -- define a selection set as all open borders
polyOp.setEdgeSelection pMesh myBorders -- set edges to selection
pMesh.bridge selLevel:#Border -- bridge the selected borders
subObjectLevel = 0 -- return to object level
convertToMesh pMesh -- convert the editable poly to a mesh



-- Smooth the resulting mesh for renders (optional)
smoothMod = turboSmooth()
smoothMod.iterations = 0
smoothMod.renderIterations = 3
smoothMod.useRenderIterations = true
addModifier $ smoothMod

) -- end n loop
) -- end function

rollout RO_Brancher "Spline Brancher"
(
fn filterShape obj = (classOf obj == splineShape or classOf obj == line)

pickButton pck_Shape "Select a shape" autoDisplay:true filter:filterShape
editText edt_Width "Width:"
button btn_MakeBranches "Make Branches" enabled:false
on pck_Shape picked obj do
(
theShape = obj
if (w != undefined) do (btn_MakeBranches.enabled = true)
)
on edt_Width changed txt do
(
-- make sure the user enters a number
try
(
val = execute txt
w = val as float
if (theShape != undefined) do (btn_MakeBranches.enabled = true)
)
catch(format "The user entered a non-number value\n")
)
on btn_MakeBranches pressed do makeBranches theShape theWidth
)
createDialog RO_Brancher


(A properly formatted version can be downloaded from: www.versatileartist.com/temp/splineBrancher15.ms (http://www.versatileartist.com/temp/splineBrancher15.ms))

Malkalypse
01-10-2009, 07:31 PM
No one? :(

CGTalk Moderation
01-10-2009, 07:31 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.