[1961] 평행 처마 쌍 라벨 통일 — OVER_EPS 1mm 클램프 보정 equalizeParallelEaveLabels
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
parent
df1a2d6fe6
commit
a8d59873b7
@ -2,7 +2,16 @@ import { fabric } from 'fabric'
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { QLine } from '@/components/fabric/QLine'
|
import { QLine } from '@/components/fabric/QLine'
|
||||||
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
||||||
import { calculateAngle, drawGableRoof, drawRoofByAttribute, drawShedRoof, equalizeSymmetricHips, snapNearAxisEdges, toGeoJSON } from '@/util/qpolygon-utils'
|
import {
|
||||||
|
calculateAngle,
|
||||||
|
drawGableRoof,
|
||||||
|
drawRoofByAttribute,
|
||||||
|
drawShedRoof,
|
||||||
|
equalizeParallelEaveLabels,
|
||||||
|
equalizeSymmetricHips,
|
||||||
|
snapNearAxisEdges,
|
||||||
|
toGeoJSON,
|
||||||
|
} from '@/util/qpolygon-utils'
|
||||||
import * as turf from '@turf/turf'
|
import * as turf from '@turf/turf'
|
||||||
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||||
import Big from 'big.js'
|
import Big from 'big.js'
|
||||||
@ -786,6 +795,18 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
if (this.innerLines && this.innerLines.length > 0) {
|
if (this.innerLines && this.innerLines.length > 0) {
|
||||||
const hips = this.innerLines.filter((l) => l && l.name === LINE_TYPE.SUBLINE.HIP)
|
const hips = this.innerLines.filter((l) => l && l.name === LINE_TYPE.SUBLINE.HIP)
|
||||||
equalizeSymmetricHips(hips, this.canvas)
|
equalizeSymmetricHips(hips, this.canvas)
|
||||||
|
|
||||||
|
// 평행 처마 쌍(고정 outerLine ↔ 이동 eaveHelpLine) 라벨 통일.
|
||||||
|
// OVER_EPS 클램프로 이동 변이 1mm 짧아져 평행인데 라벨이 1 차이 나는 케이스 보정.
|
||||||
|
// eaveHelpLine 은 이 지붕 소속(parentId)만, outerLine 은 좌표가 겹치는 것만 그룹핑되므로 cross-roof 오머지 없음.
|
||||||
|
const eaveCandidates = (this.canvas?.getObjects() || []).filter(
|
||||||
|
(o) =>
|
||||||
|
(o.name === 'outerLine' || (o.name === LINE_TYPE.WALLLINE.EAVE_HELP_LINE && o.parentId === this.id)) &&
|
||||||
|
typeof o.x1 === 'number' &&
|
||||||
|
typeof o.y1 === 'number',
|
||||||
|
)
|
||||||
|
equalizeParallelEaveLabels(eaveCandidates, this.canvas)
|
||||||
|
|
||||||
this.canvas?.renderAll()
|
this.canvas?.renderAll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6346,6 +6346,93 @@ export const equalizeSymmetricHips = (hipLines, canvas) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 평행한 처마 변 쌍(고정 outerLine ↔ 이동 eaveHelpLine)의 라벨을 통일.
|
||||||
|
*
|
||||||
|
* 배경: WallBaseLine 을 옆 변과 평행이 되도록 옮기면 OVER_EPS(0.1px=1mm) 클램프 때문에
|
||||||
|
* 이동 변이 평행 직전에서 멈춰 좌표 길이가 고정 변보다 정확히 1mm 짧아진다
|
||||||
|
* (예: 고정 2805 ↔ 이동 2804). 기하/SK 안전을 위해 OVER_EPS 는 유지해야 하므로
|
||||||
|
* 좌표는 건드리지 않고 '표시 라벨' 만 맞춘다. 고객이 1mm 차이도 거부하기 때문.
|
||||||
|
*
|
||||||
|
* 좌표 길이(calcLinePlaneSize 와 동일식)로 그룹핑한다. outerLine 의 attributes.planeSize 는
|
||||||
|
* stale 일 수 있어 신뢰하지 않는다. 클램프는 항상 '짧게' 만들므로 그룹 최대값이 의도한 값.
|
||||||
|
* 기준(최대) 변은 손대지 않고, 짧아진 변만 최대값으로 끌어올린다.
|
||||||
|
*
|
||||||
|
* @param {Array} lines - outerLine + eaveHelpLine 등 후보 라인 배열 (x1,y1,x2,y2 필수)
|
||||||
|
* @param {object} canvas - fabric canvas (lengthText 동기화용; null 이면 attribute 만 갱신)
|
||||||
|
*/
|
||||||
|
export const equalizeParallelEaveLabels = (lines, canvas) => {
|
||||||
|
if (!lines || lines.length < 2) return
|
||||||
|
|
||||||
|
const _coordLen = (l) => Math.round(Math.sqrt((l.x2 - l.x1) ** 2 + (l.y2 - l.y1) ** 2) * 10)
|
||||||
|
const _orient = (l) => {
|
||||||
|
const adx = Math.abs(l.x2 - l.x1)
|
||||||
|
const ady = Math.abs(l.y2 - l.y1)
|
||||||
|
if (adx < 0.5 && ady >= 0.5) return 'V'
|
||||||
|
if (ady < 0.5 && adx >= 0.5) return 'H'
|
||||||
|
return null // 대각선(hip 등) 은 equalizeSymmetricHips 담당 → 제외
|
||||||
|
}
|
||||||
|
// 평행 두 변이 마주보는 처마 쌍인지: 평행 방향(변화 축) 의 범위가 겹쳐야 한다.
|
||||||
|
const _overlap = (a, b, orient) => {
|
||||||
|
const [ak1, ak2, bk1, bk2] =
|
||||||
|
orient === 'V'
|
||||||
|
? [Math.min(a.y1, a.y2), Math.max(a.y1, a.y2), Math.min(b.y1, b.y2), Math.max(b.y1, b.y2)]
|
||||||
|
: [Math.min(a.x1, a.x2), Math.max(a.x1, a.x2), Math.min(b.x1, b.x2), Math.max(b.x1, b.x2)]
|
||||||
|
return Math.min(ak2, bk2) - Math.max(ak1, bk1) > 0.5
|
||||||
|
}
|
||||||
|
const _apply = (line, plane, actual) => {
|
||||||
|
const text = canvas ? canvas.getObjects().find((o) => o.name === 'lengthText' && o.parentId === line.id) : null
|
||||||
|
// 표시 모드(평면/실측) 는 갱신 전 기준으로 판정해야 잘못된 모드 전환을 막는다.
|
||||||
|
const wasPlane = text ? !text.actualSize || text.text === String(text.planeSize) : true
|
||||||
|
if (line.attributes) {
|
||||||
|
line.attributes.planeSize = plane
|
||||||
|
line.attributes.actualSize = actual
|
||||||
|
}
|
||||||
|
if (text) {
|
||||||
|
text.set({ planeSize: plane, actualSize: actual })
|
||||||
|
const __raw = wasPlane ? plane : actual
|
||||||
|
text.set({ text: Number(__raw).toFixed(1).replace(/\.0$/, '') })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const used = new Set()
|
||||||
|
lines.forEach((a, i) => {
|
||||||
|
if (used.has(i)) return
|
||||||
|
const oa = _orient(a)
|
||||||
|
if (!oa) {
|
||||||
|
used.add(i)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const baseLen = _coordLen(a)
|
||||||
|
const group = [{ idx: i, line: a, len: baseLen }]
|
||||||
|
lines.forEach((b, j) => {
|
||||||
|
if (j <= i || used.has(j)) return
|
||||||
|
if (_orient(b) !== oa) return
|
||||||
|
const lenB = _coordLen(b)
|
||||||
|
if (Math.abs(lenB - baseLen) > 5) return
|
||||||
|
if (!_overlap(a, b, oa)) return
|
||||||
|
group.push({ idx: j, line: b, len: lenB })
|
||||||
|
})
|
||||||
|
if (group.length >= 2) {
|
||||||
|
let ref = group[0]
|
||||||
|
group.forEach((g) => {
|
||||||
|
if (g.len > ref.len) ref = g
|
||||||
|
})
|
||||||
|
const plane = ref.len
|
||||||
|
group.forEach(({ idx, line, len }) => {
|
||||||
|
used.add(idx)
|
||||||
|
if (len >= plane) return // 기준(최대) 변은 손대지 않음
|
||||||
|
const oldPlane = line.attributes?.planeSize ?? len
|
||||||
|
const oldActual = line.attributes?.actualSize ?? oldPlane
|
||||||
|
const actual = oldPlane > 0 ? Math.round((oldActual * plane) / oldPlane) : plane
|
||||||
|
_apply(line, plane, actual)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
used.add(i)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 포인트와 기울기를 기준으로 선의 길이를 구한다.
|
* 포인트와 기울기를 기준으로 선의 길이를 구한다.
|
||||||
* @param lineLength
|
* @param lineLength
|
||||||
|
|||||||
@ -2111,17 +2111,6 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode, isOver
|
|||||||
const getAddLine = (p1, p2, stroke = '', lineType = 'eaveHelpLine') => {
|
const getAddLine = (p1, p2, stroke = '', lineType = 'eaveHelpLine') => {
|
||||||
movedLines.push({ index, p1, p2 })
|
movedLines.push({ index, p1, p2 })
|
||||||
|
|
||||||
// [진단] eaveHelpLine 생성 추적: 어느 index/어느 wallBaseLine-wallLine 쌍에서 만들어지는지
|
|
||||||
// logger.log('🎯 [getAddLine]', {
|
|
||||||
// index,
|
|
||||||
// lineType,
|
|
||||||
// p1: { x: Math.round(p1.x), y: Math.round(p1.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)})`,
|
|
||||||
// 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)),
|
|
||||||
// })
|
|
||||||
|
|
||||||
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);
|
||||||
const isDiagonal = dx > 0.5 && dy > 0.5; // x, y 변화가 모두 있으면 대각선
|
const isDiagonal = dx > 0.5 && dy > 0.5; // x, y 변화가 모두 있으면 대각선
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user