Notifications
Clear all

[Closed] Undo weirdness and crashes

I just put together a small script that takes a bone and generates an evenly split chain of new bones from it based on a slider value. Script works well enough, until the user performs an undo, at which point it either performs the undo incorectly or outright crashes Max. Something about the script is pissing off Max, and I don’t know what.

rollout boneSplitter_ro "Bone Splitter" width:150 height:150 (
	button 		btn_OrigBone 	"Add Bone"	 	pos:[10,20] width:130 height:20
	edittext 	txt_OrigBone 	"" 			pos:[10,50] width:130 height:20 readOnly:true
	slider	 	spn_boneNum 	"Segment count" 	pos:[10,90] width:140 height:20 range:[1,10,0] type:#integer 
	
	local vecFullDistance = 0.0
	local vecAtZero = [0,0,0]
	local curBone = ""
	local curBonePos = ""
	local curBoneChild = ""
	on btn_OrigBone pressed do (
		if (selection.count != 0) do (
			if (classOf selection[1] == BoneGeometry) do (
				curBone = selection[1]
				curBoneChild = selection[1].children[1]
				curBonePos = curBone.pos
				txt_OrigBone.text = curBone.name
				
				vecFullDistance = distance (curBone.pos) (curBone.children[1].pos)
				vecAtZero = (curBone.children[1].pos) - (curBone.pos)
			)
		)
	)
	
	local createdBones = #()
	on spn_boneNum changed countArg do (
		if (isDeleted curBone == false) do (delete curBone)
		if createdBones.count != 0 do ( delete createdBones ; createdBones = #() )
		
		vecSeg = (vecAtZero / countArg)
		prevPos = curBonePos
		newPos = vecSeg + curBonePos
		for i=1 to countArg do (
			createdBones[i] = BoneSys.createBone prevPos newPos [0,1,0]
			if i >= 2 do (createdBones[i].parent = createdBones[i-1])
			if i == countArg do (curBoneChild.parent = createdBones[i])
			prevPos = newPos
			newPos = (vecSeg * (i+1)) + curBonePos
		)
		
		select createdBones
	)
)
createDialog boneSplitter_ro
3 Replies
1 Reply
(@pi3c3)
Joined: 11 months ago

Posts: 0

if (isDeleted curBone == false) do with undo (delete curBone)

Here you have to store an undo state, otherwise it won’t be able to do it. (Always when using delete statement inside a scope…)

I dont remember the proper syntax, but check the documentation for it…

if (isDeleted curBone == false) do (undo on (delete curBone))

I checked it for you… That’s how…

Thanks, that helped a lot! I have a much better idea of the limitations of ‘undo on’ now.

One very strange issue I found was that if you want to select geometry created within an ‘undo on’, it MUST be done withing that scope, if its done afterwards but within the same script, it’ll cause Max to crash as soon as an undo is used. Here’s an updated version of the script to show this;

This works;


rollout boneSplitter_ro "Bone Splitter" width:150 height:150 (
	button 		btn_OrigBone 	"Add Bone"	 	pos:[10,20] width:130 height:20
	edittext 	txt_OrigBone 	"" 			pos:[10,50] width:130 height:20 readOnly:true
	slider	 	spn_boneNum 	"Segment count" 	pos:[10,90] width:140 height:20 range:[1,10,0] type:#integer 
	
	local vecFullDistance = 0.0
	local vecAtZero = [0,0,0]
	local curBone = ""
	local curBoneProp = #() --name, parent, children, position, etc.
	local createdBones = #()
	on btn_OrigBone pressed do (
		if (selection.count != 0) do (
			if (classOf selection[1] == BoneGeometry) do (
				curBone = selection[1]
				curBoneProp = #(
					curBone.name,		--[1]
					curBone.parent,		--[2]
					curBone.children[1],	--[3]
					curBone.pos,		--[4]
					curBone.width,		--[5]
					curBone.height,		--[6]
					curBone.taper,		--[7]
					curBone.sideFins,	--[8]
					curBone.frontFin,	--[9]
					curBone.backFin,	--[10]
					curBone.rotation	--[11]
				)
				txt_OrigBone.text = curBone.name
				vecFullDistance = distance (curBone.pos) (curBone.children[1].pos)
				vecAtZero = (curBone.children[1].pos) - (curBone.pos)
				
				createdBones = #() --reset this variable in case this isn't the first bone used since script start.
			)
		)
	)
	
	on spn_boneNum changed countArg do (
		with redraw off (
			undo "Bone Splitter" on (
				if (isDeleted curBone == false) do (delete curBone)
				if createdBones.count != 0 do (
					for node in createdBones do (
						if (isDeleted node == false) do (delete node)
					)
					createdBones = #()
				)
				
				--Generate all the vectors
				vecSeg = (vecAtZero / countArg)
				posA = #(0.0 + curBoneProp[4])
				posB = #(vecSeg + curBoneProp[4])
				for i=1 to countArg do (
					append posA (posB[i])
					append posB ((vecSeg * (i+1)) + curBoneProp[4])
				)
				
				--Perform the chain creation
				for i=1 to countArg do (
					createdBones[i] = BoneSys.createBone posA[i] posB[i] [0,1,0]
					createdBones[i].rotation = curBoneProp[11]
					createdBones[i].position = posA[i]
					if i >= 2 do (createdBones[i].parent = createdBones[i-1])
					if i == countArg do (curBoneProp[3].parent = createdBones[i])
				)
				select createdBones
			)
			
			--Transfer main properties from original bone to new bone chain
			createdBones.width		= curBoneProp[5]
			createdBones.height		= curBoneProp[6]
			createdBones.taper		= curBoneProp[7]
			createdBones.sideFins		= curBoneProp[8]
			createdBones.frontFin		= curBoneProp[9]
			createdBones.backFin		= curBoneProp[10]
		)
		--select createdBones --Causes CRASH on undo!
	)
)
createDialog boneSplitter_ro

This crashes Max;


rollout boneSplitter_ro "Bone Splitter" width:150 height:150 (
	button 		btn_OrigBone 	"Add Bone"	 	pos:[10,20] width:130 height:20
	edittext 	txt_OrigBone 	"" 			pos:[10,50] width:130 height:20 readOnly:true
	slider	 	spn_boneNum 	"Segment count" 	pos:[10,90] width:140 height:20 range:[1,10,0] type:#integer 
	
	local vecFullDistance = 0.0
	local vecAtZero = [0,0,0]
	local curBone = ""
	local curBoneProp = #() --name, parent, children, position, etc.
	local createdBones = #()
	on btn_OrigBone pressed do (
		if (selection.count != 0) do (
			if (classOf selection[1] == BoneGeometry) do (
				curBone = selection[1]
				curBoneProp = #(
					curBone.name,		--[1]
					curBone.parent,		--[2]
					curBone.children[1],	--[3]
					curBone.pos,		--[4]
					curBone.width,		--[5]
					curBone.height,		--[6]
					curBone.taper,		--[7]
					curBone.sideFins,	--[8]
					curBone.frontFin,	--[9]
					curBone.backFin,	--[10]
					curBone.rotation	--[11]
				)
				txt_OrigBone.text = curBone.name
				vecFullDistance = distance (curBone.pos) (curBone.children[1].pos)
				vecAtZero = (curBone.children[1].pos) - (curBone.pos)
				
				createdBones = #() --reset this variable in case this isn't the first bone used since script start.
			)
		)
	)
	
	on spn_boneNum changed countArg do (
		with redraw off (
			undo "Bone Splitter" on (
				if (isDeleted curBone == false) do (delete curBone)
				if createdBones.count != 0 do (
					for node in createdBones do (
						if (isDeleted node == false) do (delete node)
					)
					createdBones = #()
				)
				
				--Generate all the vectors
				vecSeg = (vecAtZero / countArg)
				posA = #(0.0 + curBoneProp[4])
				posB = #(vecSeg + curBoneProp[4])
				for i=1 to countArg do (
					append posA (posB[i])
					append posB ((vecSeg * (i+1)) + curBoneProp[4])
				)
				
				--Perform the chain creation
				for i=1 to countArg do (
					createdBones[i] = BoneSys.createBone posA[i] posB[i] [0,1,0]
					createdBones[i].rotation = curBoneProp[11]
					createdBones[i].position = posA[i]
					if i >= 2 do (createdBones[i].parent = createdBones[i-1])
					if i == countArg do (curBoneProp[3].parent = createdBones[i])
				)
				--select createdBones
			)
			
			--Transfer main properties from original bone to new bone chain
			createdBones.width		= curBoneProp[5]
			createdBones.height		= curBoneProp[6]
			createdBones.taper		= curBoneProp[7]
			createdBones.sideFins		= curBoneProp[8]
			createdBones.frontFin		= curBoneProp[9]
			createdBones.backFin		= curBoneProp[10]
		)
		select createdBones --Causes CRASH on undo!
	)
)
createDialog boneSplitter_ro

The only difference is where ‘select createdBones’ is run.