@ -96,7 +96,7 @@ const movingLineFromSkeleton = (roofId, canvas) => {
const _lp = canvas ? . skeleton ? . lastPoints
const _lp = canvas ? . skeleton ? . lastPoints
const _src = _lp ? 'lastPoints' : 'orgRoofPoints'
const _src = _lp ? 'lastPoints' : 'orgRoofPoints'
const _p7 = oldPoints ? . [ 7 ]
const _p7 = oldPoints ? . [ 7 ]
logger . 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 ) { }
} catch ( _e ) { }
const oppositeLine = findOppositeLine ( canvas . skeleton . Edges , startPoint , endPoint , oldPoints ) ;
const oppositeLine = findOppositeLine ( canvas . skeleton . Edges , startPoint , endPoint , oldPoints ) ;
@ -108,18 +108,18 @@ const movingLineFromSkeleton = (roofId, canvas) => {
const skeletonLines = canvas . getObjects ( ) . filter ( ( object ) => object . skeletonType === 'line' && object . parentId === roofId )
const skeletonLines = canvas . getObjects ( ) . filter ( ( object ) => object . skeletonType === 'line' && object . parentId === roofId )
if ( oppositeLine ) {
if ( oppositeLine ) {
logger . log ( 'Opposite line found:' , oppositeLine ) ;
// logger.log('Opposite line found:', oppositeLine);
} else {
} else {
logger . log ( 'No opposite line found' ) ;
// logger.log('No opposite line found');
}
}
if ( moveFlowLine !== 0 ) {
if ( moveFlowLine !== 0 ) {
return oldPoints . map ( ( point , index ) => {
return oldPoints . map ( ( point , index ) => {
logger . log ( 'Point:' , point ) ;
// logger.log('Point:', point);
const newPoint = { ... point } ;
const newPoint = { ... point } ;
const absMove = Big ( moveFlowLine ) . times ( 2 ) . div ( 10 ) ;
const absMove = Big ( moveFlowLine ) . times ( 2 ) . div ( 10 ) ;
logger . log ( 'skeletonBuilder moveDirection:' , moveDirection ) ;
// logger.log('skeletonBuilder moveDirection:', moveDirection);
switch ( moveDirection ) {
switch ( moveDirection ) {
case 'left' :
case 'left' :
@ -180,7 +180,7 @@ const movingLineFromSkeleton = (roofId, canvas) => {
if ( line . position === 'bottom' ) {
if ( line . position === 'bottom' ) {
logger . log ( 'oldPoint:' , point ) ;
// logger.log('oldPoint:', point);
if ( isSamePoint ( newPoint , line . start ) ) {
if ( isSamePoint ( newPoint , line . start ) ) {
newPoint . y = Big ( line . start . y ) . minus ( absMove ) . toNumber ( ) ;
newPoint . y = Big ( line . start . y ) . minus ( absMove ) . toNumber ( ) ;
@ -199,7 +199,7 @@ const movingLineFromSkeleton = (roofId, canvas) => {
// 사용 예시
// 사용 예시
}
}
logger . log ( 'newPoint:' , newPoint ) ;
// logger.log('newPoint:', newPoint);
//baseline 변경
//baseline 변경
return newPoint ;
return newPoint ;
} )
} )
@ -256,10 +256,10 @@ const movingLineFromSkeleton = (roofId, canvas) => {
affected . add ( k ) ;
affected . add ( k ) ;
affected . add ( ( k + 1 ) % nPts ) ;
affected . add ( ( k + 1 ) % nPts ) ;
} ) ;
} ) ;
logger . log ( 'absMove::' , moveUpDownLength ) ;
// logger.log('absMove::', moveUpDownLength);
// [BR-TRACE local] movingLineFromSkeleton position/direction 분기
// [BR-TRACE local] movingLineFromSkeleton position/direction 분기
const _ _isLocalMS = process . env . NEXT _PUBLIC _RUN _MODE === 'local'
const _ _isLocalMS = process . env . NEXT _PUBLIC _RUN _MODE === 'local'
if ( _ _isLocalMS ) logger . 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 ) => {
affected . forEach ( ( i ) => {
const point = newPoints [ i ] ;
const point = newPoints [ i ] ;
if ( ! point ) return ;
if ( ! point ) return ;
@ -267,19 +267,19 @@ const movingLineFromSkeleton = (roofId, canvas) => {
if ( position === 'bottom' ) {
if ( position === 'bottom' ) {
if ( moveDirection === 'in' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( ) ;
if ( moveDirection === 'in' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( ) ;
if ( _ _isLocalMS ) logger . 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' ) {
} else if ( position === 'top' ) {
if ( moveDirection === 'in' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( ) ;
if ( moveDirection === 'in' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( ) ;
if ( _ _isLocalMS ) logger . 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' ) {
} else if ( position === 'left' ) {
if ( moveDirection === 'in' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( ) ;
if ( moveDirection === 'in' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( ) ;
if ( _ _isLocalMS ) logger . 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' ) {
} else if ( position === 'right' ) {
if ( moveDirection === 'in' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( ) ;
if ( moveDirection === 'in' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( ) ;
else if ( moveDirection === 'out' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( ) ;
if ( _ _isLocalMS ) logger . 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 ) {
} else if ( _ _isLocalMS ) {
logger . warn ( ` [BR-TRACE] i= ${ i } position= ${ position } 매칭 분기 없음 (이동 안 됨) ` )
logger . warn ( ` [BR-TRACE] i= ${ i } position= ${ position } 매칭 분기 없음 (이동 안 됨) ` )
}
}
@ -409,7 +409,7 @@ const buildRawMovedPoints = (roofId, canvas) => {
// [LP-TRACE] buildRawMovedPoints 진입 — prevLast 유무 및 [7] 현재 값
// [LP-TRACE] buildRawMovedPoints 진입 — prevLast 유무 및 [7] 현재 값
try {
try {
const _pl7 = prevLast ? . [ 7 ]
const _pl7 = prevLast ? . [ 7 ]
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 ) } ) ` )
// 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 ) { }
} catch ( _e ) { }
const selectLine = roof . moveSelectLine
const selectLine = roof . moveSelectLine
@ -447,7 +447,7 @@ const buildRawMovedPoints = (roofId, canvas) => {
} )
} )
// [BR-TRACE local] buildRawMovedPoints position/direction 분기
// [BR-TRACE local] buildRawMovedPoints position/direction 분기
const _ _isLocalBR = process . env . NEXT _PUBLIC _RUN _MODE === 'local'
const _ _isLocalBR = process . env . NEXT _PUBLIC _RUN _MODE === 'local'
if ( _ _isLocalBR ) logger . 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 ) => {
affected . forEach ( ( i ) => {
const point = newPoints [ i ]
const point = newPoints [ i ]
if ( ! point ) return
if ( ! point ) return
@ -455,19 +455,19 @@ const buildRawMovedPoints = (roofId, canvas) => {
if ( position === 'bottom' ) {
if ( position === 'bottom' ) {
if ( moveDirection === 'out' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( )
if ( moveDirection === 'out' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( )
if ( _ _isLocalBR ) logger . 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' ) {
} else if ( position === 'top' ) {
if ( moveDirection === 'out' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( )
if ( moveDirection === 'out' ) point . y = Big ( point . y ) . minus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . y = Big ( point . y ) . plus ( moveUpDownLength ) . toNumber ( )
if ( _ _isLocalBR ) logger . 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' ) {
} else if ( position === 'left' ) {
if ( moveDirection === 'out' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( )
if ( moveDirection === 'out' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( )
if ( _ _isLocalBR ) logger . 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' ) {
} else if ( position === 'right' ) {
if ( moveDirection === 'out' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( )
if ( moveDirection === 'out' ) point . x = Big ( point . x ) . plus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( )
else if ( moveDirection === 'in' ) point . x = Big ( point . x ) . minus ( moveUpDownLength ) . toNumber ( )
if ( _ _isLocalBR ) logger . 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 ) {
} else if ( _ _isLocalBR ) {
logger . warn ( ` [BR-TRACE] i= ${ i } position= ${ position } 매칭 분기 없음 (이동 안 됨) ` )
logger . warn ( ` [BR-TRACE] i= ${ i } position= ${ position } 매칭 분기 없음 (이동 안 됨) ` )
}
}
@ -478,7 +478,7 @@ const buildRawMovedPoints = (roofId, canvas) => {
const _sel = selectLine
const _sel = selectLine
const _sp = _sel ? . startPoint
const _sp = _sel ? . startPoint
const _ep = _sel ? . endPoint
const _ep = _sel ? . endPoint
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 ) } ) ` )
// 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 ) { }
} catch ( _e ) { }
return newPoints
return newPoints
@ -512,7 +512,7 @@ export const verifyMoveBoundary = (roofId, canvas) => {
try {
try {
const roof = canvas ? . getObjects ( ) . find ( ( o ) => o . id === roofId )
const roof = canvas ? . getObjects ( ) . find ( ( o ) => o . id === roofId )
if ( ! roof || ! Array . isArray ( roof . points ) ) {
if ( ! roof || ! Array . isArray ( roof . points ) ) {
logger . log ( '[verifyMoveBoundary] roof/points 없음 → unknown' )
// logger.log( '[verifyMoveBoundary] roof/points 없음 → unknown')
return 'unknown'
return 'unknown'
}
}
@ -520,20 +520,20 @@ export const verifyMoveBoundary = (roofId, canvas) => {
const moveUpDown = roof . moveUpDown ? ? 0
const moveUpDown = roof . moveUpDown ? ? 0
const position = roof . movePosition
const position = roof . movePosition
const direction = roof . moveDirect
const direction = roof . moveDirect
logger . log (
// logger.log(
` [verifyMoveBoundary] 진입 position= ${ position } direction= ${ direction } moveUpDown= ${ moveUpDown } moveFlowLine= ${ moveFlowLine } `
// `[verifyMoveBoundary] 진입 position=${position} direction=${direction} moveUpDown=${moveUpDown} moveFlowLine=${moveFlowLine}`
)
// )
if ( moveFlowLine === 0 && moveUpDown === 0 ) {
if ( moveFlowLine === 0 && moveUpDown === 0 ) {
logger . log ( '[verifyMoveBoundary] 이동 없음 → ok' )
// logger.log('[verifyMoveBoundary] 이동 없음 → ok')
return 'ok'
return 'ok'
}
}
const proposed = buildRawMovedPoints ( roofId , canvas )
const proposed = buildRawMovedPoints ( roofId , canvas )
const orig = roof . points
const orig = roof . points
if ( ! Array . isArray ( proposed ) || proposed . length !== orig . length ) {
if ( ! Array . isArray ( proposed ) || proposed . length !== orig . length ) {
logger . log (
// logger.log(
` [verifyMoveBoundary] proposed 비정상 (len= ${ proposed ? . length } , orig.len= ${ orig . length } ) → unknown `
// `[verifyMoveBoundary] proposed 비정상 (len=${proposed?.length}, orig.len=${orig.length}) → unknown`
)
// )
return 'unknown'
return 'unknown'
}
}
@ -556,9 +556,9 @@ export const verifyMoveBoundary = (roofId, canvas) => {
const crossOrig = getTurnDirection ( orig [ prev ] , orig [ i ] , orig [ next ] )
const crossOrig = getTurnDirection ( orig [ prev ] , orig [ i ] , orig [ next ] )
const crossNew = getTurnDirection ( proposed [ prev ] , proposed [ i ] , proposed [ next ] )
const crossNew = getTurnDirection ( proposed [ prev ] , proposed [ i ] , proposed [ next ] )
logger . 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 ) } `
// `[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)}`
)
// )
// 원래 골짜기(>0) 였던 꼭짓점만 경계 판정 대상
// 원래 골짜기(>0) 였던 꼭짓점만 경계 판정 대상
if ( crossOrig > 0 ) {
if ( crossOrig > 0 ) {
@ -572,18 +572,18 @@ export const verifyMoveBoundary = (roofId, canvas) => {
return 'crossed'
return 'crossed'
}
}
if ( Math . abs ( crossNew ) <= boundaryTol ) {
if ( Math . abs ( crossNew ) <= boundaryTol ) {
logger . log (
// logger.log(
` [verifyMoveBoundary] 꼭짓점[ ${ i } ] 경계 도달 (on-boundary): cross ≈ 0 ( ${ crossNew . toFixed ( 1 ) } ) `
// `[verifyMoveBoundary] 꼭짓점[${i}] 경계 도달 (on-boundary): cross ≈ 0 (${crossNew.toFixed(1)})`
)
// )
if ( worst === 'ok' ) worst = 'on-boundary'
if ( worst === 'ok' ) worst = 'on-boundary'
}
}
}
}
}
}
if ( movedIdx . length === 0 ) {
if ( movedIdx . length === 0 ) {
logger . log ( '[verifyMoveBoundary] 이동된 꼭짓점 0개 (buildRawMovedPoints 매칭 실패 가능성)' )
// logger.log( '[verifyMoveBoundary] 이동된 꼭짓점 0개 (buildRawMovedPoints 매칭 실패 가능성)')
}
}
logger . log ( ` [verifyMoveBoundary] 최종 판정: ${ worst } (moved indices=[ ${ movedIdx . join ( ',' ) } ]) ` )
// logger.log(`[verifyMoveBoundary] 최종 판정: ${worst} (moved indices=[${movedIdx.join(',')}])`)
return worst
return worst
} catch ( e ) {
} catch ( e ) {
logger . warn ( '[verifyMoveBoundary] 판정 실패 → unknown:' , e )
logger . warn ( '[verifyMoveBoundary] 판정 실패 → unknown:' , e )
@ -735,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
if ( ! il || ! isFinite ( il . x1 ) || ! isFinite ( il . y1 ) || ! isFinite ( il . x2 ) || ! isFinite ( il . y2 ) ) return false
return il . lineName === 'hip' || il . name === 'hip'
return il . lineName === 'hip' || il . name === 'hip'
} )
} )
logger . 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 등).
// 절삭 대상: roofLine seg + canvas 의 다른 보조라인 (eaveHelpLine 등).
// 기존 helper 자체(BASELINE_TO_ROOFLINE_HELPER_TYPE) / wall/roof 본체 / 텍스트 등은 제외.
// 기존 helper 자체(BASELINE_TO_ROOFLINE_HELPER_TYPE) / wall/roof 본체 / 텍스트 등은 제외.
@ -765,7 +765,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
const s = segs [ k ]
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 ) ) } ` )
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 ) ) } ` )
}
}
logger . 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 만 대상.
// [v1 ext 정정] ext 는 wallbaseLine 의 꼭지점(corner) 에 outer 끝점이 일치하는 hip 만 대상.
@ -784,16 +784,16 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
// outer 끝이 wallbaseLine corner 에 일치하지 않으면 ext 대상 아님.
// outer 끝이 wallbaseLine corner 에 일치하지 않으면 ext 대상 아님.
if ( outerCornerDist > CORNER _EPS ) {
if ( outerCornerDist > CORNER _EPS ) {
logger . log (
// logger.log(
` [v1 ext] hip outer=( ${ outerEnd . x . toFixed ( 1 ) } , ${ outerEnd . y . toFixed ( 1 ) } ) ` +
// `[v1 ext] hip outer=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)}) ` +
` corner 거리= ${ outerCornerDist . toFixed ( 2 ) } > ${ CORNER _EPS } → 내부 hip skip `
// `corner 거리=${outerCornerDist.toFixed(2)} > ${CORNER_EPS} → 내부 hip skip`
)
// )
continue
continue
}
}
// 이미 처마(roofLine) 위에 있으면 ext 불필요.
// 이미 처마(roofLine) 위에 있으면 ext 불필요.
if ( isPointOnRoofLine ( outerEnd , 1.0 ) ) {
if ( isPointOnRoofLine ( outerEnd , 1.0 ) ) {
logger . 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
continue
}
}
@ -815,7 +815,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
// [BR-RAY] local only — finite t 인 모든 후보 hit 찍기 (어떤 seg 가 가장 빠른지 비교용)
// [BR-RAY] local only — finite t 인 모든 후보 hit 찍기 (어떤 seg 가 가장 빠른지 비교용)
if ( _ _isLocalRayLog && isFinite ( t ) ) {
if ( _ _isLocalRayLog && isFinite ( t ) ) {
const _ _label = ( k < m ) ? ` roofLine# ${ k } ` : ( segs [ k ] . _ _name || ` aux# ${ k - m } ` )
const _ _label = ( k < m ) ? ` roofLine# ${ k } ` : ( segs [ k ] . _ _name || ` aux# ${ k - m } ` )
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 ) } ) ` )
// 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 ) {
if ( t < bestT ) {
bestT = t
bestT = t
@ -824,7 +824,7 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
}
}
}
}
if ( ! isFinite ( bestT ) || bestT < 0.5 ) {
if ( ! isFinite ( bestT ) || bestT < 0.5 ) {
logger . 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
continue
}
}
@ -833,18 +833,18 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
// eaveHelpLine 이 ray 경로를 가로막으면 이미 eave/aux 영역에 도달한 것이므로 더 그릴 필요 없음.
// eaveHelpLine 이 ray 경로를 가로막으면 이미 eave/aux 영역에 도달한 것이므로 더 그릴 필요 없음.
// roofLine 승자만 진짜 "처마 도달" 로 인정 → ext 그림.
// roofLine 승자만 진짜 "처마 도달" 로 인정 → ext 그림.
if ( bestSegName === 'eaveHelpLine' ) {
if ( bestSegName === 'eaveHelpLine' ) {
logger . 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
continue
}
}
const hitX = outerEnd . x + ux * bestT
const hitX = outerEnd . x + ux * bestT
const hitY = outerEnd . y + uy * bestT
const hitY = outerEnd . y + uy * bestT
logger . log (
// logger.log(
` [v1 ext] hip outer=( ${ outerEnd . x . toFixed ( 1 ) } , ${ outerEnd . y . toFixed ( 1 ) } ) ` +
// `[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 } ` +
// `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 ) } ) `
// `→ ext=(${outerEnd.x.toFixed(1)},${outerEnd.y.toFixed(1)})→(${hitX.toFixed(1)},${hitY.toFixed(1)})`
)
// )
// ─────────────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────────────
// [hip 좌표 연장 2026-04-30]
// [hip 좌표 연장 2026-04-30]
@ -884,10 +884,10 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
if ( typeof hip . setLength === 'function' ) hip . setLength ( )
if ( typeof hip . setLength === 'function' ) hip . setLength ( )
if ( typeof hip . addLengthText === 'function' ) hip . addLengthText ( )
if ( typeof hip . addLengthText === 'function' ) hip . addLengthText ( )
logger . log (
// logger.log(
` [v1 ext] hip 연장 oldLen= ${ oldLen . toFixed ( 1 ) } +extLen= ${ extLen . toFixed ( 1 ) } ratio= ${ ratio . toFixed ( 3 ) } ` +
// `[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 } ) `
// `(plane ${oldPlane}→${hip.attributes.planeSize}, actual ${oldActual}→${hip.attributes.actualSize})`
)
// )
// ─────────────────────────────────────────────────────────────────────────
// ─────────────────────────────────────────────────────────────────────────
// [확장선 스타일] local: 빨강 점선(디버그 가시화), dev/prd: innerLine 동일색·실선(연결된 것처럼).
// [확장선 스타일] local: 빨강 점선(디버그 가시화), dev/prd: innerLine 동일색·실선(연결된 것처럼).
@ -983,10 +983,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
const moveUpDown = roof . moveUpDown || 0
const moveUpDown = roof . moveUpDown || 0
// 디버그: offset 변경 + moveLine 상태 확인
// 디버그: offset 변경 + moveLine 상태 확인
logger . log ( '[DEBUG] moveFlowLine:' , moveFlowLine , 'moveUpDown:' , moveUpDown )
// 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.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] 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 ) } ) ` ) )
// logger.log('[DEBUG] roof.points:', roof.points.map((p, i) => `[${i}] (${Math.round(p.x)},${Math.round(p.y)})`))
// 닫힌 폴리곤(첫점 = 끝점)인 경우 마지막 중복 점 제거
// 닫힌 폴리곤(첫점 = 끝점)인 경우 마지막 중복 점 제거
const isClosedPolygon = ( points ) =>
const isClosedPolygon = ( points ) =>
@ -1128,8 +1128,8 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
if ( SK _INPUT _USE _WALL _BASELINE _DIRECT ) {
if ( SK _INPUT _USE _WALL _BASELINE _DIRECT ) {
changRoofLinePoints = orderedBaseLinePoints . map ( ( p ) => ( { x : p . x , y : p . y } ) )
changRoofLinePoints = orderedBaseLinePoints . map ( ( p ) => ( { x : p . x , y : p . y } ) )
roofLineContactPoints = orderedBaseLinePoints . map ( ( p ) => ( { x : p . x , y : p . y } ) )
roofLineContactPoints = orderedBaseLinePoints . map ( ( p ) => ( { x : p . x , y : p . y } ) )
logger . 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 ( ' ' ) )
// changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' '))
// [v1 평행오버 corner 흡수 2026-04-29]
// [v1 평행오버 corner 흡수 2026-04-29]
// 평행오버 시 OVER_GUARD 가 인접 baseLine 길이를 OVER_EPS(0.5) 만큼만 남김.
// 평행오버 시 OVER_GUARD 가 인접 baseLine 길이를 OVER_EPS(0.5) 만큼만 남김.
@ -1162,15 +1162,15 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
kept = next
kept = next
}
}
if ( absorbedAll . length > 0 && kept . length >= 3 ) {
if ( absorbedAll . length > 0 && kept . length >= 3 ) {
logger . log ( ` [v1 SK_INPUT 평행흡수] ${ absorbedAll . length } 개 corner 제거: \n ${ absorbedAll . join ( '\n ' ) } ` )
// logger.log(`[v1 SK_INPUT 평행흡수] ${absorbedAll.length}개 corner 제거:\n ${absorbedAll.join('\n ')}`)
logger . log ( ` [v1 SK_INPUT 평행흡수] 최종 ${ kept . length } 개 corner: ` ,
// logger.log(`[v1 SK_INPUT 평행흡수] 최종 ${kept.length}개 corner:`,
kept . map ( ( p , i ) => ` [ ${ i } ]( ${ Math . round ( p . x ) } , ${ Math . round ( p . y ) } ) ` ) . join ( ' ' ) )
// kept.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`).join(' '))
changRoofLinePoints = kept
changRoofLinePoints = kept
roofLineContactPoints = kept . map ( ( p ) => ( { x : p . x , y : p . y } ) )
roofLineContactPoints = kept . map ( ( p ) => ( { x : p . x , y : p . y } ) )
}
}
}
}
logger . 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 ) =>
const invalidPoints = changRoofLinePoints . filter ( ( p , i ) =>
@ -1206,14 +1206,14 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
let isOverDetected = false
let isOverDetected = false
const origPoints = roof . points || [ ]
const origPoints = roof . points || [ ]
const n = changRoofLinePoints . length
const n = changRoofLinePoints . length
logger . log ( '[SK_OVER_DEBUG] origPoints:' , origPoints . map ( ( p , i ) => ` [ ${ i } ]( ${ Math . round ( p . x ) } , ${ Math . round ( p . y ) } ) ` ) )
// 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:', changRoofLinePoints.map((p, i) => `[${i}](${Math.round(p.x)},${Math.round(p.y)})`))
logger . log ( '[SK_OVER_DEBUG] 변경된 점:' , changRoofLinePoints . map ( ( p , i ) => {
// logger.log('[SK_OVER_DEBUG] 변경된 점:', changRoofLinePoints.map((p, i) => {
const o = origPoints [ i ]
// const o = origPoints[i]
if ( ! o ) return null
// if (!o) return null
const dist = Math . hypot ( p . x - o . x , p . y - o . y )
// const dist = Math.hypot(p.x - o.x, p.y - o.y)
return dist > 1 ? ` [ ${ i } ] dx= ${ Math . round ( p . x - o . x ) } dy= ${ Math . round ( p . y - o . y ) } ` : null
// return dist > 1 ? `[${i}] dx=${Math.round(p.x - o.x)} dy=${Math.round(p.y - o.y)}` : null
} ) . filter ( Boolean ) )
// }).filter(Boolean))
if ( origPoints . length === n && n >= 3 ) {
if ( origPoints . length === n && n >= 3 ) {
for ( let i = 0 ; i < n ; i ++ ) {
for ( let i = 0 ; i < n ; i ++ ) {
const prevOrig = origPoints [ ( i - 1 + n ) % n ]
const prevOrig = origPoints [ ( i - 1 + n ) % n ]
@ -1257,7 +1257,7 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
)
)
if ( corrected && corrected . length >= 3 ) {
if ( corrected && corrected . length >= 3 ) {
skeletonInputPoints = corrected
skeletonInputPoints = corrected
logger . 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 ) {
} catch ( e ) {
logger . error ( '[SK_OVER] 보정 실패 → fallback:' , e )
logger . error ( '[SK_OVER] 보정 실패 → fallback:' , e )
@ -1270,8 +1270,8 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
try {
try {
// SkeletonBuilder는 닫히지 않은 폴리곤을 기대하므로 마지막 점 제거
// SkeletonBuilder는 닫히지 않은 폴리곤을 기대하므로 마지막 점 제거
geoJSONPolygon . pop ( )
geoJSONPolygon . pop ( )
logger . log ( '[SkeletonBuilder] geoJSONPolygon:' , JSON . stringify ( geoJSONPolygon , null , 2 ) )
// logger.log('[SkeletonBuilder] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2))
logger . log ( '[SkeletonBuilder] 꼭짓점 수:' , geoJSONPolygon . length )
// logger.log('[SkeletonBuilder] 꼭짓점 수:', geoJSONPolygon.length)
const skeleton = SkeletonBuilder . BuildFromGeoJSON ( [ [ geoJSONPolygon ] ] )
const skeleton = SkeletonBuilder . BuildFromGeoJSON ( [ [ geoJSONPolygon ] ] )
// 스켈레톤 데이터를 기반으로 내부선 생성
// 스켈레톤 데이터를 기반으로 내부선 생성
@ -1324,9 +1324,9 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
// [LP-TRACE] 실제 저장되는 lastPoints[7] — 이 값이 다음 이동의 prevLast[7]
// [LP-TRACE] 실제 저장되는 lastPoints[7] — 이 값이 다음 이동의 prevLast[7]
try {
try {
const _p7 = rawMovedFull [ 7 ]
const _p7 = rawMovedFull [ 7 ]
logger . 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 ) { }
} catch ( _e ) { }
logger . log ( '[skeleton] lastPoints 저장: raw 풀 길이' , rawMovedFull . length , '점' )
// logger.log( '[skeleton] lastPoints 저장: raw 풀 길이', rawMovedFull.length, '점')
} else {
} else {
canvas . skeleton . lastPoints = roofLineContactPoints
canvas . skeleton . lastPoints = roofLineContactPoints
}
}
@ -1378,7 +1378,7 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) =>
const bl = wall . baseLines [ i ]
const bl = wall . baseLines [ i ]
const planeSize = bl ? . attributes ? . planeSize ? ? Infinity
const planeSize = bl ? . attributes ? . planeSize ? ? Infinity
if ( planeSize < 1 ) {
if ( planeSize < 1 ) {
logger . log ( ` [SK_OVER_FN] baseLines[ ${ i } ] SHOULDER_ABSORBED skip (planeSize= ${ planeSize } ) ` )
// logger.log(`[SK_OVER_FN] baseLines[${i}] SHOULDER_ABSORBED skip (planeSize=${planeSize})`)
continue
continue
}
}
if ( bl == null || ! isFinite ( bl . x1 ) || ! isFinite ( bl . y1 ) ) {
if ( bl == null || ! isFinite ( bl . x1 ) || ! isFinite ( bl . y1 ) ) {
@ -1393,7 +1393,7 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) =>
return
return
}
}
logger . 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 마킹 (오버 좌표 그대로)
// skeletonPoints 마킹 (오버 좌표 그대로)
roof . skeletonPoints = rawPoints . map ( ( p ) => ( { x : p . x , y : p . y } ) )
roof . skeletonPoints = rawPoints . map ( ( p ) => ( { x : p . x , y : p . y } ) )
@ -1403,8 +1403,8 @@ export const drawSkeletonRidgeRoofFromBaseLines = (roofId, canvas, textMode) =>
try {
try {
geoJSONPolygon . pop ( )
geoJSONPolygon . pop ( )
logger . log ( '[SK_OVER_FN] geoJSONPolygon:' , JSON . stringify ( geoJSONPolygon , null , 2 ) )
// logger.log('[SK_OVER_FN] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2))
logger . log ( '[SK_OVER_FN] 꼭짓점 수:' , geoJSONPolygon . length )
// logger.log('[SK_OVER_FN] 꼭짓점 수:', geoJSONPolygon.length)
const skeleton = SkeletonBuilder . BuildFromGeoJSON ( [ [ geoJSONPolygon ] ] )
const skeleton = SkeletonBuilder . BuildFromGeoJSON ( [ [ geoJSONPolygon ] ] )
roof . innerLines = roof . innerLines || [ ]
roof . innerLines = roof . innerLines || [ ]
@ -1577,7 +1577,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
if ( rotIdx > 0 ) {
if ( rotIdx > 0 ) {
wallLines = [ ... wallLines . slice ( rotIdx ) , ... wallLines . slice ( 0 , rotIdx ) ]
wallLines = [ ... wallLines . slice ( rotIdx ) , ... wallLines . slice ( 0 , rotIdx ) ]
if ( process . env . NEXT _PUBLIC _RUN _MODE === 'local' ) {
if ( process . env . NEXT _PUBLIC _RUN _MODE === 'local' ) {
logger . 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')
}
}
}
}
}
}
@ -1723,11 +1723,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
// [PAIR-DIAG] local only — top in 시 페어 인덱스 어긋남 진단 (2026-04-30)
// [PAIR-DIAG] local only — top in 시 페어 인덱스 어긋남 진단 (2026-04-30)
// 1) 길이 미스매치 / 2) _keyOfEdge 충돌 / 3) raw 단계 1:1 무너짐 가려내기 위함
// 1) 길이 미스매치 / 2) _keyOfEdge 충돌 / 3) raw 단계 1:1 무너짐 가려내기 위함
if ( process . env . NEXT _PUBLIC _RUN _MODE === 'local' ) {
if ( process . env . NEXT _PUBLIC _RUN _MODE === 'local' ) {
logger . log ( '[PAIR-DIAG] lengths' , {
// logger.log('[PAIR-DIAG] lengths', {
roofLines : roofLines . length ,
// roofLines: roofLines.length,
wallLines : wallLines . length ,
// wallLines: wallLines.length,
wallBaseLines : wall . baseLines . length ,
// wallBaseLines: wall.baseLines.length,
} )
// })
const _seen = new Map ( )
const _seen = new Map ( )
roofLines . forEach ( ( l , i ) => {
roofLines . forEach ( ( l , i ) => {
const k = _keyOfEdge ( l )
const k = _keyOfEdge ( l )
@ -1737,10 +1737,10 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
roofLines . forEach ( ( rl , i ) => {
roofLines . forEach ( ( rl , i ) => {
const wl = wallLines [ i ]
const wl = wallLines [ i ]
const wb = wall . baseLines [ i ]
const wb = wall . baseLines [ i ]
logger . 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 ) } ) ` ,
// `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=∅' ,
// 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=∅' )
// wb ? `base=(${Math.round(wb.x1)},${Math.round(wb.y1)})→(${Math.round(wb.x2)},${Math.round(wb.y2)})` : 'base=∅')
} )
} )
}
}
@ -1774,7 +1774,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const l = ` ${ lineAxis } ${ k } ` // y1 or x1
const l = ` ${ lineAxis } ${ k } ` // y1 or x1
const otherL = ` ${ lineAxis } ${ otherK } ` // y2 or x2
const otherL = ` ${ lineAxis } ${ otherK } ` // y2 or x2
logger . log ( ` ${ condition } ::::isStartEnd::::: ` )
// logger.log(`${condition}::::isStartEnd:::::`)
// [MOVE-TRACE] index 0 전용 (필요 시 주석 해제)
// [MOVE-TRACE] index 0 전용 (필요 시 주석 해제)
// if (index === 0) {
// if (index === 0) {
@ -1858,12 +1858,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
if ( prevAbsorbed || nextAbsorbed ) {
if ( prevAbsorbed || nextAbsorbed ) {
logger . 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
return
}
}
}
}
logger . log ( ` ${ condition } ::::isStartEnd (both)::::: ` )
// logger.log(`${condition}::::isStartEnd (both):::::`)
// 원본 기준: moveDistY = abs(roofLine.y - wallBaseLine.y), moveDistX = abs(roofLine.x - wallBaseLine.x)
// 원본 기준: moveDistY = abs(roofLine.y - wallBaseLine.y), moveDistX = abs(roofLine.x - wallBaseLine.x)
const moveDistY1 = Math . abs ( Big ( roofLine . y1 ) . minus ( wallBaseLine . y1 ) . toNumber ( ) )
const moveDistY1 = Math . abs ( Big ( roofLine . y1 ) . minus ( wallBaseLine . y1 ) . toNumber ( ) )
@ -1889,7 +1889,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const createHipLine = ( fromPoint , toPoint ) => {
const createHipLine = ( fromPoint , toPoint ) => {
const createdLine = getAddLine ( fromPoint , toPoint , 'orange' , 'extensionLine' )
const createdLine = getAddLine ( fromPoint , toPoint , 'orange' , 'extensionLine' )
logger . 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 . set ( { name : LINE _TYPE . SUBLINE . HIP , lineName : LINE _TYPE . SUBLINE . HIP , stroke : '#FF0000' , strokeWidth : 2 } )
createdLine . attributes = { ... createdLine . attributes , type : LINE _TYPE . SUBLINE . HIP }
createdLine . attributes = { ... createdLine . attributes , type : LINE _TYPE . SUBLINE . HIP }
innerLines . push ( createdLine )
innerLines . push ( createdLine )
@ -1915,7 +1915,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
target = { x : roofLine . x1 + xSignStart * dist , y : roofLine . y1 }
target = { x : roofLine . x1 + xSignStart * dist , y : roofLine . y1 }
}
}
if ( ! _ _isNearSkVertex ( target ) ) {
if ( ! _ _isNearSkVertex ( target ) ) {
logger . 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 {
} else {
createHipLine ( sPoint , target )
createHipLine ( sPoint , target )
}
}
@ -1931,7 +1931,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
target = { x : roofLine . x2 + xSignEnd * dist , y : roofLine . y2 }
target = { x : roofLine . x2 + xSignEnd * dist , y : roofLine . y2 }
}
}
if ( ! _ _isNearSkVertex ( target ) ) {
if ( ! _ _isNearSkVertex ( target ) ) {
logger . 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 {
} else {
createHipLine ( ePoint , target )
createHipLine ( ePoint , target )
}
}
@ -1988,7 +1988,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
// })
// })
// }
// }
logger . log ( 'wallBaseLines' , wall . baseLines )
// logger.log('wallBaseLines', wall.baseLines)
//wall.baseLine은 움직인라인
//wall.baseLine은 움직인라인
let movedLines = [ ]
let movedLines = [ ]
@ -1996,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 )
const validWallLines = [ ... wallLines ] . sort ( ( a , b ) => a . idx - b . idx ) . filter ( ( wallLine , index ) => wallLine . idx - 1 === index )
logger . log ( '' , sortRoofLines , sortWallLines , sortWallBaseLines ) ;
// logger.log('', sortRoofLines, sortWallLines, sortWallBaseLines);
( sortWallLines . length === sortWallBaseLines . length && sortWallBaseLines . length > 3 ) &&
( sortWallLines . length === sortWallBaseLines . length && sortWallBaseLines . length > 3 ) &&
sortWallLines . forEach ( ( wallLine , index ) => {
sortWallLines . forEach ( ( wallLine , index ) => {
@ -2009,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: useMovementSetting OVER_EPS=0.5 (canvas unit) 시 잔여 길이=0.5 → planeSize=5 (calcLinePlaneSize × 10).
// 안전마진 두고 < 10 으로 차단. 정상 baseLine 은 보통 50+ 이므로 false positive 없음.
// 안전마진 두고 < 10 으로 차단. 정상 baseLine 은 보통 50+ 이므로 false positive 없음.
if ( ! wallBaseLine || ( wallBaseLine . attributes ? . planeSize ? ? Infinity ) < 10 ) {
if ( ! wallBaseLine || ( wallBaseLine . attributes ? . planeSize ? ? Infinity ) < 10 ) {
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 } ` )
// 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
return
}
}
@ -2022,7 +2022,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const wallIdWl = wallLine . attributes ? . wallId ? ? wallLine . attributes ? . idx ? ? '?'
const wallIdWl = wallLine . attributes ? . wallId ? ? wallLine . attributes ? . idx ? ? '?'
const wallIdWb = wallBaseLine . attributes ? . wallId ? ? wallBaseLine . attributes ? . idx ? ? '?'
const wallIdWb = wallBaseLine . attributes ? . wallId ? ? wallBaseLine . attributes ? . idx ? ? '?'
const dirMatch = wlDir === wbDir
const dirMatch = wlDir === wbDir
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 ) } ) ` )
// 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 외곽선 설정
//roofline 외곽선 설정
@ -2066,7 +2066,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
}
}
for ( const [ p1 , p2 ] of _ _cgPairs ) {
for ( const [ p1 , p2 ] of _ _cgPairs ) {
if ( Math . hypot ( p2 . x - p1 . x , p2 . y - p1 . y ) < 0.5 ) continue
if ( Math . hypot ( p2 . x - p1 . x , p2 . y - p1 . y ) < 0.5 ) continue
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 ) } ) ` )
// 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 ] , {
const _ _cgLine = new QLine ( [ p1 . x , p1 . y , p2 . x , p2 . y ] , {
parentId : roof . id ,
parentId : roof . id ,
fontSize : roof . fontSize ,
fontSize : roof . fontSize ,
@ -2112,15 +2112,15 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
movedLines . push ( { index , p1 , p2 } )
movedLines . push ( { index , p1 , p2 } )
// [진단] eaveHelpLine 생성 추적: 어느 index/어느 wallBaseLine-wallLine 쌍에서 만들어지는지
// [진단] eaveHelpLine 생성 추적: 어느 index/어느 wallBaseLine-wallLine 쌍에서 만들어지는지
logger . log ( '🎯 [getAddLine]' , {
// logger.log('🎯 [getAddLine]', {
index ,
// index,
lineType ,
// lineType,
p1 : { x : Math . round ( p1 . x ) , y : Math . round ( p1 . y ) } ,
// p1: { x: Math.round(p1.x), y: Math.round(p1.y) },
p2 : { x : Math . round ( p2 . x ) , y : Math . round ( p2 . y ) } ,
// p2: { x: Math.round(p2.x), y: Math.round(p2.y) },
wallLine : ` ( ${ Math . round ( wallLine . x1 ) } , ${ Math . round ( wallLine . y1 ) } )→( ${ Math . round ( wallLine . x2 ) } , ${ Math . round ( wallLine . y2 ) } ) ` ,
// 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 ) } ) ` ,
// wallBaseLine: `(${Math.round(wallBaseLine.x1)},${Math.round(wallBaseLine.y1)})→(${Math.round(wallBaseLine.x2)},${Math.round(wallBaseLine.y2)})`,
wallBaseLen : Math . round ( Math . hypot ( wallBaseLine . x2 - wallBaseLine . x1 , wallBaseLine . y2 - wallBaseLine . y1 ) ) ,
// wallBaseLen: Math.round(Math.hypot(wallBaseLine.x2 - wallBaseLine.x1, wallBaseLine.y2 - wallBaseLine.y1)),
} )
// })
const dx = Math . abs ( p2 . x - p1 . x ) ;
const dx = Math . abs ( p2 . x - p1 . x ) ;
const dy = Math . abs ( p2 . y - p1 . y ) ;
const dy = Math . abs ( p2 . y - p1 . y ) ;
@ -2190,7 +2190,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const midY = ( wallBaseLine . y1 + wallBaseLine . y2 ) / 2
const midY = ( wallBaseLine . y1 + wallBaseLine . y2 ) / 2
const isH = Math . abs ( wallBaseLine . y1 - wallBaseLine . y2 ) < 0.5
const isH = Math . abs ( wallBaseLine . y1 - wallBaseLine . y2 ) < 0.5
const isV = Math . abs ( wallBaseLine . x1 - wallBaseLine . x2 ) < 0.5
const isV = Math . abs ( wallBaseLine . x1 - wallBaseLine . x2 ) < 0.5
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 ) } ) ` )
// 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' ) {
if ( getOrientation ( roofLine ) === 'vertical' ) {
@ -2247,7 +2247,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
if ( prevAbsorbed || nextAbsorbed ) {
if ( prevAbsorbed || nextAbsorbed ) {
logger . 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
return
}
}
}
}
@ -2306,7 +2306,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
}
}
if ( isStartEnd . end ) {
if ( isStartEnd . end ) {
logger . log ( 'left_out::::isStartEnd:::::' , isStartEnd )
// logger.log('left_out::::isStartEnd:::::', isStartEnd)
const moveDist = Big ( wallLine . x1 ) . minus ( wallBaseLine . x1 ) . abs ( ) . toNumber ( )
const moveDist = Big ( wallLine . x1 ) . minus ( wallBaseLine . x1 ) . abs ( ) . toNumber ( )
const aStartY = Big ( roofLine . y2 ) . plus ( moveDist ) . toNumber ( )
const aStartY = Big ( roofLine . y2 ) . plus ( moveDist ) . toNumber ( )
const bStartY = Big ( wallLine . y2 ) . plus ( moveDist ) . toNumber ( )
const bStartY = Big ( wallLine . y2 ) . plus ( moveDist ) . toNumber ( )
@ -2371,13 +2371,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
if ( prevAbsorbed || nextAbsorbed ) {
if ( prevAbsorbed || nextAbsorbed ) {
logger . 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
return
}
}
}
}
if ( isStartEnd . start ) {
if ( isStartEnd . start ) {
logger . log ( 'right_out::::isStartEnd:::::' , isStartEnd )
// logger.log('right_out::::isStartEnd:::::', isStartEnd)
const moveDist = Big ( wallLine . x1 ) . minus ( wallBaseLine . x1 ) . abs ( ) . toNumber ( )
const moveDist = Big ( wallLine . x1 ) . minus ( wallBaseLine . x1 ) . abs ( ) . toNumber ( )
const aStartY = Big ( roofLine . y1 ) . plus ( moveDist ) . toNumber ( )
const aStartY = Big ( roofLine . y1 ) . plus ( moveDist ) . toNumber ( )
const bStartY = Big ( wallLine . y1 ) . plus ( moveDist ) . toNumber ( )
const bStartY = Big ( wallLine . y1 ) . plus ( moveDist ) . toNumber ( )
@ -2532,13 +2532,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
if ( prevAbsorbed || nextAbsorbed ) {
if ( prevAbsorbed || nextAbsorbed ) {
logger . 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
return
}
}
}
}
if ( isStartEnd . start ) {
if ( isStartEnd . start ) {
logger . log ( 'top_out isStartEnd:::::::' , isStartEnd )
// logger.log('top_out isStartEnd:::::::', isStartEnd)
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const aStartX = Big ( roofLine . x1 ) . plus ( moveDist ) . toNumber ( )
const aStartX = Big ( roofLine . x1 ) . plus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x1 ) . plus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x1 ) . plus ( moveDist ) . toNumber ( )
@ -2591,7 +2591,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
getAddLine ( newPStart , newPEnd , 'red' )
getAddLine ( newPStart , newPEnd , 'red' )
}
}
if ( isStartEnd . end ) {
if ( isStartEnd . end ) {
logger . log ( 'isStartEnd:::::' , isStartEnd )
// logger.log('isStartEnd:::::', isStartEnd)
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const aStartX = Big ( roofLine . x2 ) . minus ( moveDist ) . toNumber ( )
const aStartX = Big ( roofLine . x2 ) . minus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x2 ) . minus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x2 ) . minus ( moveDist ) . toNumber ( )
@ -2643,7 +2643,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
getAddLine ( newPStart , newPEnd , 'red' )
getAddLine ( newPStart , newPEnd , 'red' )
}
}
} else if ( condition === 'bottom_out' ) {
} else if ( condition === 'bottom_out' ) {
logger . log ( 'bottom_out isStartEnd:::::::' , isStartEnd )
// logger.log('bottom_out isStartEnd:::::::', isStartEnd)
// [SHOULDER-ADJACENT _out] 인접 pair 흡수 시 현재 pair 의 끝점은 새 SK 토폴로지에서 dissolve 됨.
// [SHOULDER-ADJACENT _out] 인접 pair 흡수 시 현재 pair 의 끝점은 새 SK 토폴로지에서 dissolve 됨.
// roof.points 옛 코너 참조 → updateAndAddLine NOT FOUND + phantom eaveHelpLine 외부 출현.
// roof.points 옛 코너 참조 → updateAndAddLine NOT FOUND + phantom eaveHelpLine 외부 출현.
@ -2655,13 +2655,13 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const prevAbsorbed = ( prevBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
const nextAbsorbed = ( nextBL ? . attributes ? . planeSize ? ? Infinity ) < 10
if ( prevAbsorbed || nextAbsorbed ) {
if ( prevAbsorbed || nextAbsorbed ) {
logger . 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
return
}
}
}
}
if ( isStartEnd . start ) {
if ( isStartEnd . start ) {
logger . log ( 'isStartEnd:::::::' , isStartEnd )
// logger.log('isStartEnd:::::::', isStartEnd)
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const aStartX = Big ( roofLine . x1 ) . minus ( moveDist ) . toNumber ( )
const aStartX = Big ( roofLine . x1 ) . minus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x1 ) . minus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x1 ) . minus ( moveDist ) . toNumber ( )
@ -2714,7 +2714,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
}
}
if ( isStartEnd . end ) {
if ( isStartEnd . end ) {
logger . log ( 'isStartEnd:::::' , isStartEnd )
// logger.log('isStartEnd:::::', isStartEnd)
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const moveDist = Big ( wallLine . y1 ) . minus ( wallBaseLine . y1 ) . abs ( ) . toNumber ( )
const aStartX = Big ( roofLine . x2 ) . plus ( moveDist ) . toNumber ( )
const aStartX = Big ( roofLine . x2 ) . plus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x2 ) . plus ( moveDist ) . toNumber ( )
const bStartX = Big ( wallLine . x2 ) . plus ( moveDist ) . toNumber ( )
@ -2851,12 +2851,12 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
if ( toRemove . length > 0 ) {
if ( toRemove . length > 0 ) {
if ( process . env . NEXT _PUBLIC _RUN _MODE === 'local' ) {
if ( process . env . NEXT _PUBLIC _RUN _MODE === 'local' ) {
logger . log (
// logger.log(
` [B안 cull] dead-end ridge 제거 ${ toRemove . length } 개 ` ,
// `[B안 cull] dead-end ridge 제거 ${toRemove.length}개`,
toRemove . map (
// toRemove.map(
( l ) => ` ( ${ Math . round ( l . x1 ) } , ${ Math . round ( l . y1 ) } )→( ${ Math . round ( l . x2 ) } , ${ Math . round ( l . y2 ) } ) `
// (l) => `(${Math.round(l.x1)},${Math.round(l.y1)})→(${Math.round(l.x2)},${Math.round(l.y2)})`
)
// )
)
// )
}
}
toRemove . forEach ( ( line ) => {
toRemove . forEach ( ( line ) => {
canvas . remove ( line )
canvas . remove ( line )
@ -2915,7 +2915,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) {
if ( ! outerLine ) {
if ( ! outerLine ) {
outerLine = findMatchingLine ( edgeResult . Polygon , roof , roof . points ) ;
outerLine = findMatchingLine ( edgeResult . Polygon , roof , roof . points ) ;
logger . log ( 'Has matching line:' , outerLine ) ;
// logger.log('Has matching line:', outerLine);
//if(outerLine === null) return
//if(outerLine === null) return
}
}
// [hip pitch fallback 2026-04-30]
// [hip pitch fallback 2026-04-30]
@ -3084,12 +3084,12 @@ function logDeadEndLines(skeletonLines, roof) {
const p2Dead = deadEndVertices . has ( k2 ) ;
const p2Dead = deadEndVertices . has ( k2 ) ;
if ( p1Dead || p2Dead ) {
if ( p1Dead || p2Dead ) {
logger . log ( '⚠️ [logDeadEndLines] 삭제 후보:' , {
// logger.log('⚠️ [logDeadEndLines] 삭제 후보:', {
idx ,
// idx,
p1 : { x : Math . round ( line . p1 . x ) , y : Math . round ( line . p1 . y ) , deadEnd : p1Dead , isRoof : isRoofVertex ( line . p1 ) } ,
// 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 ) } ,
// p2: { x: Math.round(line.p2.x), y: Math.round(line.p2.y), deadEnd: p2Dead, isRoof: isRoofVertex(line.p2) },
dx : Math . round ( dx ) , dy : Math . round ( dy ) ,
// dx: Math.round(dx), dy: Math.round(dy),
} ) ;
// });
}
}
} ) ;
} ) ;
@ -3248,8 +3248,8 @@ const calcOverCorrectedPointsSafe = (points, origPoints, lastPoints) => {
changedNow [ i ] ? op : ( lastPoints [ i ] ? ? op )
changedNow [ i ] ? op : ( lastPoints [ i ] ? ? op )
)
)
logger . log ( '[calcOverCorrectedPointsSafe] changedNow:' ,
// logger.log('[calcOverCorrectedPointsSafe] changedNow:',
changedNow . map ( ( v , i ) => v ? i : null ) . filter ( v => v !== null ) )
// changedNow.map((v, i) => v ? i : null).filter(v => v !== null))
return calcOverCorrectedPoints ( points , virtualOrig )
return calcOverCorrectedPoints ( points , virtualOrig )
}
}
@ -3271,7 +3271,7 @@ const calcOverCorrectedPoints = (points, origPoints) => {
const origDir = origPoints [ movedIdx ] . x - origPoints [ fixedIdx ] . x
const origDir = origPoints [ movedIdx ] . x - origPoints [ fixedIdx ] . x
const newDir = skPoints [ movedIdx ] . x - skPoints [ fixedIdx ] . x
const newDir = skPoints [ movedIdx ] . x - skPoints [ fixedIdx ] . x
if ( origDir * newDir < 0 ) {
if ( origDir * newDir < 0 ) {
logger . 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
skPoints [ movedIdx ] . x = skPoints [ fixedIdx ] . x
didClamp = true
didClamp = true
}
}
@ -3281,7 +3281,7 @@ const calcOverCorrectedPoints = (points, origPoints) => {
const origDir = origPoints [ movedIdx ] . y - origPoints [ fixedIdx ] . y
const origDir = origPoints [ movedIdx ] . y - origPoints [ fixedIdx ] . y
const newDir = skPoints [ movedIdx ] . y - skPoints [ fixedIdx ] . y
const newDir = skPoints [ movedIdx ] . y - skPoints [ fixedIdx ] . y
if ( origDir * newDir < 0 ) {
if ( origDir * newDir < 0 ) {
logger . 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
skPoints [ movedIdx ] . y = skPoints [ fixedIdx ] . y
didClamp = true
didClamp = true
}
}
@ -4534,14 +4534,14 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => {
const { testDistance = 10 , epsilon = 0.5 , debug = false } = options ;
const { testDistance = 10 , epsilon = 0.5 , debug = false } = options ;
if ( ! wall || ! selectLine ) {
if ( ! wall || ! selectLine ) {
if ( debug ) logger . log ( 'ERROR: wall 또는 selectLine이 없음' ) ;
// if (debug) logger.log('ERROR: wall 또는 selectLine이 없음');
return { position : 'unknown' , orientation : 'unknown' , error : 'invalid_input' } ;
return { position : 'unknown' , orientation : 'unknown' , error : 'invalid_input' } ;
}
}
// selectLine의 좌표 추출
// selectLine의 좌표 추출
const lineCoords = extractLineCoords ( selectLine ) ;
const lineCoords = extractLineCoords ( selectLine ) ;
if ( ! lineCoords . valid ) {
if ( ! lineCoords . valid ) {
if ( debug ) logger . log ( 'ERROR: selectLine 좌표가 유효하지 않음' ) ;
// if (debug) logger.log( 'ERROR: selectLine 좌표가 유효하지 않음');
return { position : 'unknown' , orientation : 'unknown' , error : 'invalid_coords' } ;
return { position : 'unknown' , orientation : 'unknown' , error : 'invalid_coords' } ;
}
}
@ -4633,7 +4633,7 @@ export const getSelectLinePosition = (wall, selectLine, options = {}) => {
} else {
} else {
// 대각선
// 대각선
if ( debug ) logger . log ( '대각선은 지원하지 않음' ) ;
// if (debug) logger.log('대각선은 지원하지 않음');
return { position : 'unknown' , orientation : 'diagonal' , error : 'not_supported' } ;
return { position : 'unknown' , orientation : 'diagonal' , error : 'not_supported' } ;
}
}
@ -4848,9 +4848,9 @@ export const processEaveHelpLines = (lines) => {
const mergedHorizontal = mergeLines ( horizontalLines , 'horizontal' ) ;
const mergedHorizontal = mergeLines ( horizontalLines , 'horizontal' ) ;
// 결과 확인용 로그
// 결과 확인용 로그
logger . log ( 'Original lines:' , lines . length ) ;
// logger.log('Original lines:', lines.length);
logger . log ( 'Merged vertical:' , mergedVertical . length ) ;
// logger.log('Merged vertical:', mergedVertical.length);
logger . log ( 'Merged horizontal:' , mergedHorizontal . length ) ;
// logger.log('Merged horizontal:', mergedHorizontal.length);
return [ ... mergedVertical , ... mergedHorizontal ] ;
return [ ... mergedVertical , ... mergedHorizontal ] ;
} ;
} ;
@ -4893,7 +4893,7 @@ const mergeLines = (lines, direction) => {
merged . push ( current ) ;
merged . push ( current ) ;
// 병합 결과 로그
// 병합 결과 로그
logger . log ( ` Merged ${ direction } lines: ` , merged ) ;
// logger.log(`Merged ${direction} lines:`, merged);
return merged ;
return merged ;
} ;
} ;
@ -4959,7 +4959,7 @@ function updateAndAddLine(innerLines, targetPoint) {
if ( ! foundLine ) {
if ( ! foundLine ) {
foundLine = findLineContainingPoint ( innerLines , targetPoint ) ;
foundLine = findLineContainingPoint ( innerLines , targetPoint ) ;
if ( foundLine ) {
if ( foundLine ) {
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 || '?' } ` )
// 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 ) {
if ( ! foundLine ) {
@ -5116,7 +5116,7 @@ function isPointOnLineSegment2(point, lineStart, lineEnd, tolerance = 0.1) {
const isOnSegment = Math . abs ( ( dist1 + dist2 ) - lineLength ) <= tolerance ;
const isOnSegment = Math . abs ( ( dist1 + dist2 ) - lineLength ) <= tolerance ;
if ( isOnSegment ) {
if ( isOnSegment ) {
logger . log ( ` 점 ( ${ px } , ${ py } )은 선분 [( ${ x1 } , ${ y1 } ), ( ${ x2 } , ${ y2 } )] 위에 있습니다. ` ) ;
// logger.log(`점 (${px}, ${py})은 선분 [(${x1}, ${y1}), (${x2}, ${y2})] 위에 있습니다.`);
}
}
return isOnSegment ;
return isOnSegment ;
@ -5223,13 +5223,13 @@ function isValleyVertex(targetPoint, connectedLine, allLines, isStartVertex) {
const fmt = ( p ) => ` ( ${ Math . round ( p . x ) } , ${ Math . round ( p . y ) } ) ` ;
const fmt = ( p ) => ` ( ${ Math . round ( p . x ) } , ${ Math . round ( p . y ) } ) ` ;
const cLen = Math . hypot ( clx2 - clx1 , cly2 - cly1 ) ;
const cLen = Math . hypot ( clx2 - clx1 , cly2 - cly1 ) ;
const nLen = Math . hypot ( nlx2 - nlx1 , nly2 - nly1 ) ;
const nLen = Math . hypot ( nlx2 - nlx1 , nly2 - nly1 ) ;
logger . log (
// logger.log(
` [VALLEY] ${ isStartVertex ? 'START' : 'END' } target= ${ fmt ( targetPoint ) } ` +
// `[VALLEY] ${isStartVertex ? 'START' : 'END'} target=${fmt(targetPoint)} ` +
` conn= ${ fmt ( { x : clx1 , y : cly1 } )}→ ${ fmt ( { x : clx2 , y : cly2 } )}[len= ${ cLen . toFixed ( 2 ) } ] ` +
// `conn=${fmt({x:clx1,y:cly1})}→${fmt({x:clx2,y:cly2})}[len=${cLen.toFixed(2)}] ` +
` neigh= ${ fmt ( { x : nlx1 , y : nly1 } )}→ ${ fmt ( { x : nlx2 , y : nly2 } )}[len= ${ nLen . toFixed ( 2 ) } ] ` +
// `neigh=${fmt({x:nlx1,y:nly1})}→${fmt({x:nlx2,y:nly2})}[len=${nLen.toFixed(2)}] ` +
` p1= ${ fmt ( p1 ) } p2= ${ fmt ( p2 ) } p3= ${ fmt ( p3 ) } cross= ${ crossProduct . toFixed ( 2 ) } ` +
// `p1=${fmt(p1)} p2=${fmt(p2)} p3=${fmt(p3)} cross=${crossProduct.toFixed(2)} ` +
` valley= ${ collinearSkip ? false : ( crossProduct > 0 ) } ${ collinearSkip ? ' (collinear-skip)' : '' } `
// `valley=${collinearSkip ? false : (crossProduct > 0)}${collinearSkip ? ' (collinear-skip)' : ''}`
) ;
// );
if ( collinearSkip ) return false ;
if ( collinearSkip ) return false ;
return crossProduct > 0 ;
return crossProduct > 0 ;
}
}