From 77bca24e7c09e22eccaf156c3e7ad0908b576b3c Mon Sep 17 00:00:00 2001 From: ysCha Date: Wed, 13 May 2026 14:27:06 +0900 Subject: [PATCH] =?UTF-8?q?[SK-EXTENDED-PERSIST]=20hip=20=5F=5Fextended=20?= =?UTF-8?q?=ED=94=8C=EB=9E=98=EA=B7=B8=20save/load=20=EB=B3=B4=EC=A1=B4=20?= =?UTF-8?q?=E2=80=94=20attributes.extended=20=EB=8F=99=EA=B8=B0=20+=20cons?= =?UTF-8?q?ole=E2=86=92logger?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roofcover/useRoofAllocationSetting.js | 44 +- src/util/skeleton-utils.js | 392 +++++++++--------- 2 files changed, 222 insertions(+), 214 deletions(-) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index e99c122b..88e7162e 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -36,6 +36,7 @@ import { calcLineActualSize2 } from '@/util/qpolygon-utils' import { notifyLowPitchRestrictionForRoofs, isLowPitchRestricted, LOW_PITCH_RESTRICTED_ROOF_IDS } from '@/util/roof-pitch-warning' // [LOW-PITCH-DIAG 2026-05-06 TEMP] 진단용 — 검증 후 import + 호출 함께 제거 import { debugCapture } from '@/util/debugCapture' +import { logger } from '@/util/logger' export function useRoofAllocationSetting(id) { const canvas = useRecoilValue(canvasState) @@ -217,7 +218,7 @@ export function useRoofAllocationSetting(id) { })) setCurrentRoofList(normalizedRoofs) } catch (error) { - console.error('Data fetching error:', error) + logger.error('Data fetching error:', error) } } @@ -521,7 +522,7 @@ export function useRoofAllocationSetting(id) { const extLines = roofBase.lines.filter((l) => l.lineName === 'extensionLine') if (extLines.length === 0) { - console.log(`[INTEGRATE] roofBase.id=${roofBase.id} extensionLine 없음 → skip`) + logger.log(`[INTEGRATE] roofBase.id=${roofBase.id} extensionLine 없음 → skip`) return } @@ -563,7 +564,7 @@ export function useRoofAllocationSetting(id) { }) if (!sk) { - console.log( + logger.log( `[INTEGRATE] ext 짝없음 ` + `(${extP1.x.toFixed(1)},${extP1.y.toFixed(1)})→(${extP2.x.toFixed(1)},${extP2.y.toFixed(1)})` ) @@ -571,8 +572,9 @@ export function useRoofAllocationSetting(id) { } // [2026-04-30] sk 가 이미 SK 빌드 단계에서 연장된 경우 → merge 스킵. - if (sk.__extended) { - console.log( + // [save/load 보존 2026-05-13] runtime __extended 는 저장 시 사라지므로 attributes.extended 도 함께 검사. + if (sk.__extended || sk.attributes?.extended) { + logger.log( `[INTEGRATE] sk 이미 연장됨(id=${sk.id}) → merge 스킵, ext 는 lines 에서만 제거(canvas 유지)` ) extLinesOnly.push(ext) @@ -617,7 +619,7 @@ export function useRoofAllocationSetting(id) { }) mergedLine.length = totalLen - console.log( + logger.log( `[INTEGRATE] merge ` + `ext=(${extP1.x.toFixed(1)},${extP1.y.toFixed(1)})→(${extP2.x.toFixed(1)},${extP2.y.toFixed(1)}) len=${extLen.toFixed(1)} ` + `sk[${sk.lineName || sk.name}]=(${skP1.x.toFixed(1)},${skP1.y.toFixed(1)})→(${skP2.x.toFixed(1)},${skP2.y.toFixed(1)}) len=${skLen.toFixed(1)} ` + @@ -630,7 +632,7 @@ export function useRoofAllocationSetting(id) { }) if (merged.length === 0 && extLinesOnly.length === 0) { - console.log(`[INTEGRATE] 통합 대상 없음 ext=${extLines.length}`) + logger.log(`[INTEGRATE] 통합 대상 없음 ext=${extLines.length}`) return } @@ -646,7 +648,7 @@ export function useRoofAllocationSetting(id) { removedExt.forEach((l) => canvas.remove(l)) removedSk.forEach((l) => canvas.remove(l)) - console.log( + logger.log( `[INTEGRATE] 완료 roofBase.id=${roofBase.id} ` + `ext제거=${removedExt.length} sk제거=${removedSk.length} merged=${merged.length} ` + `extKeptInCanvas=${extLinesOnly.length} ` + @@ -660,7 +662,7 @@ export function useRoofAllocationSetting(id) { const apply = () => { const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF && !obj.roofMaterial) const wallLines = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL) - console.log(`[ALLOC] apply() 진입. roofBases=${roofBases.length}`) + logger.log(`[ALLOC] apply() 진입. roofBases=${roofBases.length}`) roofBases.forEach((roofBase) => { try { // 지붕 할당 로직에 extensionLine 추가 @@ -675,7 +677,7 @@ export function useRoofAllocationSetting(id) { snapByLineName[o.lineName] = (snapByLineName[o.lineName] || 0) + 1 snapByName[o.name || '?'] = (snapByName[o.name || '?'] || 0) + 1 }) - console.log( + logger.log( `[ALLOC-DEBUG] roofBase.id=${roofBase.id} ` + `roofBase.lines=${roofBase.lines?.length || 0} ` + `roofBase.innerLines=${roofBase.innerLines?.length || 0} ` + @@ -684,15 +686,15 @@ export function useRoofAllocationSetting(id) { `byName=${JSON.stringify(snapByName)}` ) roofEaveHelpLines.forEach((o, i) => { - console.log( + logger.log( ` [ALLOC-DEBUG] picked[${i}] name=${o.name} lineName=${o.lineName} ` + `(${o.x1?.toFixed(1)},${o.y1?.toFixed(1)})→(${o.x2?.toFixed(1)},${o.y2?.toFixed(1)}) ` + `len=${Math.hypot((o.x2 || 0) - (o.x1 || 0), (o.y2 || 0) - (o.y1 || 0)).toFixed(1)}` ) }) - // console.log('roofBase.id:', roofBase.id) - // console.log('roofEaveHelpLines:', roofEaveHelpLines) - // console.log('extensionLines found:', roofEaveHelpLines.filter(l => l.lineName === 'extensionLine')) + // logger.log('roofBase.id:', roofBase.id) + // logger.log('roofEaveHelpLines:', roofEaveHelpLines) + // logger.log('extensionLines found:', roofEaveHelpLines.filter(l => l.lineName === 'extensionLine')) if (roofEaveHelpLines.length > 0) { if (roofBase.lines) { // Filter out any eaveHelpLines that are already in lines to avoid duplicates @@ -703,8 +705,8 @@ export function useRoofAllocationSetting(id) { // extensionLine과 일반 eaveHelpLine 분리 const extensionLines = newEaveLines.filter(line => line.lineName === 'extensionLine') const normalEaveLines = newEaveLines.filter(line => line.lineName === 'eaveHelpLine') - // console.log('extensionLines count:', extensionLines.length) - // console.log('normalEaveLines count:', normalEaveLines.length) + // logger.log('extensionLines count:', extensionLines.length) + // logger.log('normalEaveLines count:', normalEaveLines.length) // 일반 eaveHelpLine만 Overlap 판단에 사용 const linesToKeep = roofBase.lines.filter(roofLine => { @@ -747,7 +749,7 @@ export function useRoofAllocationSetting(id) { isPointInside(eX2, eY2, rX1, rY1, rX2, rY2); if (isOverlapping) { - console.log('Removing overlapping line:', roofLine); + logger.log('Removing overlapping line:', roofLine); return true; // 포개지는 경우에만 삭제 } } @@ -759,7 +761,7 @@ export function useRoofAllocationSetting(id) { // Combine remaining lines with newEaveLines roofBase.lines = [...linesToKeep, ...normalEaveLines, ...extensionLines]; // [ALLOC-DEBUG] 병합 결과 breakdown - console.log( + logger.log( `[ALLOC-DEBUG] 병합완료 roofBase.id=${roofBase.id} ` + `linesToKeep=${linesToKeep.length} ` + `normalEave=${normalEaveLines.length} ` + @@ -767,7 +769,7 @@ export function useRoofAllocationSetting(id) { `total=${roofBase.lines.length}` ) roofBase.lines.forEach((ln, i) => { - console.log( + logger.log( ` [ALLOC-DEBUG] merged[${i}] name=${ln.name || '?'} lineName=${ln.lineName || '?'} ` + `(${ln.x1?.toFixed(1)},${ln.y1?.toFixed(1)})→(${ln.x2?.toFixed(1)},${ln.y2?.toFixed(1)})` ) @@ -812,7 +814,7 @@ export function useRoofAllocationSetting(id) { integrateExtensionLines(roofBase) // [ALLOC-DEBUG] split 직전 최종 입력 - console.log( + logger.log( `[ALLOC-DEBUG] split 직전 roofBase.id=${roofBase.id} ` + `separatePolygon=${roofBase.separatePolygon?.length || 0} ` + `lines=${roofBase.lines?.length || 0} ` + @@ -826,7 +828,7 @@ export function useRoofAllocationSetting(id) { splitPolygonWithLines(roofBase) } } catch (e) { - console.log(e) + logger.log(e) canvas.discardActiveObject() return } diff --git a/src/util/skeleton-utils.js b/src/util/skeleton-utils.js index 5a3e89d1..6ba11e2c 100644 --- a/src/util/skeleton-utils.js +++ b/src/util/skeleton-utils.js @@ -6,6 +6,7 @@ import { getDegreeByChon } from '@/util/canvas-util' import Big from 'big.js' import { QPolygon } from '@/components/fabric/QPolygon' import wallLine from '@/components/floor-plan/modal/wallLineOffset/type/WallLine' +import { logger } from '@/util/logger' /** * 지붕 폴리곤의 스켈레톤(중심선)을 생성하고 캔버스에 그립니다. @@ -95,7 +96,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { const _lp = canvas?.skeleton?.lastPoints const _src = _lp ? 'lastPoints' : 'orgRoofPoints' const _p7 = oldPoints?.[7] - console.log(`[LP-TRACE] movingLineFromSkeleton.in src=${_src} oldPoints[7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) pos=${movePosition} dir=${moveDirection}`) + logger.log(`[LP-TRACE] movingLineFromSkeleton.in src=${_src} oldPoints[7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) pos=${movePosition} dir=${moveDirection}`) } catch (_e) {} const oppositeLine = findOppositeLine(canvas.skeleton.Edges, startPoint, endPoint, oldPoints); @@ -107,18 +108,18 @@ const movingLineFromSkeleton = (roofId, canvas) => { const skeletonLines = canvas.getObjects().filter((object) => object.skeletonType === 'line' && object.parentId === roofId) if (oppositeLine) { - console.log('Opposite line found:', oppositeLine); + logger.log('Opposite line found:', oppositeLine); } else { - console.log('No opposite line found'); + logger.log('No opposite line found'); } if(moveFlowLine !== 0) { return oldPoints.map((point, index) => { - console.log('Point:', point); + logger.log('Point:', point); const newPoint = { ...point }; const absMove = Big(moveFlowLine).times(2).div(10); - console.log('skeletonBuilder moveDirection:', moveDirection); + logger.log('skeletonBuilder moveDirection:', moveDirection); switch (moveDirection) { case 'left': @@ -179,7 +180,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { if (line.position === 'bottom') { - console.log('oldPoint:', point); + logger.log('oldPoint:', point); if (isSamePoint(newPoint, line.start)) { newPoint.y = Big(line.start.y).minus(absMove).toNumber(); @@ -198,7 +199,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { // 사용 예시 } - console.log('newPoint:', newPoint); + logger.log('newPoint:', newPoint); //baseline 변경 return newPoint; }) @@ -207,14 +208,14 @@ const movingLineFromSkeleton = (roofId, canvas) => { // const selectLine = getSelectLine(); // - // console.log("wall::::", wall.points) - // console.log("저장된 3333moveSelectLine:", roof.moveSelectLine); - // console.log("저장된 3moveSelectLine:", selectLine); + // logger.log("wall::::", wall.points) + // logger.log("저장된 3333moveSelectLine:", roof.moveSelectLine); + // logger.log("저장된 3moveSelectLine:", selectLine); // const result = getSelectLinePosition(wall, selectLine, { // testDistance: 5, // 테스트 거리 // debug: true // 디버깅 로그 출력 // }); - // console.log("3333linePosition:::::", result.position); + // logger.log("3333linePosition:::::", result.position); const position = movePosition //result.position; const moveUpDownLength = Big(moveUpDown).times(1).div(10); @@ -255,10 +256,10 @@ const movingLineFromSkeleton = (roofId, canvas) => { affected.add(k); affected.add((k + 1) % nPts); }); - console.log('absMove::', moveUpDownLength); + logger.log('absMove::', moveUpDownLength); // [BR-TRACE local] movingLineFromSkeleton position/direction 분기 const __isLocalMS = process.env.NEXT_PUBLIC_RUN_MODE === 'local' - if (__isLocalMS) console.log(`[BR-TRACE] movingLineFromSkeleton position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) + if (__isLocalMS) logger.log(`[BR-TRACE] movingLineFromSkeleton position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) affected.forEach((i) => { const point = newPoints[i]; if (!point) return; @@ -266,21 +267,21 @@ const movingLineFromSkeleton = (roofId, canvas) => { if (position === 'bottom') { if (moveDirection === 'in') point.y = Big(point.y).minus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.y = Big(point.y).plus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}→${point.y}`) + if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}→${point.y}`) } else if (position === 'top') { if (moveDirection === 'in') point.y = Big(point.y).plus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.y = Big(point.y).minus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}→${point.y}`) + if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}→${point.y}`) } else if (position === 'left') { if (moveDirection === 'in') point.x = Big(point.x).plus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.x = Big(point.x).minus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}→${point.x}`) + if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}→${point.x}`) } else if (position === 'right') { if (moveDirection === 'in') point.x = Big(point.x).minus(moveUpDownLength).toNumber(); else if (moveDirection === 'out') point.x = Big(point.x).plus(moveUpDownLength).toNumber(); - if (__isLocalMS) console.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}→${point.x}`) + if (__isLocalMS) logger.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}→${point.x}`) } else if (__isLocalMS) { - console.warn(`[BR-TRACE] i=${i} position=${position} 매칭 분기 없음 (이동 안 됨)`) + logger.warn(`[BR-TRACE] i=${i} position=${position} 매칭 분기 없음 (이동 안 됨)`) } }); @@ -343,7 +344,7 @@ const movingLineFromSkeleton = (roofId, canvas) => { const cleaned = removeNonOrthogonalPoints(newPoints); - // console.log(cleaned) + // logger.log(cleaned) return cleaned; } @@ -408,7 +409,7 @@ const buildRawMovedPoints = (roofId, canvas) => { // [LP-TRACE] buildRawMovedPoints 진입 — prevLast 유무 및 [7] 현재 값 try { const _pl7 = prevLast?.[7] - console.log(`[LP-TRACE] buildRaw.in prevLast=${prevLast ? 'Y' : 'N'} prevLast[7]=(${_pl7?.x?.toFixed(1)},${_pl7?.y?.toFixed(1)}) oldPoints[7]=(${oldPoints[7]?.x?.toFixed(1)},${oldPoints[7]?.y?.toFixed(1)})`) + logger.log(`[LP-TRACE] buildRaw.in prevLast=${prevLast ? 'Y' : 'N'} prevLast[7]=(${_pl7?.x?.toFixed(1)},${_pl7?.y?.toFixed(1)}) oldPoints[7]=(${oldPoints[7]?.x?.toFixed(1)},${oldPoints[7]?.y?.toFixed(1)})`) } catch (_e) {} const selectLine = roof.moveSelectLine @@ -446,7 +447,7 @@ const buildRawMovedPoints = (roofId, canvas) => { }) // [BR-TRACE local] buildRawMovedPoints position/direction 분기 const __isLocalBR = process.env.NEXT_PUBLIC_RUN_MODE === 'local' - if (__isLocalBR) console.log(`[BR-TRACE] buildRawMovedPoints position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) + if (__isLocalBR) logger.log(`[BR-TRACE] buildRawMovedPoints position=${position} dir=${moveDirection} affected=${[...affected].join(',')}`) affected.forEach((i) => { const point = newPoints[i] if (!point) return @@ -454,21 +455,21 @@ const buildRawMovedPoints = (roofId, canvas) => { if (position === 'bottom') { if (moveDirection === 'out') point.y = Big(point.y).plus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.y = Big(point.y).minus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}→${point.y}`) + if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} bottom/${moveDirection} y ${__by}→${point.y}`) } else if (position === 'top') { if (moveDirection === 'out') point.y = Big(point.y).minus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.y = Big(point.y).plus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}→${point.y}`) + if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} top/${moveDirection} y ${__by}→${point.y}`) } else if (position === 'left') { if (moveDirection === 'out') point.x = Big(point.x).minus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.x = Big(point.x).plus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}→${point.x}`) + if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} left/${moveDirection} x ${__bx}→${point.x}`) } else if (position === 'right') { if (moveDirection === 'out') point.x = Big(point.x).plus(moveUpDownLength).toNumber() else if (moveDirection === 'in') point.x = Big(point.x).minus(moveUpDownLength).toNumber() - if (__isLocalBR) console.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}→${point.x}`) + if (__isLocalBR) logger.log(`[BR-TRACE] i=${i} right/${moveDirection} x ${__bx}→${point.x}`) } else if (__isLocalBR) { - console.warn(`[BR-TRACE] i=${i} position=${position} 매칭 분기 없음 (이동 안 됨)`) + logger.warn(`[BR-TRACE] i=${i} position=${position} 매칭 분기 없음 (이동 안 됨)`) } }) @@ -477,12 +478,12 @@ const buildRawMovedPoints = (roofId, canvas) => { const _sel = selectLine const _sp = _sel?.startPoint const _ep = _sel?.endPoint - console.log(`[LP-TRACE] buildRaw.out matchingLines=${matchingLines.length} selSP=(${_sp?.x?.toFixed(1)},${_sp?.y?.toFixed(1)}) selEP=(${_ep?.x?.toFixed(1)},${_ep?.y?.toFixed(1)}) bp7=(${basePoints[7]?.x?.toFixed(1)},${basePoints[7]?.y?.toFixed(1)}) newPoints[7]=(${newPoints[7]?.x?.toFixed(1)},${newPoints[7]?.y?.toFixed(1)})`) + logger.log(`[LP-TRACE] buildRaw.out matchingLines=${matchingLines.length} selSP=(${_sp?.x?.toFixed(1)},${_sp?.y?.toFixed(1)}) selEP=(${_ep?.x?.toFixed(1)},${_ep?.y?.toFixed(1)}) bp7=(${basePoints[7]?.x?.toFixed(1)},${basePoints[7]?.y?.toFixed(1)}) newPoints[7]=(${newPoints[7]?.x?.toFixed(1)},${newPoints[7]?.y?.toFixed(1)})`) } catch (_e) {} return newPoints } catch (e) { - console.warn('[buildRawMovedPoints] 실패 → null fallback:', e) + logger.warn('[buildRawMovedPoints] 실패 → null fallback:', e) return null } } @@ -511,7 +512,7 @@ export const verifyMoveBoundary = (roofId, canvas) => { try { const roof = canvas?.getObjects().find((o) => o.id === roofId) if (!roof || !Array.isArray(roof.points)) { - console.log('[verifyMoveBoundary] roof/points 없음 → unknown') + logger.log('[verifyMoveBoundary] roof/points 없음 → unknown') return 'unknown' } @@ -519,18 +520,18 @@ export const verifyMoveBoundary = (roofId, canvas) => { const moveUpDown = roof.moveUpDown ?? 0 const position = roof.movePosition const direction = roof.moveDirect - console.log( + logger.log( `[verifyMoveBoundary] 진입 position=${position} direction=${direction} moveUpDown=${moveUpDown} moveFlowLine=${moveFlowLine}` ) if (moveFlowLine === 0 && moveUpDown === 0) { - console.log('[verifyMoveBoundary] 이동 없음 → ok') + logger.log('[verifyMoveBoundary] 이동 없음 → ok') return 'ok' } const proposed = buildRawMovedPoints(roofId, canvas) const orig = roof.points if (!Array.isArray(proposed) || proposed.length !== orig.length) { - console.log( + logger.log( `[verifyMoveBoundary] proposed 비정상 (len=${proposed?.length}, orig.len=${orig.length}) → unknown` ) return 'unknown' @@ -555,7 +556,7 @@ export const verifyMoveBoundary = (roofId, canvas) => { const crossOrig = getTurnDirection(orig[prev], orig[i], orig[next]) const crossNew = getTurnDirection(proposed[prev], proposed[i], proposed[next]) - console.log( + logger.log( `[verifyMoveBoundary] [${i}] orig=(${Math.round(orig[i].x)},${Math.round(orig[i].y)}) → proposed=(${Math.round(proposed[i].x)},${Math.round(proposed[i].y)}) crossOrig=${crossOrig.toFixed(1)} crossNew=${crossNew.toFixed(1)}` ) @@ -565,13 +566,13 @@ export const verifyMoveBoundary = (roofId, canvas) => { const boundaryTol = Math.max(1.0, Math.abs(crossOrig) * 0.05) if (crossNew < -boundaryTol) { - console.warn( + logger.warn( `[verifyMoveBoundary] 꼭짓점[${i}] 골짜기 → 볼록 뒤집힘 (CROSSED): ${crossOrig.toFixed(1)} → ${crossNew.toFixed(1)}` ) return 'crossed' } if (Math.abs(crossNew) <= boundaryTol) { - console.log( + logger.log( `[verifyMoveBoundary] 꼭짓점[${i}] 경계 도달 (on-boundary): cross ≈ 0 (${crossNew.toFixed(1)})` ) if (worst === 'ok') worst = 'on-boundary' @@ -580,12 +581,12 @@ export const verifyMoveBoundary = (roofId, canvas) => { } if (movedIdx.length === 0) { - console.log('[verifyMoveBoundary] 이동된 꼭짓점 0개 (buildRawMovedPoints 매칭 실패 가능성)') + logger.log('[verifyMoveBoundary] 이동된 꼭짓점 0개 (buildRawMovedPoints 매칭 실패 가능성)') } - console.log(`[verifyMoveBoundary] 최종 판정: ${worst} (moved indices=[${movedIdx.join(',')}])`) + logger.log(`[verifyMoveBoundary] 최종 판정: ${worst} (moved indices=[${movedIdx.join(',')}])`) return worst } catch (e) { - console.warn('[verifyMoveBoundary] 판정 실패 → unknown:', e) + logger.warn('[verifyMoveBoundary] 판정 실패 → unknown:', e) return 'unknown' } } @@ -600,7 +601,7 @@ const safeMovedPointsWithFallback = (roofId, canvas) => { const movedPoints = movingLineFromSkeleton(roofId, canvas) if (!Array.isArray(movedPoints) || movedPoints.length === 0) { - console.warn('[safeMovedPointsWithFallback] movedPoints 비정상 → bail out') + logger.warn('[safeMovedPointsWithFallback] movedPoints 비정상 → bail out') return null } @@ -650,7 +651,7 @@ const safeMovedPointsWithFallback = (roofId, canvas) => { } if (zeroLenEdges > 0 || severeReduction || selfIntersect) { - console.warn('[safeMovedPointsWithFallback] 붕괴/교차 감지 (로그만)', { + logger.warn('[safeMovedPointsWithFallback] 붕괴/교차 감지 (로그만)', { movedLen: movedPoints.length, oldLen: oldPoints.length, zeroLenEdges, @@ -734,7 +735,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi if (!il || !isFinite(il.x1) || !isFinite(il.y1) || !isFinite(il.x2) || !isFinite(il.y2)) return false return il.lineName === 'hip' || il.name === 'hip' }) - console.log(`[v1 ext] HIP 추출 ${hipLines.length}개 / 전체 innerLines ${roof.innerLines.length}`) + logger.log(`[v1 ext] HIP 추출 ${hipLines.length}개 / 전체 innerLines ${roof.innerLines.length}`) // 절삭 대상: roofLine seg + canvas 의 다른 보조라인 (eaveHelpLine 등). // 기존 helper 자체(BASELINE_TO_ROOFLINE_HELPER_TYPE) / wall/roof 본체 / 텍스트 등은 제외. @@ -764,7 +765,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi const s = segs[k] auxList.push(`${s.__name || '?'}=(${Math.round(s.A.x)},${Math.round(s.A.y)})→(${Math.round(s.B.x)},${Math.round(s.B.y)}) len=${Math.round(Math.hypot(s.B.x - s.A.x, s.B.y - s.A.y))}`) } - console.log(`[BR-SEGS] roofLine=${m} aux=${segs.length - m}`, auxList) + logger.log(`[BR-SEGS] roofLine=${m} aux=${segs.length - m}`, auxList) } // [v1 ext 정정] ext 는 wallbaseLine 의 꼭지점(corner) 에 outer 끝점이 일치하는 hip 만 대상. @@ -783,7 +784,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi // outer 끝이 wallbaseLine corner 에 일치하지 않으면 ext 대상 아님. if (outerCornerDist > CORNER_EPS) { - console.log( + logger.log( `[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ` + `corner 거리=${outerCornerDist.toFixed(2)} > ${CORNER_EPS} → 내부 hip skip` ) @@ -792,7 +793,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi // 이미 처마(roofLine) 위에 있으면 ext 불필요. if (isPointOnRoofLine(outerEnd, 1.0)) { - console.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) 이미 roofLine 위 → skip`) + logger.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) 이미 roofLine 위 → skip`) continue } @@ -814,7 +815,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi // [BR-RAY] local only — finite t 인 모든 후보 hit 찍기 (어떤 seg 가 가장 빠른지 비교용) if (__isLocalRayLog && isFinite(t)) { const __label = (k < m) ? `roofLine#${k}` : (segs[k].__name || `aux#${k - m}`) - console.log(`[BR-RAY] hip outer=(${Math.round(outerEnd.x)},${Math.round(outerEnd.y)}) ${__label} t=${t.toFixed(2)} seg=(${Math.round(A.x)},${Math.round(A.y)})→(${Math.round(B.x)},${Math.round(B.y)})`) + logger.log(`[BR-RAY] hip outer=(${Math.round(outerEnd.x)},${Math.round(outerEnd.y)}) ${__label} t=${t.toFixed(2)} seg=(${Math.round(A.x)},${Math.round(A.y)})→(${Math.round(B.x)},${Math.round(B.y)})`) } if (t < bestT) { bestT = t @@ -823,7 +824,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi } } if (!isFinite(bestT) || bestT < 0.5) { - console.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) NO_HIT or 너무 가까움`) + logger.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) NO_HIT or 너무 가까움`) continue } @@ -832,14 +833,14 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi // eaveHelpLine 이 ray 경로를 가로막으면 이미 eave/aux 영역에 도달한 것이므로 더 그릴 필요 없음. // roofLine 승자만 진짜 "처마 도달" 로 인정 → ext 그림. if (bestSegName === 'eaveHelpLine') { - console.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) 승자=eaveHelpLine bestT=${bestT.toFixed(2)} → skip`) + logger.log(`[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) 승자=eaveHelpLine bestT=${bestT.toFixed(2)} → skip`) continue } const hitX = outerEnd.x + ux * bestT const hitY = outerEnd.y + uy * bestT - console.log( + logger.log( `[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ` + `dir=(${ux.toFixed(3)},${uy.toFixed(3)}) bestT=${bestT.toFixed(2)} src=${bestSrc} ` + `→ ext=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)})→(${hitX.toFixed(1)},${hitY.toFixed(1)})` @@ -871,14 +872,19 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi hip.attributes.actualSize = Math.round(oldActual * ratio * 100) / 100 // integrateExtensionLines 가 이 hip 의 좌표를 되돌리지 않게 가드. + // [save/load 보존 2026-05-13] runtime 플래그(__extended) 외에 attributes.extended 도 동기화. + // attributes 는 SAVE_KEY 에 포함 → JSON 저장/로드 후에도 살아남음. + // 저장 후 재로드 시 sk.__extended 가 사라져 INTEGRATE merge 가 다시 실행되어 + // 확장된 hip 끝점이 옛 좌표로 되돌아가는 회귀 차단. hip.__extended = true + hip.attributes.extended = true // Fabric bounding/length/text 갱신. if (typeof hip.setCoords === 'function') hip.setCoords() if (typeof hip.setLength === 'function') hip.setLength() if (typeof hip.addLengthText === 'function') hip.addLengthText() - console.log( + logger.log( `[v1 ext] hip 연장 oldLen=${oldLen.toFixed(1)} +extLen=${extLen.toFixed(1)} ratio=${ratio.toFixed(3)} ` + `(plane ${oldPlane}→${hip.attributes.planeSize}, actual ${oldActual}→${hip.attributes.actualSize})` ) @@ -947,16 +953,16 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // // 디버그: baseLines의 offset/type/좌표 확인 // baseLines.forEach((bl, i) => { - // console.log(`[skeleton] baseLine[${i}] type=${bl.attributes?.type} offset=${bl.attributes?.offset} (${Math.round(bl.x1)},${Math.round(bl.y1)})→(${Math.round(bl.x2)},${Math.round(bl.y2)})`) + // logger.log(`[skeleton] baseLine[${i}] type=${bl.attributes?.type} offset=${bl.attributes?.offset} (${Math.round(bl.x1)},${Math.round(bl.y1)})→(${Math.round(bl.x2)},${Math.round(bl.y2)})`) // }) // roof.points.forEach((p, i) => { - // console.log(`[skeleton] roof.points[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) + // logger.log(`[skeleton] roof.points[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) // }) // roof.points 순서와 맞춰야 offset 매핑이 정확함 const baseLinePoints = createOrderedBasePoints(roof.points, baseLines) // baseLinePoints.forEach((p, i) => { - // console.log(`[skeleton] orderedBase[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) + // logger.log(`[skeleton] orderedBase[${i}] (${Math.round(p.x)},${Math.round(p.y)})`) // }) @@ -969,7 +975,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // 1. 지붕 폴리곤 유효성 검사 const coordinates = preprocessPolygonCoordinates(roof.points) if (coordinates.length < 3) { - console.warn('Polygon has less than 3 unique points. Cannot generate skeleton.') + logger.warn('Polygon has less than 3 unique points. Cannot generate skeleton.') return } @@ -977,10 +983,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { const moveUpDown = roof.moveUpDown || 0 // 디버그: offset 변경 + moveLine 상태 확인 - console.log('[DEBUG] moveFlowLine:', moveFlowLine, 'moveUpDown:', moveUpDown) - console.log('[DEBUG] wall.lines:', wall.lines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})→(${Math.round(l.x2)},${Math.round(l.y2)})`)) - console.log('[DEBUG] wall.baseLines:', wall.baseLines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})→(${Math.round(l.x2)},${Math.round(l.y2)})`)) - console.log('[DEBUG] roof.points:', roof.points.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) + logger.log('[DEBUG] moveFlowLine:', moveFlowLine, 'moveUpDown:', moveUpDown) + logger.log('[DEBUG] wall.lines:', wall.lines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})→(${Math.round(l.x2)},${Math.round(l.y2)})`)) + logger.log('[DEBUG] wall.baseLines:', wall.baseLines.map((l, i) => `[${i}] (${Math.round(l.x1)},${Math.round(l.y1)})→(${Math.round(l.x2)},${Math.round(l.y2)})`)) + logger.log('[DEBUG] roof.points:', roof.points.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) // 닫힌 폴리곤(첫점 = 끝점)인 경우 마지막 중복 점 제거 const isClosedPolygon = (points) => @@ -1039,10 +1045,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // - 45도 대각 방향(signDx, signDy 각 ±1)이므로 실제 x·y 이동량은 각 축으로 maxStep const maxContactDistance = Math.hypot(maxStep, maxStep) - // console.log("orderedBaseLinePoints",orderedBaseLinePoints) - // console.log("contactData",contactData) - // console.log("roofLineContactPoints",roofLineContactPoints) - // console.log("maxContactDistance",maxContactDistance) + // logger.log("orderedBaseLinePoints",orderedBaseLinePoints) + // logger.log("contactData",contactData) + // logger.log("roofLineContactPoints",roofLineContactPoints) + // logger.log("maxContactDistance",maxContactDistance) let changRoofLinePoints = orderedBaseLinePoints.map((point, index) => { const contactPoint = roofLineContactPoints[index] @@ -1059,7 +1065,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { } }) - // console.log("changRoofLinePoints1:::", changRoofLinePoints) + // logger.log("changRoofLinePoints1:::", changRoofLinePoints) // 6. 마루이동(용마루 위치 수동 조정)이 설정된 경우 movingLineFromSkeleton 결과로 대체 if (moveFlowLine !== 0 || moveUpDown !== 0) { @@ -1070,14 +1076,14 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { try { const __moveVerdict = verifyMoveBoundary(roofId, canvas) if (__moveVerdict === 'crossed') { - console.warn('[skeleton] 경계 넘음(crossed) → 오버된 wall.baseLine 좌표 기반 재빌드 진행') + logger.warn('[skeleton] 경계 넘음(crossed) → 오버된 wall.baseLine 좌표 기반 재빌드 진행') } } catch (e) { - console.warn('[skeleton] verifyMoveBoundary 예외 → 기존 흐름 진행:', e) + logger.warn('[skeleton] verifyMoveBoundary 예외 → 기존 흐름 진행:', e) } const movedPoints = safeMovedPointsWithFallback(roofId, canvas) - // console.log("movedPoints:::", movedPoints); + // logger.log("movedPoints:::", movedPoints); // movedPoints를 roof 경계로 확장 // 이번 이동에서 실제로 변경된 포인트만 구분하여, 변경되지 않은 축만 roof 경계로 확장 @@ -1122,7 +1128,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { if (SK_INPUT_USE_WALL_BASELINE_DIRECT) { changRoofLinePoints = orderedBaseLinePoints.map((p) => ({ x: p.x, y: p.y })) roofLineContactPoints = orderedBaseLinePoints.map((p) => ({ x: p.x, y: p.y })) - console.log('[v1 SK_INPUT] wall.baseLines 직접 사용 (45° 확장/movedPoints 무시):', + logger.log('[v1 SK_INPUT] wall.baseLines 직접 사용 (45° 확장/movedPoints 무시):', changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' ')) // [v1 평행오버 corner 흡수 2026-04-29] @@ -1156,23 +1162,23 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { kept = next } if (absorbedAll.length > 0 && kept.length >= 3) { - console.log(`[v1 SK_INPUT 평행흡수] ${absorbedAll.length}개 corner 제거:\n ${absorbedAll.join('\n ')}`) - console.log(`[v1 SK_INPUT 평행흡수] 최종 ${kept.length}개 corner:`, + logger.log(`[v1 SK_INPUT 평행흡수] ${absorbedAll.length}개 corner 제거:\n ${absorbedAll.join('\n ')}`) + logger.log(`[v1 SK_INPUT 평행흡수] 최종 ${kept.length}개 corner:`, kept.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' ')) changRoofLinePoints = kept roofLineContactPoints = kept.map((p) => ({ x: p.x, y: p.y })) } } - console.log('[DEBUG] changRoofLinePoints(최종 SK입력):', changRoofLinePoints.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) + logger.log('[DEBUG] changRoofLinePoints(최종 SK입력):', changRoofLinePoints.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`)) // 좌표 유효성 검증 const invalidPoints = changRoofLinePoints.filter((p, i) => p == null || isNaN(p.x) || isNaN(p.y) || !isFinite(p.x) || !isFinite(p.y) ) if (invalidPoints.length > 0) { - console.error('[SK_ERROR] 유효하지 않은 좌표 발견:', invalidPoints) - console.error('[SK_ERROR] 전체 좌표:', JSON.stringify(changRoofLinePoints)) + logger.error('[SK_ERROR] 유효하지 않은 좌표 발견:', invalidPoints) + logger.error('[SK_ERROR] 전체 좌표:', JSON.stringify(changRoofLinePoints)) } // 인접 중복점 검출 (거리 < 0.5) @@ -1181,7 +1187,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { const next = changRoofLinePoints[(i + 1) % changRoofLinePoints.length] const dist = Math.hypot(next.x - curr.x, next.y - curr.y) if (dist < 0.5) { - console.warn(`[SK_WARN] 인접 중복점: [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) ↔ [${(i + 1) % changRoofLinePoints.length}](${Math.round(next.x)},${Math.round(next.y)}) dist=${dist.toFixed(3)}`) + logger.warn(`[SK_WARN] 인접 중복점: [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) ↔ [${(i + 1) % changRoofLinePoints.length}](${Math.round(next.x)},${Math.round(next.y)}) dist=${dist.toFixed(3)}`) } } @@ -1192,7 +1198,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { const next = changRoofLinePoints[(i + 1) % changRoofLinePoints.length] const cross = (curr.x - prev.x) * (next.y - prev.y) - (curr.y - prev.y) * (next.x - prev.x) if (Math.abs(cross) < 1.0) { - console.warn(`[SK_WARN] 일직선 점: [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) cross=${cross.toFixed(3)}`) + logger.warn(`[SK_WARN] 일직선 점: [${i}](${Math.round(curr.x)},${Math.round(curr.y)}) cross=${cross.toFixed(3)}`) } } @@ -1200,9 +1206,9 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { let isOverDetected = false const origPoints = roof.points || [] const n = changRoofLinePoints.length - console.log('[SK_OVER_DEBUG] origPoints:', origPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) - console.log('[SK_OVER_DEBUG] changRoofLinePoints:', changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) - console.log('[SK_OVER_DEBUG] 변경된 점:', changRoofLinePoints.map((p, i) => { + logger.log('[SK_OVER_DEBUG] origPoints:', origPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + logger.log('[SK_OVER_DEBUG] changRoofLinePoints:', changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + logger.log('[SK_OVER_DEBUG] 변경된 점:', changRoofLinePoints.map((p, i) => { const o = origPoints[i] if (!o) return null const dist = Math.hypot(p.x - o.x, p.y - o.y) @@ -1222,9 +1228,9 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { if (crossOrig * crossNew < 0) { isOverDetected = true - console.error(`[SK_OVER] 꼭짓점[${i}] 방향 뒤집힘! cross: ${crossOrig.toFixed(1)} → ${crossNew.toFixed(1)}`) - console.error(`[SK_OVER] 원본: [${(i-1+n)%n}](${Math.round(prevOrig.x)},${Math.round(prevOrig.y)}) → [${i}](${Math.round(currOrig.x)},${Math.round(currOrig.y)}) → [${(i+1)%n}](${Math.round(nextOrig.x)},${Math.round(nextOrig.y)})`) - console.error(`[SK_OVER] 변경: [${(i-1+n)%n}](${Math.round(prevNew.x)},${Math.round(prevNew.y)}) → [${i}](${Math.round(currNew.x)},${Math.round(currNew.y)}) → [${(i+1)%n}](${Math.round(nextNew.x)},${Math.round(nextNew.y)})`) + logger.error(`[SK_OVER] 꼭짓점[${i}] 방향 뒤집힘! cross: ${crossOrig.toFixed(1)} → ${crossNew.toFixed(1)}`) + logger.error(`[SK_OVER] 원본: [${(i-1+n)%n}](${Math.round(prevOrig.x)},${Math.round(prevOrig.y)}) → [${i}](${Math.round(currOrig.x)},${Math.round(currOrig.y)}) → [${(i+1)%n}](${Math.round(nextOrig.x)},${Math.round(nextOrig.y)})`) + logger.error(`[SK_OVER] 변경: [${(i-1+n)%n}](${Math.round(prevNew.x)},${Math.round(prevNew.y)}) → [${i}](${Math.round(currNew.x)},${Math.round(currNew.y)}) → [${(i+1)%n}](${Math.round(nextNew.x)},${Math.round(nextNew.y)})`) } } } @@ -1251,10 +1257,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { ) if (corrected && corrected.length >= 3) { skeletonInputPoints = corrected - console.log('[SK_OVER] 보정 포인트 적용:', skeletonInputPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + logger.log('[SK_OVER] 보정 포인트 적용:', skeletonInputPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) } } catch (e) { - console.error('[SK_OVER] 보정 실패 → fallback:', e) + logger.error('[SK_OVER] 보정 실패 → fallback:', e) } } @@ -1264,14 +1270,14 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { try { // SkeletonBuilder는 닫히지 않은 폴리곤을 기대하므로 마지막 점 제거 geoJSONPolygon.pop() - console.log('[SkeletonBuilder] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) - console.log('[SkeletonBuilder] 꼭짓점 수:', geoJSONPolygon.length) + logger.log('[SkeletonBuilder] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) + logger.log('[SkeletonBuilder] 꼭짓점 수:', geoJSONPolygon.length) const skeleton = SkeletonBuilder.BuildFromGeoJSON([[geoJSONPolygon]]) // 스켈레톤 데이터를 기반으로 내부선 생성 roof.innerLines = roof.innerLines || [] roof.innerLines = createInnerLinesFromSkeleton(roofId, canvas, skeleton, textMode, isOverDetected, changRoofLinePoints) - //console.log("roofInnerLines:::", roof.innerLines); + //logger.log("roofInnerLines:::", roof.innerLines); // [v1 helper 2026-04-29 B 경로] SK 입력 (offset 확장 baseLine 또는 movedPoints) 과 roofLinePoints 간 // gap 을 시각화. innerLines 와 별개 collection. 기존 working code (processInBoth 등) 무영향. @@ -1318,20 +1324,20 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { // [LP-TRACE] 실제 저장되는 lastPoints[7] — 이 값이 다음 이동의 prevLast[7] try { const _p7 = rawMovedFull[7] - console.log(`[LP-TRACE] lastPoints.save [7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) len=${rawMovedFull.length}`) + logger.log(`[LP-TRACE] lastPoints.save [7]=(${_p7?.x?.toFixed(1)},${_p7?.y?.toFixed(1)}) len=${rawMovedFull.length}`) } catch (_e) {} - console.log('[skeleton] lastPoints 저장: raw 풀 길이', rawMovedFull.length, '점') + logger.log('[skeleton] lastPoints 저장: raw 풀 길이', rawMovedFull.length, '점') } else { canvas.skeleton.lastPoints = roofLineContactPoints } canvas.set('skeleton', cleanSkeleton) canvas.renderAll() - //console.log('skeleton rendered.', canvas) + //logger.log('skeleton rendered.', canvas) } catch (e) { - console.error('[SK_ERROR] 스켈레톤 생성 중 오류 발생:', e) - console.error('[SK_ERROR] 입력 좌표:', changRoofLinePoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) - console.error('[SK_ERROR] geoJSON:', JSON.stringify(geoJSONPolygon)) + logger.error('[SK_ERROR] 스켈레톤 생성 중 오류 발생:', e) + logger.error('[SK_ERROR] 입력 좌표:', changRoofLinePoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) + logger.error('[SK_ERROR] geoJSON:', JSON.stringify(geoJSONPolygon)) // [보호] 예외 시 상태 플래그만 복구하고, 기존 SK/innerLines는 유지한다. // → 사용자가 힘들게 추가한 라인들이 순간적으로 사라지는 현상 방지. if (canvas.skeletonStates) { @@ -1357,12 +1363,12 @@ export const skeletonBuilder = (roofId, canvas, textMode) => { export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => { const roof = canvas?.getObjects().find((o) => o.id === roofId) if (!roof) { - console.warn('[SK_OVER_FN] roof 없음 → 중단') + logger.warn('[SK_OVER_FN] roof 없음 → 중단') return } const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes?.roofId === roofId) if (!wall || !wall.baseLines?.length) { - console.warn('[SK_OVER_FN] wall/baseLines 없음 → 중단') + logger.warn('[SK_OVER_FN] wall/baseLines 없음 → 중단') return } @@ -1372,22 +1378,22 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => const bl = wall.baseLines[i] const planeSize = bl?.attributes?.planeSize ?? Infinity if (planeSize < 1) { - console.log(`[SK_OVER_FN] baseLines[${i}] SHOULDER_ABSORBED skip (planeSize=${planeSize})`) + logger.log(`[SK_OVER_FN] baseLines[${i}] SHOULDER_ABSORBED skip (planeSize=${planeSize})`) continue } if (bl == null || !isFinite(bl.x1) || !isFinite(bl.y1)) { - console.warn(`[SK_OVER_FN] baseLines[${i}] invalid coord → skip`) + logger.warn(`[SK_OVER_FN] baseLines[${i}] invalid coord → skip`) continue } rawPoints.push({ x: bl.x1, y: bl.y1 }) } if (rawPoints.length < 3) { - console.warn(`[SK_OVER_FN] 유효 baseLines 끝점 ${rawPoints.length} < 3 → 중단`) + logger.warn(`[SK_OVER_FN] 유효 baseLines 끝점 ${rawPoints.length} < 3 → 중단`) return } - console.log('[SK_OVER_FN] wall.baseLines polygon:', rawPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) + logger.log('[SK_OVER_FN] wall.baseLines polygon:', rawPoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`)) // skeletonPoints 마킹 (오버 좌표 그대로) roof.skeletonPoints = rawPoints.map((p) => ({ x: p.x, y: p.y })) @@ -1397,8 +1403,8 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => try { geoJSONPolygon.pop() - console.log('[SK_OVER_FN] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) - console.log('[SK_OVER_FN] 꼭짓점 수:', geoJSONPolygon.length) + logger.log('[SK_OVER_FN] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2)) + logger.log('[SK_OVER_FN] 꼭짓점 수:', geoJSONPolygon.length) const skeleton = SkeletonBuilder.BuildFromGeoJSON([[geoJSONPolygon]]) roof.innerLines = roof.innerLines || [] @@ -1431,8 +1437,8 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) => canvas.set('skeleton', cleanSkeleton) canvas.renderAll() } catch (e) { - console.error('[SK_OVER_FN] 스켈레톤 생성 오류:', e) - console.error('[SK_OVER_FN] 입력 좌표:', rawPoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) + logger.error('[SK_OVER_FN] 스켈레톤 생성 오류:', e) + logger.error('[SK_OVER_FN] 입력 좌표:', rawPoints.map((p, i) => `[${i}] (${p.x.toFixed(2)},${p.y.toFixed(2)})`)) if (canvas.skeletonStates) canvas.skeletonStates[roofId] = false } } @@ -1571,7 +1577,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver if (rotIdx > 0) { wallLines = [...wallLines.slice(rotIdx), ...wallLines.slice(0, rotIdx)] if (process.env.NEXT_PUBLIC_RUN_MODE === 'local') { - console.log('[WL-ROT-FIX] wall.lines rotated by', -rotIdx, 'to align with wall.baseLines[0].startPoint') + logger.log('[WL-ROT-FIX] wall.lines rotated by', -rotIdx, 'to align with wall.baseLines[0].startPoint') } } } @@ -1717,7 +1723,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver // [PAIR-DIAG] local only — top in 시 페어 인덱스 어긋남 진단 (2026-04-30) // 1) 길이 미스매치 / 2) _keyOfEdge 충돌 / 3) raw 단계 1:1 무너짐 가려내기 위함 if (process.env.NEXT_PUBLIC_RUN_MODE === 'local') { - console.log('[PAIR-DIAG] lengths', { + logger.log('[PAIR-DIAG] lengths', { roofLines: roofLines.length, wallLines: wallLines.length, wallBaseLines: wall.baseLines.length, @@ -1725,13 +1731,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const _seen = new Map() roofLines.forEach((l, i) => { const k = _keyOfEdge(l) - if (_seen.has(k)) console.warn(`[PAIR-DIAG] key collision: idx ${_seen.get(k)} vs ${i} key=${k}`) + if (_seen.has(k)) logger.warn(`[PAIR-DIAG] key collision: idx ${_seen.get(k)} vs ${i} key=${k}`) else _seen.set(k, i) }) roofLines.forEach((rl, i) => { const wl = wallLines[i] const wb = wall.baseLines[i] - console.log(`[PAIR-DIAG] raw#${i}`, + logger.log(`[PAIR-DIAG] raw#${i}`, `roof=(${Math.round(rl.x1)},${Math.round(rl.y1)})→(${Math.round(rl.x2)},${Math.round(rl.y2)})`, wl ? `wall=(${Math.round(wl.x1)},${Math.round(wl.y1)})→(${Math.round(wl.x2)},${Math.round(wl.y2)})` : 'wall=∅', wb ? `base=(${Math.round(wb.x1)},${Math.round(wb.y1)})→(${Math.round(wb.x2)},${Math.round(wb.y2)})` : 'base=∅') @@ -1739,11 +1745,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver } // [MOVE-TRACE] 진입 스냅샷 (필요 시 주석 해제) - // console.log('[MOVE-TRACE] === getMoveUpDownLine entry ===', + // logger.log('[MOVE-TRACE] === getMoveUpDownLine entry ===', // 'moveUpDown=', roof.moveUpDown, // 'moveFlowLine=', roof.moveFlowLine) // sortWallBaseLines.forEach((bl, i) => { - // console.log(`[MOVE-TRACE] sortWallBaseLines[${i}]`, + // logger.log(`[MOVE-TRACE] sortWallBaseLines[${i}]`, // `(${Math.round(bl?.x1)},${Math.round(bl?.y1)})→(${Math.round(bl?.x2)},${Math.round(bl?.y2)})`, // 'vs sortWallLines', // `(${Math.round(sortWallLines[i]?.x1)},${Math.round(sortWallLines[i]?.y1)})→(${Math.round(sortWallLines[i]?.x2)},${Math.round(sortWallLines[i]?.y2)})`, @@ -1768,11 +1774,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const l = `${lineAxis}${k}` // y1 or x1 const otherL = `${lineAxis}${otherK}` // y2 or x2 - console.log(`${condition}::::isStartEnd:::::`) + logger.log(`${condition}::::isStartEnd:::::`) // [MOVE-TRACE] index 0 전용 (필요 시 주석 해제) // if (index === 0) { - // console.log('[MOVE-TRACE] processInStartEnd idx=0', + // logger.log('[MOVE-TRACE] processInStartEnd idx=0', // 'condition=', condition, // 'isStart=', isStart, // 'inSign=', inSign, @@ -1820,7 +1826,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const wbL = wallBaseLine[l] const wL = wallLine[l] if (Math.abs(wbL - wL) < 0.1) { - console.warn(`⚠️ [${condition.toUpperCase()}_${isStart ? 'START' : 'END'}] Line crossing detected! newPoint:`, newPointM, 'pLine:', pLineM) + logger.warn(`⚠️ [${condition.toUpperCase()}_${isStart ? 'START' : 'END'}] Line crossing detected! newPoint:`, newPointM, 'pLine:', pLineM) if (moveAxis === 'x') { getAddLine({ x: pLineM, y: pLineL }, { x: newPointM, y: pLineL }, 'green') getAddLine({ x: newPointM, y: pLineL }, ePoint, 'pink') @@ -1852,12 +1858,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`⏭️ [pair#${index}] ${condition} HIP_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + logger.log(`⏭️ [pair#${index}] ${condition} HIP_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } - console.log(`${condition}::::isStartEnd (both):::::`) + logger.log(`${condition}::::isStartEnd (both):::::`) // 원본 기준: moveDistY = abs(roofLine.y - wallBaseLine.y), moveDistX = abs(roofLine.x - wallBaseLine.x) const moveDistY1 = Math.abs(Big(roofLine.y1).minus(wallBaseLine.y1).toNumber()) @@ -1883,7 +1889,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const createHipLine = (fromPoint, toPoint) => { const createdLine = getAddLine(fromPoint, toPoint, 'orange', 'extensionLine') - console.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 }) + logger.log('extensionLine created - length:', createdLine.length, 'coords:', { x1: createdLine.x1, y1: createdLine.y1, x2: createdLine.x2, y2: createdLine.y2 }) createdLine.set({ name: LINE_TYPE.SUBLINE.HIP, lineName: LINE_TYPE.SUBLINE.HIP, stroke: '#FF0000', strokeWidth: 2 }) createdLine.attributes = { ...createdLine.attributes, type: LINE_TYPE.SUBLINE.HIP } innerLines.push(createdLine) @@ -1909,7 +1915,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver target = { x: roofLine.x1 + xSignStart * dist, y: roofLine.y1 } } if (!__isNearSkVertex(target)) { - console.log(`⏭️ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS start target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) + logger.log(`⏭️ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS start target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) } else { createHipLine(sPoint, target) } @@ -1925,7 +1931,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver target = { x: roofLine.x2 + xSignEnd * dist, y: roofLine.y2 } } if (!__isNearSkVertex(target)) { - console.log(`⏭️ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS end target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) + logger.log(`⏭️ [pair#${index}] ${condition} HIP_SKIP_VERTEX_MISS end target=(${target.x.toFixed(1)},${target.y.toFixed(1)})`) } else { createHipLine(ePoint, target) } @@ -1982,7 +1988,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver // }) // } - console.log('wallBaseLines', wall.baseLines) + logger.log('wallBaseLines', wall.baseLines) //wall.baseLine은 움직인라인 let movedLines = [] @@ -1990,7 +1996,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver // 조건에 맞는 라인들만 필터링 const validWallLines = [...wallLines].sort((a, b) => a.idx - b.idx).filter((wallLine, index) => wallLine.idx - 1 === index) - console.log('', sortRoofLines, sortWallLines, sortWallBaseLines); + logger.log('', sortRoofLines, sortWallLines, sortWallBaseLines); (sortWallLines.length === sortWallBaseLines.length && sortWallBaseLines.length > 3) && sortWallLines.forEach((wallLine, index) => { @@ -2003,7 +2009,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver // 임계 = 10: useMovementSetting OVER_EPS=0.5 (canvas unit) 시 잔여 길이=0.5 → planeSize=5 (calcLinePlaneSize × 10). // 안전마진 두고 < 10 으로 차단. 정상 baseLine 은 보통 50+ 이므로 false positive 없음. if (!wallBaseLine || (wallBaseLine.attributes?.planeSize ?? Infinity) < 10) { - console.log(`⏭️ [pair#${index}] SHOULDER_ABSORBED skip: wb=(${Math.round(wallBaseLine?.x1)},${Math.round(wallBaseLine?.y1)})→(${Math.round(wallBaseLine?.x2)},${Math.round(wallBaseLine?.y2)}) planeSize=${wallBaseLine?.attributes?.planeSize}`) + logger.log(`⏭️ [pair#${index}] SHOULDER_ABSORBED skip: wb=(${Math.round(wallBaseLine?.x1)},${Math.round(wallBaseLine?.y1)})→(${Math.round(wallBaseLine?.x2)},${Math.round(wallBaseLine?.y2)}) planeSize=${wallBaseLine?.attributes?.planeSize}`) return } @@ -2016,16 +2022,16 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const wallIdWl = wallLine.attributes?.wallId ?? wallLine.attributes?.idx ?? '?' const wallIdWb = wallBaseLine.attributes?.wallId ?? wallBaseLine.attributes?.idx ?? '?' const dirMatch = wlDir === wbDir - console.log(`🔗 [pair#${index}] wallId wl=${wallIdWl} wb=${wallIdWb} ${dirMatch ? '' : '⚠️DIR_MISMATCH '}wlDir=${wlDir} wbDir=${wbDir} wl=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})→(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wb=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})→(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) + logger.log(`🔗 [pair#${index}] wallId wl=${wallIdWl} wb=${wallIdWb} ${dirMatch ? '' : '⚠️DIR_MISMATCH '}wlDir=${wlDir} wbDir=${wbDir} wl=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})→(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wb=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})→(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) } //roofline 외곽선 설정 - // console.log('index::::', index) - // console.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2) - // console.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2) - // console.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2) - // console.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine)) + // logger.log('index::::', index) + // logger.log('roofLine:', roofLine.x1, roofLine.y1, roofLine.x2, roofLine.y2) + // logger.log('wallLine:', wallLine.x1, wallLine.y1, wallLine.x2, wallLine.y2) + // logger.log('wallBaseLine:', wallBaseLine.x1, wallBaseLine.y1, wallBaseLine.x2, wallBaseLine.y2) + // logger.log('isSamePoint result:', isSameLine2(wallBaseLine, wallLine)) const isCollinear = (l1, l2, tolerance = 0.1) => { const slope1 = Math.abs(l1.x2 - l1.x1) < tolerance ? Infinity : (l1.y2 - l1.y1) / (l1.x2 - l1.x1) @@ -2060,7 +2066,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver } for (const [p1, p2] of __cgPairs) { if (Math.hypot(p2.x - p1.x, p2.y - p1.y) < 0.5) continue - console.log(`🎯 [corner-gap eaveHelpLine] index=${index} p1=(${Math.round(p1.x)},${Math.round(p1.y)})→p2=(${Math.round(p2.x)},${Math.round(p2.y)}) wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})→(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})→(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) + logger.log(`🎯 [corner-gap eaveHelpLine] index=${index} p1=(${Math.round(p1.x)},${Math.round(p1.y)})→p2=(${Math.round(p2.x)},${Math.round(p2.y)}) wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})→(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})→(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) const __cgLine = new QLine([p1.x, p1.y, p2.x, p2.y], { parentId: roof.id, fontSize: roof.fontSize, @@ -2106,7 +2112,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver movedLines.push({ index, p1, p2 }) // [진단] eaveHelpLine 생성 추적: 어느 index/어느 wallBaseLine-wallLine 쌍에서 만들어지는지 - console.log('🎯 [getAddLine]', { + logger.log('🎯 [getAddLine]', { index, lineType, p1: { x: Math.round(p1.x), y: Math.round(p1.y) }, @@ -2120,7 +2126,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const dy = Math.abs(p2.y - p1.y); const isDiagonal = dx > 0.5 && dy > 0.5; // x, y 변화가 모두 있으면 대각선 - //console.log("mergeLines:::::::", mergeLines); + //logger.log("mergeLines:::::::", mergeLines); const line = new QLine([p1.x, p1.y, p2.x, p2.y], { parentId: roof.id, fontSize: roof.fontSize, @@ -2184,7 +2190,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const midY = (wallBaseLine.y1 + wallBaseLine.y2) / 2 const isH = Math.abs(wallBaseLine.y1 - wallBaseLine.y2) < 0.5 const isV = Math.abs(wallBaseLine.x1 - wallBaseLine.x2) < 0.5 - console.log(`🧭 [getSelectLinePosition 결과] idx=${index} position=${mLine.position} orient=${mLine.orientation} mid=(${Math.round(midX)},${Math.round(midY)}) isH=${isH} isV=${isV} wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})→(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})→(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) + logger.log(`🧭 [getSelectLinePosition 결과] idx=${index} position=${mLine.position} orient=${mLine.orientation} mid=(${Math.round(midX)},${Math.round(midY)}) isH=${isH} isV=${isV} wallLine=(${Math.round(wallLine.x1)},${Math.round(wallLine.y1)})→(${Math.round(wallLine.x2)},${Math.round(wallLine.y2)}) wallBaseLine=(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})→(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`) } if (getOrientation(roofLine) === 'vertical') { @@ -2241,7 +2247,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + logger.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } @@ -2300,7 +2306,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver } if (isStartEnd.end) { - console.log('left_out::::isStartEnd:::::', isStartEnd) + logger.log('left_out::::isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() const aStartY = Big(roofLine.y2).plus(moveDist).toNumber() const bStartY = Big(wallLine.y2).plus(moveDist).toNumber() @@ -2365,13 +2371,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + logger.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } if (isStartEnd.start) { - console.log('right_out::::isStartEnd:::::', isStartEnd) + logger.log('right_out::::isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.x1).minus(wallBaseLine.x1).abs().toNumber() const aStartY = Big(roofLine.y1).plus(moveDist).toNumber() const bStartY = Big(wallLine.y1).plus(moveDist).toNumber() @@ -2526,13 +2532,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + logger.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } if (isStartEnd.start) { - console.log('top_out isStartEnd:::::::', isStartEnd) + logger.log('top_out isStartEnd:::::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x1).plus(moveDist).toNumber() const bStartX = Big(wallLine.x1).plus(moveDist).toNumber() @@ -2585,7 +2591,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver getAddLine(newPStart, newPEnd, 'red') } if (isStartEnd.end) { - console.log('isStartEnd:::::', isStartEnd) + logger.log('isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x2).minus(moveDist).toNumber() const bStartX = Big(wallLine.x2).minus(moveDist).toNumber() @@ -2637,7 +2643,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver getAddLine(newPStart, newPEnd, 'red') } } else if (condition === 'bottom_out') { - console.log('bottom_out isStartEnd:::::::', isStartEnd) + logger.log('bottom_out isStartEnd:::::::', isStartEnd) // [SHOULDER-ADJACENT _out] 인접 pair 흡수 시 현재 pair 의 끝점은 새 SK 토폴로지에서 dissolve 됨. // roof.points 옛 코너 참조 → updateAndAddLine NOT FOUND + phantom eaveHelpLine 외부 출현. @@ -2649,13 +2655,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver const prevAbsorbed = (prevBL?.attributes?.planeSize ?? Infinity) < 10 const nextAbsorbed = (nextBL?.attributes?.planeSize ?? Infinity) < 10 if (prevAbsorbed || nextAbsorbed) { - console.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) + logger.log(`⏭️ [pair#${index}] ${condition} OUT_SKIP adj_absorbed prev=${prevAbsorbed} next=${nextAbsorbed}`) return } } if (isStartEnd.start) { - console.log('isStartEnd:::::::', isStartEnd) + logger.log('isStartEnd:::::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x1).minus(moveDist).toNumber() const bStartX = Big(wallLine.x1).minus(moveDist).toNumber() @@ -2708,7 +2714,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver } if (isStartEnd.end) { - console.log('isStartEnd:::::', isStartEnd) + logger.log('isStartEnd:::::', isStartEnd) const moveDist = Big(wallLine.y1).minus(wallBaseLine.y1).abs().toNumber() const aStartX = Big(roofLine.x2).plus(moveDist).toNumber() const bStartX = Big(wallLine.x2).plus(moveDist).toNumber() @@ -2845,7 +2851,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver if (toRemove.length > 0) { if (process.env.NEXT_PUBLIC_RUN_MODE === 'local') { - console.log( + logger.log( `[B안 cull] dead-end ridge 제거 ${toRemove.length}개`, toRemove.map( (l) => `(${Math.round(l.x1)},${Math.round(l.y1)})→(${Math.round(l.x2)},${Math.round(l.y2)})` @@ -2909,7 +2915,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { if(!outerLine) { outerLine = findMatchingLine(edgeResult.Polygon, roof, roof.points); - console.log('Has matching line:', outerLine); + logger.log('Has matching line:', outerLine); //if(outerLine === null) return } // [hip pitch fallback 2026-04-30] @@ -2956,7 +2962,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { return false } - // console.log('📐 [processEavesEdge] face 분석:', { + // logger.log('📐 [processEavesEdge] face 분석:', { // hasOuterLine: !!outerLine, // outerLineType: outerLine?.attributes?.type, // pitch, @@ -2976,19 +2982,19 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) { // 확장된 외곽선에 해당하는 edge는 스킵 if (_isSkipOuter) { - // console.log(' [edge', i, '] SKIP(outerEdge)', { p1: {x: Math.round(p1.x), y: Math.round(p1.y)}, p2: {x: Math.round(p2.x), y: Math.round(p2.y)} }) + // logger.log(' [edge', i, '] SKIP(outerEdge)', { p1: {x: Math.round(p1.x), y: Math.round(p1.y)}, p2: {x: Math.round(p2.x), y: Math.round(p2.y)} }) continue } // 지붕 경계선과 교차 확인 및 클리핑 const clippedLine = clipLineToRoofBoundary(p1, p2, roof.lines, roof.moveSelectLine); - //console.log('clipped line', clippedLine.p1, clippedLine.p2); + //logger.log('clipped line', clippedLine.p1, clippedLine.p2); const isOuterLine = isOuterEdge(clippedLine.p1, clippedLine.p2, [edgeResult.Edge]) // const _dx = Math.abs(clippedLine.p2.x - clippedLine.p1.x) // const _dy = Math.abs(clippedLine.p2.y - clippedLine.p1.y) // const _lineType = (_dx < 0.5 && _dy > 0.5) ? '⚠️수직' : (_dy < 0.5 && _dx > 0.5) ? '수평' : '대각' - // console.log(` [edge ${i}] ${_lineType} ${isOuterLine ? 'OUTER' : 'INNER'} (${Math.round(clippedLine.p1.x)},${Math.round(clippedLine.p1.y)})→(${Math.round(clippedLine.p2.x)},${Math.round(clippedLine.p2.y)}) dx:${Math.round(_dx)} dy:${Math.round(_dy)}`) + // logger.log(` [edge ${i}] ${_lineType} ${isOuterLine ? 'OUTER' : 'INNER'} (${Math.round(clippedLine.p1.x)},${Math.round(clippedLine.p1.y)})→(${Math.round(clippedLine.p2.x)},${Math.round(clippedLine.p2.y)}) dx:${Math.round(_dx)} dy:${Math.round(_dy)}`) addRawLine(roof.id, skeletonLines, clippedLine.p1, clippedLine.p2, 'ridge', '#1083E3', 4, pitch, isOuterLine, targetWallId); // } @@ -3068,7 +3074,7 @@ function logDeadEndLines(skeletonLines, roof) { const dy = Math.abs(line.p2.y - line.p1.y); if (dx < 0.5 && dy < 0.5) { - // console.log('⚠️ [logDeadEndLines] 제로 길이:', { idx, p1: { x: Math.round(line.p1.x), y: Math.round(line.p1.y) } }); + // logger.log('⚠️ [logDeadEndLines] 제로 길이:', { idx, p1: { x: Math.round(line.p1.x), y: Math.round(line.p1.y) } }); return; } @@ -3078,7 +3084,7 @@ function logDeadEndLines(skeletonLines, roof) { const p2Dead = deadEndVertices.has(k2); if (p1Dead || p2Dead) { - console.log('⚠️ [logDeadEndLines] 삭제 후보:', { + logger.log('⚠️ [logDeadEndLines] 삭제 후보:', { idx, p1: { x: Math.round(line.p1.x), y: Math.round(line.p1.y), deadEnd: p1Dead, isRoof: isRoofVertex(line.p1) }, p2: { x: Math.round(line.p2.x), y: Math.round(line.p2.y), deadEnd: p2Dead, isRoof: isRoofVertex(line.p2) }, @@ -3087,7 +3093,7 @@ function logDeadEndLines(skeletonLines, roof) { } }); - // console.log(`🔍 [logDeadEndLines] 전체: ${skeletonLines.length}, 유니크: ${uniqueLines.length}, dead end 꼭짓점: ${deadEndVertices.size}`); + // logger.log(`🔍 [logDeadEndLines] 전체: ${skeletonLines.length}, 유니크: ${uniqueLines.length}, dead end 꼭짓점: ${deadEndVertices.size}`); } function findMatchingLine(edgePolygon, roof, roofPoints) { @@ -3126,7 +3132,7 @@ function findMatchingLine(edgePolygon, roof, roofPoints) { function processGableEdge(edgeResult, baseLines, skeletonLines, selectBaseLine, lastSkeletonLines) { const edgePoints = edgeResult.Polygon.map(p => ({ x: p.X, y: p.Y })); //const polygons = createPolygonsFromSkeletonLines(skeletonLines, selectBaseLine); - //console.log("edgePoints::::::", edgePoints) + //logger.log("edgePoints::::::", edgePoints) // 1. Initialize processedLines with a deep copy of lastSkeletonLines let processedLines = [] // 1. 케라바 면과 관련된 불필요한 스켈레톤 선을 제거합니다. @@ -3141,8 +3147,8 @@ function processGableEdge(edgeResult, baseLines, skeletonLines, selectBaseLine, } } - //console.log("skeletonLines::::::", skeletonLines) - //console.log("lastSkeletonLines", lastSkeletonLines) + //logger.log("skeletonLines::::::", skeletonLines) + //logger.log("lastSkeletonLines", lastSkeletonLines) // 2. Find common lines between skeletonLines and lastSkeletonLines skeletonLines.forEach(line => { @@ -3168,9 +3174,9 @@ function processGableEdge(edgeResult, baseLines, skeletonLines, selectBaseLine, // return !isEdgeLine; // }); - //console.log("skeletonLines::::::", skeletonLines); - //console.log("lastSkeletonLines", lastSkeletonLines); - //console.log("processedLines after filtering", processedLines); + //logger.log("skeletonLines::::::", skeletonLines); + //logger.log("lastSkeletonLines", lastSkeletonLines); + //logger.log("processedLines after filtering", processedLines); return processedLines; @@ -3242,7 +3248,7 @@ const calcOverCorrectedPointsSafe = (points, origPoints, lastPoints) => { changedNow[i] ? op : (lastPoints[i] ?? op) ) - console.log('[calcOverCorrectedPointsSafe] changedNow:', + logger.log('[calcOverCorrectedPointsSafe] changedNow:', changedNow.map((v, i) => v ? i : null).filter(v => v !== null)) return calcOverCorrectedPoints(points, virtualOrig) @@ -3265,7 +3271,7 @@ const calcOverCorrectedPoints = (points, origPoints) => { const origDir = origPoints[movedIdx].x - origPoints[fixedIdx].x const newDir = skPoints[movedIdx].x - skPoints[fixedIdx].x if (origDir * newDir < 0) { - console.log(`[SK_OVER] 클램핑: [${movedIdx}].x ${Math.round(skPoints[movedIdx].x)} → ${Math.round(skPoints[fixedIdx].x)}`) + logger.log(`[SK_OVER] 클램핑: [${movedIdx}].x ${Math.round(skPoints[movedIdx].x)} → ${Math.round(skPoints[fixedIdx].x)}`) skPoints[movedIdx].x = skPoints[fixedIdx].x didClamp = true } @@ -3275,7 +3281,7 @@ const calcOverCorrectedPoints = (points, origPoints) => { const origDir = origPoints[movedIdx].y - origPoints[fixedIdx].y const newDir = skPoints[movedIdx].y - skPoints[fixedIdx].y if (origDir * newDir < 0) { - console.log(`[SK_OVER] 클램핑: [${movedIdx}].y ${Math.round(skPoints[movedIdx].y)} → ${Math.round(skPoints[fixedIdx].y)}`) + logger.log(`[SK_OVER] 클램핑: [${movedIdx}].y ${Math.round(skPoints[movedIdx].y)} → ${Math.round(skPoints[fixedIdx].y)}`) skPoints[movedIdx].y = skPoints[fixedIdx].y didClamp = true } @@ -3336,7 +3342,7 @@ const calcOverCorrectedPoints = (points, origPoints) => { // 보정 실패 시(꼭짓점 부족) 원본 반환 → 기존 동작 보장 if (cleaned.length >= 3) return cleaned if (deduped.length >= 3) return deduped - console.warn('[SK_OVER] calcOverCorrectedPoints: 보정 실패 → 원본 반환') + logger.warn('[SK_OVER] calcOverCorrectedPoints: 보정 실패 → 원본 반환') return points } @@ -3419,7 +3425,7 @@ function addRawLine(id, skeletonLines, p1, p2, lineType, color, width, pitch, is }; skeletonLines.push(newLine); - //console.log('skeletonLines', skeletonLines); + //logger.log('skeletonLines', skeletonLines); } /** @@ -4214,14 +4220,14 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { // p2가 다각형 내부에 있는지 확인 const p2Inside = isPointInsidePolygon(p2, roofLines); - //console.log('p1Inside:', p1Inside, 'p2Inside:', p2Inside); + //logger.log('p1Inside:', p1Inside, 'p2Inside:', p2Inside); // 두 점 모두 내부에 있으면 그대로 반환 if (p1Inside && p2Inside) { if(!selectLine || isDiagonal){ return { p1: clippedP1, p2: clippedP2 }; } - //console.log('평행선::', clippedP1, clippedP2) + //logger.log('평행선::', clippedP1, clippedP2) return { p1: clippedP1, p2: clippedP2 }; } @@ -4246,7 +4252,7 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { } } - //console.log('Found intersections:', intersections.length); + //logger.log('Found intersections:', intersections.length); // 교차점들을 t 값으로 정렬 intersections.sort((a, b) => a.t - b.t); @@ -4254,20 +4260,20 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { if (!p1Inside && !p2Inside) { // 두 점 모두 외부에 있는 경우 if (intersections.length >= 2) { - //console.log('Both outside, using intersection points'); + //logger.log('Both outside, using intersection points'); clippedP1.x = intersections[0].point.x; clippedP1.y = intersections[0].point.y; clippedP2.x = intersections[1].point.x; clippedP2.y = intersections[1].point.y; } else { - //console.log('Both outside, no valid intersections - returning original'); + //logger.log('Both outside, no valid intersections - returning original'); // 교차점이 충분하지 않으면 원본 반환 return { p1: clippedP1, p2: clippedP2 }; } } else if (!p1Inside && p2Inside) { // p1이 외부, p2가 내부 if (intersections.length > 0) { - //console.log('p1 outside, p2 inside - moving p1 to intersection'); + //logger.log('p1 outside, p2 inside - moving p1 to intersection'); clippedP1.x = intersections[0].point.x; clippedP1.y = intersections[0].point.y; // p2는 이미 내부에 있으므로 원본 유지 @@ -4277,7 +4283,7 @@ function clipLineToRoofBoundary(p1, p2, roofLines, selectLine) { } else if (p1Inside && !p2Inside) { // p1이 내부, p2가 외부 if (intersections.length > 0) { - //console.log('p1 inside, p2 outside - moving p2 to intersection'); + //logger.log('p1 inside, p2 outside - moving p2 to intersection'); // p1은 이미 내부에 있으므로 원본 유지 clippedP1.x = p1.x; clippedP1.y = p1.y; @@ -4528,32 +4534,32 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const { testDistance = 10, epsilon = 0.5, debug = false } = options; if (!wall || !selectLine) { - if (debug) console.log('ERROR: wall 또는 selectLine이 없음'); + if (debug) logger.log('ERROR: wall 또는 selectLine이 없음'); return { position: 'unknown', orientation: 'unknown', error: 'invalid_input' }; } // selectLine의 좌표 추출 const lineCoords = extractLineCoords(selectLine); if (!lineCoords.valid) { - if (debug) console.log('ERROR: selectLine 좌표가 유효하지 않음'); + if (debug) logger.log('ERROR: selectLine 좌표가 유효하지 않음'); return { position: 'unknown', orientation: 'unknown', error: 'invalid_coords' }; } const { x1, y1, x2, y2 } = lineCoords; - //console.log('wall.points', wall.baseLines); + //logger.log('wall.points', wall.baseLines); for(const line of wall.baseLines) { - //console.log('line', line); + //logger.log('line', line); const basePoint = extractLineCoords(line); const { x1: bx1, y1: by1, x2: bx2, y2: by2 } = basePoint; - //console.log('x1, y1, x2, y2', bx1, by1, bx2, by2); + //logger.log('x1, y1, x2, y2', bx1, by1, bx2, by2); // 객체 비교 대신 좌표값 비교 if (Math.abs(bx1 - x1) < 0.1 && Math.abs(by1 - y1) < 0.1 && Math.abs(bx2 - x2) < 0.1 && Math.abs(by2 - y2) < 0.1) { - //console.log('basePoint 일치!!!', basePoint); + //logger.log('basePoint 일치!!!', basePoint); } } @@ -4562,9 +4568,9 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const lineInfo = analyzeLineOrientation(x1, y1, x2, y2, epsilon); // if (debug) { - // console.log('=== getSelectLinePosition ==='); - // console.log('selectLine 좌표:', lineCoords); - // console.log('라인 방향:', lineInfo.orientation); + // logger.log('=== getSelectLinePosition ==='); + // logger.log('selectLine 좌표:', lineCoords); + // logger.log('라인 방향:', lineInfo.orientation); // } // 라인의 중점 @@ -4585,9 +4591,9 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const bottomIsInside = checkPointInPolygon(bottomTestPoint, wall); // if (debug) { - // console.log('수평선 테스트:'); - // console.log(' 위쪽 포인트:', topTestPoint, '-> 내부:', topIsInside); - // console.log(' 아래쪽 포인트:', bottomTestPoint, '-> 내부:', bottomIsInside); + // logger.log('수평선 테스트:'); + // logger.log(' 위쪽 포인트:', topTestPoint, '-> 내부:', topIsInside); + // logger.log(' 아래쪽 포인트:', bottomTestPoint, '-> 내부:', bottomIsInside); // } // top 조건: 위쪽이 외부, 아래쪽이 내부 @@ -4611,9 +4617,9 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { const rightIsInside = checkPointInPolygon(rightTestPoint, wall); // if (debug) { - // console.log('수직선 테스트:'); - // console.log(' 왼쪽 포인트:', leftTestPoint, '-> 내부:', leftIsInside); - // console.log(' 오른쪽 포인트:', rightTestPoint, '-> 내부:', rightIsInside); + // logger.log('수직선 테스트:'); + // logger.log(' 왼쪽 포인트:', leftTestPoint, '-> 내부:', leftIsInside); + // logger.log(' 오른쪽 포인트:', rightTestPoint, '-> 내부:', rightIsInside); // } // left 조건: 왼쪽이 외부, 오른쪽이 내부 @@ -4627,7 +4633,7 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { } else { // 대각선 - if (debug) console.log('대각선은 지원하지 않음'); + if (debug) logger.log('대각선은 지원하지 않음'); return { position: 'unknown', orientation: 'diagonal', error: 'not_supported' }; } @@ -4647,7 +4653,7 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => { }; // if (debug) { - // console.log('최종 결과:', result); + // logger.log('최종 결과:', result); // } return result; @@ -4658,7 +4664,7 @@ const checkPointInPolygon = (point, wall) => { // 2. wall.baseLines를 이용한 Ray Casting Algorithm if (!wall.baseLines || !Array.isArray(wall.baseLines)) { - console.warn('wall.baseLines가 없습니다'); + logger.warn('wall.baseLines가 없습니다'); return false; } @@ -4801,7 +4807,7 @@ function pointToLineDistance(point, lineP1, lineP2) { const getOrientation = (line, eps = 0.1) => { if (!line) { - console.error('line 객체가 유효하지 않습니다:', line); + logger.error('line 객체가 유효하지 않습니다:', line); return null; // 또는 적절한 기본값 반환 } @@ -4823,7 +4829,7 @@ const getOrientation = (line, eps = 0.1) => { if (dx < eps && dy < eps) return 'point'; return 'diagonal'; } catch (e) { - console.error('방향 계산 중 오류 발생:', e); + logger.error('방향 계산 중 오류 발생:', e); return null; } } @@ -4842,9 +4848,9 @@ export const processEaveHelpLines = (lines) => { const mergedHorizontal = mergeLines(horizontalLines, 'horizontal'); // 결과 확인용 로그 - console.log('Original lines:', lines.length); - console.log('Merged vertical:', mergedVertical.length); - console.log('Merged horizontal:', mergedHorizontal.length); + logger.log('Original lines:', lines.length); + logger.log('Merged vertical:', mergedVertical.length); + logger.log('Merged horizontal:', mergedHorizontal.length); return [...mergedVertical, ...mergedHorizontal]; }; @@ -4887,7 +4893,7 @@ const mergeLines = (lines, direction) => { merged.push(current); // 병합 결과 로그 - console.log(`Merged ${direction} lines:`, merged); + logger.log(`Merged ${direction} lines:`, merged); return merged; }; @@ -4953,11 +4959,11 @@ function updateAndAddLine(innerLines, targetPoint) { if (!foundLine) { foundLine = findLineContainingPoint(innerLines, targetPoint); if (foundLine) { - console.log(`[MOVE-TRACE] HIP fallback matched: position=${targetPoint.position} target=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)}) → (${foundLine.x1.toFixed(1)},${foundLine.y1.toFixed(1)})→(${foundLine.x2.toFixed(1)},${foundLine.y2.toFixed(1)}) type=${foundLine.attributes?.type||'?'}`) + logger.log(`[MOVE-TRACE] HIP fallback matched: position=${targetPoint.position} target=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)}) → (${foundLine.x1.toFixed(1)},${foundLine.y1.toFixed(1)})→(${foundLine.x2.toFixed(1)},${foundLine.y2.toFixed(1)}) type=${foundLine.attributes?.type||'?'}`) } } if (!foundLine) { - console.warn(`[updateAndAddLine] NOT FOUND position=${targetPoint.position} point=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)})`); + logger.warn(`[updateAndAddLine] NOT FOUND position=${targetPoint.position} point=(${targetPoint.x.toFixed(2)},${targetPoint.y.toFixed(2)})`); return [...innerLines]; } @@ -5110,7 +5116,7 @@ function isPointOnLineSegment2(point, lineStart, lineEnd, tolerance = 0.1) { const isOnSegment = Math.abs((dist1 + dist2) - lineLength) <= tolerance; if (isOnSegment) { - console.log(`점 (${px}, ${py})은 선분 [(${x1}, ${y1}), (${x2}, ${y2})] 위에 있습니다.`); + logger.log(`점 (${px}, ${py})은 선분 [(${x1}, ${y1}), (${x2}, ${y2})] 위에 있습니다.`); } return isOnSegment; @@ -5194,7 +5200,7 @@ function isValleyVertex(targetPoint, connectedLine, allLines, isStartVertex) { } const crossProduct = getTurnDirection(p1, p2, p3); - console.log('crossProduct:', crossProduct); + logger.log('crossProduct:', crossProduct); return crossProduct > 0; }