Welcome to the Poser Python Scripting Forum

Poser Python Scripting F.A.Q (Last Updated: 2022 Jun 29 2:07 pm)

We now have a ProPack Section in the Poser FreeStuff.
Check out the new Poser Python Wish List thread. If you have an idea for a script, jot it down and maybe someone can write it. If you're looking to write a script, check out this thread for useful suggestions.

Also, check out the official Python site for interpreters, sample code, applications, cool links and debuggers. This is THE central site for Python.

You can now attach text files to your posts to pass around scripts. Just attach the script as a txt file like you would a jpg or gif. Since the forum will use a random name for the file in the link, you should give instructions on what the file name should be and where to install it. Its a good idea to usually put that info right in the script file as well.

Checkout the Renderosity MarketPlace - Your source for digital art content!


Subject: Updating Freazypose 0.10 to work in Poser 12


3dcheapskate ( ) posted Tue, 10 May 2022 at 11:18 PM · edited Sat, 02 July 2022 at 10:49 AM

Freazypose is a very simple (only 345 lines of Python, around 70 of those being comment lines, and a further 20 or so being various 'poser.Dialog...() lines) four year old freebie script to add Ajax style easypose to a figure.

It's been uploaded to the Renderosity Free Stuff here, but I'm going to try to paste it directly into the next post 

It was written for Poser 9 (2012) and Poser 10 (2014) and seems to work fine in Poser 11 which I'm currently using.

I don't have Poser 12, and I've had a couple of people asking if it'll work in that too. So I'm trying to find out what needs to change.

I've had a quick look at what's likely to need changing ( What’s New In Python 3.0 — Python 3.10.4 documentation and The key differences between Python 2.7.x and Python 3.x with examples (sebastianraschka.com) ).

I think from looking at those that it'll be minimal - maybe even just a case of modifying the two print statements (lines 49 and 81).

In the latter of those two links under 'The print statement'  it says "Python 2 doesn’t have a problem with additional parantheses", so if it is just the print statements that need changing I could add brackets to those print statements and it would still work in Poser 9, 10 and 11 but also in Poser 12, yes?

If possible could some friendly Pythonophile take a look at the script (either download it or hopefully take a shufti at the next post) and advise me ?


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



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* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Tue, 10 May 2022 at 11:20 PM · edited Tue, 10 May 2022 at 11:23 PM

Ah... Python code still messes up when cut-and-pasted from Notepad++ to the PoserPython forum. No doubt something to do with the CR/LF pair I have at the end of each line in my Python files.

Well it was worth a try.


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 12:24 PM

Just a little test of different EOL characters. The cut-and-paste above had [CR][LF]

The following (just the first couple of lines of the file) uses [LF] only, i.e. Linux and modern Mac flavour:

# 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).


And the final variant is just [CR], i.e. old Mac style:

# 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).

Let's see if either of those work (in the edit box the final one with just [CR] looks right, but I won't believe it until I actually see it in the post.


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 12:29 PM

Okay, so this forum needs all EOLs to be converted from Windows (which I use) [CR][LF] to old Mac [CR] before pasting text. Got it. Notepad++ can do that for me in two clicks, so I just have to remember. And, at least as far as the EOL stuff goes, the edit box does appear to be WYSIWYG.

So I'll try reposting the whole script, Freazypose0-10.py, in the next post using just [CR] for EOL


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 12:33 PM

Well... that was the plan. But now it looks wrong again in the post editor, so I'll try just the first few lines again - EOL is just [CR] for the pasted text below:

# 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).





The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 12:35 PM

Oh dear.


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 12:39 PM

EOL is just [LF]:

# EOL is just LF

# 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).


EOL is just [CR]:
# EOL is just CR
# 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).

That's bizarre. In the edit box the EOL = [CR] cut-and-paste looks correct again. 


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 12:41 PM

# ignore this - a single line cut-and-pasted from Notepad++ with EOL=LF

# EOL is just CR
# 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* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



adp001 ( ) posted Wed, 11 May 2022 at 12:42 PM

Try LF (chr(10) or "\n") as EOL. This is standard for years on all systems. For Poser too.




3dcheapskate ( ) posted Wed, 11 May 2022 at 12:44 PM · edited Wed, 11 May 2022 at 12:52 PM

Hurrah ! It worked ! It appears that I have to paste a single line with just a [LF] as the EOL, and then I can paste my code with EOL=[CR] and the line spacing appears as it should in the post.


That's really, really bizarre !


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 12:51 PM

adp001 posted at 12:42 PM Wed, 11 May 2022 - #4438455

Try LF (chr(10) or "\n") as EOL. 

I'd already tried that ( https://www.renderosity.com/forums/comments/4438453/permalink ) - I get the same double spacing as when I used [CR][LF]

(Note: My "Hurrah ! It worked !" was reference to my previous post, not because LF alone worked. Your suggestion snuck in between them)


adp001 posted at 12:42 PM Wed, 11 May 2022 - #4438455

This is standard for years on all systems. For Poser too.

But apparently not for the Renderosity forums, eh ? ;)


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 1:27 PM

I mentioned earlier that maybe all I need to do to get this script to work in Poser 12 is to change the print statements from Python 2 print 'xyz' to Python 3 print ( 'xyz' ), i.e. add the brackets. But looking through the code above I'm reminded that I use the + sign to concatenate things I'm printing, e.g.

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

whereas normal people tend to use the % sign with a comma separated list (there's a name for that, something to do with format). I think Python 3 needs that format. But I'm not sure.


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



adp001 ( ) posted Wed, 11 May 2022 at 6:19 PM

Python 3 can deal with the same expressions used in Python 2. It's only that print in Python 3 is not a statement anymore but a function.

For your example above you have to write:

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

Please note that Python 3 does not like mixing tabs and spaces for indentation. You have to use one or the other.

About CR here in the editor: Works with LF. I write my code with PyCharm running under Linux. No problems at all.




adp001 ( ) posted Wed, 11 May 2022 at 6:27 PM

3dcheapskate posted at 1:27 PM Wed, 11 May 2022 - #4438463


whereas normal people tend to use the % sign with a comma separated list (there's a name for that, something to do with format). I think Python 3 needs that format. But I'm not sure.

That's Python 2 style :)

Python 3 uses something like print("Number {} and number {}".format(1, 2)) or so called "f" strings.

But P3 can also deal with Python 2 formatting (%). See this discussion: https://stackoverflow.com/questions/14753844/python-3-using-s-and-format




structure ( ) posted Wed, 11 May 2022 at 8:02 PM · edited Wed, 11 May 2022 at 8:03 PM
Site Admin

python 3 can use the print ("%s, %d, %.2f".format(string, integer, floating_point)) just like python 2,
It can also use f-strings 
print(f"{string}, {imteger}, {Floating_point}")


# ---------------------------------------------------------------------------------------------------

“I'm fairly certain YOLO is just Carpe Diem for stupid people.”― Jack Black
Poser Python Methods Manual.pdf
My Freestuff
My older Freestuff

Win11 on  i9-9900KF CPU @ 3.60GHz, 32Gb, GeForce RTX 2080 Super, 4x 2Tb ssd + 6x 1Tb hd, Poser 2014 Pro, Poser 12



3dcheapskate ( ) posted Wed, 11 May 2022 at 9:21 PM · edited Wed, 11 May 2022 at 9:26 PM

I now have somebody testing this for me in Poser 12 and as expected they get a 'SyntaxError: invalid syntax' on the print statement

Thanks adp001 and structure. Since I'm keeping my fingers crossed that it's only the print statement that needs changing for it to work in Poser 12 I'll avoid f-strings and stick to the Python 2 style that I've currently got.

According to the second Python 3 link in my OP Python 2 doesn’t have a problem with additional parantheses, so if I simply add opening and closing brackets to the two print statements the script will still work in Python 2. And if that's the only change necessary for it to work in Python 3 then it should work in Poser 12 too.

So I'm making that minor change to the script and I'll repost the whole amended script, version 0.11, in a new post below. Not forgetting to add that bizarre superfluous line at the start so that it looks correct on this forum.

It sure would be easier if we could simply attach .PY files.

Note: I've also noticed that posting the script to this forum deletes the final EOL, whether it's CR-LF, just LF, or just CR. So I'll add a comment line at the end - just so that I can post it to this darn forum.


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 9:35 PM

# Freazypose0-11.py (Prototype 0.11 - trying to get it to work in both Poser 12 and Poser 9-11)

# 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
# 0.11: Modifying for Poser 12, step 1: Added brackets to the two print statements. Nothing else.
# 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.")

# Ignore this - it's just a comment line added at the end of the script to ensure that there's an EOL after the last line of code when I post on the Renderosity forum


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 9:42 PM

(Note: I realized that after the bizarre workaround of pasting the spurious initial line with the EOL=LF at the start before pasting my code with the EOL=CR, which was the only way I could get my code to appear correctly in the edit box, I could delete that spurious initial line and my pasted code still looked correct in the edit box and, thankfully, in the post)

So anybody who wants to test this for me in Poser 12 just needs to cut-and-paste the whole thing, version 0.11, from the post above

(I wonder if the EOL-problem-when-pasting-to-this-forum is anything to do with the fact that I'm using Edge ?)


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 9:54 PM

I forget that this forum also makes it awkward to view older posts. While looking for a thread I think I saw ages ago about problems people were still having posting script snippets to this forum even after the latest edit box changes (i.e. to what they are now)I just found this thread to which only structure, adp001 and dizzi have so far posted 

Py2 to Py3 conversions | Renderosity

Another hopeful sign that just modifying the print statements might work


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 10:12 PM

Also found the thread I was after, from October 2021, about problems posting code to this forum. 

Scripting forum still totally broken. | Renderosity




The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Wed, 11 May 2022 at 11:21 PM · edited Wed, 11 May 2022 at 11:21 PM

My friendly neighbourhood Poser 12 tester got another syntax error running version 0.11.

5rCZ3hI54jJxINzhsDG5Sd7kRmA8LsDuxVFdvQou.png


Two things.

1) There's a mistake in the text string formatting of the poser.DialogSimple.MessageBox() on line 205. The last bit \No changes made.") should be \nNo changes made.") - that's easy.

2) The information that the script is trying to display is "Odd... the script created an empty easypose segment list although the selected actor had a singlechild.". That's an error-trap I put in the code for something that should never happen, which is why I've never spotted the syntax error - this particular line of code was never reached in any of my tests.

The fact that the script got this far means that several dialogues were seen and interacted with, so that's a good sign.

So now it's a case of finding out why it got to this piece of code. Maybe it's the specific figure/actor selected (I don't yet know what my tester had selected) or maybe it's something Python 3 (which I think is unlikely)

I think we need to try this using a known figure that any Poser 11 (me) or Poser 12 user has access to - so either a figure that came with Poser and didn't change between 11 and 12, or the tentacle figure included with Freazypose 0.10

If anybody else wants to join in the fun of testing this in Poser 12 please do !


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Thu, 12 May 2022 at 1:58 AM · edited Thu, 12 May 2022 at 2:00 AM

Regarding the syntax error that was thrown on the last test, which I said indicates that a condition that should never occur has occured.

This bit of code is creating the epseglist[], but would appear to be creating an empty list.

GxDnnanFYHwrooH3hUlyQL8Z9Io4HAdwkQgIjObm.png


The earlier check to confirm that the selected actor has exactly one direct child (shown below) must have passed for the script to get to thie piece of code shown above:

jNk31FMHoL81BFLpGtBliR1BVbb8P4PMp0bPmpLt.png


So what's up ?


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Thu, 12 May 2022 at 4:57 AM

Slam the brakes on ! Those EOL problems have sent me off on completely the wrong track, and I've been down this road before... don't want to go there again ! :)

If anybody wants to help get this working in Poser 12 I've now taken it over to the Hivewire3D forum - post 21 on my Freazypose topic


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



hborre ( ) posted Thu, 12 May 2022 at 1:34 PM

I'm having issues trying to post anything at Hivewire3D but I want to let you know that the script is working as it should, I got no errors adding Freasypose to a viable candidate.  I think it's good for a P12 release.


3dcheapskate ( ) posted Thu, 12 May 2022 at 9:14 PM

Well that is good news - thanks.

To be suitable for a release I want to also be sure that it terminates tidily when the selected actor is unsuitable (after displaying the appropriate dialogues of course).

I think I need to complete my dialogues flowchart. Then I'll be able to specify a small number of test runs that will exercise (almost*) every part of the code.

It'll probably take me a couple of days to do the flowchart as I'm quite busy today and this weekend. So in the meantime if anybody else would like to test prototype 11b in Poser 12 please do so, and post your results - ideally over on the Hivewire3D thread, but here is fine too.

*almost because I have a couple of bits of code to catch situations which should never occur - one of which was (or would have been if it wasn't for the syntax error in the dialogue line) displayed when you did your second test run with V3


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



3dcheapskate ( ) posted Thu, 12 May 2022 at 10:54 PM · edited Thu, 12 May 2022 at 10:54 PM

Just had time to scribble down a flowchart from looking at the code. Not sure if anybody'll be able to read or make sense of it ;)

(Based on the handwriting how old would you think I am ?  :D )

SzNB2FdIZe619pIt8lGFFvE0dyLiw7znH5Qe7O46.png


The 3Dcheapskate* occasionally posts sensible stuff. Usually by accident.

*also available in ShareCG, DeviantArt, DAZ, and CGBytes flavours.



Privacy Notice

This site uses cookies to deliver the best experience. Our own cookies make user accounts and other features possible. Third-party cookies are used to display relevant ads and to analyze how Renderosity is used. By using our site, you acknowledge that you have read and understood our Terms of Service, including our Cookie Policy and our Privacy Policy.