Merge pull request '1961, 1969, 2118 수정' (#801) from dev into prd-deploy
Reviewed-on: #801
This commit is contained in:
commit
75c2d16b05
@ -18,7 +18,8 @@ import { drawSkeletonRidgeRoof, drawSkeletonRidgeRoofFromBaseLines, verifyMoveBo
|
|||||||
const DEBUG_LABEL_NAME = '__debugLabel'
|
const DEBUG_LABEL_NAME = '__debugLabel'
|
||||||
|
|
||||||
function __isDebugLabelsEnabled() {
|
function __isDebugLabelsEnabled() {
|
||||||
return process.env.NEXT_PUBLIC_RUN_MODE === 'local'
|
// 디버그 라벨(H-1, B-4, RG-1 등) 표시 비활성화
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
function __classifyLineForLabel(obj) {
|
function __classifyLineForLabel(obj) {
|
||||||
@ -596,6 +597,52 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
drawRoofByAttribute(this.id, this.canvas, textMode)
|
drawRoofByAttribute(this.id, this.canvas, textMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 대칭 hip line 쌍의 planeSize/actualSize 를 평균값으로 통일 (예: 1637 / 1638 → 1637.5)
|
||||||
|
// hip 라인은 좌우 대칭이지만 부동소수점 round-off 로 1mm 차이가 발생함
|
||||||
|
if (this.innerLines && this.innerLines.length > 0) {
|
||||||
|
const hips = this.innerLines.filter((l) => l && l.name === LINE_TYPE.SUBLINE.HIP)
|
||||||
|
const _segLen = (l) => Math.round(Math.sqrt((l.x2 - l.x1) ** 2 + (l.y2 - l.y1) ** 2) * 10)
|
||||||
|
const _equalizeText = (line, plane, actual) => {
|
||||||
|
if (line.attributes) {
|
||||||
|
line.attributes.planeSize = plane
|
||||||
|
line.attributes.actualSize = actual
|
||||||
|
}
|
||||||
|
// canvas 위 lengthText 도 즉시 갱신
|
||||||
|
const text = this.canvas.getObjects().find((o) => o.name === 'lengthText' && o.parentId === line.id)
|
||||||
|
if (text) {
|
||||||
|
text.set({ planeSize: plane, actualSize: actual })
|
||||||
|
// 현재 표시 모드에 맞춰 text 갱신
|
||||||
|
const isPlane = !text.actualSize || text.text === String(text.planeSize)
|
||||||
|
text.set({ text: isPlane ? String(plane) : String(actual) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 길이 그룹화: ±5mm 이내 길이끼리 묶고, 그 그룹 안에서 평균값으로 통일
|
||||||
|
const used = new Set()
|
||||||
|
hips.forEach((h, i) => {
|
||||||
|
if (used.has(i)) return
|
||||||
|
const group = [{ idx: i, line: h }]
|
||||||
|
const baseLen = _segLen(h)
|
||||||
|
hips.forEach((h2, j) => {
|
||||||
|
if (j <= i || used.has(j)) return
|
||||||
|
if (Math.abs(_segLen(h2) - baseLen) <= 5) group.push({ idx: j, line: h2 })
|
||||||
|
})
|
||||||
|
if (group.length >= 2) {
|
||||||
|
const avgPlane = group.reduce((s, g) => s + (g.line.attributes?.planeSize || 0), 0) / group.length
|
||||||
|
const avgActual = group.reduce((s, g) => s + (g.line.attributes?.actualSize || 0), 0) / group.length
|
||||||
|
// 0.5 단위 정밀도 유지
|
||||||
|
const plane = Math.round(avgPlane * 2) / 2
|
||||||
|
const actual = Math.round(avgActual * 2) / 2
|
||||||
|
group.forEach(({ idx, line }) => {
|
||||||
|
used.add(idx)
|
||||||
|
_equalizeText(line, plane, actual)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
used.add(i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.canvas?.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
// [DEBUG] 로컬(NEXT_PUBLIC_RUN_MODE==='local') 에서만 라벨 부착. 그 외 즉시 return.
|
// [DEBUG] 로컬(NEXT_PUBLIC_RUN_MODE==='local') 에서만 라벨 부착. 그 외 즉시 return.
|
||||||
__attachDebugLabels(this.canvas, this.id)
|
__attachDebugLabels(this.canvas, this.id)
|
||||||
},
|
},
|
||||||
|
|||||||
@ -215,7 +215,8 @@ export const useLine = () => {
|
|||||||
|
|
||||||
line.attributes = {
|
line.attributes = {
|
||||||
...line.attributes,
|
...line.attributes,
|
||||||
actualSize: Number(line.attributes.actualSize.toFixed(0)),
|
// 0.5 단위 정밀도 유지 (대칭 분할 시 1637.5 같은 값 보존). 그 외에는 정수.
|
||||||
|
actualSize: Number((Math.round(line.attributes.actualSize * 2) / 2).toFixed(1)),
|
||||||
isCalculated: true,
|
isCalculated: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1777,7 +1777,8 @@ export function useMode() {
|
|||||||
const offsetEdges = []
|
const offsetEdges = []
|
||||||
|
|
||||||
polygon.edges.forEach((edge, i) => {
|
polygon.edges.forEach((edge, i) => {
|
||||||
const offset = lines[i % lines.length].attributes.offset === 0 ? 1 : lines[i % lines.length].attributes.offset
|
// offset 이 0(처마/케라바 출폭 0) 인 경우 그대로 0 유지 → wall == roof
|
||||||
|
const offset = lines[i % lines.length].attributes.offset
|
||||||
const dx = edge.outwardNormal.x * offset
|
const dx = edge.outwardNormal.x * offset
|
||||||
const dy = edge.outwardNormal.y * offset
|
const dy = edge.outwardNormal.y * offset
|
||||||
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
||||||
@ -1830,7 +1831,8 @@ export function useMode() {
|
|||||||
const offsetEdges = []
|
const offsetEdges = []
|
||||||
|
|
||||||
polygon.edges.forEach((edge, i) => {
|
polygon.edges.forEach((edge, i) => {
|
||||||
const offset = lines[i % lines.length].attributes.offset === 0 ? 1 : lines[i % lines.length].attributes.offset
|
// offset 이 0(처마/케라바 출폭 0) 인 경우 그대로 0 유지 → wall == roof
|
||||||
|
const offset = lines[i % lines.length].attributes.offset
|
||||||
const dx = edge.inwardNormal.x * offset
|
const dx = edge.inwardNormal.x * offset
|
||||||
const dy = edge.inwardNormal.y * offset
|
const dy = edge.inwardNormal.y * offset
|
||||||
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
||||||
@ -1977,10 +1979,20 @@ export function useMode() {
|
|||||||
const y1 = Big(line.y1)
|
const y1 = Big(line.y1)
|
||||||
const y2 = Big(line.y2)
|
const y2 = Big(line.y2)
|
||||||
const lineLength = x1.minus(x2).abs().pow(2).plus(y1.minus(y2).abs().pow(2)).sqrt().times(10).round().toNumber()
|
const lineLength = x1.minus(x2).abs().pow(2).plus(y1.minus(y2).abs().pow(2)).sqrt().times(10).round().toNumber()
|
||||||
|
const isDivLine = divWallLines.some((divLine) => divLine.index === index)
|
||||||
|
|
||||||
|
// 옵션 3: 외벽선의 입력 치수(attributes.planeSize/actualSize)를 오프셋 폴리곤 라인에도 propagate
|
||||||
|
// → polygon-offset 알고리즘의 부동소수점 누적 오차로 인한 5~10mm drift 제거
|
||||||
|
// divLine(외벽 분기로 인해 새로 삽입된 코너 라인)은 wall.lines 와 1:1 매칭이 아니므로
|
||||||
|
// geometric lineLength 그대로 사용
|
||||||
|
const sourceWallLine = !isDivLine ? wall.lines[roofWallIndex] : null
|
||||||
|
const propagatedPlaneSize = sourceWallLine?.attributes?.planeSize ?? lineLength
|
||||||
|
const propagatedActualSize = sourceWallLine?.attributes?.actualSize ?? lineLength
|
||||||
|
|
||||||
line.attributes = {
|
line.attributes = {
|
||||||
roofId: roof.id,
|
roofId: roof.id,
|
||||||
planeSize: lineLength,
|
planeSize: propagatedPlaneSize,
|
||||||
actualSize: lineLength,
|
actualSize: propagatedActualSize,
|
||||||
wallLine: wall.lines[roofWallIndex].id,
|
wallLine: wall.lines[roofWallIndex].id,
|
||||||
type: wall.lines[roofWallIndex].attributes.type,
|
type: wall.lines[roofWallIndex].attributes.type,
|
||||||
offset: wall.lines[roofWallIndex].attributes.offset,
|
offset: wall.lines[roofWallIndex].attributes.offset,
|
||||||
@ -1989,7 +2001,6 @@ export function useMode() {
|
|||||||
sleeve: wall.lines[roofWallIndex].attributes.sleeve || false,
|
sleeve: wall.lines[roofWallIndex].attributes.sleeve || false,
|
||||||
}
|
}
|
||||||
|
|
||||||
const isDivLine = divWallLines.some((divLine) => divLine.index === index)
|
|
||||||
if (!isDivLine) {
|
if (!isDivLine) {
|
||||||
roofWallIndex++
|
roofWallIndex++
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1053,26 +1053,53 @@ export const usePolygon = () => {
|
|||||||
const length1 = Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10
|
const length1 = Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10
|
||||||
const length2 = Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10
|
const length2 = Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10
|
||||||
|
|
||||||
// 원본에 planeSize/actualSize가 있으면 비율로 분배, 없으면 기하학적 길이 사용
|
// 분할 시 새 sub-segment 의 planeSize/actualSize 는 새 기하학으로 직접 계산.
|
||||||
let planeSize1, planeSize2, actualSize1, actualSize2
|
// 부모 비율을 쓰면 부모 좌표 drift 가 그대로 전파되어 사용자 기대 round 값과 어긋남.
|
||||||
if (line.attributes.planeSize && originalGeomLength > 0) {
|
// sum 이 부모 입력 planeSize 와 ±20mm 차이일 때 잔차 보정:
|
||||||
const ratio1 = length1 / originalGeomLength
|
// - sum < parent (drift +): geomLen 이 underestimated → 잔차를 SHORTER 에 더해
|
||||||
const ratio2 = length2 / originalGeomLength
|
// LONGER 의 round 값(예: 910)을 보존
|
||||||
planeSize1 = Math.round(line.attributes.planeSize * ratio1)
|
// - sum > parent (drift -): geomLen 이 overestimated → 잔차를 LONGER 에서 빼서
|
||||||
planeSize2 = Math.round(line.attributes.planeSize * ratio2)
|
// SHORTER 의 round 값(예: 460)을 보존
|
||||||
} else {
|
let planeSize1 = length1
|
||||||
planeSize1 = length1
|
let planeSize2 = length2
|
||||||
planeSize2 = length2
|
let actualSize1 = length1
|
||||||
|
let actualSize2 = length2
|
||||||
|
|
||||||
|
const _redistribute = (parentVal, p1, p2) => {
|
||||||
|
if (!parentVal) return [p1, p2]
|
||||||
|
const sum = p1 + p2
|
||||||
|
const drift = parentVal - sum
|
||||||
|
if (Math.abs(drift) > 20) return [p1, p2] // 큰 차이는 보정하지 않음
|
||||||
|
if (drift === 0) return [p1, p2]
|
||||||
|
// drift > 0: SHORTER 에 잔차 추가 → LONGER 보존
|
||||||
|
// drift < 0: LONGER 에 잔차 제거 → SHORTER 보존
|
||||||
|
if (drift > 0) {
|
||||||
|
if (length1 <= length2) return [parentVal - p2, p2]
|
||||||
|
return [p1, parentVal - p1]
|
||||||
|
} else {
|
||||||
|
if (length1 >= length2) return [parentVal - p2, p2]
|
||||||
|
return [p1, parentVal - p1]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (line.attributes.actualSize && originalGeomLength > 0) {
|
|
||||||
const ratio1 = length1 / originalGeomLength
|
const parentPlane = line.attributes?.planeSize
|
||||||
const ratio2 = length2 / originalGeomLength
|
const parentActual = line.attributes?.actualSize
|
||||||
actualSize1 = Math.round(line.attributes.actualSize * ratio1)
|
;[planeSize1, planeSize2] = _redistribute(parentPlane, planeSize1, planeSize2)
|
||||||
actualSize2 = Math.round(line.attributes.actualSize * ratio2)
|
;[actualSize1, actualSize2] = _redistribute(parentActual, actualSize1, actualSize2)
|
||||||
} else {
|
|
||||||
actualSize1 = length1
|
// 2분할에서 양 끝이 거의 같을 때(차이 ≤ 5mm) 대칭성 강제: 두 값을 평균으로 통일
|
||||||
actualSize2 = length2
|
// 예: ridge 가 wall 을 거의 정확히 양분 → 1637 / 1638 → 둘 다 1637.5
|
||||||
|
// 합이 홀수이면 0.5 소수점을 유지하여 양쪽이 정확히 같은 값으로 표시되도록 함
|
||||||
|
const _equalizeIfNearSymmetric = (p1, p2) => {
|
||||||
|
if (!p1 || !p2) return [p1, p2]
|
||||||
|
if (Math.abs(p1 - p2) > 5) return [p1, p2]
|
||||||
|
const avg = (p1 + p2) / 2 // 0.5 소수점 허용 (예: 3275 / 2 = 1637.5)
|
||||||
|
// 0.5 단위로 정밀도 유지 (Big number/이상한 부동소수점 방지)
|
||||||
|
const halfPrecision = Math.round(avg * 2) / 2
|
||||||
|
return [halfPrecision, halfPrecision]
|
||||||
}
|
}
|
||||||
|
;[planeSize1, planeSize2] = _equalizeIfNearSymmetric(planeSize1, planeSize2)
|
||||||
|
;[actualSize1, actualSize2] = _equalizeIfNearSymmetric(actualSize1, actualSize2)
|
||||||
|
|
||||||
newLine1.attributes = {
|
newLine1.attributes = {
|
||||||
...line.attributes,
|
...line.attributes,
|
||||||
@ -1159,6 +1186,23 @@ export const usePolygon = () => {
|
|||||||
newLine.length = lastCalcLength
|
newLine.length = lastCalcLength
|
||||||
|
|
||||||
newLines.push(newLine)
|
newLines.push(newLine)
|
||||||
|
|
||||||
|
// 홀수 분할(3, 5, ...) 시 양 끝(첫·마지막) sub-segment 의 길이를 평균값으로 동일하게 보정
|
||||||
|
// 패턴 A/B 등 대칭 입력 wall 이 hip lines 로 분할될 때 좌우 대칭성을 강제로 유지
|
||||||
|
const newSubsForThisLine = newLines.slice(-(intersections.length + 1)) // 이 부모 line 이 만든 sub-segment 들
|
||||||
|
const segCount = newSubsForThisLine.length
|
||||||
|
if (segCount >= 3 && segCount % 2 === 1) {
|
||||||
|
const first = newSubsForThisLine[0]
|
||||||
|
const last = newSubsForThisLine[segCount - 1]
|
||||||
|
const avgPlane = Math.round((first.attributes.planeSize + last.attributes.planeSize) / 2)
|
||||||
|
const avgActual = Math.round((first.attributes.actualSize + last.attributes.actualSize) / 2)
|
||||||
|
// 두 끝값의 차이가 작을 때만 평균화 (대칭성 가정 케이스)
|
||||||
|
if (Math.abs(first.attributes.planeSize - last.attributes.planeSize) <= 20) {
|
||||||
|
first.attributes = { ...first.attributes, planeSize: avgPlane, actualSize: avgActual }
|
||||||
|
last.attributes = { ...last.attributes, planeSize: avgPlane, actualSize: avgActual }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
divideLines.splice(i, 1) // 기존 line 제거
|
divideLines.splice(i, 1) // 기존 line 제거
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1638,18 +1682,60 @@ export const usePolygon = () => {
|
|||||||
return startFlag && endFlag
|
return startFlag && endFlag
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 1차 매칭: 양 끝점이 정확히 일치하는 부모 line 의 attributes 복사
|
||||||
|
const matchedRoofLines = new Set()
|
||||||
roofLines.forEach((line) => {
|
roofLines.forEach((line) => {
|
||||||
//console.log("::::::::::",line);
|
|
||||||
roof.lines.forEach((roofLine) => {
|
roof.lines.forEach((roofLine) => {
|
||||||
if (
|
if (
|
||||||
(isSamePoint(line.startPoint, roofLine.startPoint) && isSamePoint(line.endPoint, roofLine.endPoint)) ||
|
(isSamePoint(line.startPoint, roofLine.startPoint) && isSamePoint(line.endPoint, roofLine.endPoint)) ||
|
||||||
(isSamePoint(line.startPoint, roofLine.endPoint) && isSamePoint(line.endPoint, roofLine.startPoint))
|
(isSamePoint(line.startPoint, roofLine.endPoint) && isSamePoint(line.endPoint, roofLine.startPoint))
|
||||||
) {
|
) {
|
||||||
roofLine.attributes = { ...line.attributes }
|
roofLine.attributes = { ...line.attributes }
|
||||||
|
matchedRoofLines.add(roofLine)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 2차 매칭 (옵션 3 + α): 대각선이 외벽 중간을 분할한 sub-segment 처리
|
||||||
|
const _isCollinearWithin = (parent, p) => {
|
||||||
|
const ax = parent.startPoint.x, ay = parent.startPoint.y
|
||||||
|
const bx = parent.endPoint.x, by = parent.endPoint.y
|
||||||
|
const dx = bx - ax, dy = by - ay
|
||||||
|
const segLenSq = dx * dx + dy * dy
|
||||||
|
if (segLenSq < 1) return false
|
||||||
|
const cross = (p.x - ax) * dy - (p.y - ay) * dx
|
||||||
|
if (Math.abs(cross) / Math.sqrt(segLenSq) > 2) return false
|
||||||
|
const t = ((p.x - ax) * dx + (p.y - ay) * dy) / segLenSq
|
||||||
|
return t >= -0.001 && t <= 1.001
|
||||||
|
}
|
||||||
|
const _segLen = (s, e) => Math.sqrt((s.x - e.x) ** 2 + (s.y - e.y) ** 2)
|
||||||
|
|
||||||
|
const parentCandidates = [...polygonLines, ...polygon.innerLines]
|
||||||
|
roof.lines.forEach((roofLine) => {
|
||||||
|
if (matchedRoofLines.has(roofLine)) return
|
||||||
|
const parent = parentCandidates.find(
|
||||||
|
(p) => p?.startPoint && p?.endPoint && _isCollinearWithin(p, roofLine.startPoint) && _isCollinearWithin(p, roofLine.endPoint),
|
||||||
|
)
|
||||||
|
if (!parent) return
|
||||||
|
const parentLen = _segLen(parent.startPoint, parent.endPoint)
|
||||||
|
if (parentLen < 1) return
|
||||||
|
const subLen = _segLen(roofLine.startPoint, roofLine.endPoint)
|
||||||
|
const ratio = subLen / parentLen
|
||||||
|
|
||||||
|
const parentAttrs = parent.attributes || {}
|
||||||
|
const parentPlane = Number(parentAttrs.planeSize)
|
||||||
|
const parentActual = Number(parentAttrs.actualSize)
|
||||||
|
const propagatedPlane = Number.isFinite(parentPlane) ? Math.round(parentPlane * ratio) : null
|
||||||
|
const propagatedActual = Number.isFinite(parentActual) ? Math.round(parentActual * ratio) : null
|
||||||
|
|
||||||
|
roofLine.attributes = {
|
||||||
|
...parentAttrs,
|
||||||
|
...(propagatedPlane != null ? { planeSize: propagatedPlane } : {}),
|
||||||
|
...(propagatedActual != null ? { actualSize: propagatedActual } : {}),
|
||||||
|
}
|
||||||
|
matchedRoofLines.add(roofLine)
|
||||||
|
})
|
||||||
|
|
||||||
// canvas.add(roof)
|
// canvas.add(roof)
|
||||||
createdRoofs.push(roof)
|
createdRoofs.push(roof)
|
||||||
canvas.remove(polygon)
|
canvas.remove(polygon)
|
||||||
|
|||||||
@ -726,8 +726,11 @@ export const drawGableRoof = (roofId, canvas, textMode) => {
|
|||||||
let currentRoof
|
let currentRoof
|
||||||
if (line.attributes.offset === 0) {
|
if (line.attributes.offset === 0) {
|
||||||
checkRoofLines.forEach((roofLine) => {
|
checkRoofLines.forEach((roofLine) => {
|
||||||
const isVerticalSame = line.y1 === roofLine.y1 && line.y2 === roofLine.y2
|
// 정방향/역방향 양쪽 모두 매칭 허용 (좌표 정렬에 따라 끝점이 swap 될 수 있음)
|
||||||
const isHorizontalSame = line.x1 === roofLine.x1 && line.x2 === roofLine.x2
|
const isVerticalSame =
|
||||||
|
(line.y1 === roofLine.y1 && line.y2 === roofLine.y2) || (line.y1 === roofLine.y2 && line.y2 === roofLine.y1)
|
||||||
|
const isHorizontalSame =
|
||||||
|
(line.x1 === roofLine.x1 && line.x2 === roofLine.x2) || (line.x1 === roofLine.x2 && line.x2 === roofLine.x1)
|
||||||
if (
|
if (
|
||||||
(isVerticalSame || isHorizontalSame) &&
|
(isVerticalSame || isHorizontalSame) &&
|
||||||
(isPointOnLineNew(roofLine, { x: line.x1, y: line.y1 }) || isPointOnLineNew(roofLine, { x: line.x2, y: line.y2 }))
|
(isPointOnLineNew(roofLine, { x: line.x1, y: line.y1 }) || isPointOnLineNew(roofLine, { x: line.x2, y: line.y2 }))
|
||||||
@ -735,6 +738,11 @@ export const drawGableRoof = (roofId, canvas, textMode) => {
|
|||||||
currentRoof = { roofLine }
|
currentRoof = { roofLine }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// offset=0(처마/케라바 출폭 0) 케이스에서 매칭 실패하면 wall.line 자체를 roofLine 으로 사용
|
||||||
|
// (wall == roof 이므로 자기 자신이 맞음). currentRoof 가 undefined 일 때 NPE 방지.
|
||||||
|
if (!currentRoof) {
|
||||||
|
currentRoof = { roofLine: line }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const findEdge = { vertex1: { x: midX, y: midY }, vertex2: { x: midX + roofVector.x * offset, y: midY + roofVector.y * offset } }
|
const findEdge = { vertex1: { x: midX, y: midY }, vertex2: { x: midX + roofVector.x * offset, y: midY + roofVector.y * offset } }
|
||||||
const edgeDx =
|
const edgeDx =
|
||||||
@ -2041,8 +2049,11 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
|
|||||||
let currentRoof
|
let currentRoof
|
||||||
if (line.attributes.offset === 0) {
|
if (line.attributes.offset === 0) {
|
||||||
checkRoofLines.forEach((roofLine) => {
|
checkRoofLines.forEach((roofLine) => {
|
||||||
const isVerticalSame = line.y1 === roofLine.y1 && line.y2 === roofLine.y2
|
// 정방향/역방향 양쪽 모두 매칭 허용 (좌표 정렬에 따라 끝점이 swap 될 수 있음)
|
||||||
const isHorizontalSame = line.x1 === roofLine.x1 && line.x2 === roofLine.x2
|
const isVerticalSame =
|
||||||
|
(line.y1 === roofLine.y1 && line.y2 === roofLine.y2) || (line.y1 === roofLine.y2 && line.y2 === roofLine.y1)
|
||||||
|
const isHorizontalSame =
|
||||||
|
(line.x1 === roofLine.x1 && line.x2 === roofLine.x2) || (line.x1 === roofLine.x2 && line.x2 === roofLine.x1)
|
||||||
if (
|
if (
|
||||||
(isVerticalSame || isHorizontalSame) &&
|
(isVerticalSame || isHorizontalSame) &&
|
||||||
(isPointOnLineNew(roofLine, { x: line.x1, y: line.y1 }) || isPointOnLineNew(roofLine, { x: line.x2, y: line.y2 }))
|
(isPointOnLineNew(roofLine, { x: line.x1, y: line.y1 }) || isPointOnLineNew(roofLine, { x: line.x2, y: line.y2 }))
|
||||||
@ -2050,6 +2061,11 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
|
|||||||
currentRoof = { roofLine }
|
currentRoof = { roofLine }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// offset=0(처마/케라바 출폭 0) 케이스에서 매칭 실패하면 wall.line 자체를 roofLine 으로 사용
|
||||||
|
// (wall == roof 이므로 자기 자신이 맞음). currentRoof 가 undefined 일 때 NPE 방지.
|
||||||
|
if (!currentRoof) {
|
||||||
|
currentRoof = { roofLine: line }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const findEdge = { vertex1: { x: midX, y: midY }, vertex2: { x: midX + roofVector.x * offset, y: midY + roofVector.y * offset } }
|
const findEdge = { vertex1: { x: midX, y: midY }, vertex2: { x: midX + roofVector.x * offset, y: midY + roofVector.y * offset } }
|
||||||
const edgeDx =
|
const edgeDx =
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user