From 8657fd314c1bfafe846b9dbc84e75b099d0e1ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Mon, 9 Mar 2026 07:58:46 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[rd]=20slat=20roll=EC=9D=84=20=EB=8B=A8?= =?UTF-8?q?=EC=9D=BC=20=EC=8B=A4=EB=A6=B0=EB=8D=94=EB=A1=9C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(=EC=A0=91=EC=8B=9C=20=EB=AA=A8=EC=96=91=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 디스크/링 제거, 샤프트 전체에 감기는 하나의 실린더로 표현 - 최소 두께 8mm 보장 (적은 감김에서도 보이도록) - 나선형 표면 라인으로 감긴 질감 표현 --- .../rd/fire-shutter-drawing/index.blade.php | 111 +++++------------- 1 file changed, 28 insertions(+), 83 deletions(-) diff --git a/resources/views/rd/fire-shutter-drawing/index.blade.php b/resources/views/rd/fire-shutter-drawing/index.blade.php index baa78a8c..07c21668 100644 --- a/resources/views/rd/fire-shutter-drawing/index.blade.php +++ b/resources/views/rd/fire-shutter-drawing/index.blade.php @@ -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 ===