Forum: Poser Python Scripting


Subject: Is there any way to know if a figure is hair?

maur_2005 opened this issue on Oct 13, 2021 ยท 14 posts


maur_2005 posted Wed, 13 October 2021 at 10:26 PM

I am making a script that hides the hair of the figures and I was thinking, how can I know if a figure is hair? Some hairs have the word "Hair" in their names but some don't, so how can I make something more generic?

for fig in scene.figures():
   name=fig.Name().lower()
   if name.find("hair")
      fig.SetVisible(0)






structure posted Thu, 14 October 2021 at 1:08 AM Forum Coordinator

There is a function called IsHairProp() ( only works with hr2 (prop) hair ) I never found it to be reliable.

You could check it's name or actor names and / materials for certain things.
sadly because there is no recognised naming convention, most modellers
use a variety of names. so searching for cap or skull in actors / materials
may not work, and as you noted searching for "hair" in the name may also not work.

Seemingly, no 2 people use the same naming convention.
Some modellers don't even use the same conventions for all of their models.

*edit*

you could check it's parent or what it is conformed to.:

For figures, check the conform target :

    if figure.ParentActor().Name() == "Head": for props check the parent:
    if actor.Parent().Name() == "Head":

if the parent / conform target is "Head", it is likely ( not 100% foolproof ) that it is hair. 
( make something foolproof, they will make a better fool.)

Locked Out


HartyBart posted Thu, 14 October 2021 at 5:15 AM

The following might lead you. Eguchi showed how to find target 'eye, not brow' in the current children parts of the head. By excluding those that are not hair, presumably one would be left with the hair, without actually having to name it.

if figure:
    head_parts = figure.Actor("Head").Children()
    for target in head_parts:
        targetname = target.InternalName()
        if "Brow" not in targetname and ("Eye" in targetname or "eye" in targetname) :
            [then do something]



Learn the Secrets of Poser 11 and Line-art Filters.


HartyBart posted Thu, 14 October 2021 at 5:17 AM

Yay! Copy and paste from a code editor now works!



Learn the Secrets of Poser 11 and Line-art Filters.


structure posted Thu, 14 October 2021 at 7:09 AM Forum Coordinator

HartyBart posted at 5:15 AM Thu, 14 October 2021 - #4428909
if figure:
    head_parts = figure.Actor("Head").Children()
    for target in head_parts:
        targetname = target.InternalName()
        if "Brow" not in targetname and ("Eye" in targetname or "eye" in targetname) :
            [then do something]
You may need to edit this code since some hairs contain a part called brow, or has the word brow in it. 

Locked Out


Bastep posted Thu, 14 October 2021 at 10:27 PM

Hi, I have had a similar problem. My solution, just add the word hair in the name of the hair object. It is annoying, but actually only a small additional effort. Your Python program will always work 100% from now on.

Greetings



structure posted Fri, 15 October 2021 at 6:25 AM Forum Coordinator

Bastep posted at 10:27 PM Thu, 14 October 2021 - #4428951

Hi, I have had a similar problem. My solution, just add the word hair in the name of the hair object. It is annoying, but actually only a small additional effort. Your Python program will always work 100% from now on.

Greetings

This is a perfectly good solution if it is for personal use, however if the script is meant for distribution this approach will not work/is not optimal. 

Locked Out


structure posted Fri, 15 October 2021 at 7:33 AM Forum Coordinator

class haircontrols:
    def isithair( self, item ):
badnames = (
"eye", "tongue", "teeth", "hat", "mask", "veil" ) return True if not any(badname in item.InternalName().casefold() for badname in badnames) else False
you can expand the badnames to include anything else that you find in other 
objects that you would not normally find in a hair object.
This is untested but it should work, perhaps with a little 
modification.

Locked Out


HartyBart posted Fri, 15 October 2021 at 10:34 AM

Thanks structure. This leads me to ask: is it possible to change or append the InternalName or is it fixed forever?  I was just idly wondering if a process could quickly work through all "Hair" items in the user's personal Library, and tag them with something that could then connect with a Python script when used. A tag-name such as "Hairy", for instance.



Learn the Secrets of Poser 11 and Line-art Filters.


structure posted Fri, 15 October 2021 at 3:02 PM Forum Coordinator

afaik, it is not possible to change the internal name. However, you could change the external name with a script, combing through the folder would work with hr2 files but not with pp2 or cr2 unless there is an obvious way to tell them apart from a coat or a hat.

Or you could write a script which could ask the user for each item it finds whether it  is hair or not. 
my approach would be to build a user editable database. 

import os
import poser
import wx

extensions = ( ".hr2", ".hrz" )
prefslocation = poser.PrefsLocation()
dbfile = os.path.join( prefslocation, "hairdb", "hairdb.txt" )
if not os.path.exists( os.path.dirname( dbfile ) ):
    os.makedirs( os.path.dirname( dbfile ) )
    with open( dbfile, "w" ) as writefile:
        writefile.write("")

# create hair database
libraries = poser.Libraries()
for library in libraries:
    hairlibrary = os.path.join( library, "Runtime", "Library", "Hair" )
    for root, folders, files in os.walk( hairlibrary ):
        for file in files:
            if file.endswith( extensions ):
                file = os.path.join( root, file )
                with open( file, "r" ) as readfile:
                    lines = readfile.readlines()
                for line in lines:
                    if line.startswith( "   name" ):
                        line = line.strip()
                        line = line.replace( "name".casefold(), "" )
                        break
                with open( file, "a" ) as appendfile:
                    appendfile.write( line + "\n" )


This way, you can then write a script which accesses the database to see if any object in the scene is a recognised hair. 
Obviously any hair pp2 or cr2 would likely need to be added manually or with a combing script that allows the user to select which objects are hair and which are not. 

Locked Out


structure posted Sat, 16 October 2021 at 12:27 AM Forum Coordinator

so after some experimentation - it seems that the ParentActor of a figure is always the "Body" actor.

This script finds most hairs, but also counts things like the woodgoddess hood as a hair ( it has a material called skullcap )
with some modification ( bearing in mind your personal needs ) it could be made to ignore hoods caps veils etc.

from pathlib import Path
import poser
import wx

scene = poser.Scene()
letsdebug = False

def isvalid( actor , bodypart=False ): # a modified function of a Snarlygribbly routine
    if not actor is None:
        n       =   actor.Name().lower()
        if n.endswith("con") or n.endswith("con 1"):
            return False
        data    =   [
                    'atmosphere','background','bodymorphs','centerofmass',
                    'faceroom','focusdistancecontrol','ground','grouping',
                    'universe'
                    ]
        for d in data:
            if d in n:
                return False
        if actor.IsZone() or actor.IsBase() or actor.IsDeformer():
            return False
        if actor.IsLight() or actor.IsCamera():
            return False
        if actor.IsProp() or ( actor.IsBodyPart() and bodypart == True ):
            return True
    return False

def get_geometry( item ):
    geomName = item.GeomFileName()
   
    if (geomName and not geomName == ""):
        geom = Path( geomName ).stem
        geompath = Path( geomName ).parent.resolve()
   
    return geom, geompath

def get_parent( item, parent = None ):

    if item.IsFigure():
        try:
            parent = item.ParentActor()
        except:
            parent = None
    if item.IsProp():
        try:
            parent = actor.Parent()
        except:
            parent = None
       
    if letsdebug:
        try:
            print( f"{item.Name()}\n\t{parent.Name()}\t{isithair( item )}" )
        except:
            pass

    if parent:
        if parent.Name() == "Head" or parent.Name() == "Body":
            return isithair( item, parent )

    else:
        return False

def isithair( item, parent = None ):
    # set up list of valid materials
    validmats = ( "bangs", "skullcap", "hair" )
    # validnames = ( "hair", "hr" )
    # set case independent names
    itemname = item.Name().casefold()
    parentname = parent.Name().casefold()
    # check itemname and materials before relying on parent
   
    if "hair" in itemname or itemname.startswith("hr"):
        return True
   
    if item.IsFigure():
        for material in item.Materials():
            if any( mat in material.Name() for mat in validmats ):
                return True

    if not parentname == "head" and not parentname == "Body":
        return False
    badnames = ("eye", "tongue", "teeth", "hat", "mask", "veil" )
    try:
        for actor in figure.Actors():
            targetname = actor.InternalName().casefold()
            return True if not any(badname in targetname\
                for badname in badnames) else False
    except:
        if letsdebug:
            print( item )
        targetname = item.InternalName().casefold()
        return True if not any(badname in targetname\
                for badname in badnames) else False

for figure in scene.Figures():
    hair = get_parent( figure )
    if hair:
        print(f"{get_geometry( figure )[0]}\n\t{get_geometry( figure )[1]}")
        if letsdebug:
            print ( figure.Name(), hair, figure.ParentActor().Name() )

for actor in scene.Actors():
    hair = get_parent( actor )
    if hair and isvalid( actor ):
        print(f"{get_geometry( actor )[0]}\n\t{get_geometry( actor )[1]}")
        if letsdebug:
            print ( actor.Name(), hair, actor.Parent().Name() )

Output :
bl_RebyHair X:\Poser12\Runtime\Geometries\DAZHair\Victoria4 FoxHair V4 X:\Poser12\Runtime\Geometries\Arki\FoxHair aprilyshZed X:\Poser12\Runtime\Geometries\AprilYSH\AprilHair LLF-BastHairV4 X:\Poser12\Runtime\Geometries\Littlefox\BastV4 Genie Hair2 X:\Poser12\Runtime\Geometries\! DD_KJ\Genie hair BandanaHair X:\Poser12\Runtime\Geometries\P3DArt\CarGirlV4 p3dFWHair X:\Poser12\Runtime\Geometries\Pretty3D\FWHair pl_hair_v4 X:\Poser12\Runtime\Geometries\Pretty3D\PrettyLongHair SW_MintaHR01 X:\Poser12\Runtime\Geometries\SWAM_Art\2015_MintaHR SW_Yunko01 X:\Poser12\Runtime\Geometries\SWAM_Art\2013_YunkoHR XtremePonytail X:\Poser12\Runtime\Geometries\DIGIpixel\PAXtreme

Locked Out


adp001 posted Mon, 18 October 2021 at 1:32 PM

structure posted at 12:27 AM Sat, 16 October 2021 - #4428982

so after some experimentation - it seems that the ParentActor of a figure is always the "Body" actor.


Use figure.RootActor(). It's made for this :)




structure posted Tue, 19 October 2021 at 5:36 AM Forum Coordinator

adp001 posted at 1:32 PM Mon, 18 October 2021 - #4429062
structure posted at 12:27 AM Sat, 16 October 2021 - #4428982

so after some experimentation - it seems that the ParentActor of a figure is always the "Body" actor.


Use figure.RootActor(). It's made for this :)

Thanks ADP

Locked Out


structure posted Tue, 26 October 2021 at 8:05 PM Forum Coordinator

adp001 posted at 1:32 PM Mon, 18 October 2021 - #4429062

structure posted at 12:27 AM Sat, 16 October 2021 - #4428982

so after some experimentation - it seems that the ParentActor of a figure is always the "Body" actor.


Use figure.RootActor(). It's made for this :)

import poser

scene = poser.Scene()
figures = scene.Figures()

for figure in figures:
    print(figure.Name(), figure.RootActor().Name())


output :

Victoria4 Body
Hair Body

Locked Out