[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
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.