diff --git a/src/components/estimate/Estimate.jsx b/src/components/estimate/Estimate.jsx
index 9107dde0..6e10a1de 100644
--- a/src/components/estimate/Estimate.jsx
+++ b/src/components/estimate/Estimate.jsx
@@ -1360,11 +1360,10 @@ export default function Estimate({}) {
+ {session?.storeLvl !== '2' && (
- {/* 1차 판매점명 */}
| {getMessage('estimate.detail.saleStoreId')} |
{estimateContextState?.firstSaleStoreName} |
- {/* 견적일 */}
{getMessage('estimate.detail.estimateDate')} *
|
@@ -1374,6 +1373,19 @@ export default function Estimate({}) {
+ )}
+ {session?.storeLvl === '2' && (
+
+ |
+ {getMessage('estimate.detail.estimateDate')} *
+ |
+
+
+
+
+ |
+
+ )}
{/* 2차 판매점명 */}
| {getMessage('estimate.detail.otherSaleStoreId')} |
diff --git a/src/components/floor-plan/modal/basic/step/Trestle.jsx b/src/components/floor-plan/modal/basic/step/Trestle.jsx
index e208a286..4725962d 100644
--- a/src/components/floor-plan/modal/basic/step/Trestle.jsx
+++ b/src/components/floor-plan/modal/basic/step/Trestle.jsx
@@ -7,6 +7,7 @@ import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { roofsState } from '@/store/roofAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
+import { logger } from '@/util/logger'
import { useRecoilState, useRecoilValue } from 'recoil'
import Swal from 'sweetalert2'
import { normalizeDigits } from '@/util/input-utils'
@@ -175,6 +176,19 @@ const Trestle = forwardRef((props, ref) => {
const existingConstruction = constructionList.find((construction) => construction.constTp === trestleState.constTp)
if (existingConstruction) {
setSelectedConstruction(existingConstruction)
+ // 설치 불가능한 공법이면 강제 해제
+ if (existingConstruction.cvrYn !== 'Y') setCvrChecked(false)
+ if (existingConstruction.snowGdPossYn !== 'Y') setSnowGdChecked(false)
+ const resolvedCvrChecked = existingConstruction.cvrYn === 'Y' ? cvrChecked : false
+ const resolvedSnowGdChecked = existingConstruction.snowGdPossYn === 'Y' ? snowGdChecked : false
+ logger.debug('[Trestle handleConstruction] 복원', {
+ index: constructionList.findIndex((c) => c.constTp === existingConstruction.constTp),
+ constNm: existingConstruction.constNm ?? existingConstruction.constTp,
+ 'cvrYn (설치가능여부)': existingConstruction.cvrYn,
+ 'cvrChecked (사용자선택)': resolvedCvrChecked,
+ 'snowGdPossYn (설치가능여부)': existingConstruction.snowGdPossYn,
+ 'snowGdChecked (사용자선택)': resolvedSnowGdChecked,
+ })
} else if (autoSelectStep === 'construction') {
// 자동 선택: 첫 번째 가능한 construction 선택
const availableConstructions = constructionList.filter((construction) => construction.constPossYn === 'Y')
@@ -411,10 +425,24 @@ const Trestle = forwardRef((props, ref) => {
},
})
- setCvrYn(constructionList[index].cvrYn)
- setSnowGdPossYn(constructionList[index].snowGdPossYn)
- setCvrChecked(true)
- setSnowGdChecked(false)
+ const newCvrYn = constructionList[index].cvrYn
+ const newCvrChecked = newCvrYn === 'Y'
+ const newSnowGdPossYn = constructionList[index].snowGdPossYn
+ const newSnowGdChecked = newSnowGdPossYn === 'Y'
+
+ setCvrYn(newCvrYn)
+ setSnowGdPossYn(newSnowGdPossYn)
+ setCvrChecked(newCvrChecked)
+ setSnowGdChecked(newSnowGdChecked)
+
+ logger.debug('[Trestle handleConstruction]', {
+ index,
+ constNm: constructionList[index].constNm,
+ 'cvrYn (설치가능여부)': newCvrYn,
+ 'cvrChecked (사용자선택)': newCvrChecked,
+ 'snowGdPossYn (설치가능여부)': newSnowGdPossYn,
+ 'snowGdChecked (사용자선택)': newSnowGdChecked,
+ })
}
}
@@ -442,10 +470,10 @@ const Trestle = forwardRef((props, ref) => {
...constructionList.find((data) => data.constTp === trestleState.constTp),
cvrYn: cvrYn,
snowGdPossYn: snowGdPossYn,
- cvrChecked: cvrChecked,
- snowGdChecked: snowGdChecked,
- setupCover: cvrChecked ?? false,
- setupSnowCover: snowGdChecked ?? false,
+ cvrChecked: cvrYn === 'Y' ? cvrChecked : false,
+ snowGdChecked: snowGdPossYn === 'Y' ? snowGdChecked : false,
+ setupCover: cvrYn === 'Y' ? (cvrChecked ?? false) : false,
+ setupSnowCover: snowGdPossYn === 'Y' ? (snowGdChecked ?? false) : false,
},
trestleDetail: trestleDetail,
}
@@ -481,10 +509,10 @@ const Trestle = forwardRef((props, ref) => {
...constructionList.find((data) => trestleState.constTp === data.constTp),
cvrYn,
snowGdPossYn,
- cvrChecked,
- snowGdChecked,
- setupCover: cvrChecked ?? false,
- setupSnowCover: snowGdChecked ?? false,
+ cvrChecked: cvrYn === 'Y' ? cvrChecked : false,
+ snowGdChecked: snowGdPossYn === 'Y' ? snowGdChecked : false,
+ setupCover: cvrYn === 'Y' ? (cvrChecked ?? false) : false,
+ setupSnowCover: snowGdPossYn === 'Y' ? (snowGdChecked ?? false) : false,
},
trestleDetail: trestleDetail,
}
@@ -861,7 +889,11 @@ const Trestle = forwardRef((props, ref) => {
disabled={!cvrYn || cvrYn === 'N'}
checked={!cvrYn || cvrYn === 'N' ? false : cvrChecked ?? true}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })}
- onChange={() => setCvrChecked(!cvrChecked)}
+ onChange={() => {
+ const next = !cvrChecked
+ setCvrChecked(next)
+ logger.debug('[처마커버 체크 변경]', { 'cvrYn (설치가능여부)': cvrYn, 'cvrChecked (사용자선택)': next })
+ }}
/>
@@ -872,7 +904,11 @@ const Trestle = forwardRef((props, ref) => {
disabled={!snowGdPossYn || snowGdPossYn === 'N'}
checked={snowGdChecked || false}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, snowGdChecked: !trestleState.snowGdChecked } })}
- onChange={() => setSnowGdChecked(!snowGdChecked)}
+ onChange={() => {
+ const next = !snowGdChecked
+ setSnowGdChecked(next)
+ logger.debug('[눈막이 체크 변경]', { 'snowGdPossYn (설치가능여부)': snowGdPossYn, 'snowGdChecked (사용자선택)': next })
+ }}
/>
diff --git a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
index c98b75f0..fe7b8e11 100644
--- a/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
+++ b/src/components/floor-plan/modal/circuitTrestle/CircuitTrestleSetting.jsx
@@ -149,9 +149,9 @@ export default function CircuitTrestleSetting({ id }) {
return {
x,
y,
- width: Math.floor(width),
- height: Math.floor(height),
- area: Math.floor(width) * Math.floor(height),
+ width: Math.round(width),
+ height: Math.round(height),
+ area: Math.round(width) * Math.round(height),
}
})
@@ -227,7 +227,7 @@ export default function CircuitTrestleSetting({ id }) {
// 인접 모듈 여부 확인
const isAdjacent = (current, target) => {
const gap = getGap(current, target)
- return gap >= 0 && gap <= primaryInterval + 1
+ return gap >= 0 && gap <= primaryInterval + 2
}
// 정렬된 모듈 순회
@@ -238,7 +238,7 @@ export default function CircuitTrestleSetting({ id }) {
const directTargets = findTargetModules(current, secondaryInterval)
const closestTarget = getClosestTarget(directTargets)
- if (closestTarget && isAdjacent(current, closestTarget) && closestTarget.area > current.area) {
+ if (closestTarget && isAdjacent(current, closestTarget) && closestTarget.area - current.area > 1) {
return false // 검증 실패
}
@@ -264,7 +264,7 @@ export default function CircuitTrestleSetting({ id }) {
})
const closestHalf = getClosestTarget(findHalfTarget)
- if (closestHalf && isAdjacent(current, closestHalf) && closestHalf.area > current.area) {
+ if (closestHalf && isAdjacent(current, closestHalf) && closestHalf.area - current.area > 1) {
return false // 검증 실패
}
}
diff --git a/src/hooks/module/useModuleTabContents.js b/src/hooks/module/useModuleTabContents.js
index f6c46314..f7484bdb 100644
--- a/src/hooks/module/useModuleTabContents.js
+++ b/src/hooks/module/useModuleTabContents.js
@@ -5,6 +5,7 @@ import { useMasterController } from '@/hooks/common/useMasterController'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import { moduleSelectionDataState, moduleSelectionInitParamsState, selectedModuleState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty, isEqualObjects } from '@/util/common-utils'
+import { logger } from '@/util/logger'
export function useModuleTabContents({ tabIndex, addRoof, setAddedRoofs, roofTab, tempModuleSelectionData, setTempModuleSelectionData }) {
const globalPitchText = useRecoilValue(pitchTextSelector) //피치 텍스트
@@ -285,17 +286,58 @@ export function useModuleTabContents({ tabIndex, addRoof, setAddedRoofs, roofTab
selectedConstruction.setupSnowCover = false //눈막이금구 설치 여부
selectedConstruction.selectedIndex = index
+ logger.debug('[handleConstruction] 공법 선택', {
+ index,
+ constNm: selectedConstruction.constNm,
+ cvrYn: selectedConstruction.cvrYn,
+ snowGdPossYn: selectedConstruction.snowGdPossYn,
+ moduleConstructionSelectionData,
+ })
+
setCvrYn(selectedConstruction.cvrYn)
setSnowGdPossYn(selectedConstruction.snowGdPossYn)
- //기존에 선택된 데이터가 있으면 체크한다
+ //기존에 선택된 데이터가 있으면 복원
if (moduleConstructionSelectionData && moduleConstructionSelectionData.construction) {
selectedConstruction.setupCover = moduleConstructionSelectionData.construction.setupCover || false
selectedConstruction.setupSnowCover = moduleConstructionSelectionData.construction.setupSnowCover || false
setCvrChecked(selectedConstruction.setupCover)
setSnowGdChecked(selectedConstruction.setupSnowCover)
+ logger.debug('[handleConstruction] 기존 데이터 복원', {
+ setupCover: selectedConstruction.setupCover,
+ setupSnowCover: selectedConstruction.setupSnowCover,
+ })
+ } else {
+ // 최초 선택: 설치 가능하면 디폴트 체크
+ const defaultCvr = selectedConstruction.cvrYn === 'Y'
+ const defaultSnowGd = selectedConstruction.snowGdPossYn === 'Y'
+ selectedConstruction.setupCover = defaultCvr
+ selectedConstruction.setupSnowCover = defaultSnowGd
+ setCvrChecked(defaultCvr)
+ setSnowGdChecked(defaultSnowGd)
+ logger.debug('[handleConstruction] 최초 선택 디폴트', {
+ defaultCvr,
+ defaultSnowGd,
+ })
}
+ // 설치 불가능한 공법이면 강제 해제
+ if (selectedConstruction.cvrYn !== 'Y') {
+ selectedConstruction.setupCover = false
+ setCvrChecked(false)
+ logger.debug('[handleConstruction] 처마커버 강제 해제 (cvrYn !== Y)')
+ }
+ if (selectedConstruction.snowGdPossYn !== 'Y') {
+ selectedConstruction.setupSnowCover = false
+ setSnowGdChecked(false)
+ logger.debug('[handleConstruction] 눈막이 강제 해제 (snowGdPossYn !== Y)')
+ }
+
+ logger.debug('[handleConstruction] 최종 상태', {
+ setupCover: selectedConstruction.setupCover,
+ setupSnowCover: selectedConstruction.setupSnowCover,
+ })
+
setSelectedConstruction(selectedConstruction)
} else {
constructionRef.current.forEach((ref) => {
diff --git a/src/hooks/surface/useSurfaceShapeBatch.js b/src/hooks/surface/useSurfaceShapeBatch.js
index 7d47b02e..602dd209 100644
--- a/src/hooks/surface/useSurfaceShapeBatch.js
+++ b/src/hooks/surface/useSurfaceShapeBatch.js
@@ -315,6 +315,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
// console.log('yInversion', yInversion) //좌우반전
changeSurfaceLineType(batchSurface)
+ setPolygonLinesActualSize(batchSurface, true)
if (setIsHidden) setIsHidden(false)
})
@@ -1670,6 +1671,11 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
drawDirectionArrow(currentObject)
setTimeout(() => {
changeSurfaceLineType(currentObject)
+ // 회전된 배치면뿐 아니라 모든 배치면의 길이를 재계산
+ const allRoofs = canvas.getObjects().filter((o) => o.name === POLYGON_TYPE.ROOF && o.type === 'QPolygon')
+ allRoofs.forEach((roof) => {
+ setPolygonLinesActualSize(roof, true)
+ })
currentObject.dirty = true
currentObject.setCoords()
canvas.renderAll()
diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js
index 8ee92c3c..a7868622 100644
--- a/src/util/skeleton-utils.js
+++ b/src/util/skeleton-utils.js
@@ -324,7 +324,7 @@ const movingLineFromSkeleton = (roofId, canvas) => {
const cleaned = removeNonOrthogonalPoints(newPoints);
- console.log(cleaned); // 결과: 4개의 점만 남음 (P1, P2, P3, P4)
+ // console.log(cleaned)
return cleaned;
}
@@ -380,127 +380,98 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
const hipLines = canvas.getObjects().filter((object) => object.name === 'hip' && object.parentId === roofId) || []
const ridgeLines = canvas.getObjects().filter((object) => object.name === 'ridge' && object.parentId === roofId) || []
- //const skeletonLines = [];
- // 1. 지붕 폴리곤 좌표 전처리
+ // 1. 지붕 폴리곤 유효성 검사
const coordinates = preprocessPolygonCoordinates(roof.points)
if (coordinates.length < 3) {
console.warn('Polygon has less than 3 unique points. Cannot generate skeleton.')
return
}
- const moveFlowLine = roof.moveFlowLine || 0 // Provide a default value
- const moveUpDown = roof.moveUpDown || 0 // Provide a default value
+ const moveFlowLine = roof.moveFlowLine || 0
+ const moveUpDown = roof.moveUpDown || 0
+ // 닫힌 폴리곤(첫점 = 끝점)인 경우 마지막 중복 점 제거
const isClosedPolygon = (points) =>
points.length > 1 && isSamePoint(points[0], points[points.length - 1])
+ // roofLinePoints : drawRoofPolygon에서 baseLine + 각 변의 offset으로 계산된 실제 처마 외곽선
+ // orderedBaseLinePoints: 외벽선(baseLine) 꼭짓점을 연결 순서대로 정렬
+ const roofLinePoints = isClosedPolygon(roof.points) ? roof.points.slice(0, -1) : roof.points
const orderedBaseLinePoints = isClosedPolygon(baseLinePoints) ? baseLinePoints.slice(0, -1) : baseLinePoints
- // baseLine 폴리곤의 중심 계산 (offset 방향 결정용)
+ // 2. 각 baseLine 꼭짓점 → 대응하는 roofLine 꼭짓점 방향벡터 계산
+ // 각 변의 offset이 달라도 방향은 항상 baseLine → roofLine (바깥쪽)
+ const contactData = orderedBaseLinePoints.map((point, index) => {
+ const roofPoint = roofLinePoints[index]
+ if (!roofPoint) return { dx: 0, dy: 0, signDx: 0, signDy: 0 }
+
+ const dx = roofPoint.x - point.x
+ const dy = roofPoint.y - point.y
+ return { dx, dy, signDx: Math.sign(dx), signDy: Math.sign(dy) }
+ })
+
+ // 3. maxStep: 모든 꼭짓점 중 가장 큰 45도 대각 step 값
+ // - 각 꼭짓점의 min(|dx|, |dy|) = 해당 꼭짓점에서 45도 방향으로 확장 가능한 거리
+ // - 전체에서 max를 취해 SkeletonBuilder 입력 다각형이 충분히 확장되도록 보장
+ // - 모든 꼭짓점에 동일한 maxStep을 적용하므로 각 변의 offset이 달라도
+ // SkeletonBuilder 입력은 항상 대칭 다각형이 되어 스켈레톤이 안정적으로 생성됨
+ const maxStep = contactData.reduce((max, data) => {
+ return Math.max(max, Math.min(Math.abs(data.dx), Math.abs(data.dy)))
+ }, 0)
+
+ // baseLine 폴리곤 중심점 (dx 또는 dy가 0인 꼭짓점의 확장 방향 보정에 사용)
const centroid = orderedBaseLinePoints.reduce((acc, p) => {
acc.x += p.x / orderedBaseLinePoints.length
acc.y += p.y / orderedBaseLinePoints.length
return acc
}, { x: 0, y: 0 })
- // baseLine을 offset만큼 바깥으로 평행이동
- const offsetLine = (line, index) => {
- const sp = line.startPoint, ep = line.endPoint
- let offset = line.attributes?.offset ?? 0
+ // 4. 모든 꼭짓점을 동일한 maxStep으로 45도 대각 방향 확장
+ // - dx 또는 dy가 0인 경우(한 축 이동만 있는 꼭짓점): centroid 기준 바깥 방향으로 보정
+ let roofLineContactPoints = orderedBaseLinePoints.map((point, index) => {
+ const data = contactData[index]
- // 모든 offset이 동일할 때 수치적 안정성을 위해 약간의 변화 추가
- if (baseLines.every(l => (l.attributes?.offset ?? 0) === offset)) {
- offset += index * 0.01 // 각 라인에 약간의 차이 추가
- }
-
- const edx = ep.x - sp.x, edy = ep.y - sp.y
- const len = Math.hypot(edx, edy)
- if (len === 0) return { sp: { ...sp }, ep: { ...ep } }
-
- // 수직 벡터 계산 (항상 바깥 방향)
- let nx = -edy / len, ny = edx / len
-
- // 방향 결정 로직 제거 - 항상 바깥으로 확장
- // if (nx * (centroid.x - mid.x) + ny * (centroid.y - mid.y) > 0) { nx = -nx; ny = -ny }
+ let signDx = data.signDx
+ let signDy = data.signDy
+ if (signDx === 0) signDx = point.x >= centroid.x ? 1 : -1
+ if (signDy === 0) signDy = point.y >= centroid.y ? 1 : -1
return {
- sp: { x: sp.x + nx * offset, y: sp.y + ny * offset },
- ep: { x: ep.x + nx * offset, y: ep.y + ny * offset }
+ x: point.x + signDx * maxStep,
+ y: point.y + signDy * maxStep
}
- }
-
- // 두 직선의 교차점
- const lineIntersect = (a1, a2, b1, b2) => {
- const denom = (a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)
- if (Math.abs(denom) < 0.001) return null
- const t = ((a1.x - b1.x) * (b1.y - b2.y) - (a1.y - b1.y) * (b1.x - b2.x)) / denom
- return { x: a1.x + t * (a2.x - a1.x), y: a1.y + t * (a2.y - a1.y) }
- }
-
- // 각 꼭짓점에서 인접 두 baseLine의 offset 교차점으로 확장 좌표 계산
- let changRoofLinePoints = orderedBaseLinePoints.map((point) => {
- const adjLines = baseLines.filter(line =>
- isSamePoint(point, line.startPoint) || isSamePoint(point, line.endPoint)
- )
- if (adjLines.length < 2) return { ...point }
- const idx1 = baseLines.indexOf(adjLines[0])
- const idx2 = baseLines.indexOf(adjLines[1])
- const oL1 = offsetLine(adjLines[0], idx1), oL2 = offsetLine(adjLines[1], idx2)
- return lineIntersect(oL1.sp, oL1.ep, oL2.sp, oL2.ep) || { ...point }
})
- // 중복 좌표 및 일직선 위의 불필요한 점 제거 (L자 확장 시 사각형으로 단순화)
- // const simplifyPolygon = (pts) => {
- // let result = [...pts]
+ // 5. changRoofLinePoints: SkeletonBuilder에 입력할 최종 다각형
+ // - roofLineContactPoints 방향으로 maxContactDistance(= √2 × maxStep) 만큼 확장
+ // - 45도 대각 방향(signDx, signDy 각 ±1)이므로 실제 x·y 이동량은 각 축으로 maxStep
+ const maxContactDistance = Math.hypot(maxStep, maxStep)
- // // 복잡한 형태 보호를 위해 최소 6개 점 유지
- // if (result.length <= 6) return result
+ let changRoofLinePoints = orderedBaseLinePoints.map((point, index) => {
+ const contactPoint = roofLineContactPoints[index]
+ if (!contactPoint) return point
- // let changed = true
- // while (changed) {
- // changed = false
- // for (let i = result.length - 1; i >= 0; i--) {
- // const next = result[(i + 1) % result.length]
- // if (Math.abs(result[i].x - next.x) < 1.0 && Math.abs(result[i].y - next.y) < 1.0) {
- // result.splice(i, 1)
- // changed = true
- // }
- // }
- // // 최소 6개 점 유지 확인
- // if (result.length <= 6) break
+ const dx = contactPoint.x - point.x
+ const dy = contactPoint.y - point.y
+ const len = Math.hypot(dx, dy)
+ if (len === 0 || maxContactDistance === 0) return point
- // for (let i = result.length - 1; i >= 0 && result.length > 3; i--) {
- // const prev = result[(i - 1 + result.length) % result.length]
- // const curr = result[i]
- // const next = result[(i + 1) % result.length]
- // const cross = (curr.x - prev.x) * (next.y - prev.y) - (curr.y - prev.y) * (next.x - prev.x)
- // // 복잡한 형태 보호를 위해 임계값 대폭 증가
- // if (Math.abs(cross) < 50) {
- // result.splice(i, 1)
- // changed = true
- // }
- // }
- // }
- // return result
- // }
- // changRoofLinePoints = simplifyPolygon(changRoofLinePoints)
+ return {
+ x: point.x + (dx / len) * maxContactDistance,
+ y: point.y + (dy / len) * maxContactDistance
+ }
+ })
- let roofLineContactPoints = [...changRoofLinePoints]
-
- console.log('baseLinePoints:', orderedBaseLinePoints)
- console.log('changRoofLinePoints:', changRoofLinePoints)
-
- //마루이동
+ // 6. 마루이동(용마루 위치 수동 조정)이 설정된 경우 movingLineFromSkeleton 결과로 대체
if (moveFlowLine !== 0 || moveUpDown !== 0) {
const movedPoints = movingLineFromSkeleton(roofId, canvas)
roofLineContactPoints = movedPoints
changRoofLinePoints = movedPoints
}
- // changRoofLinePoints 좌표를 roof.skeletonPoints에 저장 (원본 roof.points는 유지)
+ // changRoofLinePoints를 roof.skeletonPoints에 저장 (roof.points 원본은 유지)
roof.skeletonPoints = changRoofLinePoints.map(p => ({ x: p.x, y: p.y }))
- console.log('points:', changRoofLinePoints)
const geoJSONPolygon = toGeoJSON(changRoofLinePoints)
try {
@@ -511,7 +482,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
// 스켈레톤 데이터를 기반으로 내부선 생성
roof.innerLines = roof.innerLines || []
roof.innerLines = createInnerLinesFromSkeleton(roofId, canvas, skeleton, textMode)
- console.log("roofInnerLines:::", roof.innerLines);
+ //console.log("roofInnerLines:::", roof.innerLines);
// 캔버스에 스켈레톤 상태 저장
if (!canvas.skeletonStates) {
canvas.skeletonStates = {}