tailor(voyager): strapless bandeau lane -- elastic-free zero-g recipe + 23-variant work log

Female voyager bandeau top from the fc-voyager-kilt-gpt refs. Winning
construction (md_final_v16/v23): seamless-loop doctrine -- NO edge elastic
(gathering is what notched the armpit junctions), body-snug cut, zero-g wrap
(SetSimulationGravity(0) BEFORE ResetClothArrangement -- one full-gravity
materialization tick drops the bistable tube into the low basin), stiff 40f
positioning then soft 110f conform, self-measuring retry loop.

Hard-won facts logged in the scripts: strapless garments never survive full
gravity (frictionless skin + stretchy elastic); arrangement x IS azimuth
(Body_Back_Center_1 native x=0, forcing 50/50 sews both panels into a front
pouch); SetArrangementOrientation flips panel facing but mirrors it L/R;
frozen cloth renders cyan; back panel textured face points at the skin --
fix is manual Flip Normal (no API).

Texture: positioned plait/rope/fringe tile spanning the 170mm panel once
(hem anchors at v=0.5 and wraps). manual_fit_backup.zprj preserves Can's
hand-fitted drape. Exports still pending final drape approval.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Can Narin
2026-08-18 22:46:31 +03:00
parent fea96bf582
commit 0fae2b514c
38 changed files with 3410 additions and 0 deletions
@@ -0,0 +1,65 @@
{
"name": "voyager_bandeau_v1",
"_worksheet": {
"block": "custom strapless band (fitted_top numbers, no straps)",
"reference": "docs/conceptart/generated-images/female-clothing/fc-voyager-kilt-gpt-piece-top*.png",
"placement": {"band_top_z": 1.31, "hem_z": 1.14, "tol_m": 0.03},
"body_circ_mm": {"bust_max": 1083.4, "at_top_1.31": 917.4, "at_hem_1.14": 794.4},
"ease_mm": 17,
"panel": "hexagon: bust-width waist at y=123.6, tapered to snug top and hem",
"hold_mechanism": "top edge cut 467 vs 550 needed to clear the bust; elastic cinch mid-settle (strength 60 -- default never reaches target)",
"lines": {"top": 0, "side_r_up": 1, "side_r_low": 2, "hem": 3, "side_l_low": 4, "side_l_up": 5},
"strapless_rationale": "reference has no straps; skill says straps-beat-tubes, but a tube can only slide down by stretching its 467mm top edge over the 550mm bust -- geometry blocks it. If QC shows it slid to the underbust anyway, v2 adds straps."
},
"md": {
"reset": "new_project",
"avatar_fbx": "C:/Users/CAN/tinqs-ltd/animation/tools/tailor/avatar/Lena_QuatSkin_Avatar.fbx",
"avatar_scale": 10.0,
"add_arrangement_points": true,
"auto_translate": true,
"zfab": "C:/Users/Public/Documents/MarvelousDesigner/New Assets/Fabric/(Default for Simulation).zfab",
"texture": "C:/Users/CAN/tinqs-ltd/animation/tools/tailor/textures/voyager_bandeau.png",
"panels": [
{"name": "front", "dx": 0.0,
"note": "strapless band: W 550.2 at bust (circ 1083.4 + ease 17), top 467.2, hem 405.7, H 170",
"points": [[41.5, 170.0], [508.7, 170.0], [550.2, 123.6], [478.0, 0.0], [72.2, 0.0], [0.0, 123.6]]},
{"name": "back", "dx": 800.2,
"note": "identical panel; lines 0 top | 1 sideR-up | 2 sideR-low | 3 hem | 4 sideL-low | 5 sideL-up",
"points": [[41.5, 170.0], [508.7, 170.0], [550.2, 123.6], [478.0, 0.0], [72.2, 0.0], [0.0, 123.6]]}
],
"seams": [
{"a": "front", "a_line": 1, "b": "back", "b_line": 1, "reverse_a": true, "reverse_b": true},
{"a": "front", "a_line": 2, "b": "back", "b_line": 2, "reverse_a": true, "reverse_b": true},
{"a": "front", "a_line": 4, "b": "back", "b_line": 4, "reverse_a": true, "reverse_b": true},
{"a": "front", "a_line": 5, "b": "back", "b_line": 5, "reverse_a": true, "reverse_b": true}
],
"arrangements": [
{"panel": "front", "point": "Body_Front_Center_1", "offset": [50, 55, 50]},
{"panel": "back", "point": "Body_Back_Center_1", "offset": [50, 55, 50]}
],
"sim": {
"strengthen": true,
"settle_frames": 250,
"elastic": [
{"panel": "front", "line": 0, "total_length": 450.0, "strength": 60},
{"panel": "back", "line": 0, "total_length": 450.0, "strength": 60},
{"panel": "front", "line": 3, "total_length": 390.0, "strength": 60},
{"panel": "back", "line": 3, "total_length": 390.0, "strength": 60}
],
"elastic_frames": 80,
"relax_frames": 50
},
"cam_viewpoint": 2,
"presim_snapshot": "C:/Users/CAN/AppData/Local/Temp/claude/C--Users-CAN-tinqs-ltd-docs/3744fd8e-1342-446c-9677-7aafbafc8d18/scratchpad/voyager_bandeau_presim.png",
"snapshot": "C:/Users/CAN/AppData/Local/Temp/claude/C--Users-CAN-tinqs-ltd-docs/3744fd8e-1342-446c-9677-7aafbafc8d18/scratchpad/voyager_bandeau_front.png",
"back_snapshot": "C:/Users/CAN/AppData/Local/Temp/claude/C--Users-CAN-tinqs-ltd-docs/3744fd8e-1342-446c-9677-7aafbafc8d18/scratchpad/voyager_bandeau_back.png",
"export_dir": "C:/Users/CAN/tinqs-ltd/animation/tools/tailor",
"export_basename": "lena_voyager_bandeau_v1",
"exports": []
},
"expect": {
"bands": {
"voyager_bandeau_v1": {"top_m": 1.31, "bottom_m": 1.14, "tol_m": 0.03, "from": "cover"}
}
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

@@ -0,0 +1,133 @@
# FINAL v10: BACK SCOOP -- the construction fix for the back-edge flip.
#
# Every sim-side variant (v8 stiff, B brief-soften, C half-solidify) flips or rolls
# the back panel's top edge, because at 1.31 m the back edge lands ON the baked-in
# bra's back-band ridge: collision pushes it off and it folds. The reference back
# sits LOWER between the shoulder blades anyway. Fix in the outline (never partial
# seams): scoop the back top edge down 32 mm at the centre; the side edges are
# untouched so all four seam pairs still fingerprint equal.
#
# Back top edge becomes 4 segments (lines 0-3); side/hem line indices shift +3.
# No elastic on the scooped back top (it is below the ridge and shorter by cut);
# front top + both hems keep the v8 elastic. Sim recipe = v8 (zero-g, stiff 60,
# soften front only, 90).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS_F = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
# back: same corners, top edge scooped 24/32/24 mm at 35%/50%/65% of the top span
PTS_B = [(41.5, 170.0), (181.5, 146.0), (275.1, 138.0), (368.7, 146.0),
(508.7, 170.0), (550.2, 123.6), (478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
# front lines: 0 top | 1 sideR_up | 2 sideR_low | 3 hem | 4 sideL_low | 5 sideL_up
# back lines: 0-3 scooped top | 4 sideR_up | 5 sideR_low | 6 hem | 7 sideL_low | 8 sideL_up
SEAMS = [(1, 4), (2, 5), (4, 7), (5, 8)]
def span():
path = os.path.join(SCRATCH, "_probe10.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS_F])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS_B])
for lf, lb in SEAMS:
pattern_api.AddSeamlinePairGroup(f, lf, b, lb, True, True)
# fingerprint the pairs -- unequal seam edges are a construction bug
_bad = []
results["seam_lengths"] = []
for lf, lb in SEAMS:
fa = pattern_api.GetLineLength(f, lf)
fb = pattern_api.GetLineLength(b, lb)
results["seam_lengths"].append((lf, round(fa, 3), lb, round(fb, 3)))
if abs(fa - fb) > 0.1:
_bad.append("f.%d=%.3f vs b.%d=%.3f" % (lf, fa, lb, fb))
if _bad:
raise RuntimeError("seam length mismatch: " + "; ".join(_bad))
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 68, 50)
# elastic: front top (line 0) + both hems (front 3, back 6); scooped back top bare
pattern_api.SetPatternPieceElastic(f, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(f, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(f, 0, 60.0)
pattern_api.SetPatternPieceElastic(f, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(f, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(f, 3, 60.0)
pattern_api.SetPatternPieceElastic(b, 6, True)
pattern_api.SetPatternPieceElasticTotalLength(b, 6, 390.0)
pattern_api.SetPatternPieceElasticStrength(b, 6, 60.0)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60) # stiff positioning
pattern_api.SetPatternStrengthen(f, False) # soften only the front
utility_api.Simulate(90)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final10_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final10_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final10_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,139 @@
# FINAL v10: BACK SCOOP -- the construction fix for the back-edge flip.
#
# Every sim-side variant (v8 stiff, B brief-soften, C half-solidify) flips or rolls
# the back panel's top edge, because at 1.31 m the back edge lands ON the baked-in
# bra's back-band ridge: collision pushes it off and it folds. The reference back
# sits LOWER between the shoulder blades anyway. Fix in the outline (never partial
# seams): scoop the back top edge down 32 mm at the centre; the side edges are
# untouched so all four seam pairs still fingerprint equal.
#
# Back top edge becomes 4 segments (lines 0-3); side/hem line indices shift +3.
# No elastic on the scooped back top (it is below the ridge and shorter by cut);
# front top + both hems keep the v8 elastic. Sim recipe = v8 (zero-g, stiff 60,
# soften front only, 90).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS_F = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
# back: same corners, top edge scooped 24/32/24 mm at 35%/50%/65% of the top span
PTS_B = [(41.5, 170.0), (181.5, 146.0), (275.1, 138.0), (368.7, 146.0),
(508.7, 170.0), (550.2, 123.6), (478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
# front lines: 0 top | 1 sideR_up | 2 sideR_low | 3 hem | 4 sideL_low | 5 sideL_up
# back lines: 0-3 scooped top | 4 sideR_up | 5 sideR_low | 6 hem | 7 sideL_low | 8 sideL_up
SEAMS = [(1, 4), (2, 5), (4, 7), (5, 8)]
def span():
path = os.path.join(SCRATCH, "_probe11.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS_F])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS_B])
for lf, lb in SEAMS:
pattern_api.AddSeamlinePairGroup(f, lf, b, lb, True, True)
# fingerprint the pairs -- unequal seam edges are a construction bug
_bad = []
results["seam_lengths"] = []
for lf, lb in SEAMS:
fa = pattern_api.GetLineLength(f, lf)
fb = pattern_api.GetLineLength(b, lb)
results["seam_lengths"].append((lf, round(fa, 3), lb, round(fb, 3)))
if abs(fa - fb) > 0.1:
_bad.append("f.%d=%.3f vs b.%d=%.3f" % (lf, fa, lb, fb))
if _bad:
raise RuntimeError("seam length mismatch: " + "; ".join(_bad))
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 78, 50)
pattern_api.SetArrangementPosition(b, 0, 78, 50)
# elastic: front top (line 0) + both hems (front 3, back 6); scooped back top bare
pattern_api.SetPatternPieceElastic(f, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(f, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(f, 0, 60.0)
pattern_api.SetPatternPieceElastic(f, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(f, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(f, 3, 60.0)
# v11: the scooped back top needs its own anchor -- v10 (bare scoop) slid 10 cm.
# Segment cut lengths 142/93.9/93.9/142; cinch to ~0.93.
for _ln, _tl in ((0, 132.0), (1, 87.0), (2, 87.0), (3, 132.0)):
pattern_api.SetPatternPieceElastic(b, _ln, True)
pattern_api.SetPatternPieceElasticTotalLength(b, _ln, _tl)
pattern_api.SetPatternPieceElasticStrength(b, _ln, 60.0)
pattern_api.SetPatternPieceElastic(b, 6, True)
pattern_api.SetPatternPieceElasticTotalLength(b, 6, 390.0)
pattern_api.SetPatternPieceElasticStrength(b, 6, 60.0)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60) # stiff positioning
pattern_api.SetPatternStrengthen(f, False) # soften only the front
utility_api.Simulate(90)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final11_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final11_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final11_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,95 @@
# FINAL v13: TWO FABRICS -- back panel on Tyvek (board-stiff MATERIAL), front on
# the default sim fabric. Every strengthen-based recipe folds the back at the bra
# ridge because SetPatternStrengthen is a solver constraint, not bending stiffness;
# the crease forms during the phase-1 seam pull and survives. Tyvek resists the
# crease itself. Back is NOT strengthened (material does the work), front keeps
# the proven stiff-then-soft phases. Ends UNFROZEN (texture visible; don't press play).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB_SOFT = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
ZFAB_STIFF = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\V2_Non-Fabric_Tyvek_1.zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab_soft = fabric_api.AddFabric(ZFAB_SOFT)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab_soft)
fab_stiff = fabric_api.AddFabric(ZFAB_STIFF)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab_stiff)
pattern_api.SetPatternPieceFabricIndex(f, fab_soft)
pattern_api.SetPatternPieceFabricIndex(b, fab_stiff)
for p in (f, b):
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 78, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
pattern_api.SetPatternStrengthen(f, True) # front: proven stiff-then-soft
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60)
pattern_api.SetPatternStrengthen(f, False)
utility_api.Simulate(90)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
path = os.path.join(SCRATCH, "_probe13.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
results["final"] = {"bottom_m": round(zmin * 0.001, 4), "top_m": round(zmax * 0.001, 4),
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final13_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final13_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final13_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
# UNFROZEN on purpose: Can inspects with texture; nothing simulates unless play is pressed
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab_soft,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,97 @@
# FINAL v13: TWO FABRICS -- back panel on Tyvek (board-stiff MATERIAL), front on
# the default sim fabric. Every strengthen-based recipe folds the back at the bra
# ridge because SetPatternStrengthen is a solver constraint, not bending stiffness;
# the crease forms during the phase-1 seam pull and survives. Tyvek resists the
# crease itself. Back is NOT strengthened (material does the work), front keeps
# the proven stiff-then-soft phases. Ends UNFROZEN (texture visible; don't press play).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB_SOFT = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
ZFAB_STIFF = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\V2_Non-Fabric_Tyvek_1.zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab_soft = fabric_api.AddFabric(ZFAB_SOFT)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab_soft)
fab_stiff = fabric_api.AddFabric(ZFAB_STIFF)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab_stiff)
pattern_api.SetPatternPieceFabricIndex(f, fab_soft)
pattern_api.SetPatternPieceFabricIndex(b, fab_stiff)
pattern_api.SetAddlThicknessCollision(f, 2.0)
pattern_api.SetAddlThicknessCollision(b, 8.0) # glide over the bra ridge during the wrap
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 78, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
pattern_api.SetPatternStrengthen(f, True) # front: proven stiff-then-soft
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60)
pattern_api.SetPatternStrengthen(f, False)
utility_api.Simulate(60)
pattern_api.SetAddlThicknessCollision(b, 3.0) # now settle against the skin
utility_api.Simulate(40)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
path = os.path.join(SCRATCH, "_probe14.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
results["final"] = {"bottom_m": round(zmin * 0.001, 4), "top_m": round(zmax * 0.001, 4),
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final14_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final14_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final14_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
# UNFROZEN on purpose: Can inspects with texture; nothing simulates unless play is pressed
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab_soft,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,102 @@
# FINAL v15: match the reference's construction truth -- a SEAMLESS RIGID LOOP.
#
# Reference re-read (fc-voyager-kilt-gpt-piece-top-front.png): continuous band,
# NO visible side seams, NO gathering, level edges, standing slightly proud of the
# body like stiff basketry. Every "connection point" artifact at the armpits came
# from the elastic: two independent edge-drawstrings meeting at a seam corner
# always notch and curl there.
#
# So: NO elastic anywhere. The cut is already body-snug (top 934 mm circ vs 917 mm
# body), and in zero-g nothing needs holding up. Seam tension + collision form the
# smooth proud oval. Two conform styles swept in one call:
# A_soft everything soft, zero-g 150 f
# B_stiff stiff 40 f (clean positioning) -> soften both, 110 f
# Judge: underarm junction on the side turntables + spans. Ends UNFROZEN.
#
# (Known cosmetic issue left alone: the back panel's textured face points at the
# skin -- no API can flip it; right-click the back panel in 3D > Flip Normal, or
# let the Blender pass recalculate normals.)
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def build():
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0) # proud of the skin, like the ref
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 78, 50)
return f, b
def measure_and_shoot(tag):
utility_api.Refresh3DWindow()
out = {}
path = os.path.join(SCRATCH, "_probe15.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
out["bottom_m"], out["top_m"] = round(zmin * 0.001, 4), round(zmax * 0.001, 4)
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v15_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
return out
results = {}
utility_api.SetSimulationGravity(0.0)
try:
# A_soft: everything soft from frame 0, weightless
f, b = build()
utility_api.ResetClothArrangement()
utility_api.Simulate(150)
results["A_soft"] = measure_and_shoot("A_soft")
# B_stiff: stiff positioning, then both soften and conform
f, b = build()
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
results["B_stiff"] = measure_and_shoot("B_stiff")
finally:
utility_api.SetSimulationGravity(-9800.0)
# scene ends holding B_stiff, UNFROZEN (do not press play)
result = results
@@ -0,0 +1,83 @@
# FINAL v16: v15's clean no-elastic seamless-loop recipe + height calibration.
# v15 proved the reference-matching construction (no elastic, no gathering, smooth
# proud band, clean underarm junctions) but the tube slides ~0.11 m down the torso
# taper during the weightless conform. Calibrate arrangement height: two cells,
# keep the closer one in scene. Ends UNFROZEN (do not press play).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def build(front_y, back_y):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, front_y, 50)
pattern_api.SetArrangementPosition(b, 0, back_y, 50)
return f, b
def measure_and_shoot(tag):
utility_api.Refresh3DWindow()
out = {}
path = os.path.join(SCRATCH, "_probe16.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
out["bottom_m"], out["top_m"] = round(zmin * 0.001, 4), round(zmax * 0.001, 4)
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v16_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
return out
results = {}
utility_api.SetSimulationGravity(0.0)
try:
for tag, fy, by in (("H74", 74, 82),): # calibrated winner -- leave in scene
f, b = build(fy, by)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
results[tag] = measure_and_shoot(tag)
finally:
utility_api.SetSimulationGravity(-9800.0)
# scene ends holding H88, UNFROZEN (do not press play)
result = results
@@ -0,0 +1,98 @@
# FINAL v17: v16's elastic-free recipe + the BACK PANEL BUILT WITH REVERSED
# 2D WINDING -- Can's diagnosis: "backside tailored backwards". MD orients a
# draped panel's front face from its 2D winding, so the back panel (which the
# Body_Back arrangement turns 180 degrees toward the body) ended up textured-face-in.
# Reversing its point order flips the face outward at build time; no flip API needed.
#
# Seam mapping with reversed back winding (same physical edges, new indices):
# front 1 sideR_up <-> back 4 sideR_up front 2 sideR_low <-> back 3 sideR_low
# front 4 sideL_low <-> back 1 sideL_low front 5 sideL_up <-> back 0 sideL_up
# Flags: front keeps its proven reversal (True); the back's natural traversal now
# matches what (True,True) used to produce, so back flag is False -> (True, False).
# A wrong flag shows as a twist -- judge from the snapshots.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS_F = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
PTS_B = list(reversed(PTS_F))
PTS_B = PTS_B[-1:] + PTS_B[:-1] # start at (41.5,170) like the front
# back lines: 0 sideL_up | 1 sideL_low | 2 hem | 3 sideR_low | 4 sideR_up | 5 top
SEAMS = [(1, 4), (2, 3), (4, 1), (5, 0)]
results = {"pts_back": PTS_B}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS_F])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS_B])
for lf, lb in SEAMS:
pattern_api.AddSeamlinePairGroup(f, lf, b, lb, True, False)
_bad = []
results["seam_lengths"] = []
for lf, lb in SEAMS:
fa = pattern_api.GetLineLength(f, lf)
fb = pattern_api.GetLineLength(b, lb)
results["seam_lengths"].append((lf, round(fa, 3), lb, round(fb, 3)))
if abs(fa - fb) > 0.1:
_bad.append("f.%d=%.3f vs b.%d=%.3f" % (lf, fa, lb, fb))
if _bad:
raise RuntimeError("seam length mismatch: " + "; ".join(_bad))
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 74, 50)
pattern_api.SetArrangementPosition(b, 0, 82, 50)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
path = os.path.join(SCRATCH, "_probe17.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
results["final"] = {"bottom_m": round(zmin * 0.001, 4), "top_m": round(zmax * 0.001, 4),
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v17_%s.png" % view)
shutil.copyfile(_tt[i], dst)
results[view] = dst
# UNFROZEN; do not press play
result = results
@@ -0,0 +1,83 @@
# FINAL v18: reversed-winding back panel (textured face OUT -- proven by v17's
# bright back) + seam-flag sweep. v17's (True, False) crossed the back into an X.
# MD's reverse flags do not behave like simple traversal reversal (the tooling
# table shows (True,True) != (False,False) on identical panels), so sweep the
# remaining combos empirically: (False, True), (False, False), (True, True).
# Recipe otherwise = v16 winner: no elastic, zero-g, stiff 40 + soft 110, y 74/82.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS_F = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
_r = list(reversed(PTS_F))
PTS_B = _r[-1:] + _r[:-1]
SEAMS = [(1, 4), (2, 3), (4, 1), (5, 0)]
def run_cell(tag, rev_a, rev_b):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS_F])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS_B])
for lf, lb in SEAMS:
pattern_api.AddSeamlinePairGroup(f, lf, b, lb, rev_a, rev_b)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 74, 50)
pattern_api.SetArrangementPosition(b, 0, 82, 50)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
out = {}
path = os.path.join(SCRATCH, "_probe18.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
out["bottom_m"], out["top_m"] = round(zmin * 0.001, 4), round(zmax * 0.001, 4)
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v18_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
return out
results = {}
results["FT"] = run_cell("FT", False, True)
results["FF"] = run_cell("FF", False, False)
results["TT"] = run_cell("TT", True, True)
# scene ends holding TT, UNFROZEN (do not press play)
result = results
@@ -0,0 +1,89 @@
# FINAL v19: v16's proven clean construction (identical winding, (True,True) seams,
# no elastic) + SetArrangementOrientation on the BACK panel to face its textured
# side OUTWARD. v17/v18 proved reversed winding twists with every flag combo; the
# facing must come from the ARRANGEMENT, not the pattern. Introspect the setter's
# doc, flip the back panel's orientation (180 -> 0), verify with a presim turntable
# before simulating.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
results = {"orient_doc": pattern_api.SetArrangementOrientation.__doc__}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 74, 50)
pattern_api.SetArrangementPosition(b, 0, 82, 50)
# flip the back panel's facing at the arrangement level (front keeps its default 180)
try:
pattern_api.SetArrangementOrientation(b, 0)
results["orient_set"] = True
except Exception as exc:
results["orient_set"] = "failed: %r" % (exc,)
utility_api.ResetClothArrangement()
utility_api.Refresh3DWindow()
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
dst = os.path.join(SCRATCH, "v19_presim_back.png")
shutil.copyfile(_tt[2], dst)
results["presim_back"] = dst
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
path = os.path.join(SCRATCH, "_probe19.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
results["final"] = {"bottom_m": round(zmin * 0.001, 4), "top_m": round(zmax * 0.001, 4)}
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v19_%s.png" % view)
shutil.copyfile(_tt[i], dst)
results[view] = dst
# UNFROZEN; do not press play
result = results
@@ -0,0 +1,131 @@
# FINAL v2: pure zero-g wrap, no gravity pass at all.
#
# md_final_zerog.py showed even 45 frames at 0.1 g slides the band 16 cm. But the
# reference garment is stiff plaited basketry -- it should NOT have gravity folds;
# the elastic cinch wrinkles are the right surface detail. So: wrap weightless at
# the correct height, freeze, done. offset-y mapping measured: 55 -> mid 1.172,
# 70 -> mid 1.269, 85 -> mid 1.326; target mid 1.225 -> y ~62.
import os
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
TARGET_MID = (1.31 + 1.14) / 2.0
def build(arr_y):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, arr_y, 50)
pattern_api.SetArrangementPosition(b, 50, arr_y, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 430.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 380.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
return f, b
def span():
path = os.path.join(SCRATCH, "_probe4.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {"cells": {}}
best = None
utility_api.SetSimulationGravity(0.0)
try:
for arr_y in (60, 62, 65):
f, b = build(arr_y)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(80)
# brief soften so the plait relaxes against the skin (still weightless)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(30)
lo, hi = span()
mid = round((lo + hi) / 2.0, 4)
results["cells"][arr_y] = {"bottom_m": lo, "top_m": hi, "mid_m": mid}
if best is None or abs(mid - TARGET_MID) < abs(best[1] - TARGET_MID):
best = (arr_y, mid, f, b, lo, hi)
if abs(mid - TARGET_MID) <= 0.015:
break # good enough; keep this scene
finally:
utility_api.SetSimulationGravity(-9800.0) # restore; NEVER simulate after
# If the loop ended on a non-best cell, rebuild the best one (weightless again).
arr_y, mid, f, b, lo, hi = best
if results["cells"].get(arr_y) is None or list(results["cells"])[-1] != arr_y:
utility_api.SetSimulationGravity(0.0)
try:
f, b = build(arr_y)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(80)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(30)
lo, hi = span()
finally:
utility_api.SetSimulationGravity(-9800.0)
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
utility_api.Refresh3DWindow()
results["final"] = {"arr_y": arr_y, "bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final2_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
import shutil
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final2_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final2_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
result = results
@@ -0,0 +1,85 @@
# FINAL v20: back panel = arrangement orientation 0 (bright face out, proven v19)
# + horizontally MIRRORED 2D pattern to compensate the L/R swap that the vertical-
# axis orientation spin introduces (v19: tube tilted, front panel pulled up to the
# collarbone). Mirrored coords need REMAPPED seam indices (tooling 3.3); the table
# says mirrored+remapped takes (False, False) -- sweep (False,False) and (True,True).
#
# back (mirrored) lines: 0 top | 1 sideL_up | 2 sideL_low | 3 hem | 4 sideR_low | 5 sideR_up
# pairs: front1(sideR_up)<->back5, front2(sideR_low)<->back4,
# front4(sideL_low)<->back2, front5(sideL_up)<->back1
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS_F = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
PTS_B = [(550.2 - x, y) for (x, y) in PTS_F] # horizontal mirror, same order
SEAMS = [(1, 5), (2, 4), (4, 2), (5, 1)]
def run_cell(tag, rev):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS_F])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS_B])
for lf, lb in SEAMS:
pattern_api.AddSeamlinePairGroup(f, lf, b, lb, rev, rev)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 74, 50)
pattern_api.SetArrangementPosition(b, 0, 82, 50)
pattern_api.SetArrangementOrientation(b, 0) # bright face out (proven v19)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
out = {}
path = os.path.join(SCRATCH, "_probe20.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
out["bottom_m"], out["top_m"] = round(zmin * 0.001, 4), round(zmax * 0.001, 4)
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v20_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
return out
results = {}
results["FF"] = run_cell("FF", False)
results["TT"] = run_cell("TT", True)
# scene ends holding TT, UNFROZEN (do not press play)
result = results
@@ -0,0 +1,80 @@
# FINAL v21: v20-FF construction (mirrored back + orientation 0 + remapped seams,
# (False,False)) -- back is PERFECT. Only defect: the tube tilts front-high (~8 cm,
# front panel at the collarbone). Calibrate the FRONT arrangement y down; back stays 82.
# Two cells, winner stays in scene UNFROZEN.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS_F = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
PTS_B = [(550.2 - x, y) for (x, y) in PTS_F]
SEAMS = [(1, 5), (2, 4), (4, 2), (5, 1)]
def run_cell(tag, front_y):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS_F])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS_B])
for lf, lb in SEAMS:
pattern_api.AddSeamlinePairGroup(f, lf, b, lb, False, False)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, front_y, 50)
pattern_api.SetArrangementPosition(b, 0, 82, 50)
pattern_api.SetArrangementOrientation(b, 0)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
out = {}
path = os.path.join(SCRATCH, "_probe21.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
out["bottom_m"], out["top_m"] = round(zmin * 0.001, 4), round(zmax * 0.001, 4)
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v21_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
return out
results = {}
results["F70"] = run_cell("F70", 70)
results["F72"] = run_cell("F72", 72)
# scene ends holding F64, UNFROZEN (do not press play)
result = results
@@ -0,0 +1,83 @@
# FINAL v22: the front-height system is BISTABLE (bust deflects the tube over or
# under; y=70 slips low, y=72 catches high -- no stable middle by start height).
# Fix: start from the HIGH catch (y=72) and bias it down with a FEW PERCENT of
# gravity at the end -- the bust ledge stops it exactly where a real bandeau sits.
# Cells: bias -300 and -600 mm/s^2 (3% / 6% g), 50 frames each. Winner stays.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS_F = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
PTS_B = [(550.2 - x, y) for (x, y) in PTS_F]
SEAMS = [(1, 5), (2, 4), (4, 2), (5, 1)]
def run_cell(tag, bias):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS_F])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS_B])
for lf, lb in SEAMS:
pattern_api.AddSeamlinePairGroup(f, lf, b, lb, False, False)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 72, 50)
pattern_api.SetArrangementPosition(b, 0, 82, 50)
pattern_api.SetArrangementOrientation(b, 0)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(70) # conform at the high catch
utility_api.SetSimulationGravity(bias) # gentle downward bias
utility_api.Simulate(50)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
out = {}
path = os.path.join(SCRATCH, "_probe22.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
out["bottom_m"], out["top_m"] = round(zmin * 0.001, 4), round(zmax * 0.001, 4)
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v22_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
return out
results = {}
results["g300"] = run_cell("g300", -300.0)
results["g600"] = run_cell("g600", -600.0)
# scene ends holding g600, UNFROZEN (do not press play)
result = results
@@ -0,0 +1,81 @@
# FINAL v23: v16-H74 recipe + SELF-MEASURING RETRY LOOP. The elastic-free tube's
# final height is BISTABLE and lands non-deterministically run-to-run (same script
# gave 1.08-1.32 twice, then 1.01-1.19). Rebuild until the garment top lands in
# [1.27, 1.35] m, max 6 attempts, keep the good drape in scene UNFROZEN.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
results = {"attempts": []}
ok = False
for attempt in range(6):
# CRITICAL: zero gravity BEFORE the scene is built. ResetClothArrangement
# materializes cloth with a solver tick; under full gravity that one tick
# biases the bistable tube into the LOW basin (the cross-session mystery).
utility_api.SetSimulationGravity(0.0)
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 3.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 74, 50)
pattern_api.SetArrangementPosition(b, 0, 82, 50)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement() # gravity already zero (see above)
try:
utility_api.Simulate(40)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(110)
finally:
utility_api.SetSimulationGravity(-9800.0)
path = os.path.join(SCRATCH, "_probe23.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
span = (round(zmin * 0.001, 4), round(zmax * 0.001, 4))
results["attempts"].append(span)
if 1.27 <= span[1] <= 1.35:
ok = True
break
results["accepted"] = ok
utility_api.Refresh3DWindow()
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "v23_%s.png" % view)
shutil.copyfile(_tt[i], dst)
results[view] = dst
# UNFROZEN; do not press play. Back panel face: right-click > Flip Normal (manual).
result = results
@@ -0,0 +1,102 @@
# FINAL v3: stiff-only zero-g wrap.
#
# v2 defects and their causes:
# - cyan/white band in renders = MD's FROZEN display tint -> snapshot BEFORE freezing
# - crumpled top edge + back panel ballooning off the back = the 30-frame soften:
# weightless soft cloth has nothing pressing its middle to the body. The garment
# is stiff plaited basketry -- stay strengthened for the WHOLE wrap.
# - 2 mm collision stand-off for a clean skin offset.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def span():
path = os.path.join(SCRATCH, "_probe5.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 65, 50)
pattern_api.SetArrangementPosition(b, 50, 65, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 430.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 380.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
pattern_api.SetPatternStrengthen(p, True) # stiff the WHOLE wrap
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(140) # weightless wrap + cinch
finally:
utility_api.SetSimulationGravity(-9800.0) # restore; never simulate after
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
# snapshots BEFORE freezing (frozen cloth renders cyan)
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final3_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final3_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final3_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,101 @@
# FINAL v4: SOFT zero-g wrap -- the winning recipe.
#
# probe_1_zerog.png (soft + elastic, weightless, 60f) already read like the
# reference: plait + rope bands + fringe all visible, band conformed to the chest.
# The stiff wrap (v3) tents off the bust and juts at the sides; the soften-after-
# stiff (v2) crumples. Soft-from-frame-0 is SAFE here because the bunching rule
# exists for gravity, and there is none. 150 frames for a tighter cinch, y=68 to
# center the band on target (measured map: 55->1.172, 65->1.218, 70->1.269).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def span():
path = os.path.join(SCRATCH, "_probe6.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 50, 68, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 430.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 380.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
# NO strengthen: soft conforms; safe under zero-g (bunching needs gravity)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(150)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final4_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final4_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final4_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,104 @@
# FINAL v5: soft zero-g wrap, elastic targets = body circumference (NOT smaller).
# v4 taught: an over-tight ring (430 vs 459 body) cannot grip in zero-g frictionless
# contact -- it is EXPELLED off the torso like a squeezed seed and floats as a pouch
# in front of the chest. Snug-not-tight keeps the ring seated where it is placed.
#
# probe_1_zerog.png (soft + elastic, weightless, 60f) already read like the
# reference: plait + rope bands + fringe all visible, band conformed to the chest.
# The stiff wrap (v3) tents off the bust and juts at the sides; the soften-after-
# stiff (v2) crumples. Soft-from-frame-0 is SAFE here because the bunching rule
# exists for gravity, and there is none. 150 frames for a tighter cinch, y=68 to
# center the band on target (measured map: 55->1.172, 65->1.218, 70->1.269).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def span():
path = os.path.join(SCRATCH, "_probe6.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 50, 68, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 465.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 402.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
# NO strengthen: soft conforms; safe under zero-g (bunching needs gravity)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(120)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final5_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final5_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final5_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,104 @@
# FINAL v5: soft zero-g wrap, elastic targets = body circumference (NOT smaller).
# v4 taught: an over-tight ring (430 vs 459 body) cannot grip in zero-g frictionless
# contact -- it is EXPELLED off the torso like a squeezed seed and floats as a pouch
# in front of the chest. Snug-not-tight keeps the ring seated where it is placed.
#
# probe_1_zerog.png (soft + elastic, weightless, 60f) already read like the
# reference: plait + rope bands + fringe all visible, band conformed to the chest.
# The stiff wrap (v3) tents off the bust and juts at the sides; the soften-after-
# stiff (v2) crumples. Soft-from-frame-0 is SAFE here because the bunching rule
# exists for gravity, and there is none. 150 frames for a tighter cinch, y=68 to
# center the band on target (measured map: 55->1.172, 65->1.218, 70->1.269).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def span():
path = os.path.join(SCRATCH, "_probe6.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 68, 50) # x IS azimuth: back panel at its native x=0, NOT 50
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 465.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 402.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
# NO strengthen: soft conforms; safe under zero-g (bunching needs gravity)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(120)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final6_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final6_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final6_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,102 @@
# FINAL v7: correct azimuth (back x=0) + two-phase weightless wrap.
# v6 made a true tube (front reads like the reference) but the back panel kept a
# fold-over and a drooping corner from being soft during the whole wrap. Phase it:
# 1. STIFF 60 f zero-g -- panels take position cleanly, no corner folds
# 2. soften + 90 f zero-g -- cloth conforms to the skin (no gravity, no slide)
# Cinch slightly tighter (450/390 @ 60): safe now that the ring encircles the body.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def span():
path = os.path.join(SCRATCH, "_probe7.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 68, 50) # x IS azimuth; back native x=0
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
pattern_api.SetPatternStrengthen(p, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60) # phase 1: stiff positioning
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(90) # phase 2: soft conform, weightless
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final7_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final7_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final7_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,101 @@
# FINAL v7: correct azimuth (back x=0) + two-phase weightless wrap.
# v6 made a true tube (front reads like the reference) but the back panel kept a
# fold-over and a drooping corner from being soft during the whole wrap. Phase it:
# 1. STIFF 60 f zero-g -- panels take position cleanly, no corner folds
# 2. soften + 90 f zero-g -- cloth conforms to the skin (no gravity, no slide)
# Cinch slightly tighter (450/390 @ 60): safe now that the ring encircles the body.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def span():
path = os.path.join(SCRATCH, "_probe7.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 68, 50) # x IS azimuth; back native x=0
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
pattern_api.SetPatternStrengthen(p, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60) # phase 1: stiff positioning
pattern_api.SetPatternStrengthen(f, False) # soften ONLY the front (v7: the
utility_api.Simulate(90) # soft back rolled its top edge)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final8_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final8_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final8_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,100 @@
# FINAL v7: correct azimuth (back x=0) + two-phase weightless wrap.
# v6 made a true tube (front reads like the reference) but the back panel kept a
# fold-over and a drooping corner from being soft during the whole wrap. Phase it:
# 1. STIFF 60 f zero-g -- panels take position cleanly, no corner folds
# 2. soften + 90 f zero-g -- cloth conforms to the skin (no gravity, no slide)
# Cinch slightly tighter (450/390 @ 60): safe now that the ring encircles the body.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def span():
path = os.path.join(SCRATCH, "_probe7.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {}
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 68, 50) # x IS azimuth; back native x=0
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
pattern_api.SetPatternStrengthen(p, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(150) # ALL-stiff wrap: basketry stays basketry
# (v3 tenting was the wrong azimuth, not stiffness)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final9_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
_tt = export_api.ExportTurntableImages(4)
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final9_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final9_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r" % (exc,)
TINQS_GARMENT = {"garment": "voyager_bandeau_v1", "fabric": fab,
"patterns": {"front": f, "back": b}}
result = results
@@ -0,0 +1,137 @@
# FINAL RECIPE: shaped-in-place strapless bandeau (no full-gravity exposure).
#
# Probe findings (md_probe_slide.py): MD cloth-skin contact is ~frictionless and
# elastic edges stretch under load, so NO strapless band survives -9800 gravity on
# this body -- it rides over the bust by stretching (30 frames: chest -> hips).
# Even 0.1 g slides it. Straps are the physical fix but the reference has none, and
# the deliverable is a STATIC export for the clothing pipeline -- so the drape is a
# shaping tool, not a stability proof:
#
# 1. sweep arrangement offset-y under a zero-g wrap to land the band on target
# (y=55 landed 1.078-1.287; target 1.14-1.31 -> need ~+4 cm)
# 2. rebuild at the best y: stiff zero-g wrap 80f -> soften, 0.1 g for 45f (folds
# form, slide ~2-4 cm only) -> restore full gravity WITHOUT simulating
# 3. freeze the patterns (best effort) so a stray UI play won't drop it
#
# Numbers target: band top 1.31 m, hem 1.14 m, tol 0.03.
import os
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
TARGET_MID = (1.31 + 1.14) / 2.0
def build(arr_y):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, arr_y, 50)
pattern_api.SetArrangementPosition(b, 50, arr_y, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 430.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 380.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
return f, b
def span():
path = os.path.join(SCRATCH, "_probe3.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
results = {"sweep": {}}
# --- part 1: offset-y sweep under a pure zero-g wrap ------------------------
utility_api.SetSimulationGravity(0.0)
try:
for arr_y in (55, 70, 85):
f, b = build(arr_y)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(60)
lo, hi = span()
results["sweep"][arr_y] = {"bottom_m": lo, "top_m": hi,
"mid_m": round((lo + hi) / 2.0, 4)}
finally:
utility_api.SetSimulationGravity(-9800.0)
best_y = min(results["sweep"],
key=lambda k: abs(results["sweep"][k]["mid_m"] - TARGET_MID))
results["best_y"] = best_y
# --- part 2: final shape at best_y ------------------------------------------
f, b = build(best_y)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(80) # weightless wrap + cinch, stiff
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.SetSimulationGravity(-980.0) # 0.1 g: folds without the slide
utility_api.Simulate(45)
finally:
utility_api.SetSimulationGravity(-9800.0) # restore; do NOT simulate again
# best-effort freeze so a stray UI play won't drop the band
try:
for p in (f, b):
pattern_api.SetPatternFreeze(p, True)
results["frozen"] = True
except Exception as exc:
results["frozen"] = "failed: %r (doc: %s)" % (exc, pattern_api.SetPatternFreeze.__doc__)
utility_api.Refresh3DWindow()
lo, hi = span()
results["final"] = {"bottom_m": lo, "top_m": hi,
"target": {"bottom_m": 1.14, "top_m": 1.31, "tol_m": 0.03}}
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "final_front.png")
export_api.ExportSnapshot3D(snap)
results["front_snap"] = snap
import shutil
_tt = export_api.ExportTurntableImages(4) # 0 front, 1 side, 2 BACK, 3 side
if _tt and len(_tt) > 2:
back = os.path.join(SCRATCH, "final_back.png")
shutil.copyfile(_tt[2], back)
results["back_snap"] = back
side = os.path.join(SCRATCH, "final_side.png")
shutil.copyfile(_tt[1], side)
results["side_snap"] = side
result = results
@@ -0,0 +1,30 @@
# 1. Backup Can's manual state (zprj) BEFORE any change.
# 2. FlipPatternPiece on the back panel -- its reverse face points outward, which
# MD renders dull/dark ("under a shadow"). Introspect the doc first.
# 3. Snapshot to verify. NO sim, NO rebuild.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
BACKUP = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\work\voyager_bandeau_v1\manual_fit_backup.zprj"
results = {}
export_api.ExportZPrj(BACKUP)
results["backup"] = BACKUP
results["flip_doc"] = pattern_api.FlipPatternPiece.__doc__
try:
pattern_api.FlipPatternPiece(1) # back panel
results["flipped"] = True
except Exception as exc:
results["flipped"] = "failed: %r" % (exc,)
utility_api.Refresh3DWindow()
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "flip_%s.png" % view)
shutil.copyfile(_tt[i], dst)
results[view] = dst
result = results
@@ -0,0 +1,45 @@
# INSPECT + MINIMAL FIX after Can's MANUAL fitting pass. His drape is sacred:
# NO NewProject, NO Simulate, NO arrangement calls, NO freeze.
# 1. snapshot the CURRENT state (front + full turntable) before touching anything
# 2. report patterns + their fabric indices
# 3. fix the "shadowed back": reassign the back panel to the front's fabric
# (v14 gave the back Tyvek, whose PBR renders darker) -- appearance only
# 4. snapshot again for comparison
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
results = {}
n = pattern_api.GetPatternCount()
results["pattern_count"] = n
results["fabrics_before"] = [pattern_api.GetPatternPieceFabricIndex(i) for i in range(n)]
utility_api.Refresh3DWindow()
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "manual_front_before.png")
export_api.ExportSnapshot3D(snap)
results["front_before"] = snap
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "manual_before_%s.png" % view)
shutil.copyfile(_tt[i], dst)
results["before_" + view] = dst
# ---- fix the shadowed back: give the back panel the FRONT panel's fabric ----
# pattern 0 = front, 1 = back (created in that order by every recipe)
if n >= 2:
fab_front = pattern_api.GetPatternPieceFabricIndex(0)
pattern_api.SetPatternPieceFabricIndex(1, fab_front)
results["fabrics_after"] = [pattern_api.GetPatternPieceFabricIndex(i) for i in range(n)]
utility_api.Refresh3DWindow()
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "manual_after_%s.png" % view)
shutil.copyfile(_tt[i], dst)
results["after_" + view] = dst
result = results
@@ -0,0 +1,66 @@
# PROBE: where do the two panels actually START, and how do they move in zero-g?
# Turntable (front/side/back/side) at 0 frames, then after 30 zero-g frames.
# Suspicion: the BACK panel is not behind the body at all, so every "tube" was
# really two panels sewing themselves into a bag in front of the chest.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 50, 68, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 465.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 402.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
utility_api.ResetClothArrangement()
utility_api.Refresh3DWindow()
results = {}
_tt = export_api.ExportTurntableImages(4)
for i, tag in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "arrange0_%s.png" % tag)
shutil.copyfile(_tt[i], dst)
results["presim_" + tag] = dst
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(30)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
_tt = export_api.ExportTurntableImages(4)
for i, tag in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "arrange30_%s.png" % tag)
shutil.copyfile(_tt[i], dst)
results["f30_" + tag] = dst
result = results
@@ -0,0 +1,69 @@
# PROBE: why is the back panel in FRONT? Dump Body_* arrangement entries, then
# sweep the back panel's arrangement (point name and offset z) with 0-frame
# turntable side shots. No simulation -- placement only.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
results = {}
# one throwaway import just to read the arrangement table
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
full = pattern_api.GetArrangementList()
results["entry_keys"] = sorted(full[0].keys()) if full else []
results["body_entries"] = [e for e in full
if "Body" in str(e.get("ArrangementName", ""))][:40]
CELLS = [
("bp_z50", "Body_Back_Center_1", (50, 68, 50)),
("bp_zneg", "Body_Back_Center_1", (50, 68, -50)),
("bp_z150", "Body_Back_Center_1", (50, 68, 150)),
]
for tag, point, (ox, oy, oz) in CELLS:
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
if point not in arr:
results[tag] = "POINT %r NOT IN LIST" % point
continue
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr[point])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, ox, oy, oz)
utility_api.ResetClothArrangement()
utility_api.Refresh3DWindow()
_tt = export_api.ExportTurntableImages(4)
cell = {}
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "bp_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
cell[view] = dst
results[tag] = cell
result = results
@@ -0,0 +1,104 @@
# PROBE: where exactly does the bandeau escape? Three checkpoints, snapshots + spans.
# 1. after a 60-frame zero-g wrap+cinch (is it actually snug at the chest?)
# 2. after 30 frames of full gravity (has it moved? how far?)
# 3. after 60 more frames (where does it end up?)
# Also probes a LOW-gravity settle as a candidate fix in the same session.
import os
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def build():
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 55, 50)
pattern_api.SetArrangementPosition(b, 50, 55, 50)
return f, b
def set_elastic(p, line, total, strength):
pattern_api.SetPatternPieceElastic(p, line, True)
pattern_api.SetPatternPieceElasticTotalLength(p, line, total)
pattern_api.SetPatternPieceElasticStrength(p, line, strength)
def span():
path = os.path.join(SCRATCH, "_probe2.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
def shoot(tag):
utility_api.Refresh3DWindow()
utility_api.SetCamViewPoint(2)
p = os.path.join(SCRATCH, "probe_%s.png" % tag)
export_api.ExportSnapshot3D(p)
lo, hi = span()
return {"bottom_m": lo, "top_m": hi, "snap": p}
results = {}
# --- part 1: checkpointed failure ------------------------------------------
f, b = build()
for p in (f, b):
set_elastic(p, 0, 430.0, 60.0)
set_elastic(p, 3, 380.0, 60.0)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60)
results["1_after_zerog_wrap"] = shoot("1_zerog")
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Simulate(30)
results["2_gravity_30f"] = shoot("2_g30")
utility_api.Simulate(60)
results["3_gravity_90f"] = shoot("3_g90")
# --- part 2: candidate fix -- LOW gravity settle after the zero-g wrap ------
f, b = build()
for p in (f, b):
set_elastic(p, 0, 430.0, 60.0)
set_elastic(p, 3, 380.0, 60.0)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60)
utility_api.SetSimulationGravity(-980.0) # 1/10 g: folds form, slide force 10x smaller
utility_api.Simulate(120)
results["4_lowg_settle"] = shoot("4_lowg")
finally:
utility_api.SetSimulationGravity(-9800.0)
result = results
@@ -0,0 +1,162 @@
# GENERATED by tools/tailor/draft_garment.py -- DO NOT EDIT.
# garment : voyager_bandeau_v1
# stage : all
# config : C:\Users\CAN\tinqs-ltd\animation\tools\tailor\work\voyager_bandeau_v1\config.json
# emitted : 2026-08-18T20:09:18
# Edit the config's `md` block and re-emit; edits here are lost.
#
# Run inside an MD bridge session (a human must click Plugin > TinqsMDBridge first):
# python tools/md_bridge.py --file <this file> --timeout 310
#
# HARD-WON MD FACTS (do not rediscover -- see tools/tailor/draft_garment.py):
# - 2D pattern space: units are mm, +y is UP in the 3D mapping. Straps/neck at
# HIGH y, hem at y=0. Panels built y-down drape upside-down over the head and
# tangle -- it looks like a seam bug, it isn't.
# - MD's 3D unit is mm (gravity -9800). Blender-exported FBX avatars land 10x
# small -- import with op.scale = 10. ImportAvatar() is .avt only (False on FBX).
# - op.bAddArrangementPoints = True generates the ~98 named arrangement points;
# without it there is nowhere to hang cloth.
# - CreatePatternWithPoints takes (x, y, type) triples, type 0 = corner. Returned
# id is the pattern index; line i = edge point[i] -> point[i+1].
# - Seams are WHOLE-EDGE only: AddSeamlinePairGroup(a, lineA, b, lineB, False,
# False) with the SAME line index on mirrored front/back panels. Cross-pairing
# sews the garment over the face. Neck gaps and armholes are extra points in
# the outline, not partial seams.
# - SetArrangement() only ASSIGNS; utility_api.ResetClothArrangement() APPLIES it
# (ReDrape3DArrangement only materializes not-yet-draped cloth).
# - Arrangement INDICES REGENERATE on every avatar import -- always look up by
# NAME from GetArrangementList(). Offsets aren't stable either; verify with a
# 0-frame snapshot (the diagnostic that unsticks everything).
# - utility_api.NewProject() DELETES the avatar -- re-import after.
# - AddFabric() needs a .zfab FILE PATH (a name string silently fails); fabric 0
# is the shared default, coloring it dyes every garment. Assign with
# SetPatternPieceFabricIndex -- AssignFabricToPattern() never worked.
# - SetBaseTextureMapImageGivenFilePath(PATH, fabricIdx) -- path is arg 0.
# - PNG DPI sets a texture's physical size in MD; control tiling via dpi in PIL.
# - Strengthen through the WHOLE settle, relax only at the end; elastic (if any)
# mid-settle, never from frame 0. One garment per scene.
# - Simulate(n) is synchronous, ~1 min per 300 frames -- raise the client timeout.
# - GetClothPositions() stays empty: no mesh introspection, QC is image-based.
import os
report = {"garment": 'voyager_bandeau_v1', "stage": 'all'}
# ---- scene ---------------------------------------------------------------
utility_api.NewProject() # NOTE: this DELETES the avatar; re-import below
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
op = ApiTypes.ImportExportOption()
op.scale = 10.0 # Blender-exported FBX lands 10x small
op.bAddArrangementPoints = True # ~98 named points to hang cloth on
op.bAutoTranslate = True
report["avatar"] = import_api.ImportFBX(AVATAR_FBX, op) # ImportAvatar() is .avt only
# ---- pattern (mm; +y is UP in 3D -- hem at y=0, neck/straps at high y) ----
# front: strapless band: W 550.2 at bust (circ 1083.4 + ease 17), top 467.2, hem 405.7, H 170
_pts_p_front = [
(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6),
]
p_front = pattern_api.CreatePatternWithPoints([(x + 0.0, y + 0.0, 0) for (x, y) in _pts_p_front])
# back: identical panel; lines 0 top | 1 sideR-up | 2 sideR-low | 3 hem | 4 sideL-low | 5 sideL-up
_pts_p_back = [
(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6),
]
p_back = pattern_api.CreatePatternWithPoints([(x + 800.2, y + 0.0, 0) for (x, y) in _pts_p_back])
report["ids"] = [p_front, p_back]
# ---- seams (WHOLE-EDGE only; same line index on mirrored panels) ---------
pattern_api.AddSeamlinePairGroup(p_front, 1, p_back, 1, True, True)
pattern_api.AddSeamlinePairGroup(p_front, 2, p_back, 2, True, True)
pattern_api.AddSeamlinePairGroup(p_front, 4, p_back, 4, True, True)
pattern_api.AddSeamlinePairGroup(p_front, 5, p_back, 5, True, True)
# Paired edges must be EQUAL, not close: a 0.075 mm 'rounding' difference was
# a slanted edge that notched a waistband. Fail loudly BEFORE wasting a sim.
_pairs = [(p_front, 1, p_back, 1), (p_front, 2, p_back, 2), (p_front, 4, p_back, 4), (p_front, 5, p_back, 5)]
report["seam_lengths"] = []
_bad = []
for _pa, _la, _pb, _lb in _pairs:
_fa = pattern_api.GetLineLength(_pa, _la)
_fb = pattern_api.GetLineLength(_pb, _lb)
report["seam_lengths"].append((_pa, _la, round(_fa, 3), _pb, _lb, round(_fb, 3)))
if abs(_fa - _fb) > 0.1:
_bad.append("%s.%s=%.3f vs %s.%s=%.3f" % (_pa, _la, _fa, _pb, _lb, _fb))
if _bad:
raise RuntimeError("seam length mismatch (construction bug, not rounding): "
+ "; ".join(_bad))
# ---- fabric (never touch index 0: it is the scene-wide default) ---------
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
fab = fabric_api.AddFabric(ZFAB) # a name string silently fails
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab) # path is arg 0
for _p in (p_front, p_back,):
pattern_api.SetPatternPieceFabricIndex(_p, fab) # AssignFabricToPattern never worked
# ---- arrangement (BY NAME: indices regenerate on every avatar import) ----
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(p_front, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(p_back, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(p_front, 50, 55, 50)
pattern_api.SetArrangementPosition(p_back, 50, 55, 50)
# Hand the pattern ids to a later --stage drape/publish in the SAME session.
TINQS_GARMENT = {"garment": 'voyager_bandeau_v1', "fabric": fab, "patterns": {'front': p_front, 'back': p_back}}
# THE diagnostic: apply the arrangement and shoot with ZERO sim frames. It shows
# where the panels actually start, before physics muddies it. Reach for this the
# moment a drape misbehaves -- upside-down panels look exactly like seam bugs.
utility_api.ResetClothArrangement()
utility_api.SetCamViewPoint(2) # SetViewPoint() does nothing; 2 = front
presim_snap = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad\voyager_bandeau_presim.png"
os.makedirs(os.path.dirname(presim_snap), exist_ok=True)
export_api.ExportSnapshot3D(presim_snap)
report['presim_snap'] = presim_snap
# ---- drape ---------------------------------------------------------------
# Stiffen through the WHOLE settle: soft fabric from frame 0 rolls into a bunch.
pattern_api.SetPatternStrengthen(p_front, True)
pattern_api.SetPatternStrengthen(p_back, True)
utility_api.ResetClothArrangement() # SetArrangement only assigns; THIS applies it
report["sim1"] = utility_api.Simulate(250) # synchronous, ~1 min / 300 frames
# Elastic MID-SETTLE, never from frame 0 -- elastic-first drapes cinch the
# garment off one hip before the cloth has wrapped.
pattern_api.SetPatternPieceElastic(p_front, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p_front, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p_front, 0, 60.0)
pattern_api.SetPatternPieceElastic(p_back, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p_back, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p_back, 0, 60.0)
pattern_api.SetPatternPieceElastic(p_front, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p_front, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p_front, 3, 60.0)
pattern_api.SetPatternPieceElastic(p_back, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p_back, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p_back, 3, 60.0)
report["sim_elastic"] = utility_api.Simulate(80)
# Relax only at the end, so the settled shape falls into natural folds.
pattern_api.SetPatternStrengthen(p_front, False)
pattern_api.SetPatternStrengthen(p_back, False)
report["sim2"] = utility_api.Simulate(50)
utility_api.Refresh3DWindow()
utility_api.SetCamViewPoint(2) # SetViewPoint() does nothing; 2 = front
snap = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad\voyager_bandeau_front.png"
os.makedirs(os.path.dirname(snap), exist_ok=True)
export_api.ExportSnapshot3D(snap)
report['snap'] = snap
# ExportSnapshot3D has NO rear view (SetCamViewPoint 0-7: none is the back).
# Turntable ignores any path arg and reuses the same output names -- copy now.
import shutil
_tt = export_api.ExportTurntableImages(4) # 0 front, 1 side, 2 BACK, 3 side
if _tt and len(_tt) > 2:
back_snap = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad\voyager_bandeau_back.png"
os.makedirs(os.path.dirname(back_snap), exist_ok=True)
shutil.copyfile(_tt[2], back_snap)
report["back_snapshot"] = back_snap
else:
report["back_snapshot"] = "TURNTABLE RETURNED NOTHING -- rear unverified"
result = report
@@ -0,0 +1,127 @@
# SWEEP 3: iron the diagonal fold out of the BACK panel (v8 = current best).
# v8: back stiff whole wrap -> diagonal fold from phase-1 seam pull.
# v7: back soft in phase 2 -> top edge rolled.
# Variants (all inherit the v8 recipe otherwise: zero-g, y=68, back x=0,
# elastic 450/390 @ 60, stiff 60f, soften front, 90f):
# B_brief +20f with the back briefly soft at the very end (release the fold)
# C_solid phase 2 with back at SolidifyStrengthen 0.5 (half-stiff conform)
# D_delay elastic OFF during phase 1 (seams settle first), ON for phase 2
# Judge: turntable BACK shot per variant + garment z-span.
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def build(with_elastic=True):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, 68, 50)
if with_elastic:
enable_elastic(f, b)
return f, b
def enable_elastic(f, b):
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
def span():
path = os.path.join(SCRATCH, "_probe8.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
def shoot(tag):
utility_api.Refresh3DWindow()
out = {}
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "bf_%s_%s.png" % (tag, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
lo, hi = span()
out["bottom_m"], out["top_m"] = lo, hi
return out
results = {}
utility_api.SetSimulationGravity(0.0)
try:
# --- B_brief: v8 + 20f brief back-soften at the end ----------------------
f, b = build()
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(60)
pattern_api.SetPatternStrengthen(f, False)
utility_api.Simulate(90)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(20)
results["B_brief"] = shoot("B_brief")
# --- C_solid: back at half stiffness during phase 2 ----------------------
f, b = build()
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(60)
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
pattern_api.SetPatternPieceSolidifyStrengthen(b, 0.5)
utility_api.Simulate(90)
results["C_solid"] = shoot("C_solid")
# --- D_delay: seams settle 60f stiff WITHOUT elastic, then elastic 90f ---
f, b = build(with_elastic=False)
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(60)
enable_elastic(f, b)
pattern_api.SetPatternStrengthen(f, False)
utility_api.Simulate(90)
results["D_delay"] = shoot("D_delay")
finally:
utility_api.SetSimulationGravity(-9800.0)
result = results
@@ -0,0 +1,82 @@
# SWEEP 4: lift the drooping BACK of the band. Arrangement is PER-PANEL, so the
# back can start higher (y 78 / 85) while the front stays at its proven 68.
# Recipe otherwise = v8 (zero-g, stiff 60, soften front only, 90, elastic 450/390).
# Ends UNFROZEN so Can sees the texture in the viewport (freeze tint hides it).
import os
import shutil
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def run_cell(back_y):
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
pattern_api.SetAddlThicknessCollision(p, 2.0)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 68, 50)
pattern_api.SetArrangementPosition(b, 0, back_y, 50)
for p in (f, b):
pattern_api.SetPatternPieceElastic(p, 0, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 0, 450.0)
pattern_api.SetPatternPieceElasticStrength(p, 0, 60.0)
pattern_api.SetPatternPieceElastic(p, 3, True)
pattern_api.SetPatternPieceElasticTotalLength(p, 3, 390.0)
pattern_api.SetPatternPieceElasticStrength(p, 3, 60.0)
pattern_api.SetPatternStrengthen(p, True)
utility_api.ResetClothArrangement()
utility_api.SetSimulationGravity(0.0)
try:
utility_api.Simulate(60)
pattern_api.SetPatternStrengthen(f, False)
utility_api.Simulate(90)
finally:
utility_api.SetSimulationGravity(-9800.0)
utility_api.Refresh3DWindow()
out = {}
path = os.path.join(SCRATCH, "_probe12.obj")
xop = ApiTypes.ImportExportOption()
xop.bExportGarment = True
xop.bExportAvatar = False
export_api.ExportOBJ(path, xop)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
zmin, zmax = min(zmin, yv), max(zmax, yv)
out["bottom_m"], out["top_m"] = round(zmin * 0.001, 4), round(zmax * 0.001, 4)
_tt = export_api.ExportTurntableImages(4)
for i, view in enumerate(("front", "side1", "back", "side2")):
if _tt and len(_tt) > i:
dst = os.path.join(SCRATCH, "bh_%d_%s.png" % (back_y, view))
shutil.copyfile(_tt[i], dst)
out[view] = dst
return out
results = {}
results["y78"] = run_cell(78)
results["y85"] = run_cell(85)
# scene ends holding y85, UNFROZEN (texture visible; do not press play)
result = results
@@ -0,0 +1,110 @@
# SWEEP: can the strapless voyager bandeau hold at the bust, and with which recipe?
#
# v1 (md_script.py) slid to the floor: 250 stiff settle frames gave gravity time to
# push the rigid band off the chest before the elastic ever engaged. Axis swept here:
# WHEN the elastic lands and whether the cloth is stiff or soft while it cinches.
#
# A_control v1 recipe verbatim -- must land on the floor or the harness is broken
# B_early stiff 40 -> elastic (430/380 @ 60) -> 120 -> relax 40
# C_soft stiff 40 -> soften + elastic (430/380 @ 60) -> 120
# D_hard stiff 30 -> elastic (400/370 @ 100) -> 130 -> relax 40
#
# Pass/fail is read from GEOMETRY (tooling.md section 7): probe OBJ, garment z-span
# in metres. Target: top ~1.31, bottom ~1.14 (tol 0.03). Floor = top < 0.3.
# OBJ is in mm with y as height (tooling.md section 2).
import os
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
def build():
utility_api.NewProject() # deletes the avatar; re-import below
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5): # identical panels -> (True, True)
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 55, 50) # Body_*_Center_1: SAME x
pattern_api.SetArrangementPosition(b, 50, 55, 50)
return f, b
def set_elastic(p, line, total, strength):
pattern_api.SetPatternPieceElastic(p, line, True)
pattern_api.SetPatternPieceElasticTotalLength(p, line, total)
pattern_api.SetPatternPieceElasticStrength(p, line, strength)
def garment_span_m():
path = os.path.join(SCRATCH, "_sweep_probe.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2]) # OBJ: y is height, mm
if yv < zmin:
zmin = yv
if yv > zmax:
zmax = yv
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
VARIANTS = [
("A_control", dict(pre=250, top=450.0, hem=390.0, strength=60.0,
elastic_frames=80, soften_at_elastic=False, relax=50)),
("B_early", dict(pre=40, top=430.0, hem=380.0, strength=60.0,
elastic_frames=120, soften_at_elastic=False, relax=40)),
("C_soft", dict(pre=40, top=430.0, hem=380.0, strength=60.0,
elastic_frames=120, soften_at_elastic=True, relax=0)),
("D_hard", dict(pre=30, top=400.0, hem=370.0, strength=100.0,
elastic_frames=130, soften_at_elastic=False, relax=40)),
]
results = {}
for name, v in VARIANTS:
f, b = build()
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.ResetClothArrangement()
utility_api.Simulate(v["pre"])
if v["soften_at_elastic"]:
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
for p in (f, b):
set_elastic(p, 0, v["top"], v["strength"]) # line 0 = top edge
set_elastic(p, 3, v["hem"], v["strength"]) # line 3 = hem
utility_api.Simulate(v["elastic_frames"])
if v["relax"]:
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(v["relax"])
utility_api.Refresh3DWindow()
lo, hi = garment_span_m()
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "sweep_%s.png" % name)
export_api.ExportSnapshot3D(snap)
results[name] = {"band_bottom_m": lo, "band_top_m": hi,
"on_body": hi > 1.0, "snap": snap}
result = results
@@ -0,0 +1,133 @@
# SWEEP 2: zero-gravity donning for the strapless voyager bandeau.
#
# Sweep 1 (md_sweep_hold.py) proved elastic timing is NOT the lever: all four
# variants (incl. frame-30 elastic at strength 100) ended on the floor. The probe
# span showed the loop passed the 1095 mm hips, which a closed 812 mm hem cannot --
# so the panels FREE-FALL ~0.5 m during the first frames while the side seams are
# still closing, finish sewing around the shins, and slide off. Straps fixed this
# for the pari by catching the shoulders during the drop.
#
# Fix: utility_api.SetSimulationGravity(0) while the seams close and the elastic
# cinches the band onto the chest, then restore gravity and settle. Gravity is
# restored in try/finally -- the exec namespace persists in-session and a later
# garment must not inherit zero-g.
#
# E_zerog_soft zero-g wrap 100 soft + elastic 430/380 @ 60 -> gravity, 150, snapshot
# F_zerog_stiff zero-g wrap 100 stiff + same elastic -> gravity, stiff 100 -> relax 50
# G_zerog_tight like E but top cinched to 410
#
# Pass/fail from geometry: band top ~1.31 m, bottom ~1.14 m (tol 0.03).
import os
SCRATCH = r"C:\Users\CAN\AppData\Local\Temp\claude\C--Users-CAN-tinqs-ltd-docs\3744fd8e-1342-446c-9677-7aafbafc8d18\scratchpad"
AVATAR_FBX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
TEX = r"C:\Users\CAN\tinqs-ltd\animation\tools\tailor\textures\voyager_bandeau.png"
PTS = [(41.5, 170.0), (508.7, 170.0), (550.2, 123.6),
(478.0, 0.0), (72.2, 0.0), (0.0, 123.6)]
GRAVITY_DOC = utility_api.SetSimulationGravity.__doc__
def set_gravity(g):
"""Signature unverified -- try scalar, fall back to a vector (MD 3D is y-up)."""
try:
utility_api.SetSimulationGravity(float(g))
return "scalar"
except TypeError:
utility_api.SetSimulationGravity(0.0, float(g), 0.0)
return "vector"
def build():
utility_api.NewProject()
op = ApiTypes.ImportExportOption()
op.scale = 10.0
op.bAddArrangementPoints = True
op.bAutoTranslate = True
import_api.ImportFBX(AVATAR_FBX, op)
f = pattern_api.CreatePatternWithPoints([(x, y, 0) for (x, y) in PTS])
b = pattern_api.CreatePatternWithPoints([(x + 800.2, y, 0) for (x, y) in PTS])
for ln in (1, 2, 4, 5):
pattern_api.AddSeamlinePairGroup(f, ln, b, ln, True, True)
fab = fabric_api.AddFabric(ZFAB)
fabric_api.SetBaseTextureMapImageGivenFilePath(TEX, fab)
for p in (f, b):
pattern_api.SetPatternPieceFabricIndex(p, fab)
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
for a in pattern_api.GetArrangementList()}
pattern_api.SetArrangement(f, arr['Body_Front_Center_1'])
pattern_api.SetArrangement(b, arr['Body_Back_Center_1'])
pattern_api.SetArrangementPosition(f, 50, 55, 50)
pattern_api.SetArrangementPosition(b, 50, 55, 50)
return f, b
def set_elastic(p, line, total, strength):
pattern_api.SetPatternPieceElastic(p, line, True)
pattern_api.SetPatternPieceElasticTotalLength(p, line, total)
pattern_api.SetPatternPieceElasticStrength(p, line, strength)
def garment_span_m():
path = os.path.join(SCRATCH, "_sweep_probe.obj")
op = ApiTypes.ImportExportOption()
op.bExportGarment = True
op.bExportAvatar = False
export_api.ExportOBJ(path, op)
zmin, zmax = 1e9, -1e9
with open(path) as fh:
for ln in fh:
if ln.startswith("v "):
yv = float(ln.split()[2])
if yv < zmin:
zmin = yv
if yv > zmax:
zmax = yv
return round(zmin * 0.001, 4), round(zmax * 0.001, 4)
VARIANTS = [
("E_zerog_soft", dict(stiff_wrap=False, top=430.0, hem=380.0, strength=60.0,
wrap_frames=100, settle_stiff=False, settle=150, relax=0)),
("F_zerog_stiff", dict(stiff_wrap=True, top=430.0, hem=380.0, strength=60.0,
wrap_frames=100, settle_stiff=True, settle=100, relax=50)),
("G_zerog_tight", dict(stiff_wrap=False, top=410.0, hem=380.0, strength=60.0,
wrap_frames=100, settle_stiff=False, settle=150, relax=0)),
]
results = {"gravity_doc": GRAVITY_DOC}
for name, v in VARIANTS:
f, b = build()
if v["stiff_wrap"]:
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
for p in (f, b):
set_elastic(p, 0, v["top"], v["strength"])
set_elastic(p, 3, v["hem"], v["strength"])
utility_api.ResetClothArrangement()
mode = set_gravity(0.0)
try:
utility_api.Simulate(v["wrap_frames"]) # weightless wrap + cinch
finally:
if mode == "scalar":
utility_api.SetSimulationGravity(-9800.0)
else:
utility_api.SetSimulationGravity(0.0, -9800.0, 0.0)
if v["settle_stiff"] and not v["stiff_wrap"]:
pattern_api.SetPatternStrengthen(f, True)
pattern_api.SetPatternStrengthen(b, True)
utility_api.Simulate(v["settle"]) # now fight gravity
if v["relax"]:
pattern_api.SetPatternStrengthen(f, False)
pattern_api.SetPatternStrengthen(b, False)
utility_api.Simulate(v["relax"])
utility_api.Refresh3DWindow()
lo, hi = garment_span_m()
utility_api.SetCamViewPoint(2)
snap = os.path.join(SCRATCH, "sweep_%s.png" % name)
export_api.ExportSnapshot3D(snap)
results[name] = {"band_bottom_m": lo, "band_top_m": hi,
"gravity_mode": mode, "snap": snap}
result = results