From 2101d30d98546fe8729b4118f6d07fe8ebb3d656 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Wed, 1 Apr 2026 17:11:58 +0900 Subject: [PATCH] =?UTF-8?q?1830=20=EB=AA=A8=EB=93=88=20=EC=84=A4=EC=B9=98?= =?UTF-8?q?=EC=98=81=EC=97=AD=20=EB=8C=80=EA=B0=81=20=EC=9E=88=EB=8A=94=20?= =?UTF-8?q?=ED=98=84=EC=83=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 48 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 6 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 8492042a..d014b25f 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1756,6 +1756,11 @@ export function useMode() { offsetEdges.push(createOffsetEdge(edge, dx, dy)) }) + // 폴리곤의 감김 방향(signed area)을 구한다 + const signedArea = bevelJoin + ? polygon.edges.reduce((sum, edge) => sum + (edge.vertex1.x * edge.vertex2.y - edge.vertex2.x * edge.vertex1.y), 0) + : 0 + const vertices = [] offsetEdges.forEach((thisEdge, i) => { @@ -1765,9 +1770,22 @@ export function useMode() { if (!vertex.isIntersectionOutside || !bevelJoin) { vertices.push({ x: vertex.x, y: vertex.y }) } else { - // 둔각 + bevelJoin: offset edge 끝점 사용 - vertices.push({ x: prevEdge.vertex2.x, y: prevEdge.vertex2.y }) - vertices.push({ x: thisEdge.vertex1.x, y: thisEdge.vertex1.y }) + // 꼭짓점의 볼록/오목 판별 (cross product와 signed area 비교) + const origPrev = polygon.edges[(i + polygon.edges.length - 1) % polygon.edges.length] + const origThis = polygon.edges[i] + const cross = + (origPrev.vertex2.x - origPrev.vertex1.x) * (origThis.vertex2.y - origThis.vertex1.y) - + (origPrev.vertex2.y - origPrev.vertex1.y) * (origThis.vertex2.x - origThis.vertex1.x) + const isConvex = cross * signedArea > 0 + + if (isConvex) { + // 볼록 꼭짓점: bevel 적용 (offset edge 끝점 사용) + vertices.push({ x: prevEdge.vertex2.x, y: prevEdge.vertex2.y }) + vertices.push({ x: thisEdge.vertex1.x, y: thisEdge.vertex1.y }) + } else { + // 오목 꼭짓점: 교차점 사용 (90도 유지) + vertices.push({ x: vertex.x, y: vertex.y }) + } } } }) @@ -1795,6 +1813,11 @@ export function useMode() { offsetEdges.push(createOffsetEdge(edge, dx, dy)) }) + // 폴리곤의 감김 방향(signed area)을 구한다 + const signedArea = bevelJoin + ? polygon.edges.reduce((sum, edge) => sum + (edge.vertex1.x * edge.vertex2.y - edge.vertex2.x * edge.vertex1.y), 0) + : 0 + const vertices = [] offsetEdges.forEach((thisEdge, i) => { @@ -1804,9 +1827,22 @@ export function useMode() { if (!vertex.isIntersectionOutside || !bevelJoin) { vertices.push({ x: vertex.x, y: vertex.y }) } else { - // 둔각 + bevelJoin: offset edge 끝점 사용 - vertices.push({ x: prevEdge.vertex2.x, y: prevEdge.vertex2.y }) - vertices.push({ x: thisEdge.vertex1.x, y: thisEdge.vertex1.y }) + // 꼭짓점의 볼록/오목 판별 (cross product와 signed area 비교) + const origPrev = polygon.edges[(i + polygon.edges.length - 1) % polygon.edges.length] + const origThis = polygon.edges[i] + const cross = + (origPrev.vertex2.x - origPrev.vertex1.x) * (origThis.vertex2.y - origThis.vertex1.y) - + (origPrev.vertex2.y - origPrev.vertex1.y) * (origThis.vertex2.x - origThis.vertex1.x) + const isConvex = cross * signedArea > 0 + + if (isConvex) { + // 볼록 꼭짓점: bevel 적용 (offset edge 끝점 사용) + vertices.push({ x: prevEdge.vertex2.x, y: prevEdge.vertex2.y }) + vertices.push({ x: thisEdge.vertex1.x, y: thisEdge.vertex1.y }) + } else { + // 오목 꼭짓점: 교차점 사용 (90도 유지) + vertices.push({ x: vertex.x, y: vertex.y }) + } } } })