124 lines
5.1 KiB
Python
124 lines
5.1 KiB
Python
|
|
# Arrangement-x sweep for bottomTest1 (strand skirt). Diagnostic, exports nothing.
|
||
|
|
#
|
||
|
|
# python tools/md_bridge.py --file tools/tailor/md_piupiu_arrsweep.py --timeout 880
|
||
|
|
#
|
||
|
|
# WHY: the skirt's waistband folds on ONE SIDE only (turntable side B shows a flap
|
||
|
|
# jutting out with a grey reverse-face wedge and a strand group folded over itself;
|
||
|
|
# side A is clean). An asymmetric defect needs an asymmetric input, and the only one
|
||
|
|
# is SetArrangementPosition x: front=50 / back=0, inherited from md_piupiu.py.
|
||
|
|
#
|
||
|
|
# But setting BOTH to 50 — which is exactly what fixed the same one-sided fold on the
|
||
|
|
# TOP — makes this skirt fall to the floor (band top measured 0.117 m). The top
|
||
|
|
# arranges on Body_*_Center_1; this arranges on Leg_Skirt_Front/Back, and those points
|
||
|
|
# evidently do not share a convention. So 50/0 may be load-bearing here rather than a
|
||
|
|
# copy-paste artefact.
|
||
|
|
#
|
||
|
|
# CONTROL: combo (50, 0) must reproduce the shipped drape (band top ~1.09 m). If the
|
||
|
|
# control fails, the harness is broken and every cell is void — that is exactly how
|
||
|
|
# md_pari_armsweep.py wasted a run.
|
||
|
|
#
|
||
|
|
# Pass/fail is read from GEOMETRY, not images: a garment on the floor measures
|
||
|
|
# band_top < 0.3 m, so the sweep self-reports which cells even stayed on the body.
|
||
|
|
import os
|
||
|
|
import shutil
|
||
|
|
import tempfile
|
||
|
|
|
||
|
|
AVATAR_FBX = r"C:\Users\Jeremy\tinqs\animation\tools\tailor\avatar\Lena_QuatSkin_Avatar.fbx"
|
||
|
|
ZFAB = r"C:\Users\Public\Documents\MarvelousDesigner\New Assets\Fabric\(Default for Simulation).zfab"
|
||
|
|
|
||
|
|
M_TEETH = 16
|
||
|
|
W_TOP, W_HEM = 375.0, 700.0
|
||
|
|
BAND_H, STRAND_H = 60.0, 530.0
|
||
|
|
GAP_TOP, GAP_HEM = 6.0, 11.0
|
||
|
|
PARTICLE_MM, COLLISION_MM = 20.0, 10.0
|
||
|
|
ARR_Y = 92
|
||
|
|
COMBOS = [(50, 0), (50, 50), (0, 0), (0, 50)] # (50,0) is the CONTROL
|
||
|
|
|
||
|
|
BAND_TOP = STRAND_H + BAND_H
|
||
|
|
BAND_BOT = STRAND_H
|
||
|
|
INSET = (W_HEM - W_TOP) / 2.0
|
||
|
|
pitch_top = W_TOP / M_TEETH
|
||
|
|
pitch_hem = W_HEM / M_TEETH
|
||
|
|
|
||
|
|
report = {"combos": {}, "control": "50_0", "control_expect_band_top_m": 1.09}
|
||
|
|
|
||
|
|
def outline(dx):
|
||
|
|
pts = [(INSET, BAND_TOP), (INSET + W_TOP, BAND_TOP), (INSET + W_TOP, BAND_BOT)]
|
||
|
|
for j in range(M_TEETH - 1, -1, -1):
|
||
|
|
ta = INSET + j * pitch_top + GAP_TOP / 2.0
|
||
|
|
tb = INSET + (j + 1) * pitch_top - GAP_TOP / 2.0
|
||
|
|
ha = j * pitch_hem + GAP_HEM / 2.0
|
||
|
|
hb = (j + 1) * pitch_hem - GAP_HEM / 2.0
|
||
|
|
if j < M_TEETH - 1:
|
||
|
|
pts.append((tb, BAND_BOT))
|
||
|
|
pts += [(hb, 0.0), (ha, 0.0), (ta, BAND_BOT)]
|
||
|
|
return [(x + dx, y, 0) for (x, y) in pts]
|
||
|
|
|
||
|
|
for xf, xb in COMBOS:
|
||
|
|
key = "%d_%d" % (xf, xb)
|
||
|
|
utility_api.NewProject()
|
||
|
|
op = ApiTypes.ImportExportOption()
|
||
|
|
op.scale = 10.0
|
||
|
|
op.bAddArrangementPoints = True
|
||
|
|
op.bAutoTranslate = True
|
||
|
|
import_api.ImportFBX(AVATAR_FBX, op)
|
||
|
|
|
||
|
|
pts_f = outline(0.0)
|
||
|
|
sf = pattern_api.CreatePatternWithPoints(pts_f)
|
||
|
|
sb = pattern_api.CreatePatternWithPoints(outline(W_HEM + 400.0))
|
||
|
|
L_RIGHT, L_LEFT = 1, len(pts_f) - 1
|
||
|
|
# crossed pairing: for a two-panel tube with only side seams this IS the reversal
|
||
|
|
pattern_api.AddSeamlinePairGroup(sf, L_RIGHT, sb, L_LEFT, False, False)
|
||
|
|
pattern_api.AddSeamlinePairGroup(sf, L_LEFT, sb, L_RIGHT, False, False)
|
||
|
|
|
||
|
|
fab = fabric_api.AddFabric(ZFAB) # PLAIN: judge shape, not motif
|
||
|
|
for p in (sf, sb):
|
||
|
|
pattern_api.SetPatternPieceFabricIndex(p, fab)
|
||
|
|
pattern_api.SetParticleDistanceOfPattern(p, PARTICLE_MM)
|
||
|
|
pattern_api.SetAddlThicknessCollision(p, COLLISION_MM)
|
||
|
|
|
||
|
|
arr = {a["ArrangementName"]: int(a["ArrangementIndex"])
|
||
|
|
for a in pattern_api.GetArrangementList()}
|
||
|
|
pattern_api.SetArrangement(sf, arr["Leg_Skirt_Front"])
|
||
|
|
pattern_api.SetArrangement(sb, arr["Leg_Skirt_Back"])
|
||
|
|
pattern_api.SetArrangementPosition(sf, xf, ARR_Y, 50)
|
||
|
|
pattern_api.SetArrangementPosition(sb, xb, ARR_Y, 50)
|
||
|
|
|
||
|
|
pattern_api.SetPatternStrengthen(sf, True)
|
||
|
|
pattern_api.SetPatternStrengthen(sb, True)
|
||
|
|
utility_api.ResetClothArrangement()
|
||
|
|
ok1 = utility_api.Simulate(250)
|
||
|
|
pattern_api.SetPatternStrengthen(sf, False)
|
||
|
|
pattern_api.SetPatternStrengthen(sb, False)
|
||
|
|
ok2 = utility_api.Simulate(50)
|
||
|
|
utility_api.Refresh3DWindow()
|
||
|
|
|
||
|
|
# geometry verdict: on the floor measures band_top < 0.3 m
|
||
|
|
mop = ApiTypes.ImportExportOption()
|
||
|
|
mop.bExportGarment = True
|
||
|
|
mop.bExportAvatar = False
|
||
|
|
probe = os.path.join(tempfile.gettempdir(), "tinqs_arrsweep_%s.obj" % key)
|
||
|
|
export_api.ExportOBJ(probe, mop)
|
||
|
|
ys = []
|
||
|
|
with open(probe) as fh:
|
||
|
|
for line in fh:
|
||
|
|
if line.startswith("v "):
|
||
|
|
ys.append(float(line.split()[2]) * 0.001) # OBJ is MM
|
||
|
|
ys.sort()
|
||
|
|
on_body = bool(ys) and ys[-1] > 0.30
|
||
|
|
|
||
|
|
shots = export_api.ExportTurntableImages(4)
|
||
|
|
saved = {}
|
||
|
|
for i, label in ((0, "front"), (1, "sideA"), (3, "sideB")):
|
||
|
|
if i < len(shots) and os.path.exists(shots[i]):
|
||
|
|
dst = os.path.join(tempfile.gettempdir(),
|
||
|
|
"tinqs_arrsweep_%s_%s.png" % (key, label))
|
||
|
|
shutil.copyfile(shots[i], dst)
|
||
|
|
saved[label] = dst
|
||
|
|
report["combos"][key] = {
|
||
|
|
"x": [xf, xb], "sim": [ok1, ok2], "on_body": on_body,
|
||
|
|
"band_top_m": round(ys[-1], 3) if ys else None,
|
||
|
|
"hem_m": round(ys[0], 3) if ys else None, "shots": saved}
|
||
|
|
|
||
|
|
result = report
|