보조라인1
This commit is contained in:
parent
beeeeb32e5
commit
645e8b344d
@ -18,8 +18,7 @@ import { drawSkeletonRidgeRoof, drawSkeletonRidgeRoofFromBaseLines, verifyMoveBo
|
|||||||
const DEBUG_LABEL_NAME = '__debugLabel'
|
const DEBUG_LABEL_NAME = '__debugLabel'
|
||||||
|
|
||||||
function __isDebugLabelsEnabled() {
|
function __isDebugLabelsEnabled() {
|
||||||
// 디버그 라벨(H-1, B-4, RG-1 등) 표시 비활성화
|
return process.env.NEXT_PUBLIC_RUN_MODE === 'local'
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function __classifyLineForLabel(obj) {
|
function __classifyLineForLabel(obj) {
|
||||||
|
|||||||
@ -443,11 +443,11 @@ export function useMovementSetting(id) {
|
|||||||
// 실패 시 mouseDownEvent 가 wallbaseLine 그리기/handleSave 를 차단.
|
// 실패 시 mouseDownEvent 가 wallbaseLine 그리기/handleSave 를 차단.
|
||||||
lastMoveValidRef.current = __inputUpdated
|
lastMoveValidRef.current = __inputUpdated
|
||||||
if (__isLocalMV) {
|
if (__isLocalMV) {
|
||||||
console.log(
|
// console.log(
|
||||||
`[MV-TRACE] type=${typeRef.current} value=${value?.toString?.()} dir=${direction} ` +
|
// `[MV-TRACE] type=${typeRef.current} value=${value?.toString?.()} dir=${direction} ` +
|
||||||
`inputUpdated=${__inputUpdated} ` +
|
// `inputUpdated=${__inputUpdated} ` +
|
||||||
`selectedObject=${selectedObject.current ? 'set' : 'null'}`
|
// `selectedObject=${selectedObject.current ? 'set' : 'null'}`
|
||||||
)
|
// )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -529,6 +529,7 @@ export function useMovementSetting(id) {
|
|||||||
//console.log("target::::", target, roof.moveSelectLine)
|
//console.log("target::::", target, roof.moveSelectLine)
|
||||||
const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes.roofId === roofId)
|
const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes.roofId === roofId)
|
||||||
const baseLines = wall.baseLines
|
const baseLines = wall.baseLines
|
||||||
|
|
||||||
let centerPoint = wall.getCenterPoint()
|
let centerPoint = wall.getCenterPoint()
|
||||||
let targetBaseLines = []
|
let targetBaseLines = []
|
||||||
let isGableRoof
|
let isGableRoof
|
||||||
|
|||||||
@ -240,6 +240,7 @@ export function useRoofShapePassivitySetting(id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
wall.lines = [...lines]
|
wall.lines = [...lines]
|
||||||
|
|
||||||
// 기존 그려진 지붕이 없다면
|
// 기존 그려진 지붕이 없다면
|
||||||
|
|
||||||
if (isFix.current) {
|
if (isFix.current) {
|
||||||
|
|||||||
@ -754,7 +754,17 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
|
|||||||
if (excludedNames.has(obj.name) || excludedNames.has(obj.lineName)) continue
|
if (excludedNames.has(obj.name) || excludedNames.has(obj.lineName)) continue
|
||||||
if (!isFinite(obj.x1) || !isFinite(obj.y1) || !isFinite(obj.x2) || !isFinite(obj.y2)) continue
|
if (!isFinite(obj.x1) || !isFinite(obj.y1) || !isFinite(obj.x2) || !isFinite(obj.y2)) continue
|
||||||
if (Math.hypot(obj.x2 - obj.x1, obj.y2 - obj.y1) < 0.5) continue
|
if (Math.hypot(obj.x2 - obj.x1, obj.y2 - obj.y1) < 0.5) continue
|
||||||
segs.push({ A: { x: obj.x1, y: obj.y1 }, B: { x: obj.x2, y: obj.y2 } })
|
segs.push({ A: { x: obj.x1, y: obj.y1 }, B: { x: obj.x2, y: obj.y2 }, __name: obj.name || obj.lineName })
|
||||||
|
}
|
||||||
|
|
||||||
|
// [BR-SEGS] local only — segs 에 들어간 aux 라인 목록 (eaveHelpLine 등 보조선 포함 여부 진단)
|
||||||
|
if (process.env.NEXT_PUBLIC_RUN_MODE === 'local') {
|
||||||
|
const auxList = []
|
||||||
|
for (let k = m; k < segs.length; 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))}`)
|
||||||
|
}
|
||||||
|
console.log(`[BR-SEGS] roofLine=${m} aux=${segs.length - m}`, auxList)
|
||||||
}
|
}
|
||||||
|
|
||||||
// [v1 ext 정정] ext 는 wallbaseLine 의 꼭지점(corner) 에 outer 끝점이 일치하는 hip 만 대상.
|
// [v1 ext 정정] ext 는 wallbaseLine 의 꼭지점(corner) 에 outer 끝점이 일치하는 hip 만 대상.
|
||||||
@ -796,12 +806,20 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
|
|||||||
// outerEnd 에서 unit 방향으로 ray cast → 가장 먼저 만나는 segment 까지.
|
// outerEnd 에서 unit 방향으로 ray cast → 가장 먼저 만나는 segment 까지.
|
||||||
let bestT = Infinity
|
let bestT = Infinity
|
||||||
let bestSrc = ''
|
let bestSrc = ''
|
||||||
|
let bestSegName = ''
|
||||||
|
const __isLocalRayLog = process.env.NEXT_PUBLIC_RUN_MODE === 'local'
|
||||||
for (let k = 0; k < segs.length; k++) {
|
for (let k = 0; k < segs.length; k++) {
|
||||||
const { A, B } = segs[k]
|
const { A, B } = segs[k]
|
||||||
const t = rayHitSegment(outerEnd, { x: ux, y: uy }, A, B)
|
const t = rayHitSegment(outerEnd, { x: ux, y: uy }, A, B)
|
||||||
|
// [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)})`)
|
||||||
|
}
|
||||||
if (t < bestT) {
|
if (t < bestT) {
|
||||||
bestT = t
|
bestT = t
|
||||||
bestSrc = (k < m) ? 'roofLine' : 'aux'
|
bestSrc = (k < m) ? 'roofLine' : 'aux'
|
||||||
|
bestSegName = (k < m) ? 'roofLine' : (segs[k].__name || 'aux')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isFinite(bestT) || bestT < 0.5) {
|
if (!isFinite(bestT) || bestT < 0.5) {
|
||||||
@ -809,6 +827,15 @@ const drawBaselineToRooflineHelpers = (roof, canvas, baseLinePoints, roofLinePoi
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// [2026-04-30] 승자가 eaveHelpLine 이면 ext skip.
|
||||||
|
// ext 의 목적은 hip 을 outer roofLine(처마) 까지 연장하는 것.
|
||||||
|
// 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`)
|
||||||
|
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
|
||||||
|
|
||||||
@ -1494,8 +1521,20 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
|
|||||||
|
|
||||||
//처마라인
|
//처마라인
|
||||||
const roofLines = roof.lines
|
const roofLines = roof.lines
|
||||||
//벽라인
|
//벽라인 — wall.baseLines 의 회전 기준에 맞춰 wall.lines 정렬 보정 (createOrderedBasePoints 사상)
|
||||||
const wallLines = wall.lines
|
let wallLines = wall.lines
|
||||||
|
if (wall.baseLines && wall.baseLines.length > 0 && wallLines && wallLines.length === wall.baseLines.length) {
|
||||||
|
const refStart = wall.baseLines[0].startPoint
|
||||||
|
if (refStart) {
|
||||||
|
let rotIdx = wallLines.findIndex((wl) => wl.startPoint && isSamePoint(wl.startPoint, refStart, 1.0))
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
skeletonLines.forEach((sktLine, skIndex) => {
|
skeletonLines.forEach((sktLine, skIndex) => {
|
||||||
let { p1, p2, attributes, lineStyle } = sktLine;
|
let { p1, p2, attributes, lineStyle } = sktLine;
|
||||||
@ -1630,6 +1669,30 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
|
|||||||
return origIdx != null && wall.baseLines[origIdx] ? wall.baseLines[origIdx] : sl
|
return origIdx != null && wall.baseLines[origIdx] ? wall.baseLines[origIdx] : sl
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// [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', {
|
||||||
|
roofLines: roofLines.length,
|
||||||
|
wallLines: wallLines.length,
|
||||||
|
wallBaseLines: wall.baseLines.length,
|
||||||
|
})
|
||||||
|
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}`)
|
||||||
|
else _seen.set(k, i)
|
||||||
|
})
|
||||||
|
roofLines.forEach((rl, i) => {
|
||||||
|
const wl = wallLines[i]
|
||||||
|
const wb = wall.baseLines[i]
|
||||||
|
console.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=∅')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// [MOVE-TRACE] 진입 스냅샷 (필요 시 주석 해제)
|
// [MOVE-TRACE] 진입 스냅샷 (필요 시 주석 해제)
|
||||||
// console.log('[MOVE-TRACE] === getMoveUpDownLine entry ===',
|
// console.log('[MOVE-TRACE] === getMoveUpDownLine entry ===',
|
||||||
// 'moveUpDown=', roof.moveUpDown,
|
// 'moveUpDown=', roof.moveUpDown,
|
||||||
@ -1936,6 +1999,46 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isCollinear(wallBaseLine, wallLine)) {
|
if (isCollinear(wallBaseLine, wallLine)) {
|
||||||
|
// [v1 corner-gap eaveHelpLine 2026-04-30] collinear 라도 한쪽 끝점이 어긋난 corner shift 라면
|
||||||
|
// gap segment 를 eaveHelpLine 으로 추가. 미적용 시 SK ext 가 다른 짧은 보조선에 잘못 hit.
|
||||||
|
// tolerance=1.0 (메모리: UI/Big.js drift 고려).
|
||||||
|
const __cgStartSame = Math.abs(wallLine.x1 - wallBaseLine.x1) < 1.0 && Math.abs(wallLine.y1 - wallBaseLine.y1) < 1.0
|
||||||
|
const __cgEndSame = Math.abs(wallLine.x2 - wallBaseLine.x2) < 1.0 && Math.abs(wallLine.y2 - wallBaseLine.y2) < 1.0
|
||||||
|
const __cgPairs = []
|
||||||
|
if (__cgStartSame && !__cgEndSame) {
|
||||||
|
__cgPairs.push([{ x: wallLine.x2, y: wallLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 }])
|
||||||
|
} else if (!__cgStartSame && __cgEndSame) {
|
||||||
|
__cgPairs.push([{ x: wallLine.x1, y: wallLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 }])
|
||||||
|
} else if (!__cgStartSame && !__cgEndSame) {
|
||||||
|
__cgPairs.push([{ x: wallLine.x1, y: wallLine.y1 }, { x: wallBaseLine.x1, y: wallBaseLine.y1 }])
|
||||||
|
__cgPairs.push([{ x: wallLine.x2, y: wallLine.y2 }, { x: wallBaseLine.x2, y: wallBaseLine.y2 }])
|
||||||
|
}
|
||||||
|
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)})`)
|
||||||
|
const __cgLine = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
||||||
|
parentId: roof.id,
|
||||||
|
fontSize: roof.fontSize,
|
||||||
|
stroke: '',
|
||||||
|
strokeWidth: 4,
|
||||||
|
name: 'eaveHelpLine',
|
||||||
|
lineName: 'eaveHelpLine',
|
||||||
|
visible: true,
|
||||||
|
roofId: roofId,
|
||||||
|
selectable: true,
|
||||||
|
hoverCursor: 'pointer',
|
||||||
|
attributes: {
|
||||||
|
type: 'eaveHelpLine',
|
||||||
|
isStart: true,
|
||||||
|
pitch: wallLine.attributes?.pitch,
|
||||||
|
planeSize: calcLinePlaneSize({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }),
|
||||||
|
actualSize: calcLinePlaneSize({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
canvas.add(__cgLine)
|
||||||
|
__cgLine.bringToFront()
|
||||||
|
}
|
||||||
|
if (__cgPairs.length > 0) canvas.renderAll()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1976,7 +2079,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
|
|||||||
const line = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
const line = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
||||||
parentId: roof.id,
|
parentId: roof.id,
|
||||||
fontSize: roof.fontSize,
|
fontSize: roof.fontSize,
|
||||||
stroke: 'black',
|
stroke: stroke,
|
||||||
strokeWidth: 4,
|
strokeWidth: 4,
|
||||||
name: lineType,
|
name: lineType,
|
||||||
lineName: lineType,
|
lineName: lineType,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user