diff --git a/tools/tailor/textures/make_voyager_bandeau.py b/tools/tailor/textures/make_voyager_bandeau.py new file mode 100644 index 0000000..0ab23df --- /dev/null +++ b/tools/tailor/textures/make_voyager_bandeau.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +""" +make_voyager_bandeau.py -- golden plaited-weave band with black rope trim and +fringe for the female voyager bandeau top (lena_voyager_bandeau_v1). + + python tools/tailor/textures/make_voyager_bandeau.py + +Derived from the reference renders +`fc-voyager-kilt-gpt-piece-top{,-front,-back}.png` (docs repo): a diagonal +basket plait in warm golden tan, a black braided-rope band near each edge, and +a short strand fringe extending to both the top and bottom edges. + +POSITIONED TILE, same doctrine as make_kilt_cloth.py: spans the bandeau +panel's full 170 mm height exactly once (dpi = SIZE / (170 / 25.4)), so every +zone lands at a fixed garment height: + + garment y (mm, hem=0) zone + 0-15 bottom fringe strands (painted, not geometry -- v1) + 15-27 black rope band (braided chevrons) + 27-143 diagonal basket plait + 143-155 black rope band + 155-170 top fringe strands (tips ragged toward y=170) + +Horizontally the 170 mm tile repeats ~3.2x across the 550 mm panel; the plait +pitch (17 mm) and rope chevron pitch (32 px) both divide the tile exactly, so +the wrap is seamless. + +IF THE ROPE BANDS RENDER SWAPPED TOP-FOR-BOTTOM, MD anchored the texture the +other way up -- regenerate with FLIP=True rather than touching the pattern. +(Anchor per probe 2026-08-18: pattern y=0 (hem) maps to image v=0.5 and wraps.) + +Albedo baked in (pipeline has no alpha, texture replaces base color), warm +ramp r>g>b, deterministic LCG noise. +""" +import os + +from PIL import Image + +HERE = os.path.dirname(os.path.abspath(__file__)) +OUT = os.path.join(HERE, "voyager_bandeau.png") + +SIZE = 1024 # px, square +CLOTH_MM = 170.0 # spans the bandeau panel height exactly once +DPI = SIZE / (CLOTH_MM / 25.4) +FLIP = False # set True if MD anchors the texture upside-down +PX_PER_MM = SIZE / CLOTH_MM + +# zone boundaries in garment mm (hem = 0) +BOT_FRINGE_TOP = 15.0 +BOT_ROPE_TOP = 27.0 +TOP_ROPE_BOT = 143.0 +TOP_FRINGE_BOT = 155.0 + +PLAIT_PITCH_MM = 17.0 # strand pitch; 170/17 = 10 cells -> seamless wrap +ROPE_CHEVRON_PX = 32 # divides 1024 -> seamless wrap + +BASE = (203, 165, 110) # golden tan plait (reference lit mean ~(200,165,115)) +ROPE = (46, 40, 32) # near-black rope, warm +ROPE_HI = (92, 80, 62) # braid ridge highlight +FRINGE = (191, 152, 100) +FRINGE_GAP = (140, 108, 70) +SHADOW = (74, 60, 44) # past a fringe tip: painted shadow (no alpha in lane) + +OVER_LIFT = 14 +UNDER_DROP = 18 +ROUND_SHADE = 12 +SLUB_RANGE = 8 +MOTTLE = 10 +MOTTLE_CELL = 64 + + +def lcg(seed): + state = seed + while True: + state = (1103515245 * state + 12345) % (2 ** 31) + yield state / float(2 ** 31) + + +def value_noise(cells, seed): + g = lcg(seed) + grid = [[(next(g) * 2.0 - 1.0) for _ in range(cells + 1)] for _ in range(cells + 1)] + + def sample(x, y): + fx, fy = x * cells / SIZE, y * cells / SIZE + x0, y0 = int(fx), int(fy) + tx, ty = fx - x0, fy - y0 + tx, ty = tx * tx * (3 - 2 * tx), ty * ty * (3 - 2 * ty) + a = grid[y0][x0] * (1 - tx) + grid[y0][x0 + 1] * tx + b = grid[y0 + 1][x0] * (1 - tx) + grid[y0 + 1][x0 + 1] * tx + return a * (1 - ty) + b * ty + + return sample + + +def mm_from_row(y): + """Garment height in mm for an image row (anchor: hem -> v=0.5, wraps).""" + v = (y + 0.5) / SIZE + return CLOTH_MM - CLOTH_MM * ((v - 0.5) % 1.0) + + +def plait(xm, ym, slub_a, slub_b): + """Diagonal basket plait: two +/-45-degree strand families, over-under. + Returns a brightness delta to apply on BASE.""" + u = (xm + ym) / PLAIT_PITCH_MM + w = (xm - ym) / PLAIT_PITCH_MM + a, b = int(u // 1), int(w // 1) + fu, fw = u - a, w - b + over_a = ((a + b) % 2) == 0 + if over_a: + # family A on top: rounded across its width (fw), slub along it + c = abs(fw - 0.5) * 2.0 + d = OVER_LIFT - int(ROUND_SHADE * c * c) + d += slub_a[a % len(slub_a)] + if fw < 0.06 or fw > 0.94: # crevice against the under strand + d -= 26 + else: + c = abs(fu - 0.5) * 2.0 + d = OVER_LIFT - int(ROUND_SHADE * c * c) - UNDER_DROP // 2 + d += slub_b[b % len(slub_b)] + if fu < 0.06 or fu > 0.94: + d -= 26 + return d + + +def main(): + g = lcg(20260818) + slub_a = [int((next(g) * 2.0 - 1.0) * SLUB_RANGE) for _ in range(64)] + slub_b = [int((next(g) * 2.0 - 1.0) * SLUB_RANGE) for _ in range(64)] + tip_noise = [next(g) for _ in range(64)] + mottle = value_noise(SIZE // MOTTLE_CELL, seed=777) + + img = Image.new("RGB", (SIZE, SIZE)) + px = img.load() + + strand_w = 16 # fringe strand width px (~2.7 mm) + for y in range(SIZE): + mm = mm_from_row(y) + ym = mm + for x in range(SIZE): + xm = x / PX_PER_MM + wear = int(mottle(x, y) * MOTTLE) + + if mm < BOT_FRINGE_TOP or mm > TOP_FRINGE_BOT: + # fringe: vertical strands, ragged tips toward the edge + sid = (x // strand_w) % len(tip_noise) + ragged = tip_noise[sid] * 8.0 + past_tip = (mm < ragged if mm < BOT_FRINGE_TOP + else mm > CLOTH_MM - ragged) + if past_tip: + base, d = SHADOW, wear // 2 + else: + u = x % strand_w + base = FRINGE_GAP if u < 2 else FRINGE + d = slub_a[sid] + wear + elif mm < BOT_ROPE_TOP or mm > TOP_ROPE_BOT: + # braided rope: diagonal chevron ridges + phase = ((x + int(mm * PX_PER_MM) * 2) % ROPE_CHEVRON_PX) \ + / float(ROPE_CHEVRON_PX) + base = ROPE_HI if phase < 0.30 else ROPE + d = wear // 2 + else: + base = BASE + d = plait(xm, ym, slub_a, slub_b) + wear + + r = base[0] + d + gg = base[1] + int(d * 0.82) + b = base[2] + int(d * 0.66) + px[x, y] = (max(0, min(255, r)), max(0, min(255, gg)), + max(0, min(255, b))) + + if FLIP: + img = img.transpose(Image.FLIP_TOP_BOTTOM) + img.save(OUT, dpi=(DPI, DPI)) + print("wrote %s (%dx%d, dpi %.2f -> %.0f mm of cloth; ropes %d-%d & %d-%d mm, " + "fringe 0-%d & %d-%d mm)" + % (OUT, SIZE, SIZE, DPI, CLOTH_MM, BOT_FRINGE_TOP, BOT_ROPE_TOP, + TOP_ROPE_BOT, TOP_FRINGE_BOT, BOT_FRINGE_TOP, TOP_FRINGE_BOT, CLOTH_MM)) + + +if __name__ == "__main__": + main() diff --git a/tools/tailor/textures/voyager_bandeau.png b/tools/tailor/textures/voyager_bandeau.png new file mode 100644 index 0000000..37a5068 Binary files /dev/null and b/tools/tailor/textures/voyager_bandeau.png differ diff --git a/tools/tailor/work/voyager_bandeau_v1/config.json b/tools/tailor/work/voyager_bandeau_v1/config.json new file mode 100644 index 0000000..e3bf02a --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/config.json @@ -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"} + } + } +} diff --git a/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup.zprj b/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup.zprj new file mode 100644 index 0000000..ae6a5b4 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup.zprj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cf22ae8404de67977782057f514eec27322c077adf8865eb7c9b2ba28501179 +size 9758086 diff --git a/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup_Default Colorway.png b/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup_Default Colorway.png new file mode 100644 index 0000000..bed1106 Binary files /dev/null and b/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup_Default Colorway.png differ diff --git a/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup_Default Colorway2.zprj b/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup_Default Colorway2.zprj new file mode 100644 index 0000000..1996cf6 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/manual_fit_backup_Default Colorway2.zprj @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b82ebb9e2c6647618550832a42129c1023ba105ab615536ef273a1192b581bdb +size 9699039 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v10.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v10.py new file mode 100644 index 0000000..15622a5 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v10.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v11.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v11.py new file mode 100644 index 0000000..10c0ec9 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v11.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v13.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v13.py new file mode 100644 index 0000000..ab768ef --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v13.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v14.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v14.py new file mode 100644 index 0000000..eec3635 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v14.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v15.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v15.py new file mode 100644 index 0000000..9ddea88 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v15.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v16.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v16.py new file mode 100644 index 0000000..5f3ddaf --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v16.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v17.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v17.py new file mode 100644 index 0000000..49f6e9f --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v17.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v18.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v18.py new file mode 100644 index 0000000..ad449b3 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v18.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v19.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v19.py new file mode 100644 index 0000000..09ea0df --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v19.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v2.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v2.py new file mode 100644 index 0000000..472c55d --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v2.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v20.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v20.py new file mode 100644 index 0000000..11b617a --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v20.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v21.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v21.py new file mode 100644 index 0000000..ebb39ee --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v21.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v22.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v22.py new file mode 100644 index 0000000..36646f8 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v22.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v23.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v23.py new file mode 100644 index 0000000..4f590e5 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v23.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v3.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v3.py new file mode 100644 index 0000000..2a5af8a --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v3.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v4.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v4.py new file mode 100644 index 0000000..2636d2c --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v4.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v5.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v5.py new file mode 100644 index 0000000..48db007 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v5.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v6.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v6.py new file mode 100644 index 0000000..4a1869f --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v6.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v7.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v7.py new file mode 100644 index 0000000..e7162fa --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v7.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v8.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v8.py new file mode 100644 index 0000000..1503876 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v8.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_v9.py b/tools/tailor/work/voyager_bandeau_v1/md_final_v9.py new file mode 100644 index 0000000..24035ac --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_v9.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_final_zerog.py b/tools/tailor/work/voyager_bandeau_v1/md_final_zerog.py new file mode 100644 index 0000000..564689e --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_final_zerog.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_flip_back.py b/tools/tailor/work/voyager_bandeau_v1/md_flip_back.py new file mode 100644 index 0000000..5a3938e --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_flip_back.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_inspect_fix.py b/tools/tailor/work/voyager_bandeau_v1/md_inspect_fix.py new file mode 100644 index 0000000..8136457 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_inspect_fix.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_probe_arrange.py b/tools/tailor/work/voyager_bandeau_v1/md_probe_arrange.py new file mode 100644 index 0000000..e523cdc --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_probe_arrange.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_probe_backpoint.py b/tools/tailor/work/voyager_bandeau_v1/md_probe_backpoint.py new file mode 100644 index 0000000..ead0c3a --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_probe_backpoint.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_probe_slide.py b/tools/tailor/work/voyager_bandeau_v1/md_probe_slide.py new file mode 100644 index 0000000..8767cf4 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_probe_slide.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_script.py b/tools/tailor/work/voyager_bandeau_v1/md_script.py new file mode 100644 index 0000000..180ebb5 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_script.py @@ -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 --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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_sweep_backfix.py b/tools/tailor/work/voyager_bandeau_v1/md_sweep_backfix.py new file mode 100644 index 0000000..d565ccb --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_sweep_backfix.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_sweep_backheight.py b/tools/tailor/work/voyager_bandeau_v1/md_sweep_backheight.py new file mode 100644 index 0000000..e7a5867 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_sweep_backheight.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_sweep_hold.py b/tools/tailor/work/voyager_bandeau_v1/md_sweep_hold.py new file mode 100644 index 0000000..0928d48 --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_sweep_hold.py @@ -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 diff --git a/tools/tailor/work/voyager_bandeau_v1/md_sweep_zerog.py b/tools/tailor/work/voyager_bandeau_v1/md_sweep_zerog.py new file mode 100644 index 0000000..db99dda --- /dev/null +++ b/tools/tailor/work/voyager_bandeau_v1/md_sweep_zerog.py @@ -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