API Scenario

Auto Hanging

import utility_api
import ApiTypes

print("API Test Scenario - Auto Hanging")

#Adding Path for Garment and Hanger
garmentPath = utility_api.GetCLOAssetFolderPath(True) + "Garment/Male_T-shirt.zpac"  # change this to a path to your garment
hangerPath = utility_api.GetCLOAssetFolderPath(True) + "Avatar/Hanger/Adult_Shirt_Hanger_V3.avt"  # change this to a path to your hanger.

#Option for importing OBJ
objoption = ApiTypes.ImportExportOption()
objoption.scale = 1 # specify this if your hanger is in OBJ. For example, 1 means 1mm scale. 10 means 10cm scale.

#Auto Hanging API
utility_api.AutoHang(garmentPath, hangerPath, 0, objoption) # toggle the third input - 0 for top hangers, 1 for bottom hangers
_images/shirtspants.gif



Please Download the Auto Hanging Sample Sample Python Script Here -> S3 , OSS

Simulation

import import_api
import utility_api

print("API Test Scenario - Simulation")

cloAssetFolderPath = "C:/Users/Public/Documents/CLO/Assets/Avatar/Avatar/"
cloGarmentFolderPath = "C:/Users/Public/Documents/CLO/Assets/Garment/"

# Import Avatar File(avt)
avtfilePath = cloAssetFolderPath + "/Female_V2/FV2_Feifei.avt"
import_api.ImportFile(avtfilePath)

# Import Cloth File(zpac)
zpacfilePath = cloGarmentFolderPath + "/Female_T-shirt.zpac"
import_api.ImportFile(zpacfilePath)

# Simulation
utility_api.Simulate(100)
_images/simulation.gif



Rendering

import import_api
import utility_api
import fabric_api
import pattern_api
import export_api
print("API Test Scenario - Rendering")
cloAssetFolderPath = "C:/Users/Public/Documents/CLO/Assets"
# Import Avatar File(avt)
avtfilePath = cloAssetFolderPath + "/Avatar/Avatar/Female_V2/FV2_Feifei.avt"
import_api.ImportFile(avtfilePath)
# Import Cloth File(zpac)
zpacfilePath = cloAssetFolderPath + "/Garment/Female_T-shirt.zpac"
import_api.ImportFile(zpacfilePath)
# Simulation
utility_api.Simulate(100)
# Import Fabric(zfab)
zfabFilePath = cloAssetFolderPath + "/Materials/Fabric/Cotton_Oxford.zfab"
addedFabricIndex = fabric_api.AddFabric(zfabFilePath)
# Assign Fabric to Pattern
patternCount = pattern_api.GetPatternCount()
for i in range(patternCount):
    fabric_api.AssignFabricToPattern(addedFabricIndex, i)
# Rendering
savedFilepathList = export_api.ExportRenderingImage(False)
print(savedFilepathList) #utility_api.GetCLOTemporaryFolderPath()
automatic



Pattern

import import_api
import utility_api
import fabric_api
import pattern_api
import export_api
print("API Test Scenario - Pattern")
# init rectangle
rectPoints = (
    (0.0, -300.0, 0)
    , (0.0, -200.0, 0)
    , (100.0, -200.0, 0)
    , (100.0, -300.0, 0)
    )
pattern_api.CreatePatternWithPoints(rectPoints)
# free polygon
freePolygonPoints = (
    (0.0, 0.0, 0)
    , (0.0, 100.0, 3)
    , (100.0, 100.0, 0)
    , (200.0, 100.0, 3)
    , (150.0, 50.0, 3)
    , (200.0, 50.0, 0)
    , (300.0, 50.0, 3)
    , (250.0, 100.0, 3)
    , (300.0, 100.0, 0)
    , (350.0, 100.0, 3)
    , (300.0, 50.0, 0)
    , (150.0, 0.0, 2)
    )
pattern_api.CreatePatternWithPoints(freePolygonPoints)
# half circle
halfCirclePoints = (
    (0.0, 200.0, 0)
    , (0.0, 300.0, 3)
    , (100.0, 300.0, 0)
    , (200.0, 300.0, 3)
    , (200.0, 200.0, 0)
    )
pattern_api.CreatePatternWithPoints(halfCirclePoints)
#Path
cloAssetFolderPath = "C:/Users/Public/Documents/CLO/Assets"
# Import Fabric(zfab)
zfabFilePath = cloAssetFolderPath + "/Materials/Fabric/Cotton_Oxford.zfab"
addedFabricIndex = fabric_api.AddFabric(zfabFilePath)
# Assign Fabric to Pattern
patternCount = pattern_api.GetPatternCount()
for i in range(patternCount):
    fabric_api.AssignFabricToPattern(addedFabricIndex, i)
automatic



Colorway

import import_api
import utility_api
import fabric_api
print("API Test Scenario - Colorway")
cloAssetFolderPath = "C:/Users/Public/Documents/CLO/Assets"
# Import Cloth File(zpac)
zpacfilePath = cloAssetFolderPath + "/Garment/Female_T-shirt.zpac"
import_api.ImportFile(zpacfilePath)
# set target fabric index
targetFabricIndex = fabric_api.GetFabricCount() - 1
for i in range(3):
    # copy last colorway
    colorwayCount = utility_api.GetColorwayCount()
    copiedColorwayIndex = utility_api.CopyColorway(colorwayCount-1)
    # set current colorway
    utility_api.SetCurrentColorwayIndex(copiedColorwayIndex)
    currentColorwayIndex = utility_api.GetCurrentColorwayIndex()
    # set colorway name
    alphabet = chr(ord('A') + (i + 1))
    colorwayName = "Colorway " + alphabet
    print(currentColorwayIndex, colorwayName)
    utility_api.SetColorwayName(currentColorwayIndex, colorwayName)
    # set diffuse color
    # materialFace - 0:Front, 1:Back, 2:Side
    r = 0.0
    g = 0.0
    b = 0.0
    if i == 0:
        r = 1.0
    elif i == 1:
        g = 1.0
    else:
        b = 1.0
    fabric_api.SetFabricPBRMaterialBaseColor(currentColorwayIndex, targetFabricIndex, 0, r, g, b, 1.0)
    # update colorway
    utility_api.UpdateColorways(True)
automatic



Substance

import import_api
import utility_api
import fabric_api
print("API Test Scenario - Substance")
cloAssetFolderPath = "C:/Users/Public/Documents/CLO/Assets"
# Import Cloth File(zpac)
zpacfilePath = cloAssetFolderPath + "/Garment/Female_T-shirt.zpac"
import_api.ImportFile(zpacfilePath)
# Import Substance
lastFabricIndex = fabric_api.GetFabricCount() - 1
sbsarFilePath = cloAssetFolderPath + "/Materials/Material/Substance/basicjean.sbsar"
fabric_api.ImportSubstanceFileAsFaceType(lastFabricIndex, sbsarFilePath, "Front") # FaceType: Front, Back, Side
automatic



Schematic Render

import import_api
import utility_api
import fabric_api
print("API Test Scenario - Schematic Render")
cloAssetFolderPath = "C:/Users/Public/Documents/CLO/Assets"
# Import Cloth File(zpac)
zpacfilePath = cloAssetFolderPath + "/Garment/Male_T-shirt.zpac"
import_api.ImportFile(zpacfilePath)
#Activate SchematicRender
utility_api.SetSchematicRender(True)
#Deactivate SchematicRender
utility_api.SetSchematicRender(False)
automatic



Animation Render

import import_api
import utility_api
import fabric_api
import pattern_api
import export_api
import ApiTypes

avatar_path = "C:/Users/Public/Documents/CLO/CLO Assets/Avatar/Male/MV2.1_Luka.avt"
hair_path = "C:/Users/Public/Documents/CLO/CLO Assets/Avatar/Male/Accessory/Hair/MV2_Shaved.zacs"
motion_path = "C:/Users/Public/Documents/CLO/CLO Assets/Avatar/Male/Motion/MV2_Idle.mtn"
garment_path = "C:/Users/Public/Documents/CLO/CLO Assets/Garment/MV2_Regular Fit Crewneck Half-Sleeve T-Shirts.zpac"
fabric_path = "C:/Users/Public/Documents/CLO/CLO Assets/Fabric/V1_Woven_Denim_Stretch.zfab"
output_zprj_path = "C:/Users/Public/Documents/CLO/CLO Assets/MyProject.zprj"

# Import avatar, hair accessory, motion, garment, and fabric files
AvatarOp = ApiTypes.ImportExportOption()
import_api.ImportAvatar(avatar_path, AvatarOp)
import_api.ImportFile(hair_path)
import_api.ImportFile(motion_path)
import_api.ImportFile(garment_path)
addedFabricIndex = fabric_api.AddFabric(fabric_path)

# Assign fabric to all patterns in the current project for the current colorway
pattern_count = pattern_api.GetPatternCount()
for pattern_index in range(pattern_count):
  fabric_api.AssignFabricToPattern(addedFabricIndex, pattern_index, 1)

# Set animation frame range
utility_api.SetStartAnimationFrame(0)
utility_api.SetEndAnimationFrame(450)

# Start animation recording
utility_api.SetAnimationRecording(True)

# Save the current CLO project as a Zprj file to the specified path
export_path = export_api.ExportZPrj(output_zprj_path)

if export_path == "":
  raise Exception("Failed to export project file to the specified path.")
automatic



Scenarios with API Options

These scenarios focus on option objects from the ApiTypes module: each one creates the option object, sets its fields with a comment on what every field does, passes it to the API call and prints the result, so you can see what each option changes. All fields are listed in API Option & Type.


Import Options

Level: Medium

Options used: ImportZPRJOption, ImportExportOption

Control what an import brings in and where it goes. ImportZPRJOption loads only the garment or only the avatar of a project, appends a second copy next to the first, and ImportExportOption turns an OBJ into an avatar placed beside them.

import os
import import_api
import utility_api
import export_api
import pattern_api
import ApiTypes

print("API Test Scenario - Import Options")

# NOTE: update these paths for your machine.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"
OUTPUT_DIR = "C:/Users/Public/Documents/CLO/API_Output/"

work = os.path.join(OUTPUT_DIR, "import_options")
os.makedirs(work, exist_ok=True)


def report(step):
    print("%-38s patterns=%2d  avatars=%d" % (step, pattern_api.GetPatternCount(), export_api.GetAvatarCount()))


# ----------------------------------------------------------------------------
# 0) Prepare the inputs: a project file and an avatar OBJ
# ----------------------------------------------------------------------------
utility_api.NewProject()
import_api.ImportAvatar(ASSET_ROOT + "Avatar/Female/FV2.1_Mia.avt", ApiTypes.ImportExportOption())
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())
utility_api.Simulate(40)
projectPath = export_api.ExportZPrj(os.path.join(work, "dress.zprj"), False)

avatarOnly = ApiTypes.ImportExportOption()
avatarOnly.bExportGarment = False    # leave the dress out of the OBJ
avatarOnly.bExportAvatar = True      # the avatar mesh only
avatarOnly.bSingleObject = True      # one mesh instead of one object per avatar part
avatarOnly.scale = 1.0               # keep millimetres (CLO's unit) in the file
avatarObj = export_api.ExportOBJ(os.path.join(work, "avatar.obj"), avatarOnly)[0]

# ----------------------------------------------------------------------------
# 1) ImportZPRJOption: open only the garment of the project
# ----------------------------------------------------------------------------
utility_api.NewProject()
garmentOnly = ApiTypes.ImportZPRJOption()
garmentOnly.bAppend = False          # replace the current project (the default)
garmentOnly.bLoadGarment = True      # patterns, fabrics, sewing
garmentOnly.bLoadAvatar = False      # skip the avatar stored in the file
import_api.ImportZprj(projectPath, garmentOnly)
report("1) garment only")

# ----------------------------------------------------------------------------
# 2) Add only the avatar from the same file
# ----------------------------------------------------------------------------
avatarAppend = ApiTypes.ImportZPRJOption()
avatarAppend.bAppend = True          # add to the open project instead of replacing it
avatarAppend.bLoadGarment = False
avatarAppend.bLoadAvatar = True
import_api.ImportZprj(projectPath, avatarAppend)
report("2) + avatar from the same file")

# ----------------------------------------------------------------------------
# 3) Add a second copy of the garment, moved 900 mm to the right
# ----------------------------------------------------------------------------
secondCopy = ApiTypes.ImportZPRJOption()
secondCopy.bAppend = True
secondCopy.bLoadGarment = True
secondCopy.bLoadAvatar = False
secondCopy.translationValueX = 900.0     # mm: shifts the 2D patterns and the 3D garment
import_api.ImportZprj(projectPath, secondCopy)
report("3) + second garment, X +900 mm")

# ----------------------------------------------------------------------------
# 4) ImportExportOption: the OBJ becomes an avatar for the second garment
# ----------------------------------------------------------------------------
objAsAvatar = ApiTypes.ImportExportOption()
objAsAvatar.ImportObjectType = 0         # 0 = Avatar, 1 = Trim, 2 = Garment
objAsAvatar.bAdd = True                  # add to the scene, keep what is already there
objAsAvatar.scale = 1.0                  # the file is in millimetres
objAsAvatar.bAutoTranslate = False       # keep the file's position, then move it:
objAsAvatar.translationValueX = 900.0    # under the second garment
import_api.ImportOBJ(avatarObj, objAsAvatar)
report("4) + OBJ as avatar, X +900 mm")

utility_api.Simulate(30)
utility_api.SetCamViewPoint(9)           # zoom extents: both avatars in view
Import Options



Export Options

Level: High

Options used: ImportExportOption, ExportUSDOption

Export one draped garment with different ImportExportOption settings (single object or one object per pattern, thick or thin, scale, up axis) plus USD with ExportUSDOption and Alembic, then compare the files: vertex count, object count and size along each axis.

import os
import re
import import_api
import utility_api
import export_api
import ApiTypes

print("API Test Scenario - Export Options")

# NOTE: update these paths for your machine.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"
OUTPUT_DIR = "C:/Users/Public/Documents/CLO/API_Output/"

out = os.path.join(OUTPUT_DIR, "export_options")
os.makedirs(out, exist_ok=True)

utility_api.NewProject()
import_api.ImportAvatar(ASSET_ROOT + "Avatar/Female/FV2.1_Mia.avt", ApiTypes.ImportExportOption())
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())
utility_api.Simulate(60)
utility_api.SetCamViewPoint(1)   # 3/4 right
utility_api.SetCamViewPoint(9)   # then zoom to fit the garment


def options(**fields):
    """An ImportExportOption with the given fields set; everything else keeps its default."""
    option = ApiTypes.ImportExportOption()
    for name, value in fields.items():
        setattr(option, name, value)
    return option


def obj_stats(path):
    """(vertices, objects, size x, size y, size z) of an OBJ file."""
    count, objects, low, high = 0, 0, [float("inf")] * 3, [float("-inf")] * 3
    with open(path) as f:
        for line in f:
            if line.startswith("v "):
                count += 1
                xyz = [float(v) for v in line.split()[1:4]]
                low = [min(a, b) for a, b in zip(low, xyz)]
                high = [max(a, b) for a, b in zip(high, xyz)]
            elif line.startswith(("o ", "g ")):
                objects += 1
    return (count, objects) + tuple(h - l for l, h in zip(low, high))


# Garment only, one mesh, single-layer (thin) surface, millimetres, Y up: the reference
reference = options(bExportGarment=True, bExportAvatar=False, bSingleObject=True, bThin=True, scale=1.0)

variants = [
    ("reference (mm, thin, one mesh)", reference),
    # bSingleObject=False: one object per pattern; bThin=False: both cloth surfaces and the
    # edges between them; scale=0.1: millimetres -> centimetres
    ("per pattern, thick, cm", options(bExportGarment=True, bExportAvatar=False, bSingleObject=False,
                                       bThin=False, scale=0.1)),
    # metres with Z up: axisX/Y/Z pick the source axis for each output axis (0=X, 1=Y, 2=Z),
    # bInvertZ flips the new Z so the garment stays upright and faces the same way
    ("Z up, metres", options(bExportGarment=True, bExportAvatar=False, bSingleObject=True, bThin=True,
                             scale=0.001, axisX=0, axisY=2, axisZ=1, bInvertZ=True)),
]

print("%-32s %8s %8s %10s %10s %10s" % ("OBJ variant", "vertices", "objects", "size X", "size Y", "size Z"))
for label, option in variants:
    path = export_api.ExportOBJ(os.path.join(out, label.split(" (")[0].replace(", ", "_").replace(" ", "_") + ".obj"), option)[0]
    print("%-32s %8d %8d %10.3f %10.3f %10.3f" % ((label,) + obj_stats(path)))

# FBX: garment and avatar together, one object per pattern, centimetres
fbx = export_api.ExportFBX(os.path.join(out, "garment_and_avatar.fbx"),
                           options(bExportGarment=True, bExportAvatar=True, bSingleObject=False, scale=0.1))
print("FBX:", fbx[0])

# USD: ExportUSDOption names the stage layout, ImportExportOption picks what goes in
usd = ApiTypes.ExportUSDOption()
usd.m_upAxis = 1                    # 1 = Y up
usd.m_metersPerUnit = 0.01          # stage unit: centimetres
usd.m_bAssignAvatar = True
usd.m_avatarPrimPath = "/Avatar"    # where the avatar meshes live in the stage
usd.m_bAssignGarment = True
usd.m_garmentPrimPath = "/Dress"    # where the garment meshes live
usd.m_bExportSimulationData = False
usdFiles = export_api.ExportUSD(os.path.join(out, "scene.usda"),
                                options(bExportGarment=True, bExportAvatar=True), usd)
with open(usdFiles[0]) as f:
    stage = f.read()
scopes = sorted(set(re.findall(r'def Scope "(\w+)"', stage)))
print("USD:", usdFiles[0])
print("     upAxis Y: %s, metersPerUnit 0.01: %s, top-level prims: %s"
      % ('upAxis = "Y"' in stage, "metersPerUnit = 0.01" in stage, scopes))

# Alembic: garment only, centimetres
abc = export_api.ExportAlembic(os.path.join(out, "garment.abc"),
                               options(bExportGarment=True, bExportAvatar=False, bSingleObject=True, scale=0.1))
print("Alembic:", abc[0])
Export Options



Wind & Animation Video Options

Level: Medium

Options used: WindControllerOptions, VideoExportOption

Read the wind controller with WindControllerOptions, turn it into a strong, gusty side wind, record the dress moving in it and save the animation with VideoExportOption.

import os
import import_api
import utility_api
import export_api
import ApiTypes

print("API Test Scenario - Wind & Animation Video Options")

# NOTE: update these paths for your machine.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"
OUTPUT_DIR = "C:/Users/Public/Documents/CLO/API_Output/"

os.makedirs(OUTPUT_DIR, exist_ok=True)

utility_api.NewProject()
import_api.ImportAvatar(ASSET_ROOT + "Avatar/Female/FV2.1_Mia.avt", ApiTypes.ImportExportOption())
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())
utility_api.Simulate(40)
utility_api.SetCamViewPoint(1)   # 3/4 right
utility_api.SetCamViewPoint(9)   # then zoom to fit the garment


def show(label, w):
    print("%-8s active=%s type=%d strength=%.0f decay=%.0f frequency=%.1f turbulence=%.0f "
          "position=(%.0f, %.0f, %.0f) rotation=(%.0f, %.0f, %.0f)"
          % (label, w.bWindActive, w.windType, w.strength, w.decay, w.frequency, w.turbulence,
             w.x, w.y, w.z, w.rotationX, w.rotationY, w.rotationZ))


# Every project has one wind controller (index 0); it starts inactive
wind = utility_api.GetWindControllerSettings(0)
show("before", wind)

wind.bWindActive = True
wind.windType = 1          # 0 = Spherical (blows outwards from a point), any other value = Planar
wind.strength = 4000.0     # force of the wind
wind.decay = 0.0           # 0-100: how fast the force fades with distance (0 = no fade)
wind.frequency = 2.0       # 0-100000: gusts per second (0 = constant)
wind.turbulence = 60.0     # 0-100: random variation of direction and force
wind.x, wind.y, wind.z = -900.0, 1000.0, 0.0           # mm: left of the avatar, at hip height
wind.rotationX, wind.rotationY, wind.rotationZ = 0.0, 90.0, 0.0   # degrees: blow towards +X
utility_api.SetWindControllerSettings(0, wind)
show("after", utility_api.GetWindControllerSettings(0))

# Record 90 frames of the simulation in the wind (frames are recorded at the animation frame rate)
utility_api.RunAnimationRecording(0, 90)

video = ApiTypes.VideoExportOption()
video.width = 720                # pixels
video.height = 960
video.fps = 2                    # an index, not frames per second: 0 = 24, 1 = 25, 2 = 30, 3 = 60
                                 # (-1 = the animation's own frame rate)
video.startFrame = 0             # -1 = from the first recorded frame
video.endFrame = 90              # -1 = to the last recorded frame
path = export_api.ExportAnimationVideo(os.path.join(OUTPUT_DIR, "dress_in_wind.mp4"), video)
print("Video:", path)

# Calm again for the next script
wind.bWindActive = False
utility_api.SetWindControllerSettings(0, wind)
Wind & Animation Video Options



Material Property Options

Level: High

Options used: ObjectBrowserContextOptions, MaterialPropertyOptions, MaterialContextOptions, PropertyEditorContextOptions, TransformOptions

Edit a fabric’s material through option objects: ObjectBrowserContextOptions finds the fabric, MaterialPropertyOptions reads and writes its front face (color, normal map, reflection) in one call, and PropertyEditorContextOptions + TransformOptions rotate and scale its texture.

import json
import import_api
import utility_api
import fabric_api
import pattern_api
import ApiTypes

print("API Test Scenario - Material Property Options")

# NOTE: update this path to your local CLO asset folder.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"

utility_api.NewProject()
import_api.ImportAvatar(ASSET_ROOT + "Avatar/Female/FV2.1_Mia.avt", ApiTypes.ImportExportOption())
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())
utility_api.Simulate(40)
utility_api.SetCamViewPoint(2)   # front
utility_api.SetCamViewPoint(9)   # then zoom to fit the garment

# A plaid fabric on every pattern
plaid = fabric_api.AddFabric(ASSET_ROOT + "Fabric/V2_Woven_Plaid_1.zfab")
for patternIndex in range(pattern_api.GetPatternCount()):
    fabric_api.AssignFabricToPattern(plaid, patternIndex, 1)

# ----------------------------------------------------------------------------
# 1) ObjectBrowserContextOptions: which Object Browser list the index refers to
# ----------------------------------------------------------------------------
browser = ApiTypes.ObjectBrowserContextOptions()
browser.type = ApiTypes.ObjectBrowserType.OB_TYPE_FABRIC   # the Fabric list
browser.targetPrint = False           # the fabric itself, not its print / AOP
browser.bIncludeUnusedObjects = True  # also list fabrics no pattern uses
items = json.loads(utility_api.GetObjectBrowserItems(browser))["ObjectNameList"]
index = items.index(fabric_api.GetFabricName(plaid))       # index within that list
print("Fabric list:", items, "-> plaid at", index)

# ----------------------------------------------------------------------------
# 2) MaterialPropertyOptions: read and write a whole material face in one call
# ----------------------------------------------------------------------------
request = ApiTypes.MaterialPropertyOptions()
request.contextOptions.targetComponent = ApiTypes.MaterialTargetComponent.COMPONENT_FRONT  # Front tab

material = utility_api.GetMaterialProperties(-1, index, browser, request)   # -1 = current colorway
front = material.front
print("before: type=%s mapping=%s color=%s normal=%d reflection=%d maps=%s"
      % (front.type, front.texture_mapping, front.base_color.color_hex_or_name,
         front.normal_map.intensity, front.reflection.reflection_intensity, front.GetPresentMaps()))

front.base_color.color_hex_or_name = "#C9D8F0"   # tint the plaid light blue
front.normal_map.intensity = 80                  # 0-100: stronger weave relief
front.reflection.reflection_intensity = 25       # 0-100: a little sheen
print(utility_api.SetMaterialProperties(-1, index, browser, request, material))

front = utility_api.GetMaterialProperties(-1, index, browser, request).front
print("after:  type=%s mapping=%s color=%s normal=%d reflection=%d"
      % (front.type, front.texture_mapping, front.base_color.color_hex_or_name,
         front.normal_map.intensity, front.reflection.reflection_intensity))

# ----------------------------------------------------------------------------
# 3) PropertyEditorContextOptions + TransformOptions: transform the texture
# ----------------------------------------------------------------------------
editor = ApiTypes.PropertyEditorContextOptions()
editor.materialSide = 0                                         # 0 = Front, 1 = Back, 2 = Side
editor.mapType = ApiTypes.TextureMapTarget.TEXTURE_MAP_BASE_COLOR

transform = utility_api.GetMaterialTextureTransformations(-1, index, browser, editor)
print("texture before: angle=%.0f width=%.1f mm (%.0f%%) height=%.1f mm"
      % (transform.angle, transform.widthMm, transform.widthPercent, transform.heightMm))
transform.angle = 45.0              # degrees: a bias-cut plaid
transform.widthMm = 0.0             # 0 = use the percentages below instead of millimetres
transform.heightMm = 0.0
transform.widthPercent = 250.0      # bigger checks
transform.heightPercent = 250.0
transform.aspectRatioFixed = True
utility_api.SetMaterialTextureTransformations(-1, index, browser, editor, transform)

transform = utility_api.GetMaterialTextureTransformations(-1, index, browser, editor)
print("texture after:  angle=%.0f width=%.1f mm (%.0f%%) height=%.1f mm"
      % (transform.angle, transform.widthMm, transform.widthPercent, transform.heightMm))
utility_api.Refresh3DWindow()
Material Property Options



Pattern Snapshot Options

Level: Medium

Options used: PatternSnapShotSizeOption, PatternSnapShotLineOption, PatternSnapShotImageOption, PatternSnapShotInformationOption

Configure the 2D pattern snapshot with its four option types (paper size, lines, textures, labels) and export it before and after, so the two images show what each option changes.

import os
import import_api
import utility_api
import export_api
import fabric_api
import pattern_api
import ApiTypes

print("API Test Scenario - Pattern Snapshot Options")

# NOTE: update these paths for your machine.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"
OUTPUT_DIR = "C:/Users/Public/Documents/CLO/API_Output/"

out = os.path.join(OUTPUT_DIR, "pattern_snapshot")
os.makedirs(out, exist_ok=True)

utility_api.NewProject()
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())
plaid = fabric_api.AddFabric(ASSET_ROOT + "Fabric/V2_Woven_Plaid_1.zfab")
for patternIndex in range(pattern_api.GetPatternCount()):
    fabric_api.AssignFabricToPattern(plaid, patternIndex, 1)

WINDOW = 0     # 0 = the 2D Pattern window, 1 = the Print Layout window

print("default:", export_api.ExportSnapshot2D(os.path.join(out, "default.png"), WINDOW)[0])

# Paper: A4 landscape at 150 dpi
size = utility_api.GetPatternSnapShotSizeOption(WINDOW)
size.m_paperPreset = 0        # 0 = A4, 1 = A3, 2 = B5, 3 = B4, 4 = Letter, 5 = Legal, 6 = Tabloid, 7 = 24" roll
size.m_orientation = 1        # 0 = portrait, 1 = landscape
size.m_resolution = 150       # dots per inch
utility_api.SetPatternSnapShotSizeOption(size, WINDOW)

# Lines: red outlines, show internal lines and notches
lines = utility_api.GetPatternSnapShotLineOption(WINDOW)
lines.ShowPatternOutline = True
lines.PatternOutlineColor.R, lines.PatternOutlineColor.G, lines.PatternOutlineColor.B = 200, 30, 30
lines.ShowInternalLine = True
lines.ShowNotch = True
utility_api.SetPatternSnapShotLineOption(lines, WINDOW)

# Textures: show the fabric at 60 % opacity
image = utility_api.GetPatternSnapShotImageOption(WINDOW)
image.ShowFabricTexture = True
image.FabricTextureOpacity = 60.0
utility_api.SetPatternSnapShotImageOption(image, WINDOW)

# Labels: pattern names, line lengths and grain lines
info = utility_api.GetPatternSnapShotInformationOption(WINDOW)
info.ShowPatternName = True
info.ShowLineLength = True
info.ShowGrainLine = True
utility_api.SetPatternSnapShotInformationOption(info, WINDOW)

print("styled: ", export_api.ExportSnapshot2D(os.path.join(out, "styled.png"), WINDOW)[0])
size = utility_api.GetPatternSnapShotSizeOption(WINDOW)
print("paper now %.0f x %.0f mm at %d dpi" % (size.m_width, size.m_height, size.m_resolution))
Pattern Snapshot Options



Button Pair Options

Level: Medium

Options used: ButtonPairPlacementOption

Build a two-piece placket, then place matching buttons and buttonholes along its edges. ButtonPairPlacementOption sets the start corner, the buttonhole size and its rotation; the same option object drives both explicit positions and even distribution.

import json
import pattern_api
import utility_api
import ApiTypes

print("API Test Scenario - Button Pair Options")

utility_api.NewProject()

# Two placket strips, 120 x 600 mm. Lines of a rectangle follow its points:
# 0 = left edge, 1 = top edge, 2 = right edge, 3 = bottom edge
W, H = 120.0, 600.0
left = pattern_api.CreatePatternWithPoints([(0.0, 0.0, 0), (0.0, H, 0), (W, H, 0), (W, 0.0, 0)])
right = pattern_api.CreatePatternWithPoints([(300.0, 0.0, 0), (300.0, H, 0), (300.0 + W, H, 0), (300.0 + W, 0.0, 0)])
pattern_api.SetPatternPieceName(left, "Button placket")
pattern_api.SetPatternPieceName(right, "Buttonhole placket")
# The two pieces must belong to one sewn garment: sew their bottom edges
pattern_api.AddSeamlinePairGroup(left, 3, right, 3, True, True)


def report(label, result):
    r = json.loads(result)
    print("%-22s %s (%s) created=%s skipped=%s" % (label, r["status"], r["reason"], r.get("createdCount"), r.get("skippedCount")))
    for p in r.get("placements", []):
        print("    at %5.0f mm  button #%d  buttonhole #%d" % (p["distanceAlongEdge"], p["button"]["buttonIndex"], p["buttonhole"]["buttonholeIndex"]))


option = ApiTypes.ButtonPairPlacementOption()
option.edgeOrigin = ApiTypes.ButtonPairEdgeOrigin.BUTTON_PAIR_EDGE_ORIGIN_TOP  # measure from the top corner
option.buttonHeadStyleIndex = -1     # -1 = the current button style
option.buttonholeStyleIndex = -1     # -1 = the current buttonhole style
option.buttonholeWidth = 22.0        # mm (0 = the style's size)
option.buttonholeHeight = 3.0        # mm
option.buttonholeAngleDegree = 90.0  # vertical buttonholes, as on a shirt front
option.buttonAngleDegree = 0.0

# Explicit positions: buttons on the right edge of the button placket, buttonholes on the left
# edge of the other piece, 20 mm in from each edge
report("explicit positions",
       pattern_api.PlaceButtonPairsOnEdge(left, 2, right, 0, 20.0, [60.0, 180.0], option))

# Even spacing for the lower part, now measured from the bottom corner: 5 pairs from 40 mm
# (start margin) up to 280 mm, leaving 320 mm (end margin) for the explicit pairs above.
# A pair that would land on an existing one is reported as "skipped", never duplicated.
option.edgeOrigin = ApiTypes.ButtonPairEdgeOrigin.BUTTON_PAIR_EDGE_ORIGIN_BOTTOM
report("evenly distributed",
       pattern_api.DistributeButtonPairsOnEdge(left, 2, right, 0, 20.0, 5, 40.0, 320.0, option))
utility_api.Refresh3DWindow()
Button Pair Options



Render Options

Level: High

Options used: RenderPropertyOptions, RenderImageVideoOptions

Set up a render without the Render window: RenderPropertyOptions controls the renderer (time limit, noise, quality), RenderImageVideoOptions the picture (custom size, background, file name). Both setters write every field, so the script starts from the current values.

import json
import os
import time
import import_api
import utility_api
import export_api
import ApiTypes

print("API Test Scenario - Render Options")

# NOTE: update these paths for your machine.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"
OUTPUT_DIR = "C:/Users/Public/Documents/CLO/API_Output/"

out = os.path.join(OUTPUT_DIR, "render_options")
os.makedirs(out, exist_ok=True)

utility_api.NewProject()
import_api.ImportAvatar(ASSET_ROOT + "Avatar/Female/FV2.1_Mia.avt", ApiTypes.ImportExportOption())
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())
utility_api.Simulate(60)
utility_api.SetCamViewPoint(1)   # 3/4 right
utility_api.SetCamViewPoint(9)   # then zoom to fit the garment

# ----------------------------------------------------------------------------
# 1) RenderPropertyOptions: how the renderer works
# ----------------------------------------------------------------------------
current = json.loads(utility_api.GetRenderingProperties())
print("renderer before:", {k: current[k] for k in ("MaxRenderTime", "NoiseThreshold", "LightQuality", "MaterialQuality")})

renderer = ApiTypes.RenderPropertyOptions()
renderer.m_RenderEngineType = current["RenderEngineType"]
renderer.m_bCPUSupporting = current["CPUSupporting"]
renderer.m_bLowGPUPriority = False
renderer.m_MaxRenderTime = 1                  # minutes (1-100): stop refining after 1 minute at most
renderer.m_NoiseThreshold = 0.08              # 0.001-0.1: stop earlier once the image is this clean
renderer.m_SeamPuckeringIntensity = current["SeamPuckeringMult"]
renderer.m_LightQuality = current["LightQuality"]
renderer.m_MaterialQuality = current["MaterialQuality"]
renderer.m_bKnitWearFrustumCulling = current["KnitwearMeshOptimizationFrustumCulling"]
renderer.m_bKnitWearLOD = current["KnitwearMeshOptimizationLOD"]
renderer.m_ColorMappingType = current["ColorMappingType"]
renderer.m_BurnValue = current["BurnValue"]
renderer.m_bLinearWorkFlow = current["LinearWorkflow"]
renderer.m_ImageFilterSize = current["ImageFilterSize"]
utility_api.SetRenderingProperties(renderer)
after = json.loads(utility_api.GetRenderingProperties())
print("renderer after: ", {k: after[k] for k in ("MaxRenderTime", "NoiseThreshold", "LightQuality", "MaterialQuality")})

# ----------------------------------------------------------------------------
# 2) RenderImageVideoOptions: what the picture looks like and where it goes
# ----------------------------------------------------------------------------
c = json.loads(utility_api.GetRenderImageVideoProperties())
picture = ApiTypes.RenderImageVideoOptions()
picture.m_Colorway = c["colorway"]
picture.m_ImageVideo = 0                      # 0 = image (not turntable / animation)
picture.m_ViewPoint = c["viewPoint"]
picture.m_ImageCount = 1
picture.m_RotationAngle = c["turntableRotationAngle"]
picture.m_RotationAxis = c["turntableRotationAxis"]
picture.m_RotationAxisX = c["turntableRotationAxisX"]
picture.m_RotationAxisZ = c["turntableRotationAxisZ"]
picture.m_RotationDirection = c["turntableRotationDirection"]
picture.m_TotalDuration = c["turntableTotalDuration"]
picture.m_FrameDuration = c["turntableFrameDuration"]
picture.m_AnimationFPS = c["animationFPS"]
# Size: m_ImageSize 0-11 is a preset (0 = 640x480 ... 4 = 1920x1080, then paper sizes) that
# ignores width/height; 12 = Custom uses m_Width x m_Height in m_Unit at m_Resolution
picture.m_ImageSize = 12
picture.m_Unit = 0                            # 0 = pixels
picture.m_Width = 800.0
picture.m_Height = 1000.0
picture.m_bMaintainRatio = False
picture.m_Resolution = 72
picture.m_Orientation = 0                     # 0 = portrait
# Background: a flat color instead of the default backdrop image
picture.m_ColorHex = "#DDE3EC"
picture.m_Texture = ""
picture.m_bTransparency = False
picture.m_bBackgroundCrop = False
# Output
picture.m_FileName = "dress_render"
picture.m_FilePath = out
picture.m_ImageFormat = 0                     # 0 = PNG
picture.m_VideoFormat = 0
utility_api.SetRenderImageVideoProperties(picture)
c = json.loads(utility_api.GetRenderImageVideoProperties())
print("picture: %dx%d px, background %s, file %s" % (c["width"], c["height"], c["colorHex"], c["fileName"]))

start = time.time()
images = export_api.ExportRenderingImage(os.path.join(out, "dress_render.png"), False, 0)
print("rendered in %.0f s:" % (time.time() - start), images[0][0])
Render Options



DXF Options

Level: Medium

Options used: ExportDxfOption, ImportDxfOption

Round-trip patterns through DXF without dialogs: ExportDxfOption picks the format, unit, rotation and curve handling; ImportDxfOption controls scale, placement and appending.

import os
import import_api
import utility_api
import export_api
import pattern_api
import ApiTypes

print("API Test Scenario - DXF Options")

# NOTE: update these paths for your machine.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"
OUTPUT_DIR = "C:/Users/Public/Documents/CLO/API_Output/"

out = os.path.join(OUTPUT_DIR, "dxf_options")
os.makedirs(out, exist_ok=True)

utility_api.NewProject()
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())

# ----------------------------------------------------------------------------
# 1) ExportDxfOption: two files from the same patterns
# ----------------------------------------------------------------------------
aama = ApiTypes.ExportDxfOption()
aama.m_ExportDXFFormatType = 0        # 0 = DXF-AAMA, 1 = DXF-ASTM, 2 = Gerber (DXF-AAMA)
aama.m_bMetric = True                 # units written in the file: metric (False = English)
aama.m_fScale = 1.0
aama.m_RotateAngle = 0.0
aama.m_ExportBBType = 1               # 1 = one bounding box for all patterns, 2 = one per pattern
aama.m_bOptimizeCurvePoints = True    # drop curve points that lie very close to others
aama.m_bDuplicateNotch = True         # copy sewing-line notches onto the seam allowance
aama.m_bExportWithoutGrading = True   # base size only
aama.m_bExportWithoutBaselines = True # outlines only
aamaPath = export_api.ExportDXF(os.path.join(out, "dress_aama.dxf"), aama)

astm = ApiTypes.ExportDxfOption()
astm.m_ExportDXFFormatType = 1        # ASTM
astm.m_RotateAngle = 90.0             # turn every pattern 90 degrees
astm.m_bCheckedConvertCtoS = True     # curve points -> straight points (for older CAD)
astm.m_bExportWithoutGrading = True
astmPath = export_api.ExportDXF(os.path.join(out, "dress_astm_rotated.dxf"), astm)
for path in (aamaPath, astmPath):
    print("%-26s %7d bytes" % (os.path.basename(path), os.path.getsize(path)))

# ----------------------------------------------------------------------------
# 2) ImportDxfOption: open one file, then add the other next to it
# ----------------------------------------------------------------------------
utility_api.NewProject()
first = ApiTypes.ImportDxfOption()
first.m_bAppend = False               # replace the open project
first.m_bAutoScale = False            # trust the file's unit ...
first.m_Scale = 1.0                   # ... at 1:1
first.m_bAutoDistribute = True        # spread the pieces in the 2D window
first.m_bIncludeNotchs = True
import_api.ImportDXF(aamaPath, first)
print("after AAMA import:           %d patterns" % pattern_api.GetPatternCount())

second = ApiTypes.ImportDxfOption()
second.m_bAppend = True               # add to what is open
second.m_bAutoScale = False
second.m_Scale = 1.0
second.m_Angle = 90.0                 # degrees: turn the pieces back on import
second.m_bAutoDistribute = True
import_api.ImportDXF(astmPath, second)
print("after appending ASTM import: %d patterns" % pattern_api.GetPatternCount())
utility_api.Refresh3DWindow()
DXF Options



Tech Pack Options

Level: Medium

Options used: ExportTechpackOption

Export a tech pack twice with ExportTechpackOption: a light package with only the captures a product page needs, and a full one with project files, textures and every thumbnail.

import os
import time
import import_api
import utility_api
import export_api
import ApiTypes

print("API Test Scenario - Tech Pack Options")

# NOTE: update these paths for your machine.
ASSET_ROOT = "C:/Users/Public/Documents/CLO/Assets/"
OUTPUT_DIR = "C:/Users/Public/Documents/CLO/API_Output/"

utility_api.NewProject()
import_api.ImportAvatar(ASSET_ROOT + "Avatar/Female/FV2.1_Mia.avt", ApiTypes.ImportExportOption())
import_api.ImportZpac(ASSET_ROOT + "Garment/FV2_French Sleeve Mini Dress.zpac", ApiTypes.ImportExportOption())
utility_api.Simulate(60)


def export(name, option):
    folder = os.path.join(OUTPUT_DIR, "techpack_" + name)
    os.makedirs(folder, exist_ok=True)
    start = time.time()
    export_api.ExportTechPack(os.path.join(folder, name + ".json"), option)   # the path must end in .json
    files = sorted(os.listdir(folder))
    size = sum(os.path.getsize(os.path.join(folder, f)) for f in files)
    print("%-6s %3d files, %6.1f MB, %4.1f s" % (name, len(files), size / 1e6, time.time() - start))
    return files


# Light: no project files, no textures, front captures with the avatar and the pattern layout
light = ApiTypes.ExportTechpackOption()
light.m_ExportType = ApiTypes.ExportTechPackType.CLO_API_TECH_PACK
light.m_bSaveZprj = False                          # no .zprj copy
light.m_bSaveZpac = False                          # no .zpac copy
light.m_bExportTextures = False                    # no fabric / graphic textures
light.m_bCaptureSceneThumbnailWithAvatar = True    # 3D captures with the avatar
light.m_bCaptureSceneThumbnailWithoutAvatar = False
light.m_bCaptureSceneThumbnailWithMark = False
light.m_bCaptureItemThumbnail = False              # one image per fabric / trim
light.m_bCaptureHighResolutionFrontThumbnail = False
light.m_bCapturePatternLayoutThumbnail = True      # the 2D pattern layout
light.m_bCapturePatternLayoutThumbnailWithFabricMark = False
light.m_bCapturePatternLayoutThumbnailWithTrimMark = False
light.m_bCapturePatternLayoutThumbnailForCloset2DPatternWindow = False
light.m_bCaptureTurntableThumbnail = False
light.m_bShowModalProgressBar = False              # no blocking progress dialog
lightFiles = export("light", light)

# Full: everything, as the Tech Pack export dialog's defaults
full = ApiTypes.ExportTechpackOption()             # the defaults turn every capture on
full.m_bShowModalProgressBar = False
fullFiles = export("full", full)

print("only in the full package:", [f for f in fullFiles if f not in lightFiles][:8], "...")
Tech Pack Options