Notifications
Clear all

[Closed] IsoSplines script

I’m creating a script that will create splineshapes from isolines, just for fun and for learning maxscript.




-- IsoSplines v -0.1 :)

-- **UNFINISHED**

-- **EPOLY ONLY**


max create mode
-- disableScreenRedraw()
-- undo off

fn polyFn blabla =
(
    print blabla
    modPanel.setCurrentObject selObj.baseObject
    polyOp.setEdgeSelection selObj #all
    addModifier selObj (mesh_select())
    isoEdges = getEdgeSelection selObj as array 
	
    -- Vertex check for double verts ( mesh = double edges = 
    -- double vertices), because of this k isn't an acceptable 
    -- index anymore for the spline, hence the second counter.
    -- I love eMesh...

	vertCheck = #{}
	indexCounter = 1
	
    isoSpl = splineshape pos:selObj.pos name:(uniqueName "IsoSpline")

    -- And another loop-dee-loop to tie your verts together to
    -- form the splineShape.
    -- First an array is created from the two vertices using the 
    -- edge array index, this is why it couldn't be a bitarray.
    -- blabla

    for k = 1 to isoEdges.count do
    (
      isoVerts = meshop.getVertsUsingEdge selObj isoEdges[k] as array
      isoVertsPos = #()
      append isoVertsPos (meshop.getVert selObj isoVerts[1])
      append isoVertsPos (meshop.getVert selObj isoVerts[2])  
      one = isoVerts[1]
      two = isoVerts[2]

      if not ( vertCheck[one] == true and vertCheck[two] == true ) do
      (	 
        -- The inactive code will be necessary for welding when 
        -- the rest of the code is working properly.
		
	-- isoSplTemp = splineshape pos:selObj.pos \
        -- name:(uniqueName "IsoSplineTemp")
        addNewSpline isoSpl
        addKnot isoSpl indexCounter #smooth #curve isoVertsPos[1]
        addKnot isoSpl indexCounter #smooth #curve isoVertsPos[2] 
        -- updateShape isoSplTemp
        -- addAndWeld isoSpl isoSplTemp 0.1
        updateShape isoSpl
	indexCounter += 1
      ) 
      append vertCheck one
      append vertCheck two
    )
	
    vertCheck = #{}
)



-- Selection tests and object loops. Also the 'polyFn'
-- spline creation funtion will be called.
-- The isoSplineSel array stores the spline shapes so
-- they can be selected later on.

if selection.count == 0 then
messageBox (
  "No objects are selected!"
) title:"IsoSplines" else
(
  if selection.count == 1 then   
  (
    isoSplineSel = #()
    selObj = selection[1]
    polyFn "1 object selected" --test
    deleteModifier selObj 1
    append isoSplineSel isoSpl
    select isoSplineSel
  ) else
  (
    selArray = selection as array
    isoSplineSel = #()
    for j = 1 to selection.count do 
    (
      select selArray[j]
      selObj = selArray[j]
      polyFn "Multiple objects selected" --test
      deleteModifier selObj 1
      append isoSplineSel isoSpl
    )
    select isoSplineSel
  ) 
)

-- enableScreenRedraw()

After ages of finding ways around problems I got it to work…
until I realised that emesh uses double edges which would cause double splines. Then I made an array in which I stored used vertices, the if not ( vertCheck[one] == true and vertCheck[two] == true ) do line should then make sure that no double splines were added.

But this causes to leave some in-between splines to be excluded, as they are ofcourse then also seen as double.

How can I solve this?

Poly Select instead of Mesh Select won’t do the trick as you can’t seem to get an edge selection from it.

BTW the script works nice bar that little problem. Just make a box, convert to ePoly, then add a meshsmooth modifier and run the script with the object selected. It also works with multiple objects, but be sure to activate the redraw and undo lines when you have many objects.

All in all I’m pretty satisfied already as this is just the third script I wrote, but if you see things that could be done better just let me know

  • Rens
12 Replies

Oh, and if you want to see how it should look, I posted this a few days ago. Basically it’s the same thing, only manually.

Originally posted by Rens Heeren

  • Create a box: 1/1/1 segments;
  • convert it into editable mesh/poly. It doesn’t matter, as long as you can select edges;
  • now select all the edges in your box, then go top-level again;
  • apply a meshsmooth modifier, 2 iterations;
  • convert to editable mesh/poly or just apply an edit mesh modifier, no mesh select modifier;
  • now go to edge level, if you used a modifier in the previous step you use that one;
  • your isoline should be selected now;
  • press ‘create shape from edges’ in edit(able) mesh/poly;
  • go top-level again and select the new shape, just play around with the thickness and settings;
  • there’s your isoline wireframe! When you think about it isolines are just the smoothed original (base) edges.

An example:

I used a solidify/shell modifier before the last editable mesh modifier, which gives you this effect.

And if you want to use an eMesh base object with the script, just change

polyOp.setEdgeSelection selObj #all
into
meshOp.setEdgeSelection selObj #all

(I think)

-R

I played with your idea a bit to see if I could get around the double-edge obstacle, and here’s what I came up with:

-- IsoSplines v -0.2

-- Streamlined version of original concept by Rens Heeren.
-- Works on EPoly with NURMS turned on or MeshSmooth modifier applied,
-- and also on EMesh with MeshSmooth modifier applied.

-- Note that ll edges must be selected in the base object
-- for this script to work, and MeshSmooth must be the active
-- modifier if used. 

fn polyFn obj =
(
	-- by getting the .mesh property of the current state
	-- of the stack, we have immediate access to the selected
	-- isoline edges :)
	obj_mesh = obj.mesh
	isoEdges = obj_mesh.selectedEdges
	
	-- container for "used" vertex pairs, between which a
	-- spline segment has already been created.
	used_pairs = #()
	
	isoSpl = splineshape transform:obj.transform name:(uniqueName "IsoSpline")

	for edge in isoEdges do
	(
		isoVerts = meshOp.getVertsUsingEdge obj_mesh edge as array
		isoVertsPos = #()
		
		-- getVert() returns object-space positions, so they
		-- should be multiplied by the local transform to
		-- put them in world space.  This allows the generated
		-- splines to be positioned correctly no matter how the
		-- object is oriented.
		append isoVertsPos ((meshop.getVert obj_mesh isoVerts[1]) * obj.transform)
		append isoVertsPos ((meshop.getVert obj_mesh isoVerts[2]) * obj.transform)  
		
		-- if a segment has not already been created between
		-- this vertex pair, then create one!
		if findItem used_pairs isoVerts == 0 do
		(
			idx = addNewSpline isoSpl
			addKnot isoSpl idx #smooth #curve isoVertsPos[1]
			addKnot isoSpl idx #smooth #curve isoVertsPos[2]
			append used_pairs isoVerts	-- flag this vertex pair as "used"
		)      
	)
	
	updateShape isoSpl
	
	-- return the SplineShape so it can be accessed by the
	-- calling context
	return isoSpl
)

if selection.count == 0 then
(
	messageBox "No objects are selected!" title:"IsoSplines"
)

isoSplineSel = #()

for obj in (getCurrentSelection()) do
(
	append isoSplineSel (polyFn obj)
)

select isoSplineSel

It seems to work pretty good, I guess. I think more can be done with it, but it’s late and I need to make myself go to bed;)

RH

Thank you very much for the polishing RH

It still creates double splines though.

Currently I’m trying to get rid of the double edges in the first place, just to see if it can be done.

I have the following code instead of yours between the start of the function and used_pairs:


	modPanel.setCurrentObject obj.baseObject
	polyOp.setEdgeSelection obj #all
	addModifier obj (mesh_select())
	
	-- as the mesh select modifier is now active the edge selection
	-- will now be the smoothed original edges: the isolines. 
	isoEdges = getEdgeSelection obj as array 
	
	-- This is a simple check. The vertex indices are added paired, so
	-- VertContainer[1] may look like this: #(1,15). This way the edges
	-- can be easily checked for duplicates. 
	isoEdgesIndices = #()
	VertContainer = #()
	
	for a = 1 to isoEdges.count do 
	(		
		holdOne = meshop.getVertsUsingEdge obj a
		holdTwo = holdOne as string
		if findItem VertContainer holdTwo == 0 do append isoEdgesIndices a	
		append VertContainer holdTwo
	)
		

The first three lines are there so I don’t have to have anything selected.

I figured this would leave me the indices of the single edges, and it seems to work when I evaluate every line separately.

But evaluated like this isoEdgesIndices.count is 32.
When I remove the line holdTwo and change the code accordingly I get a count of 48… the same as isoEdges.count (it should be 24). :surprised

Strange. Is there something I’m missing here?

Damn, never mind, I got it to work already

It should be:
for a in isoEdges do

But it still eludes me as to why the other code didn’t work…
(apart from that it never returned edge indices in the first place)

The reason mine still doubled edges was because of a flaw in logic – #(1, 2) == #(1, 2) is false in maxscript. Also, #(1, 2) == #(2, 1) would certainly be false. I was way too tired to be doing any scripting last night, and I noticed my mistake today:wip:

If you’ve already got something working, then I don’t want to beat a dead horse. But I did come up with another solution if you’re interested:shrug:

RH

Another update:

-- IsoSplines v0.3

-- By Rens Heeren.
-- v0.1: basic setup.
--
-- v0.2: streamlined by Roger Hyde, with basic double spline detection.
--
-- v0.3: streamlined code extended.
--	added double edge detection.
--	added spline welding function.

-- Works on EPoly with NURMS or MeshSmooth modifier applied.

max create mode
setWaitCursor()
--disableScreenRedraw()
undo off

fn polyFn obj =
(
				-- *EDGE SELECTION* --
	
	-- goto base, select all edges, apply mesh select.
	modPanel.setCurrentObject obj.baseObject
	polyOp.setEdgeSelection obj #all
	addModifier obj (mesh_select())
	
	-- as the mesh select modifier is now active the edge selection
	-- will now be the smoothed original edges: the isolines.
	isoEdges = getEdgeSelection obj as array 
	
	-- but where there's smoke there's eMesh. 
	-- In this case we have to ditch the double edges.
	-- This is a simple check. The vertex indices are added paired, so
	-- VertContainer[1] may look like this: #(1,15). This way the edges
	-- can be easily checked for duplicates.
	singleEdges = #()
	VertContainer = #()
	
	for a in isoEdges do 
	(		
		hold = (meshop.getVertsUsingEdge obj a) as string
		if findItem VertContainer hold == 0 do append singleEdges a	
		append VertContainer hold
	)
	
	VertContainer = #()
	isoEdges = #()

	-- the next piece of code creates a new array with the contents in
	-- singleEdges. The contents are sorted so that an edge in the array 
	-- is always 'connected' to a previous edge in the array.
	-- This is necessary for the welding process.
	-- The first edge in the singleEdges array is added before the loop
	-- as a starting point.

	singleEdgesSorted = #()
	append singleEdgesSorted singleEdges[1]

	usedVertices = meshOp.getVertsUsingEdge obj singleEdges[1]

	-- array conversion to make sure the array is a copy, not a reference.
	unUsedEdges = (singleEdges as bitArray) as array

	counter = 1

	while counter != singleEdges.count do 
	(
		for i in unUsedEdges do
		(
			if findItem singleEdgesSorted i == 0 then
			(
		 		vertices = meshOp.getVertsUsingEdge obj i as array
 				one = vertices[1]
				two = vertices[2]	
				
				-- see if one of the edge's verts is already used. If this 
				-- is the case the edge can be appended to singleEdgesSorted as
				-- now at least one side is 'connected'.
				if usedVertices[one] == true or usedVertices[two] == true then
				(
					append singleEdgesSorted i
					append usedVertices one
					append usedvertices two
					deleteItem unUsedEdges (findItem unUsedEdges i)
					counter += 1
				) else OK
			) else OK
		)
	)
	
	singleEdges = #()
	usedVertices = #()
	
	
				-- *SPLINE CREATION* --
	
	-- main spline shape creation.
	isoSpl = splineShape transform:obj.transform name:(uniqueName "IsoSpline")	
	
	for c in singleEdgesSorted do 
	( 
		splineVerts = meshOp.getVertsUsingEdge obj c as array
		
		isoSplTemp = splineshape transform:obj.transform \
		name:(uniqueName "IsoSplineTemp")
		
		addNewSpline isoSplTemp
		addKnot isoSplTemp 1 #smooth #curve (meshop.getVert obj splineVerts[1])
		addKnot isoSplTemp 1 #smooth #curve (meshop.getVert obj splineVerts[2])
		updateShape isoSplTemp
		addAndWeld isoSpl isoSplTemp 0.1
		updateShape isoSpl 		
		)

	singleEdgesSorted = #()
	deleteModifier obj 1
	
	-- return the SplineShape so it can be accessed by the
	-- calling context
	return isoSpl
)

if selection.count == 0 then
(
	messageBox "No objects are selected!" title:"IsoSplines"
)

isoSplineSel = #()

for obj in (getCurrentSelection()) do
(
	append isoSplineSel (polyFn obj)
)

select isoSplineSel
--enableScreenRedraw()

LFShade:
I wanted to see if I could fix it myself, and after the ninth complete rewrite of the main code I got it to work pretty nice
However I’m still very interested in your method (if it’s different from mine), to see how else it can be done.

There’s only one problem left with this code. It still creates some double vertices in the spline.
I think addAndWeld() won’t weld the second endpoint of a spline if the first endpoint can’t be welded.

Is there some workaround for this?

I’ve come across Swami’s ShapeFixer! script and that seems to fix unwelded isosplines correctly, so I’m looking trough the code if I can find out if there’s a solution.

You had a solution for this that you did by hand, correct? Converting and adding modifiers and such until it popped out. Wouldn’t it be easiest to disable screen redraw, do all of that within an undo item, copy the created isospline, then undo that newly created undo item? You wouldn’t have to code much of the nitty gritty stuff yourself, just the basic overview. I may be wrong though.

I’m not sure if I know what you mean, can you explain a bit more?
Would something like that also work on say, 100 different objects? Because that basically is the reason why I’m writing that script; a single object can be done very easily using the manual method (see a few posts up).

The manual method depends on ‘create shape from edges’, which creates as-good-as-they-get splineshapes. The problem with the script is getting the splines to get welded properly, though I now have a good idea to sort them properly so you’d (finally) get a similar result as the manual method, but that’ll have to wait a day or so because there’s one or two scripts I want to finish first.
Too bad splineOp methods are so limited (in comparison with mesh or poly), but I’ll get there.

Ok, tried my way. Works fine, but there are a couple kinks. One, you have to hit ok on a dialog for every item. Easy enough to work with, though, just pick your options, and hold the ‘enter’ key, and it will do that for all of them. The second one, you have to set your rendering options first. Create one isospline, turn on renderable and whatever thickness you want, delete it, then run it with all the items selected. They will all get those options you just set.

I did not realize the create shape from edges button is actually coded in c++. Makes it harder to use, as you can see. But try it out…it may be good enough for you.

--Isospline
--"Isospline"
--Created by Eric Burnett, based on work from Rens Heeren
--and LFShade (sorry man, not your real name)
--3ds MAX r6
--
--Version 1.0
--Version Hisory: 
--1.0 - 11/05/04 - Script created. 

fn polyFn = (
	setWaitCursor()
	--disableSceneRedraw()

	undo "IsoSpline Creation" on
	(
		max modify mode
		addModifier $ (edit_mesh())
		subObjectLevel = 2
		max select all

		
	)
	undo off (
		ApplyOperation Edit_Mesh meshops.CreateShapeFromEdges
	)
	max undo
	setArrowCursor()
	--enableSceneRedraw()
	
	)

fn startFn = (
	disableSceneRedraw()
	if selection.count == 0 then
		messageBox (
  		"No objects are selected!"
	) title:"IsoSplines" else
	(
  		if selection.count == 1 then   
  		(
    		selObj = selection[1]
    		polyFn()
  		) else
  		(
    		selArray = selection as array
    		for j = 1 to selection.count do 
    		(
      			select selArray[j]
      			polyFn()
    		)
  		) 
	)
	enableSceneRedraw()
)

global roll_IsoSpline
try(
	destroyDialog roll_IsoSpline
) 
catch ()


rollout roll_IsoSpline "CreateIsoSpline"
(
	button but_createIsoSpline "Create IsoSpline" width:125 height:20 tooltip: "Create IsoSplines of the selected objects"
	on but_createIsoSpline pressed do
	( startFn() )
	--destroyDialog roll_IsoSpline
)

createDialog roll_IsoSpline width:200 height:50\
style:#(#style_sysmenu,#style_titlebar,#style_minimizebox,#style_maximizebox)


Page 1 / 2