Forum: Poser Python Scripting


Subject: Updating Freazypose 0.10 to work in Poser 12

3dcheapskate opened this issue on May 10, 2022 ยท 27 posts


3dcheapskate posted Tue, 10 May 2022 at 11:18 PM

# Freazypose0-10.py (Prototype 0.10)

# Test script for adding Ajax Easypose style ERC to a figure.


# 0.00: Adds a 'ConstantBend' dial to the current actor and slaves the 'zRotate' of every body part to this

# 0.01: Playing with 'AddValueOperation()'. If the 'AddValueOperation()' already exists it does NOT seem to create a duplicate (which is good),but ValueOperations().SetDelta() will change the delta (which is also good - sort of).

# 0.02: Added the main Ajax EP dials for the main (body) node as per B.L.Render book, p103. Did S-bend, spiral and wave for twist too since XYZ are used differently in Tetradactylus. Probably want user input so match ERC channels to dial. Could check main rotation dial names for 'bend', 'twist', and 'side'to do this automatically,but I'd still need user input if that fails.

# 0.03: Got the Bef/Aft dials working on each segment.

# 0.04: Playing with a way to automatically determine which x/y/z rotations are bend/twist/side. Decided to scrap this idea and just ask the user. But save the code first as 0.04

# 0.05: More or less there now.

# 0.06: Tidying up messages and cancellation logic. A bit.

#       I know that my 'Wave' dials cause the wavy bit to bend off-axis, whereas Ajax EasyPose keeps the wavy bit aligned along the longitudinal axis

# 0.07: Trying out a new word, 'return', de-indent-ifying, and shortening text to fit in dialogue boxes

# 0.08: Modified the calculation of Wave deltas to match Ajax original Easypose tube,which is ALMOST as per the book. Discovered that OCCASIONALLY Poser will create a NEW ValueOp, contradicting what I thought in 0.01. Using the -1 index to access the ValueOp I just added... weird how that SEEMED to work, even if a deltaAddDelta ValueOp with the relevant SourceParameter ALREADY existed. I'm fairly sure nowthat I needto check whether it already exists and, if it does, explicitly MODIFY the delta...

#       Added 'setValueOp()'to check whether a Value Op of the required thingy already exists,and based on this either modify the existing one or add anew one

#       Changed 'SSide..." to simply "Side..."

#       LOADSA DEBUG !!! All (non-calculated) deltas have a decimal part to identify which line of code wrote it-to help in debuggingwhat was overwriting the Wave2 stuff

# 0.09: Removed old code lines that were commented out while I tested their replacement 'setValueOp()', removed most of the debug, set modified delta values back to 1.0,and renamed all my dials to start with 'FP-'

# 0.10: Modified the delta values/calculations to match (a) the book and (b)the original Ajax EasyPose tube. Tested by adding my dials to an EPTube, setting an EP parameter to value N, and setting my corresponding FP dial to -N. My Spiral is still slightly out (slightly smaller values than Ajax EasyPose), and there's a difference in the S-Curves with the delta for the middle segment


# NOTES:

# - Master dials are in the parent of the first EP actor. I could put them in the BODY (p103), but if you want several EP chains in a figure?

# - The master dials use 'valueParm' so they won't be saved in poses. If I make them 'targetGeom' instead they will, provided you select 'include morphs' (bottom of p103)

# - p104 says the figure must have an IK chain encompassing the entire EP chain for all the Bef/Aft dials to work correctly.This doesn't appear to be true(I think I tried one of my saved test CR2s in Poser6 ?)

# - could use a different tiny incremental adjustment on the deltas, e.g. 1.00000123for twist, 1.00000456 for bends, 1.00000789 for sides to make global edits to the CR2. But it's probably easier to simply rerun this script - as long as I provide a way to specify the deltas, which I don't.

# I'm not using anti-crosstalk prefixes, since crosstalk was (I believe?) a pre Poser 6 problem.



import poser,math


# I've started putting my scripts into classes to avoid possible conflict with stuff in the __main__ module.

# Did they forget to mention that in the PoserPython Manual ? 

# Did the folks who wrote the scripts that come with Poser know about that ?

# See my "PoserPython: Global Variables - Question about scope and lifespan" thread at CGBytes for more information, specifically post #7 by bagginsbill:

# http://www.cgbytes.com/community/forums.aspx?g=posts&m=125183#post125183

class cheapskatesTemporaryClass(object):

# Checks the Value Operations for the specified parameter and returns the index of the one with the specified valueopname,or -1 if it isn't found

# segidx - just for debugging

# slaveparameter - e.g. act.Parameter(twistdial)

# masterparameter - e.g. curact.Parameter("TwistAll")

# deltavalue

def setValueOp(self,segidx,slaveparameter,masterparameter,deltavalue):

vopidx=-1

idx=0

vops = slaveparameter.ValueOperations()

if vops:

for vop in vops:

if vop.SourceParameter().Name() == masterparameter.Name():

if vopidx !=-1:

print "- *** ALERT: slaveparameter "+slaveparameter.Name()+" has 2+ ValueOps using source parameter "+masterparameter.Name()

vopidx=idx

idx=idx+1

if vopidx == -1:

slaveparameter.AddValueOperation(poser.kValueOpTypeCodeDELTAADD, masterparameter)

slaveparameter.ValueOperations()[-1].SetDelta(deltavalue)

else:

slaveparameter.ValueOperations()[vopidx].SetDelta(deltavalue)

def main(self):

# Get Poser version for doing version-specific stuff

poserversion = '0'

posermainversion = '0'

try:

self.poserversion = poser.AppVersion()

temp = self.poserversion.split('.')

self.posermainversion = temp[0]

except:

pass

# Abort if using Poser 8/Pp2010 or earlier 

# (probably better to use 'hasattr(curact.Parameter("xTran"),"AddValueOperation")' tocheck whether the method exists...

# https://forum.smithmicro.com/topic/3792/poserpython-was-addvalueoperation-added-in-p8-or-p9/4

if int(self.posermainversion) <= 8:

poser.DialogSimple.MessageBox("This script uses the Poser 9/PP2012 PoserPython 'AddValueOperation()' method.\nSince you're using Poser 8/PP2010 or earlier this script won't work.\n\nScript cancelled, no changes made.")

return

# Abort if the user gets cold feet right at the start

confirmed = poser.DialogSimple.YesNo("This script adds EasyPose to the current figure.\n\nDo you wish to continue?")

if not confirmed:

confirmed = poser.DialogSimple.YesNo("Do you want to see more information about what this script does?\n\n(This opens a debug window with extra information and terminates the script. You'll probably need to resize the debug window to read the information.)")

if confirmed:

# Briefly explain to the user what this script does and give an option toabort straight away

print "This script adds EasyPose (based on the Ajax Easypose stuff described on p103 of B.L.Render's 'Secrets Of Figure Creation With Poser 5'), to the currently selected figure.\n\nThe currently selected actor will be the start of the easypose chain, and the master dials will be placed in it.\n(Note: the current actor must have a single child for this script to work - if it is childless or has two or more children the script will abort.\n\nThe easypose chain will continue down the hierarchy (child, grandchild, great-grandchild, etc) as long as the actor has a single child. \nThe easypose chain will terminate at the first actor that has 0 or 2+ children. \nEach actor in the easypose chain will be also given the relevant Before/After master dials.\n\nBecause the 'twist' ERC channel corresponds to rotation about the longitudinal axis of the easypose bodypart sequence, and because the rotation axes corresponding to the 'bend' and 'side-to-side' ERC channels depend on the specific model, I've opted to ask the user to identify which is which,rather than try and do any automatic decision-making.\n\nThe BendAll, TwistAll, SideSideAll, BendBef/BendAft, TwistBef/TwistAft, and SideBef/SideAft controls should work in the same way as the standard EasyPose.\n\nHowever,the formulae that I've used for the deltas for the S, spiral, and wave ERC are different from the book."

return

# Abort if no figure selected

scn = poser.Scene()

curfig = scn.CurrentFigure()

curact = scn.CurrentActor()

if not curfig:

poser.DialogSimple.MessageBox("You need to have a figure selected for this script to do anything.\n\nScript aborted, no changes made.")

return

# Abort if the currently selected actor is NOT part of the figure (parented props are NOT part of the figure)

if not(curact.IsBodyPart() and curact.ItsFigure().Name()==curfig.Name()):

poser.DialogSimple.MessageBox("The selected actor is not an integral part of the selected figure.\n\nScript aborted, no changes made.")

return

# Abort if the currently selected actor is childless or has two or more children 

# (N.B. V4 collar has V4 shoulder PLUS lots of magnets, so it fails this test

kids = curact.Children()

if len(kids) != 1:

poser.DialogSimple.MessageBox("The selected actor must have a single child for this script to do anything.\n\nScript aborted, no changes made.")

return

# If this actor already has any easypose dials then ask the user if they REALLY want to add more. Abort if they don't

# (Original Ajax EasyPose tube uses BWave1, BWave2, SWave1, SWave2)

if curact.Parameter("Wave2B") or curact.Parameter("BWave2") or curact.Parameter("Wave1B") or curact.Parameter("BWave1") or curact.Parameter("Wave2S") or curact.Parameter("SWave2") or curact.Parameter("Wave1S") or curact.Parameter("SWave1") or curact.Parameter("SpiralBend") or curact.Parameter("SpiralSide") or curact.Parameter("S-Bend") or curact.Parameter("S-Side") or curact.Parameter("SideSideAll") or curact.Parameter("BendAll") or curact.Parameter("TwistAll") or curact.Parameter("TwistBef") or curact.Parameter("BendBef") or curact.Parameter("SSideBef") or curact.Parameter("TwistAft") or curact.Parameter("BendAft") or curact.Parameter("SSideAft"):

confirmed = poser.DialogSimple.YesNo("This actor ("+curact.Name()+") already appears to have some proper Ajax EasyPose dials.\n\nDo you really want to continue ?\n\n(FreazyPose dial names begin 'FP-'. Existing EasyPose dials are unaffected)")

if not confirmed:

poser.DialogSimple.MessageBox("User aborted because this actor already has some proper Ajax EasyPose dials.\n\nScript cancelled, no changes made.")

return

# If this actor already has any Freazypose dials then ask the user if they REALLY want to add more. Abort if they don't

if curact.Parameter("FP-Wave2-Bend") or curact.Parameter("FP-Wave1-Bend") or curact.Parameter("FP-Wave2-Side") or curact.Parameter("FP-Wave1-Side") or curact.Parameter("FP-Spiral-Bend") or curact.Parameter("FP-Spiral-Side") or curact.Parameter("FP-SCurve-Bend") or curact.Parameter("FP-SCurve-Side") or curact.Parameter("FP-Side-All") or curact.Parameter("FP-Bend-All") or curact.Parameter("FP-Twist-All") or curact.Parameter("FP-Twist-Before") or curact.Parameter("FP-Bend-Before") or curact.Parameter("FP-Side-Before") or curact.Parameter("FP-Twist-After") or curact.Parameter("FP-Bend-After") or curact.Parameter("FP-Side-After"):

confirmed = poser.DialogSimple.YesNo("This actor ("+curact.Name()+") already appears to have some FreazyPose dials.\n\nDo you really want to continue ?")

if not confirmed:

poser.DialogSimple.MessageBox("User aborted because this actor already has some Freazypose dials.\n\nScript cancelled, no changes made.")

return

# Ask the user whether they can identify the ERC channel to rotation dial correlations.If not, abort.

confirmed = poser.DialogSimple.YesNo("You'll now be asked to identify which of the rotation dials on the current actor correspond to the easypose bend, twist, and side-to-side channels.\n\nDo you know, or can you work it out from what you can see on the Poser UI ?")

if not confirmed:

poser.DialogSimple.MessageBox("You can't tell which rotation dial is which. Work it out, write it down,and then run this script again.\n\nScript cancelled, no changes made.")

return

# Ask the user to identify which of the x/y/z rotation dials correspond to bend/twist/side.

intdialnames = ["xRotate", "yRotate", "zRotate"]

extdialnames = [curact.Parameter("xRotate").Name()+" (x)", curact.Parameter("yRotate").Name()+" (y)", curact.Parameter("zRotate").Name()+" (z)"]

twistidx=-1

bendidx=-1

sideidx=-1

twistdial = ""

benddial = ""

sidedial = ""

# Abort if the user fails to identify a dial for the bend ERC channel...

asklistitem = poser.DialogSimple.AskMenu("Rotation dial for Bend ERC channel","Bend channel dial:",extdialnames)

if asklistitem == None: # 'Cancel' selected

poser.DialogSimple.MessageBox("'Cancel' button clicked\nScript cancelled, no changes made.")

return

elif asklistitem == "": # Default 'None' selected

poser.DialogSimple.MessageBox("Default option 'None' selected\nScript cancelled, no changes made.")

return

# ...otherwise note which dial the user has selected for the bend channel

i=-1

for extdialname in extdialnames:

i=i+1

if asklistitem==extdialname:

bendidx=i

benddial=intdialnames[i]

# Abort if the user fails to identify a dial for the twist ERC channel...

asklistitem = poser.DialogSimple.AskMenu("Rotation dial for Twist ERC channel","Twist channel dial:",extdialnames)

if asklistitem == None: # 'Cancel' selected

poser.DialogSimple.MessageBox("'Cancel' button clicked\nScript cancelled, no changes made.")

return

elif asklistitem == "": # Default 'None' selected

poser.DialogSimple.MessageBox("Default option 'None' selected\nScript cancelled, no changes made.")

return


# ...otherwise note which dial the user has selected for the twist channel

i=-1

for extdialname in extdialnames:

i=i+1

if asklistitem==extdialname:

twistidx=i

twistdial=intdialnames[i]

# Abort if the user fails to identify a dial for the side-to-side ERC channel...

asklistitem = poser.DialogSimple.AskMenu("Rotation dial for Side ERC channel","Side channel dial:",extdialnames)

if asklistitem == None: # 'Cancel' selected

poser.DialogSimple.MessageBox("'Cancel' button clicked\nScript cancelled, no changes made.")

return

elif asklistitem == "": # Default 'None' selected

poser.DialogSimple.MessageBox("Default option 'None' selected\nScript cancelled, no changes made.")

return


# ...otherwise note which dial the user has selected for the side-to-side channel

i=-1

for extdialname in extdialnames:

i=i+1

if asklistitem==extdialname:

sideidx=i

sidedial=intdialnames[i]

# Abort if user doesn't confirm the correlations

confirmed=poser.DialogSimple.YesNo("Your selections:\nTwist ERC = "+extdialnames[twistidx]+" ("+twistdial+")\nBend ERC = "+extdialnames[bendidx]+" ("+benddial+")\nSideSide ERC = "+extdialnames[sideidx]+" ("+sidedial+")\n\nPlease confirm that this is correct.")

if not confirmed:

poser.DialogSimple.MessageBox("Mapping of ERC channels to rotation dials not confirmed\nScript cancelled, no changes made.")

return

# Create a list of the string of segments to be made EP

# - the epseglist[] starts with the only child of the selected actor

# - the list ends with the first descendant (only child, only grandchild, only greatgrandchild, etc) actor having anything except one single child.

epseglist = [] 

kids = curact.Children()

while len(kids) == 1:

epseglist.append(kids[0])

segact = kids[0]

kids = segact.Children()

# Abort if the epseglist[] is empty - THIS INDICATES SOMETHING'S GONE WRONG SINCE WEALREADY CHECKED THE ONLY CONDITION THAT SHOULD CAUSE THIS, I.E.SELECTED CHILD REQUIRES A SINGLE CHILD

if len(epseglist) == 0:

kids = curact.Children()

poser.DialogSimple.MessageBox("Odd... the script created an empty easypose segment list although the selected actor had a singlechild.\n(the current actor '"+curact.Name()+"' has "+str(len(kids))+" children.\n\nScript cancelled (no list to work with!)\No changes made.")

return

# Abort if user doesn't make the final confirmation

confirmed=poser.DialogSimple.YesNo("Master EP dials in: "+curact.Name()+"\n\nEP slave length = "+str(len(epseglist))+"\nFirst EP slave: "+epseglist[0].Name()+"\nLast EP slave: "+epseglist[len(epseglist)-1].Name()+"\n\nPlease confirm (final confirmation)")

if not confirmed:

poser.DialogSimple.MessageBox("Final details of EP not confirmed.\nScript cancelled, no changes made.")

return

# Add basic EP dials to the current actor 

# - Check if they're already there to avoid "poser.error: A value-parameter with that name already exists for this actor"

# (if they're already there

# (first added appears at bottom of list on UI)

if not curact.Parameter("FP-Wave2-Bend"):

curact.CreateValueParameter("FP-Wave2-Bend")

if not curact.Parameter("FP-Wave1-Bend"):

curact.CreateValueParameter("FP-Wave1-Bend")

if not curact.Parameter("FP-Wave2-Side"):

curact.CreateValueParameter("FP-Wave2-Side")

if not curact.Parameter("FP-Wave1-Side"):

curact.CreateValueParameter("FP-Wave1-Side")

if not curact.Parameter("FP-Spiral-Bend"):

curact.CreateValueParameter("FP-Spiral-Bend")

if not curact.Parameter("FP-Spiral-Side"):

curact.CreateValueParameter("FP-Spiral-Side")

if not curact.Parameter("FP-SCurve-Bend"):

curact.CreateValueParameter("FP-SCurve-Bend")

if not curact.Parameter("FP-SCurve-Side"):

curact.CreateValueParameter("FP-SCurve-Side")

if not curact.Parameter("FP-Side-All"):

curact.CreateValueParameter("FP-Side-All")

if not curact.Parameter("FP-Bend-All"):

curact.CreateValueParameter("FP-Bend-All")

if not curact.Parameter("FP-Twist-All"):

curact.CreateValueParameter("FP-Twist-All")

# Slave the rotations of all actors in the EP segment list to the appropriate dial

epsegidx=0

#sbendmultiplier = 6.2831853/len(epseglist) # My first S-bend calculation was math.sin(epsegidx*sbendmultiplier) and bends around four times more thanthe Ajax Easypose S-Curve

#wavemultiplier = 4*6.2831853/len(epseglist) # My first Wave1 delta was math.sin(epsegidx*wavemultiplier),and Wave2 delta was math.cos(epsegidx*wavemultiplier)

for act in epseglist:

# Set up the delta values for this segment. Bend/Twist/SideAll deltas set to 0.25 to match originalAjax EasyPose tube

bendalldelta = 0.25

twistalldelta = 0.25

sidealldelta = 0.25

# Wave deltas in accordance with the B.L.Render book, except the first delta for Wave 1 is +1(not -1) to match the original Ajax Easypose Tube

# Wave1 = +1, -1, -1, -1, +1,+1, +1, +1, -1, -1, -1, -1, +1,+1, +1, +1, etc (four times -1,then four times +1, repeat...)

# Wave2 = -1, -1, +1,+1, +1, +1, -1, -1, -1, -1, +1,+1, +1, +1, etc (four times -1,then four times +1, repeat...)

wave1delta = 1.0

wave2delta = 1.0

if ((epsegidx % 8) <= 3) and (epsegidx != 0):

wave1delta = -1.0

if ((epsegidx+2) % 8 <=3 ):

wave2delta = -1.0

# S-Curve deltas as per the B.L.Render book: The first half of the segments all use 0.25, the second half all use -0.25. I've opted to make the middle segment delta  0.0 if there's an odd number of segments (the book said it doesn't matter. But of course it'll make a difference) 

if (len(epseglist) - epsegidx) > epsegidx:

if (len(epseglist) - epsegidx)== epsegidx+1:

scurvedelta = 0.0

else:

scurvedelta = 0.25

else:

scurvedelta = -0.25

# Spiral deltas as per the B.L.Render book. My original calculation was actually the same as the one in the book, so it's my comments in previous versions of this script that were wrong!

spiraldelta = float(epsegidx*epsegidx)/float(len(epseglist)*len(epseglist))

# Add the 'before' and 'after' master dials to each actor (checking whether they're already there to avoid the "poser error

if not act.Parameter("FP-Twist-Before"):

act.CreateValueParameter("FP-Twist-Before")

if not act.Parameter("FP-Bend-Before"):

act.CreateValueParameter("FP-Bend-Before")

if not act.Parameter("FP-Side-Before"):

act.CreateValueParameter("FP-Side-Before")

if not act.Parameter("FP-Twist-After"):

act.CreateValueParameter("FP-Twist-After")

if not act.Parameter("FP-Bend-After"):

act.CreateValueParameter("FP-Bend-After")

if not act.Parameter("FP-Side-After"):

act.CreateValueParameter("FP-Side-After")

# Add the slaving to each of the dials on the EP base actor

# (my deltas for the S-curve and spiral are different from the normal easypose. Waves should be the same as the normal easypose now.)

self.setValueOp(epsegidx,act.Parameter(twistdial),curact.Parameter("FP-Twist-All"),twistalldelta)

self.setValueOp(epsegidx,act.Parameter(benddial),curact.Parameter("FP-Bend-All"),bendalldelta)

self.setValueOp(epsegidx,act.Parameter(sidedial),curact.Parameter("FP-Side-All"),sidealldelta)

self.setValueOp(epsegidx,act.Parameter(sidedial),curact.Parameter("FP-SCurve-Side"),scurvedelta)

self.setValueOp(epsegidx,act.Parameter(benddial),curact.Parameter("FP-SCurve-Bend"),scurvedelta)

self.setValueOp(epsegidx,act.Parameter(sidedial),curact.Parameter("FP-Spiral-Side"),spiraldelta)

self.setValueOp(epsegidx,act.Parameter(benddial),curact.Parameter("FP-Spiral-Bend"),spiraldelta)

self.setValueOp(epsegidx,act.Parameter(sidedial),curact.Parameter("FP-Wave1-Side"),wave1delta)

self.setValueOp(epsegidx,act.Parameter(sidedial),curact.Parameter("FP-Wave2-Side"),wave2delta)

self.setValueOp(epsegidx,act.Parameter(benddial),curact.Parameter("FP-Wave1-Bend"),wave1delta)

self.setValueOp(epsegidx,act.Parameter(benddial),curact.Parameter("FP-Wave2-Bend"),wave2delta)

epsegidx=epsegidx+1

# Add the slaving to the 'before' dial of the next actor and the 'after' dial of the previous actor

# - If any of these slave parameters already exist their delta values will simply be overwritten

# - using the [-1] index to pick up the most recently added ValueOperations() was something I found in a script (D3D?) along with the comment "there's probably a better way to do this but I can't find it!" (or something similar).

epsegidx=0

for act in epseglist:

# Slave each actors twist, bend, sideside to its own TwistBef/TwistAft, BendBef/BendAft, SSideBef/SSideAft dials

self.setValueOp(epsegidx,act.Parameter(twistdial),act.Parameter("FP-Twist-After"),1.0)

self.setValueOp(epsegidx,act.Parameter(benddial),act.Parameter("FP-Bend-After"),1.0)

self.setValueOp(epsegidx,act.Parameter(sidedial),act.Parameter("FP-Side-After"),1.0)

self.setValueOp(epsegidx,act.Parameter(twistdial),act.Parameter("FP-Twist-Before"),1.0)

self.setValueOp(epsegidx,act.Parameter(benddial),act.Parameter("FP-Bend-Before"),1.0)

self.setValueOp(epsegidx,act.Parameter(sidedial),act.Parameter("FP-Side-Before"),1.0)

# Slave the 'after' dials to the previous actor/segment in the EP chain

# But DON'T try to slave the 'after' dial of the first segment to anything !

if epsegidx != 0: 

prevact = epseglist[epsegidx-1]

self.setValueOp(epsegidx,act.Parameter("FP-Twist-After"),prevact.Parameter("FP-Twist-After"),1.0)

self.setValueOp(epsegidx,act.Parameter("FP-Bend-After"),prevact.Parameter("FP-Bend-After"),1.0)

self.setValueOp(epsegidx,act.Parameter("FP-Side-After"),prevact.Parameter("FP-Side-After"),1.0)

# Slave the 'before' dials to the next actor/segment in the EP chain

# But DON'T try to slave the 'before' dial of the last segment to anything

if epsegidx != len(epseglist)-1: 

nextact = epseglist[epsegidx+1]

self.setValueOp(epsegidx,act.Parameter("FP-Twist-Before"),nextact.Parameter("FP-Twist-Before"),1.0)

self.setValueOp(epsegidx,act.Parameter("FP-Bend-Before"),nextact.Parameter("FP-Bend-Before"),1.0)

self.setValueOp(epsegidx,act.Parameter("FP-Side-Before"),nextact.Parameter("FP-Side-Before"),1.0)

epsegidx=epsegidx+1

# Inform the user that it's all done

poser.DialogSimple.MessageBox("Easypose added to the figure IN THE SCENE. Manually save the modified figure to your library. DON'T ACCIDENTALLY OVERWRITE THE ORIGINAL FIGURE - maybe use the original figure name PLUS '(FP010)' to indicate Freazypose 0.10 ?")

poser.DialogSimple.MessageBox("Running 3DCheapskates Freazypose0-10.py script (prototype 0.10)\n\n(N.B. You should see a similar message when the script finishes. If you don't then the script has crashed!)")

cheapskatesTemporaryInstance = cheapskatesTemporaryClass()

cheapskatesTemporaryInstance.main()

poser.DialogSimple.MessageBox("3DCheapskates Freazypose0-10.py script (prototype 0.10) all done.")


The 3Dcheapskate (also available in DAZ and HiveWire3D flavours) occasionally posts sensible stuff. Usually by accident.
And it usually uses Poser 11, with units set to inches. Except when it's using Poser 6 or PP2014, or when its units are set to PNU.