Picking spline shapes during boolean operations?


#1

Hello, does anyone know how you could pick a spline shape in maxscript when you’re using the boolean operations in splines? For example, with splineOps.startSubtract it only puts you into the mode where you have to manually pick the shape with your mouse. I’ve tried using setSplineSelection afterwards, but that just selects the shape instead of picking it. Is there maybe a way to have max pick the currently selected shape after selecting it? I know there are ways to move your cursor and click mouse buttons to a certain spot on screen with maxscript, but that sounds pretty complicated and messy. There has to be another way…


#2

you are right and wrong at the same time.
you are right that it has to be a built-in mxs access to node picking protocol… and wrong by thinking that it exists.

so the mouse click simulation is your only chance via mxs


#3

Hmmm well that’s a bummer! You would think they would have these operations built so that you could specify a shape. The only other thing I can think of is to build new functions that booleans two shapes. Maybe by copying all of the positions of the knots and tangents for the parts that stay the same, adding new knots on the overlapping segments, and deleting segments that are to be removed. Any ideas on how to about this?


#4

OK… the way you want to go is probably safest and achievable by meaning a guaranteed success.

But it’s the matter of different topic - “How to do Spline Boolean?”
If you want to go this way just open a new discussion.

Good luck!


#5

That’s true I’ll make sure to do that thanks. With the mouse click simulation, is that reliable in most cases? How would you write that?


#6

Actually it seems like possible. Just found an example in my heap of prototypes :slight_smile:
That’s how you can Union two splines. Spline node must be selected.


fn splineUnion shp index1 index2 = if index1 > 0 and index2 > 0 and isKindOf shp SplineShape do
(
	
	local ns = numSplines shp
	
	if index1 <= ns or index2 <= ns and ns > 1 then
	(
		
		if isClosed shp index1 and isClosed shp index2 then
		(
			
			local g = (dotNetClass "Autodesk.Max.GlobalInterface").Instance
			local inode = g.COREInterface.GetINodeByHandle shp.inode.handle asdotnetobject:true
			local iobj  = (inode.EvalWorldState g.COREInterface.Time true asdotnetobject:true).Obj
							
			splinesAreIntersecting = (iobj.Shape_.getSpline (index1-1)).IntersectsSpline (iobj.Shape_.getSpline (index2-1))
	
			
			if not splinesAreIntersecting then
			(

				format "Splines % and % are not intersecting.\n" index1 index2
				false
			
			) 
			else
			(					
				if getCommandPanelTaskMode() != #modify do max modify mode
				
				setSplineSelection shp #(index1)				
				iobj.BooleanStartUp
				iobj.BoolOperation = 0		-- BOOL_UNION 	
				
				iobj.doBoolean (index2-1)
				
				if theHold.Holding() do theHold.Accept "Spline Union"

				true
			)
		
		) else false
		
	) else false
	
)

// The boolean operations
#define BOOL_UNION 0
#define BOOL_SUBTRACTION 1
#define BOOL_INTERSECTION 2

// The mirror operations
#define MIRROR_HORIZONTAL 3
#define MIRROR_VERTICAL 4
#define MIRROR_BOTH 5

haha…
This forum became an absolute garbage. I had to google several times to find my own posts. :rofl:
Advanced search will be available in February they said… :rofl:

How to click in viewport:


#7

OMG! Thank you so much Serejah it works! Adding a “Mode” parameter to the function for changing the BoolOperation works too. This will be a good way to create pre-built parametric shapes.

Also yeah its crazy there isn’t an advanced search on here yet. You would think such a common thing that’s in most forums nowadays would be easy to implement lol. Not sure about others’ experience, but even the normal search hangs for me.


#8

i guess it must be iobj.BooleanStartUp()
the method returns BOOL, and you have to check that Boolean mode set well. If the Boolean mode was not set it might cause crash.


#9

Thanks, didn’t notice it when copy pasted the code.
What strange is that it is used only to uncheck the button afterwards as I understand.
And also for unknown reason BooleanStartUp is a property and not a function :rofl: (at least in mxs) and that’s probably why I left it there as it was. Try to run iobj.BooleanStartUp() and you’ll see an error.
I failed to find a place in code where it should be used as it is always returns True

...
 case IDC_ES_BOOLEAN: {
               if(!es->BooleanStartUp()) {
                  ICustButton *but = GetICustButton(GetDlgItem(hDlg,IDC_ES_BOOLEAN));
                  but->SetCheck(FALSE);

#10

So would you use that code to fix it? Because yeah the iobj.BooleanStartUp() does crash the script. I guess to uncheck the button afterwards you could go back into select or move mode with “max move”, which for me isn’t a big deal.


#11

For those interested I made my version of your function, with a Mode parameter and added a Difference mode, which is the inverse of intersection, where both shapes are subtracted from each other.

fn BooleanSpline Obj:selection[1] Spline1:1 Spline2:2 Mode:"Union" = (
	local SplineSel = #(); -- Initial spline selection
	if (Obj != undefined and (iskindof Obj SplineShape or iskindof Obj Line)) then (
		local CurrentObjSel = selection as array; if (CurrentObjSel.count != 1 or Obj != CurrentObjSel[1]) then select #(Obj) -- Get current object selection and select input object if its not selected or multiple objects are selected
		local SplineNum = numSplines Obj;  -- Get number of splines
		if (SplineNum > 1 and Spline1 <= SplineNum and Spline1 > 0 and Spline2 <= SplineNum and Spline2 > 0) do (
			if isClosed Obj Spline1 and isClosed Obj Spline2 then (
				local g = (dotNetClass "Autodesk.Max.GlobalInterface").Instance; local INode = g.COREInterface.GetINodeByHandle Obj.INode.handle asdotnetobject:true; local IObj = (INode.EvalWorldState g.COREInterface.Time true asdotnetobject:true).Obj; local IsIntersecting = ((IObj.Shape_.getSpline (Spline1 - 1)).IntersectsSpline (IObj.Shape_.getSpline (Spline2 - 1))); -- Check if splines are intersecting.
				if IsIntersecting then (			
					Mode = Mode as string; if (Mode.count > 0) do (Mode[1] = toUpper Mode[1]); -- Convert Mode to string and set the first letter to uppercase
					if (Mode != "Union" and Mode != "Subtraction" and Mode != "Intersection" and Mode != "Difference") do (Mode = "Union"); -- Set the default value for mode to Union if it isn't a correct choice
					if getCommandPanelTaskMode() != #modify do max modify mode -- Go to modify panel
					if (Mode == "Difference") then (
						NewObjA1 = copy Obj name:(uniquename "NEWOBJECTA"); NewObjB1 = copy Obj name:(uniquename "NEWOBJECTB"); NewObjC1 = copy Obj name:(uniquename "NEWOBJECTC");--  Clone all splines to new objects
						for i = SplineNum to 1 by -1 do (if (i == Spline1) then (deleteSpline Obj Spline1; deleteSpline NewObjB1 Spline1; deleteSpline NewObjC1 Spline1;) else if (i == Spline2) then (deleteSpline Obj Spline2; deleteSpline NewObjA1 Spline2; deleteSpline NewObjC1 Spline2;) else (deleteSpline Obj i; deleteSpline NewObjA1 i; deleteSpline NewObjB1 i;)) -- Delete splines from new objects so that they are seperated into the first, second, and remaining splines and clear original object
						NewObjA2 = copy NewObjA1 name:(uniquename "NEWOBJECTA"); NewObjB2 = copy NewObjB1 name:(uniquename "NEWOBJECTB"); -- Duplicate first and second splines
						addAndWeld Obj NewObjA1 0.0; addAndWeld Obj NewObjB1 0.0; -- Attach first and second splines to original object
						setSplineSelection Obj #(1); IObj.BooleanStartUp; IObj.BoolOperation = 1; IObj.doBoolean (2 - 1); if theHold.Holding() do theHold.Accept "Spline Subtraction"; -- Boolean subtact splines and exit selected boolean picking mode
						NewObjC2 = copy Obj name:(uniquename "NEWOBJECTC"); resetShape Obj; -- Clone boolean result to new object and reset original object
						addAndWeld Obj NewObjB2 0.0; addAndWeld Obj NewObjA2 0.0; -- Attach second and first splines to original object
						setSplineSelection Obj #(1); IObj.BooleanStartUp; IObj.BoolOperation = 1; IObj.doBoolean (2 - 1); if theHold.Holding() do theHold.Accept "Spline Subtraction"; -- Boolean subtact splines and exit selected boolean picking mode
						addAndWeld Obj NewObjC2 0.0; SplineSel = #{1..(numSplines Obj)} as array; addAndWeld Obj NewObjC1 0.0; setSplineSelection Obj SplineSel; -- Attach everything back to original object and get and set boolean result's selection
					) else (
						setSplineSelection Obj #(Spline1); IObj.BooleanStartUp; IObj.BoolOperation = (case Mode of ("Union": 0; "Subtraction": 1; "Intersection": 2;);); IObj.doBoolean (Spline2 - 1); if theHold.Holding() do theHold.Accept ("Spline Subtraction"); -- Boolean splines and exit selected boolean picking mode
						SplineSel = getSplineSelection Obj; -- Get resulting spline selection after boolean
					) -- Check if Mode is set to Difference or not
					max move -- Return to move transform mode
				) -- If shapes intersect
			) -- If shapes are closed
		) -- If spline numbers are within range
		if (CurrentObjSel.count != 1 or Obj != CurrentObjSel[1]) then select CurrentObjSel; -- Reset original object selection
		SplineSel -- Return spline selection
	) -- If current object is editable spline
)

#12

why do you clone on Node level and attach after? you use API, so do cloning on BezierShape level.


#13

BTW… I have a very … hmm… distinctive coding style :wink: In some companies, programmers are paid, including for the number of lines of code. So you will come off the loser.


#14

I get this error
“-- Unknown property: “Shape_” in dotNetObject:Autodesk.Max.Wrappers.ShapeObject”

3ds MAX 2020 here


#15

first check if such property exists at all. AD loves to rename things for no reason

(
	shp = circle()
	convertToSplineshape shp
	g = (dotNetClass "Autodesk.Max.GlobalInterface").Instance
	inode = g.COREInterface.GetINodeByHandle shp.inode.handle asdotnetobject:true
	iobj  = (inode.EvalWorldState g.COREInterface.Time true asdotnetobject:true).Obj

	show iobj
)

actually there was .Shape without underscore and it should be ok to use


#16

property exist

.Shape : <Autodesk.Max.IBezierShape>
.Shape_ : <Autodesk.Max.IBezierShape>, read-only

Change to .Shape works , maybe was a corrupt MAX… before…

And now the the old code works too… :slight_smile:

Thanks for the help