Skip to content

Commit 8c66a80

Browse files
committed
fix: align PinMode default to 'none' in controller; robust pointer anisotropy; shared pointer radius helper; reset new palette states; raise MIN clamp to 46
1 parent 09220bf commit 8c66a80

File tree

3 files changed

+50
-15
lines changed

3 files changed

+50
-15
lines changed

src/App.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,11 +610,15 @@ function Demo() {
610610
setGravity(9.81)
611611
setImpulseMultiplier(1)
612612
setTessellationSegments(24)
613+
setAutoTessellation(true)
614+
setTessellationMin(6)
615+
setTessellationMax(24)
613616
setConstraintIterations(4)
614617
setSubsteps(1)
615618
setCameraZoom(1)
616619
setPointerColliderVisible(false)
617620
setPinMode("none")
621+
setWorldSleepGuard(true)
618622
controllerRef.current?.setSleepConfig({ velocityThreshold: 0.001, frameThreshold: 60 })
619623
actionsRef.current?.setSleepConfig(0.001, 60)
620624
}}

src/engine/render/DebugOverlaySystem.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,21 @@ export class DebugOverlaySystem implements EngineSystem {
8282
// Try to compensate for orthographic anisotropy; fall back to uniform if anything is invalid.
8383
const cam = this.view.camera as THREE.Camera & Partial<THREE.OrthographicCamera>
8484
const maybeOrtho = cam as Partial<THREE.OrthographicCamera>
85-
const left = Number((maybeOrtho.left as unknown) ?? NaN)
86-
const right = Number((maybeOrtho.right as unknown) ?? NaN)
87-
const top = Number((maybeOrtho.top as unknown) ?? NaN)
88-
const bottom = Number((maybeOrtho.bottom as unknown) ?? NaN)
85+
const left = typeof maybeOrtho.left === 'number' ? maybeOrtho.left : NaN
86+
const right = typeof maybeOrtho.right === 'number' ? maybeOrtho.right : NaN
87+
const top = typeof maybeOrtho.top === 'number' ? maybeOrtho.top : NaN
88+
const bottom = typeof maybeOrtho.bottom === 'number' ? maybeOrtho.bottom : NaN
8989
const hasOrthoExtents = [left, right, top, bottom].every((v) => Number.isFinite(v))
9090
if (hasOrthoExtents) {
91-
const worldWidth = Math.max(1e-6, right - left)
92-
const worldHeight = Math.max(1e-6, top - bottom)
91+
const rawWidth = right - left
92+
const rawHeight = top - bottom
93+
const worldWidth = Math.max(1e-6, Math.abs(rawWidth))
94+
const worldHeight = Math.max(1e-6, Math.abs(rawHeight))
95+
// Guard: inverted extents → uniform scale fallback
96+
if (rawWidth <= 0 || rawHeight <= 0) {
97+
mesh.scale.set(r, r, 1)
98+
return
99+
}
93100
let viewportWidth = 1
94101
let viewportHeight = 1
95102
const anyView = this.view as unknown as { getViewportPixels?: () => { width: number; height: number } }

src/lib/clothSceneController.ts

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ export class ClothSceneController {
374374
tessellationMin: 6,
375375
tessellationMax: 24,
376376
pointerCollider: false,
377-
pinMode: 'top',
377+
pinMode: 'none',
378378
worldSleepGuardEnabled: true,
379379
}
380380
private onPointerMove = (event: PointerEvent) => this.handlePointerMove(event)
@@ -523,7 +523,7 @@ export class ClothSceneController {
523523
const screenArea = Math.max(1, vpW * vpH)
524524
const s = Math.sqrt(area / screenArea) // proportion of screen by diagonal
525525

526-
const MIN_SEGMENTS = clamp(round(this.debug.tessellationMin ?? 6), 1, 40)
526+
const MIN_SEGMENTS = clamp(round(this.debug.tessellationMin ?? 6), 1, 46)
527527
const MAX_TESSELLATION_CAP = 48
528528
const rawMax = round(maxCap)
529529
const maxUser = clamp(round(this.debug.tessellationMax ?? maxCap), MIN_SEGMENTS + 2, MAX_TESSELLATION_CAP)
@@ -825,20 +825,44 @@ export class ClothSceneController {
825825
for (const p of pts) pins.push(p)
826826
}
827827
this.overlayState.pinMarkers = pins
828-
// Pointer collider radius: derive from first available record (active or static) to approximate
828+
// Pointer collider radius: derive using shared helper; prefer first active item, else first available
829829
let r = 0.01
830+
let found = false
830831
for (const item of this.items.values()) {
831-
const record = item.record
832-
if (!record) continue
833-
const base = Math.min(record.widthMeters || 0, record.heightMeters || 0)
834-
const MIN = 0.0006
835-
const DEFAULT = 0.0012
836-
r = base > 0 ? Math.max(MIN, base / 12) : DEFAULT
832+
if (!item.isActive) continue
833+
r = this.computePointerRadiusFor(item)
834+
found = true
837835
break
838836
}
837+
if (!found) {
838+
for (const item of this.items.values()) {
839+
r = this.computePointerRadiusFor(item)
840+
found = true
841+
break
842+
}
843+
}
839844
this.overlayState.pointerRadius = r
840845
}
841846

847+
/** Computes pointer collider radius in meters mirroring ClothBodyAdapter.getImpulseRadius logic. */
848+
private computePointerRadiusFor(item: ClothItem) {
849+
const attr = item.element.dataset.clothImpulseRadius
850+
const parsed = attr ? Number.parseFloat(attr) : NaN
851+
if (!Number.isNaN(parsed) && parsed > 0) {
852+
return parsed
853+
}
854+
const record = item.record
855+
const widthMeters = record?.widthMeters ?? 0
856+
const heightMeters = record?.heightMeters ?? 0
857+
const base = Math.min(widthMeters, heightMeters)
858+
const MIN_POINTER_RADIUS = 0.0006
859+
const DEFAULT_POINTER_RADIUS = 0.0012
860+
if (base > 0) {
861+
return Math.max(MIN_POINTER_RADIUS, base / 12)
862+
}
863+
return DEFAULT_POINTER_RADIUS
864+
}
865+
842866
private isSimSnapshot(value: unknown): value is import('./simWorld').SimWorldSnapshot {
843867
return Boolean(value && typeof value === 'object' && Array.isArray((value as import('./simWorld').SimWorldSnapshot).bodies))
844868
}

0 commit comments

Comments
 (0)