fix: [rd] slat roll을 단일 실린더로 변경 (접시 모양 제거)

- 디스크/링 제거, 샤프트 전체에 감기는 하나의 실린더로 표현
- 최소 두께 8mm 보장 (적은 감김에서도 보이도록)
- 나선형 표면 라인으로 감긴 질감 표현
This commit is contained in:
김보곤
2026-03-09 07:58:46 +09:00
parent 3ad8e24ac3
commit 8657fd314c

View File

@@ -1294,101 +1294,46 @@ function fs3dBuild() {
meshes.slats.add(lineGroup);
}
// === SLAT ROLL (샤프트에 감긴 슬랫 — 어느 각도에서도 잘 보이도록) ===
// === SLAT ROLL (샤프트에 감긴 슬랫 — 샤프트 전체에 두껍게 감기는 실린더) ===
const rolledH = H - shutterH;
if (rolledH > 0) {
const wrapThick = S.productType === 'steel' ? 10 : 1;
const shaftR = b.shaftDia / 2;
const rollThick = Math.sqrt(rolledH * wrapThick / Math.PI);
const rollThick = Math.max(Math.sqrt(rolledH * wrapThick / Math.PI), 8);
const rollOuterR = shaftR + rollThick;
const rollLen = W - 40; // 샤프트보다 약간 짧게 (양쪽 20mm씩)
const rollLen = W - 30;
meshes.slatRoll = new THREE.Group();
// 메인 롤 실린더
const rollGeo = new THREE.CylinderGeometry(rollOuterR, rollOuterR, rollLen, 48);
// 하나의 Mesh로 표현 (접시 모양 방지)
const rollGeo = new THREE.CylinderGeometry(rollOuterR, rollOuterR, rollLen, 48, 1, false);
rollGeo.rotateZ(Math.PI / 2);
const rollColor = S.productType === 'steel' ? 0xC9B89A : 0xc084fc;
const rollMat = new THREE.MeshStandardMaterial({
color: rollColor,
metalness: S.productType === 'steel' ? 0.25 : 0,
roughness: S.productType === 'steel' ? 0.55 : 0.8,
color: S.productType === 'steel' ? 0xC9B89A : 0xc084fc,
metalness: S.productType === 'steel' ? 0.2 : 0,
roughness: S.productType === 'steel' ? 0.5 : 0.8,
opacity: 0.93,
transparent: true,
opacity: 0.92,
});
const rollMesh = new THREE.Mesh(rollGeo, rollMat);
meshes.slatRoll.add(rollMesh);
// 양쪽 끝 디스크 (측면에서 롤 두께 확인 가능)
const discMat = new THREE.MeshStandardMaterial({
color: S.productType === 'steel' ? 0xA89070 : 0x9b6dff,
metalness: 0.3, roughness: 0.5, side: THREE.DoubleSide,
});
const discShape = new THREE.Shape();
discShape.absarc(0, 0, rollOuterR, 0, Math.PI * 2, false);
const innerHole = new THREE.Path();
innerHole.absarc(0, 0, shaftR + 1, 0, Math.PI * 2, true);
discShape.holes.push(innerHole);
const discGeo = new THREE.ShapeGeometry(discShape, 32);
// 좌측 디스크
const discL = new THREE.Mesh(discGeo, discMat);
discL.rotation.y = Math.PI / 2;
discL.position.set(-rollLen / 2, 0, 0);
meshes.slatRoll.add(discL);
// 우측 디스크
const discR = new THREE.Mesh(discGeo.clone(), discMat);
discR.rotation.y = Math.PI / 2;
discR.position.set(rollLen / 2, 0, 0);
meshes.slatRoll.add(discR);
// 감긴 슬랫 골 표현 (나선형 라인 — 여러 위치에서 반복)
if (rollThick > 3) {
const ringGroup = new THREE.Group();
const ringCount = Math.min(Math.ceil(rollThick / (wrapThick * 0.12)), 15);
const ringPositions = [-rollLen * 0.4, -rollLen * 0.15, rollLen * 0.15, rollLen * 0.4];
const ringLineMat = new THREE.LineBasicMaterial({
color: S.productType === 'steel' ? 0x8B7355 : 0x7c4dff,
opacity: 0.6, transparent: true
});
for (const xPos of ringPositions) {
for (let i = 1; i <= ringCount; i++) {
const r = shaftR + (rollThick * i / (ringCount + 1));
const pts = [];
for (let a = 0; a <= 64; a++) {
const angle = (a / 64) * Math.PI * 2;
pts.push(new THREE.Vector3(0, Math.sin(angle) * r, Math.cos(angle) * r));
}
const ringGeo = new THREE.BufferGeometry().setFromPoints(pts);
const ring = new THREE.Line(ringGeo, ringLineMat);
ring.position.x = xPos;
ringGroup.add(ring);
}
}
meshes.slatRoll.add(ringGroup);
}
// 나선형 표면 라인 (롤 표면에 감긴 느낌)
if (rollThick > 5) {
const spiralMat = new THREE.LineBasicMaterial({ color: S.productType === 'steel' ? 0x7A6040 : 0x6a3ddd, opacity: 0.4, transparent: true });
const spiralCount = 3;
for (let s = 0; s < spiralCount; s++) {
const spiralPts = [];
const turns = Math.max(2, Math.min(rollLen / 80, 20));
const startAngle = (s / spiralCount) * Math.PI * 2;
for (let i = 0; i <= turns * 32; i++) {
const t = i / (turns * 32);
const angle = startAngle + t * turns * Math.PI * 2;
const x = -rollLen / 2 + t * rollLen;
const surfR = rollOuterR + 0.5;
spiralPts.push(new THREE.Vector3(x, Math.sin(angle) * surfR, Math.cos(angle) * surfR));
}
const spiralGeo = new THREE.BufferGeometry().setFromPoints(spiralPts);
meshes.slatRoll.add(new THREE.Line(spiralGeo, spiralMat));
}
}
meshes.slatRoll = new THREE.Mesh(rollGeo, rollMat);
meshes.slatRoll.position.set(0, shaftY, 0);
scene.add(meshes.slatRoll);
// 표면 나선 라인 (감긴 슬랫 질감)
if (rollThick > 5) {
const spiralMat = new THREE.LineBasicMaterial({ color: S.productType === 'steel' ? 0x8B7355 : 0x7c4dff, opacity: 0.5, transparent: true });
for (let s = 0; s < 4; s++) {
const pts = [];
const turns = Math.max(3, Math.min(rollLen / 60, 25));
const startA = (s / 4) * Math.PI * 2;
for (let i = 0; i <= turns * 32; i++) {
const t = i / (turns * 32);
const angle = startA + t * turns * Math.PI * 2;
const x = -rollLen / 2 + t * rollLen;
pts.push(new THREE.Vector3(x, Math.sin(angle) * (rollOuterR + 0.5), Math.cos(angle) * (rollOuterR + 0.5)));
}
const geo = new THREE.BufferGeometry().setFromPoints(pts);
meshes.slatRoll.add(new THREE.Line(geo, spiralMat));
}
}
}
// === BOTTOM BAR ===