qcast-front/src/hooks/useText.js
2026-04-30 12:21:59 +09:00

136 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useRecoilValue } from 'recoil'
import { corridorDimensionSelector } from '@/store/settingAtom'
import { canvasState } from '@/store/canvasAtom'
export function useText() {
const canvas = useRecoilValue(canvasState)
const corridorDimension = useRecoilValue(corridorDimensionSelector)
const changeCorridorDimensionText = (columnText) => {
let { column } = corridorDimension
if (columnText) {
column = columnText
}
const lengthTexts = canvas.getObjects().filter((obj) => obj.name === 'lengthText')
const group = canvas.getObjects().filter((obj) => obj.type === 'group')
group.forEach((obj) => {
obj._objects
.filter((obj2) => obj2.name === 'lengthText')
.forEach((obj3) => {
lengthTexts.push(obj3)
})
})
switch (column) {
case 'corridorDimension':
lengthTexts.forEach((obj) => {
if (obj.planeSize) {
obj.set({ text: obj.planeSize.toString() })
}
})
break
case 'realDimension':
lengthTexts.forEach((obj) => {
if (obj.actualSize) {
obj.set({ text: obj.actualSize.toString() })
}
})
break
case 'noneDimension':
lengthTexts.forEach((obj) => {
obj.set({ text: '' })
})
break
}
// ─────────────────────────────────────────────────────────────────────────
// [hip 라벨 cross-display 통일 2026-04-30 by Claude]
//
// 배경:
// 좌우대칭 hip(대각선) 라인의 라벨이 0.5mm 차이로 갈리는 케이스 발생
// (예: 좌측 5048.5 / 우측 5048).
//
// 원인:
// 1) polygon split / ridge intersection 단계 부동소수점 연산
// → QLine.js:23 toFixed(1) 0.1mm 단위 절단 → 좌우 hip 좌표가 0.1mm 차
// 2) useLine.js setActualSize 의 axisLength * tan + sqrt 계산에서
// 좌표 0.1mm 차가 ×10 으로 1mm 차로 증폭
// 3) 마지막 `Math.round(actualSize * 2) / 2` (0.5mm 단위) boundary 에서 갈림
// → 한쪽은 5048.0, 한쪽은 5048.5 로 결정
// 4) planeSize 도 usePolygon.js 의 _equalizeIfNearSymmetric([avg, avg])
// 경로에서 0.5mm 단위 값이 만들어질 수 있음 (corridorDimension 모드에 영향)
//
// 수정 방침:
// - **표시 레이어 (lengthText.text) 만 보정**.
// - line.attributes.actualSize / line.attributes.planeSize 는 절대 안 건드림.
// → 다운스트림 read 경로 (라인 분할 비율 / 확장라인 병합 / 모듈배치 shape
// 상속 / JSON 저장 / DB / 복제 플랜) 영향 0.
//
// 처리:
// - 대각선 hip 라벨끼리 묶어서, 표시값이 ±2mm 이내이면 정수 round 한
// 공통값으로 스냅 (5048.5/5048 → 둘 다 5049 또는 5048).
// - corridorDimension / realDimension 모드 모두 적용.
// - noneDimension 모드는 빈 문자열이라 패스.
//
// 추후 근본 수정 (다른 개발자 검토 필요):
// - QLine.js:23 toFixed(1) → toFixed(2) 정밀도 상향 (영향 범위 넓어 별건)
// - skeleton-utils.js getLineIntersection 결과 좌표 round
// - useLine.js setActualSize 마지막 round 단위 변경 (0.5 → 1.0)
// 위 근본수정이 들어가면 이 보정 코드는 제거 가능.
//
// 메모리: feedback_display_vs_internal_values.md 참고.
// ─────────────────────────────────────────────────────────────────────────
if (column === 'corridorDimension' || column === 'realDimension') {
// 대각선(hip) lengthText 추출.
// lengthText 객체는 QPolygon.js:712-734 에서 minX/maxX/minY/maxY 로 부모 라인의
// 바운딩박스를 저장. 둘 다 양수 차이 = 대각선.
const hipTexts = lengthTexts.filter((obj) => {
const dx = (obj.maxX ?? 0) - (obj.minX ?? 0)
const dy = (obj.maxY ?? 0) - (obj.minY ?? 0)
return dx > 0.5 && dy > 0.5
})
if (hipTexts.length >= 2) {
// 현재 표시 텍스트를 숫자로 파싱.
const items = hipTexts
.map((obj) => ({ obj, value: parseFloat(obj.text) }))
.filter((item) => !isNaN(item.value))
// 그룹화: 표시값이 ±TOLERANCE mm 이내면 동일 그룹.
// TOLERANCE=2 는 0.5 단위 round drift + sqrt fp 노이즈 흡수.
// 대칭 hip 들의 정상 길이차 (예: 다른 지붕면의 hip) 보다는 작아야 함.
const TOLERANCE = 2
const groups = []
for (const item of items) {
const matched = groups.find((g) => Math.abs(g.representative - item.value) <= TOLERANCE)
if (matched) {
matched.items.push(item)
// 그룹 대표값은 첫 항목 값 유지 (안정적인 클러스터링).
} else {
groups.push({ representative: item.value, items: [item] })
}
}
// 각 그룹의 멤버가 2개 이상이면 정수 round 평균값으로 통일.
// 1개 그룹은 단독 hip 이므로 보정 불필요.
for (const group of groups) {
if (group.items.length < 2) continue
const avg = group.items.reduce((s, i) => s + i.value, 0) / group.items.length
const unified = Math.round(avg).toString()
for (const item of group.items) {
// text 만 set. obj.actualSize / obj.planeSize 는 그대로.
item.obj.set({ text: unified })
}
}
}
}
// ─────────────────────────────────────────────────────────────────────────
canvas?.renderAll()
}
return {
changeCorridorDimensionText,
}
}