Merge pull request 'dev' (#402) from dev into dev-deploy
Reviewed-on: #402
This commit is contained in:
commit
354582d74b
@ -6,7 +6,6 @@ import { calculateAngle, drawGableRoof, drawRidgeRoof, drawShedRoof, toGeoJSON }
|
|||||||
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'
|
||||||
import { drawSkeletonRidgeRoof } from '@/util/skeleton-utils'
|
|
||||||
|
|
||||||
export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||||
type: 'QPolygon',
|
type: 'QPolygon',
|
||||||
@ -376,9 +375,27 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
const dy = Big(end.y).minus(Big(start.y))
|
const dy = Big(end.y).minus(Big(start.y))
|
||||||
const length = dx.pow(2).plus(dy.pow(2)).sqrt().times(10).round().toNumber()
|
const length = dx.pow(2).plus(dy.pow(2)).sqrt().times(10).round().toNumber()
|
||||||
|
|
||||||
|
const direction = getDirectionByPoint(start, end)
|
||||||
|
|
||||||
|
let left, top
|
||||||
|
|
||||||
|
if (direction === 'bottom') {
|
||||||
|
left = (start.x + end.x) / 2 - 50
|
||||||
|
top = (start.y + end.y) / 2
|
||||||
|
} else if (direction === 'top') {
|
||||||
|
left = (start.x + end.x) / 2 + 30
|
||||||
|
top = (start.y + end.y) / 2
|
||||||
|
} else if (direction === 'left') {
|
||||||
|
left = (start.x + end.x) / 2
|
||||||
|
top = (start.y + end.y) / 2 - 30
|
||||||
|
} else if (direction === 'right') {
|
||||||
|
left = (start.x + end.x) / 2
|
||||||
|
top = (start.y + end.y) / 2 + 30
|
||||||
|
}
|
||||||
|
|
||||||
let midPoint
|
let midPoint
|
||||||
|
|
||||||
midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
|
midPoint = new fabric.Point(left, top)
|
||||||
|
|
||||||
const degree = Big(Math.atan2(dy.toNumber(), dx.toNumber())).times(180).div(Math.PI).toNumber()
|
const degree = Big(Math.atan2(dy.toNumber(), dx.toNumber())).times(180).div(Math.PI).toNumber()
|
||||||
|
|
||||||
|
|||||||
@ -133,17 +133,21 @@ export default function Header(props) {
|
|||||||
{ id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
{ id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
||||||
{ id: 2, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
{ id: 2, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
||||||
{ id: 3, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
|
{ id: 3, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
|
||||||
|
{ id: 4, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' },
|
||||||
|
|
||||||
]
|
]
|
||||||
: userSession.groupId === '60000'
|
: userSession.groupId === '60000'
|
||||||
? [
|
? [
|
||||||
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
|
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
|
||||||
{ id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
{ id: 1, name: 'HANASYS ORDER', link: `${qOrderUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
||||||
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
|
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
|
||||||
|
{ id: 3, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' },
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
|
{ id: 0, name: getMessage('site.header.link1'), target: '_blank' },
|
||||||
{ id: 1, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
{ id: 1, name: 'HANASYS Musubi', link: `${qMusubiUrl}?autoLoginParam1=${encodeURIComponent(res.data)}`, target: '_blank' },
|
||||||
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
|
{ id: 2, name: getMessage('site.header.link2'), link: `https://q-warranty.q-cells.jp/seller_login`, target: '_blank' },
|
||||||
|
{ id: 3, name: 'Q.PARTNERS', link: `https://q-partners.q-cells.jp/qcast_login.php`, target: '_blank' },
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
onChangeSelect({ id: 0, name: getMessage('site.header.link1') })
|
onChangeSelect({ id: 0, name: getMessage('site.header.link1') })
|
||||||
|
|||||||
@ -217,6 +217,8 @@ export function useMovementSetting(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let currentCalculatedValue = 0
|
||||||
|
|
||||||
const mouseMoveEvent = (e) => {
|
const mouseMoveEvent = (e) => {
|
||||||
const target = canvas.getActiveObject()
|
const target = canvas.getActiveObject()
|
||||||
if (!target) return
|
if (!target) return
|
||||||
@ -224,61 +226,71 @@ export function useMovementSetting(id) {
|
|||||||
const { top: targetTop, left: targetLeft } = target
|
const { top: targetTop, left: targetLeft } = target
|
||||||
const currentX = Big(getIntersectMousePoint(e).x) //.round(0, Big.roundUp)
|
const currentX = Big(getIntersectMousePoint(e).x) //.round(0, Big.roundUp)
|
||||||
const currentY = Big(getIntersectMousePoint(e).y) //.round(0, Big.roundUp)
|
const currentY = Big(getIntersectMousePoint(e).y) //.round(0, Big.roundUp)
|
||||||
|
|
||||||
let value = ''
|
let value = ''
|
||||||
if (target.y1 === target.y2) {
|
if (Math.abs(target.y1 - target.y2) < 0.5) {
|
||||||
|
// 가로라인의 경우
|
||||||
value = Big(targetTop).minus(currentY).times(10).round(0)
|
value = Big(targetTop).minus(currentY).times(10).round(0)
|
||||||
|
console.log('가로라인 계산:', `${targetTop} - ${currentY.toNumber()} = ${value.toNumber()}`)
|
||||||
} else {
|
} else {
|
||||||
|
// 세로라인의 경우
|
||||||
value = Big(targetLeft).minus(currentX).times(10).round(0).neg()
|
value = Big(targetLeft).minus(currentX).times(10).round(0).neg()
|
||||||
|
console.log('세로라인 계산:', `-(${targetLeft} - ${currentX.toNumber()}) = ${value.toNumber()}`)
|
||||||
}
|
}
|
||||||
if (typeRef.current === TYPE.FLOW_LINE) {
|
|
||||||
FLOW_LINE_REF.POINTER_INPUT_REF.current.value = value.toNumber()
|
currentCalculatedValue = value.toNumber()
|
||||||
} else {
|
|
||||||
UP_DOWN_REF.POINTER_INPUT_REF.current.value = value.abs().toNumber()
|
if (typeRef.current === TYPE.FLOW_LINE) {
|
||||||
const midX = Big(target.x1).plus(target.x2).div(2)
|
FLOW_LINE_REF.POINTER_INPUT_REF.current.value = value.toNumber()
|
||||||
const midY = Big(target.y1).plus(target.y2).div(2)
|
|
||||||
const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId)
|
|
||||||
let checkPoint
|
|
||||||
if (target.y1 === target.y2) {
|
|
||||||
checkPoint = { x: midX.toNumber(), y: midY.plus(10).toNumber() }
|
|
||||||
if (wall.inPolygon(checkPoint)) {
|
|
||||||
if (value.s === -1) {
|
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
|
||||||
} else {
|
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (value.s === 1) {
|
UP_DOWN_REF.POINTER_INPUT_REF.current.value = value.abs().toNumber()
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
const midX = Big(target.x1).plus(target.x2).div(2)
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
const midY = Big(target.y1).plus(target.y2).div(2)
|
||||||
|
const wall = canvas.getObjects().find((obj) => obj.id === target.attributes.wallId)
|
||||||
|
let checkPoint
|
||||||
|
if (target.y1 === target.y2) {
|
||||||
|
checkPoint = { x: midX.toNumber(), y: midY.plus(10).toNumber() }
|
||||||
|
if (wall.inPolygon(checkPoint)) {
|
||||||
|
if (value.s === -1) {
|
||||||
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
||||||
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
||||||
|
} else {
|
||||||
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
||||||
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (value.s === 1) {
|
||||||
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
||||||
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
||||||
|
} else {
|
||||||
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
||||||
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
checkPoint = { x: midX.plus(10).toNumber(), y: midY.toNumber() }
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
if (wall.inPolygon(checkPoint)) {
|
||||||
}
|
if (value.s === 1) {
|
||||||
}
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
||||||
} else {
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
||||||
checkPoint = { x: midX.plus(10).toNumber(), y: midY.toNumber() }
|
} else {
|
||||||
if (wall.inPolygon(checkPoint)) {
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
||||||
if (value.s === 1) {
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
}
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
} else {
|
||||||
} else {
|
if (value.s === -1) {
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
||||||
}
|
} else {
|
||||||
} else {
|
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
||||||
if (value.s === -1) {
|
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = false
|
}
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = true
|
}
|
||||||
} else {
|
|
||||||
UP_DOWN_REF.UP_RADIO_REF.current.checked = true
|
|
||||||
UP_DOWN_REF.DOWN_RADIO_REF.current.checked = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const mouseDownEvent = (e) => {
|
const mouseDownEvent = (e) => {
|
||||||
canvas
|
canvas
|
||||||
@ -287,6 +299,7 @@ export function useMovementSetting(id) {
|
|||||||
.forEach((obj) => canvas.remove(obj))
|
.forEach((obj) => canvas.remove(obj))
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
|
|
||||||
|
//const target = selectedObject.current
|
||||||
const target = selectedObject.current
|
const target = selectedObject.current
|
||||||
if (!target) return
|
if (!target) return
|
||||||
|
|
||||||
@ -321,6 +334,12 @@ export function useMovementSetting(id) {
|
|||||||
FOLLOW_LINE_REF.current = null
|
FOLLOW_LINE_REF.current = null
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
}
|
}
|
||||||
|
if (UP_DOWN_REF.current !== null) {
|
||||||
|
canvas.remove(UP_DOWN_REF.current)
|
||||||
|
UP_DOWN_REF.current = null
|
||||||
|
canvas.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const target = selectedObject.current !== null ? selectedObject.current : CONFIRM_LINE_REF.current?.target
|
const target = selectedObject.current !== null ? selectedObject.current : CONFIRM_LINE_REF.current?.target
|
||||||
if (!target) return
|
if (!target) return
|
||||||
@ -329,8 +348,14 @@ export function useMovementSetting(id) {
|
|||||||
const roof = canvas.getObjects().find((obj) => obj.id === roofId)
|
const roof = canvas.getObjects().find((obj) => obj.id === roofId)
|
||||||
|
|
||||||
// 현이동, 동이동 추가
|
// 현이동, 동이동 추가
|
||||||
const moveFlowLine = typeRef.current === TYPE.FLOW_LINE ? FLOW_LINE_REF.POINTER_INPUT_REF.current.value : 0
|
let flPointValue = FLOW_LINE_REF.POINTER_INPUT_REF.current?.value??0;
|
||||||
const moveUpDown = typeRef.current === TYPE.UP_DOWN ? UP_DOWN_REF.POINTER_INPUT_REF.current.value : 0
|
let flFilledValue = FLOW_LINE_REF.FILLED_INPUT_REF.current?.value??0;
|
||||||
|
flPointValue = (flFilledValue > 0 || flFilledValue < 0)? flFilledValue : flPointValue;
|
||||||
|
const moveFlowLine = typeRef.current === TYPE.FLOW_LINE ? flPointValue : 0
|
||||||
|
let udPointValue = UP_DOWN_REF.POINTER_INPUT_REF.current?.value??0;
|
||||||
|
let udFilledValue = UP_DOWN_REF.FILLED_INPUT_REF.current?.value??0;
|
||||||
|
udPointValue = udFilledValue > 0 ? udFilledValue : udPointValue;
|
||||||
|
const moveUpDown = typeRef.current === TYPE.UP_DOWN ? udPointValue: 0
|
||||||
roof.moveFlowLine = parseInt(moveFlowLine, 10) || 0;
|
roof.moveFlowLine = parseInt(moveFlowLine, 10) || 0;
|
||||||
roof.moveUpDown = parseInt(moveUpDown, 10) || 0;
|
roof.moveUpDown = parseInt(moveUpDown, 10) || 0;
|
||||||
roof.moveDirect = "";
|
roof.moveDirect = "";
|
||||||
|
|||||||
@ -123,7 +123,7 @@ export function useContextMenu() {
|
|||||||
}, [currentContextMenu])
|
}, [currentContextMenu])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('currentObject', currentObject)
|
//console.log('currentObject', currentObject)
|
||||||
if (currentObject?.name) {
|
if (currentObject?.name) {
|
||||||
switch (currentObject.name) {
|
switch (currentObject.name) {
|
||||||
case 'triangleDormer':
|
case 'triangleDormer':
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { QLine } from '@/components/fabric/QLine'
|
|||||||
import { getDegreeByChon } from '@/util/canvas-util'
|
import { getDegreeByChon } from '@/util/canvas-util'
|
||||||
import Big from 'big.js'
|
import Big from 'big.js'
|
||||||
import { line } from 'framer-motion/m'
|
import { line } from 'framer-motion/m'
|
||||||
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 지붕 폴리곤의 스켈레톤(중심선)을 생성하고 캔버스에 그립니다.
|
* 지붕 폴리곤의 스켈레톤(중심선)을 생성하고 캔버스에 그립니다.
|
||||||
@ -25,65 +26,97 @@ export const drawSkeletonRidgeRoof = (roofId, canvas, textMode) => {
|
|||||||
const movingRidgeFromSkeleton = (roofId, canvas) => {
|
const movingRidgeFromSkeleton = (roofId, canvas) => {
|
||||||
|
|
||||||
let roof = canvas?.getObjects().find((object) => object.id === roofId)
|
let roof = canvas?.getObjects().find((object) => object.id === roofId)
|
||||||
|
|
||||||
let moveDirection = roof.moveDirect;
|
let moveDirection = roof.moveDirect;
|
||||||
let moveFlowLine = roof.moveFlowLine??0;
|
let moveFlowLine = roof.moveFlowLine??0;
|
||||||
const selectLine = roof.moveSelectLine;
|
const selectLine = roof.moveSelectLine;
|
||||||
|
|
||||||
const startPoint = selectLine.startPoint
|
const startPoint = selectLine.startPoint
|
||||||
const endPoint = selectLine.endPoint
|
const endPoint = selectLine.endPoint
|
||||||
const oldPoints = canvas?.movePoints?.points ?? roof.points
|
const orgRoofPoints = roof.points; // orgPoint를 orgPoints로 변경
|
||||||
|
const oldPoints = canvas?.skeleton.lastPoints ?? orgRoofPoints // 여기도 변경
|
||||||
const oppositeLine = findOppositeLine(canvas.skeleton.Edges, startPoint, endPoint, oldPoints);
|
const oppositeLine = findOppositeLine(canvas.skeleton.Edges, startPoint, endPoint, oldPoints);
|
||||||
|
|
||||||
|
const skeletonPolygon = canvas.getObjects().filter((object) => object.skeletonType === 'polygon' && object.parentId === roofId)
|
||||||
|
const skeletonLines = canvas.getObjects().filter((object) => object.skeletonType === 'line' && object.parentId === roofId)
|
||||||
|
|
||||||
if (oppositeLine) {
|
if (oppositeLine) {
|
||||||
console.log('Opposite line found:', oppositeLine);
|
console.log('Opposite line found:', oppositeLine);
|
||||||
} else {
|
} else {
|
||||||
console.log('No opposite line found');
|
console.log('No opposite line found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return oldPoints.map((point) => {
|
let baseLines = canvas.getObjects().filter((object) => object.name === 'baseLine' && object.parentId === roofId) || [];
|
||||||
|
console.log('baseLines::::', baseLines);
|
||||||
|
let baseLinePoints = baseLines.map((line) => ({x:line.x1, y:line.y1}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
walls.forEach((wall) => {
|
||||||
|
if (wall.baseLines.length === 0) {
|
||||||
|
wall.baseLines = canvas.getObjects().filter((obj) => obj.name === 'baseLine' && obj.attributes.wallId === wall.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract points from each baseLine
|
||||||
|
wall.baseLines.forEach(line => {
|
||||||
|
console.log("useSk:::", line.x1, line.y1, line.x2, line.y2);
|
||||||
|
// 시작점과 끝점을 배열에 추가
|
||||||
|
const points = [
|
||||||
|
{ x: line.x1, y: line.y1 },
|
||||||
|
{ x: line.x2, y: line.y2 }
|
||||||
|
];
|
||||||
|
|
||||||
|
points.forEach(point => {
|
||||||
|
const key = `${point.x},${point.y}`;
|
||||||
|
if (!pointSet.has(key)) {
|
||||||
|
pointSet.add(key);
|
||||||
|
baseLinePoints.push(point);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
return [...baseLinePoints];
|
||||||
|
*/
|
||||||
|
|
||||||
|
return oldPoints.map((point, index) => {
|
||||||
|
|
||||||
const newPoint = { ...point };
|
const newPoint = { ...point };
|
||||||
const absMove = Big(moveFlowLine).abs().times(2).div(10);
|
const absMove = Big(moveFlowLine).times(2).div(10);
|
||||||
//console.log('absMove:', absMove);
|
//console.log('absMove:', absMove);
|
||||||
|
|
||||||
const skeletonLines = canvas.skeletonLines;
|
const skeletonLines = canvas.skeletonLines;
|
||||||
|
|
||||||
console.log('skeleton line:', canvas.skeletonLines);
|
//console.log('skeleton line:', canvas.skeletonLines);
|
||||||
const changeSkeletonLine = (canvas, oldPoint, newPoint, str) => {
|
// const changeSkeletonLine = (canvas, oldPoint, newPoint, str) => {
|
||||||
for (const line of canvas.skeletonLines) {
|
// for (const line of canvas.skeletonLines) {
|
||||||
if (str === 'start' && isSamePoint(line.startPoint, oldPoint)) {
|
// if (str === 'start' && isSamePoint(line.startPoint, oldPoint)) {
|
||||||
// Fabric.js 객체의 set 메서드로 속성 업데이트
|
// // Fabric.js 객체의 set 메서드로 속성 업데이트
|
||||||
line.set({
|
// line.set({
|
||||||
x1: newPoint.x,
|
// x1: newPoint.x,
|
||||||
y1: newPoint.y,
|
// y1: newPoint.y,
|
||||||
x2: line.x2 || line.endPoint?.x,
|
// x2: line.x2 || line.endPoint?.x,
|
||||||
y2: line.y2 || line.endPoint?.y
|
// y2: line.y2 || line.endPoint?.y
|
||||||
});
|
// });
|
||||||
line.startPoint = newPoint; // 참조 업데이트
|
// line.startPoint = newPoint; // 참조 업데이트
|
||||||
}
|
// }
|
||||||
else if (str === 'end' && isSamePoint(line.endPoint, oldPoint)) {
|
// else if (str === 'end' && isSamePoint(line.endPoint, oldPoint)) {
|
||||||
line.set({
|
// line.set({
|
||||||
x1: line.x1 || line.startPoint?.x,
|
// x1: line.x1 || line.startPoint?.x,
|
||||||
y1: line.y1 || line.startPoint?.y,
|
// y1: line.y1 || line.startPoint?.y,
|
||||||
x2: newPoint.x,
|
// x2: newPoint.x,
|
||||||
y2: newPoint.y
|
// y2: newPoint.y
|
||||||
});
|
// });
|
||||||
line.endPoint = newPoint; // 참조 업데이트
|
// line.endPoint = newPoint; // 참조 업데이트
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
canvas.requestRenderAll();
|
// canvas.requestRenderAll();
|
||||||
console.log('skeleton line:', canvas.skeletonLines);
|
// console.log('skeleton line:', canvas.skeletonLines);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(moveFlowLine > 0) {
|
|
||||||
if(moveDirection === 'down'){
|
|
||||||
moveDirection = 'up';
|
|
||||||
}else if(moveDirection === 'left'){
|
|
||||||
moveDirection = 'right';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('skeletonBuilder moveDirection:', moveDirection);
|
console.log('skeletonBuilder moveDirection:', moveDirection);
|
||||||
|
|
||||||
switch (moveDirection) {
|
switch (moveDirection) {
|
||||||
@ -92,14 +125,22 @@ const movingRidgeFromSkeleton = (roofId, canvas) => {
|
|||||||
for (const line of oppositeLine) {
|
for (const line of oppositeLine) {
|
||||||
if (line.position === 'left') {
|
if (line.position === 'left') {
|
||||||
if (isSamePoint(newPoint, line.start)) {
|
if (isSamePoint(newPoint, line.start)) {
|
||||||
newPoint.x = Big(line.start.x).minus(absMove).toNumber();
|
newPoint.x = Big(line.start.x).plus(absMove).toNumber();
|
||||||
//changeSkeletonLine(canvas, line.start, newPoint, 'start')
|
|
||||||
} else if (isSamePoint(newPoint, line.end)) {
|
} else if (isSamePoint(newPoint, line.end)) {
|
||||||
newPoint.x = Big(line.end.x).minus(absMove).toNumber();
|
newPoint.x = Big(line.end.x).plus(absMove).toNumber();
|
||||||
//changeSkeletonLine(canvas, line.end, newPoint, 'end')
|
|
||||||
}
|
}
|
||||||
break
|
|
||||||
|
break;
|
||||||
|
// } else if (line.position === 'right') {
|
||||||
|
// if (isSamePoint(newPoint, line.start)) {
|
||||||
|
// newPoint.x = Big(line.start.x).minus(absMove).toNumber();
|
||||||
|
// (newPoint.x < originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용
|
||||||
|
// }else if(isSamePoint(newPoint, line.end)) {
|
||||||
|
// newPoint.x = Big(line.end.x).minus(absMove).toNumber();
|
||||||
|
// (newPoint.x < originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -107,29 +148,46 @@ const movingRidgeFromSkeleton = (roofId, canvas) => {
|
|||||||
for (const line of oppositeLine) {
|
for (const line of oppositeLine) {
|
||||||
if (line.position === 'right') {
|
if (line.position === 'right') {
|
||||||
if (isSamePoint(newPoint, line.start)) {
|
if (isSamePoint(newPoint, line.start)) {
|
||||||
newPoint.x = Big(line.start.x).plus(absMove).toNumber();
|
newPoint.x = Big(line.start.x).minus(absMove).toNumber();
|
||||||
//changeSkeletonLine(canvas, line.start, newPoint, 'start')
|
|
||||||
} else if (isSamePoint(newPoint, line.end)) {
|
} else if (isSamePoint(newPoint, line.end)) {
|
||||||
newPoint.x = Big(line.end.x).plus(absMove).toNumber();
|
newPoint.x = Big(line.end.x).minus(absMove).toNumber();
|
||||||
//changeSkeletonLine(canvas, line.end, newPoint, 'end')
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
// }else if(line.position === 'left') {
|
||||||
|
// if (isSamePoint(newPoint, line.start)) {
|
||||||
|
// newPoint.x = Big(line.start.x).plus(absMove).toNumber();
|
||||||
|
// (newPoint.x > originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용
|
||||||
|
// } else if (isSamePoint(newPoint, line.end)) {
|
||||||
|
// newPoint.x= Big(line.end.x).plus(absMove).toNumber();
|
||||||
|
// (newPoint.x > originalPoint.x)? newPoint.x = originalPoint.x : newPoint.x; // 변경된 이름 사용
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 'up':
|
case 'up':
|
||||||
// Move up: decrease Y (toward top of screen)
|
// Move up: decrease Y (toward top of screen)
|
||||||
|
|
||||||
for (const line of oppositeLine) {
|
for (const line of oppositeLine) {
|
||||||
if (line.position === 'top') {
|
if (line.position === 'top') {
|
||||||
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();
|
||||||
//changeSkeletonLine(canvas, line.start, newPoint, 'start')
|
|
||||||
} else if (isSamePoint(newPoint, line.end)) {
|
} else if (isSamePoint(newPoint, line.end)) {
|
||||||
newPoint.y = Big(line.end.y).minus(absMove).toNumber();
|
newPoint.y = Big(line.end.y).minus(absMove).toNumber();
|
||||||
//changeSkeletonLine(canvas, line.end, newPoint, 'end')
|
|
||||||
}
|
}
|
||||||
break
|
break;
|
||||||
|
|
||||||
|
// }else if(line.position === 'bottom') {
|
||||||
|
// if(newPoint.y !== originalPoint.y) {
|
||||||
|
// if (isSamePoint(newPoint, line.start)) {
|
||||||
|
// newPoint.y = Big(line.start.y).minus(absMove).toNumber();
|
||||||
|
// (newPoint.y < originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용
|
||||||
|
// }else if(isSamePoint(newPoint, line.end)) {
|
||||||
|
// newPoint.y = Big(line.end.y).minus(absMove).toNumber();
|
||||||
|
// (newPoint.y < originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,23 +195,42 @@ const movingRidgeFromSkeleton = (roofId, canvas) => {
|
|||||||
case 'down':
|
case 'down':
|
||||||
// Move down: increase Y (toward bottom of screen)
|
// Move down: increase Y (toward bottom of screen)
|
||||||
for (const line of oppositeLine) {
|
for (const line of oppositeLine) {
|
||||||
if (line.position === 'bottom') {
|
|
||||||
if (isSamePoint(newPoint, line.start)) {
|
|
||||||
newPoint.y = Big(line.start.y).plus(absMove).toNumber();
|
|
||||||
//changeSkeletonLine(canvas, line.start, newPoint, 'start')
|
|
||||||
|
|
||||||
|
if (line.position === 'bottom') {
|
||||||
|
|
||||||
|
console.log('oldPoint:', point);
|
||||||
|
|
||||||
|
if (isSamePoint(newPoint, line.start)) {
|
||||||
|
newPoint.y = Big(line.start.y).minus(absMove).toNumber();
|
||||||
} else if (isSamePoint(newPoint, line.end)) {
|
} else if (isSamePoint(newPoint, line.end)) {
|
||||||
newPoint.y = Big(line.end.y).plus(absMove).toNumber();
|
newPoint.y = Big(line.end.y).minus(absMove).toNumber();
|
||||||
//changeSkeletonLine(canvas, line.end, newPoint, 'end')
|
|
||||||
}
|
}
|
||||||
break
|
|
||||||
|
// }else if(line.position === 'top') {
|
||||||
|
//
|
||||||
|
// if(newPoint.y !== originalPoint.y) {
|
||||||
|
//
|
||||||
|
// if (isSamePoint(newPoint, line.start)) {
|
||||||
|
// newPoint.y = Big(line.start.y).plus(absMove).toNumber();
|
||||||
|
// (newPoint.y > originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용
|
||||||
|
// } else if (isSamePoint(newPoint, line.end)) {
|
||||||
|
// newPoint.y = Big(line.end.y).plus(absMove).toNumber();
|
||||||
|
// (newPoint.y > originalPoint.y)? newPoint.y = originalPoint.y : newPoint.y; // 변경된 이름 사용
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
console.log('newPoint:', newPoint);
|
||||||
|
//baseline 변경
|
||||||
return newPoint;
|
return newPoint;
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,19 +245,23 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
|
|
||||||
//처마
|
//처마
|
||||||
let roof = canvas?.getObjects().find((object) => object.id === roofId)
|
let roof = canvas?.getObjects().find((object) => object.id === roofId)
|
||||||
//벽
|
|
||||||
const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
|
|
||||||
|
|
||||||
// const hasNonParallelLines = roof.lines.filter((line) => Big(line.x1).minus(Big(line.x2)).gt(1) && Big(line.y1).minus(Big(line.y2)).gt(1))
|
|
||||||
// if (hasNonParallelLines.length > 0) {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
|
|
||||||
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
|
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
|
||||||
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
|
||||||
|
|
||||||
/** 외벽선 */
|
/** 외벽선 */
|
||||||
const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0)
|
const wall = canvas.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roofId)
|
||||||
|
//const baseLines = wall.baseLines.filter((line) => line.attributes.planeSize > 0)
|
||||||
|
|
||||||
|
const baseLines = canvas.getObjects().filter((object) => object.name === 'baseLine' && object.parentId === roofId) || [];
|
||||||
|
const baseLinePoints = baseLines.map((line) => ({x:line.left, y:line.top}));
|
||||||
|
|
||||||
|
const outerLines = canvas.getObjects().filter((object) => object.name === 'outerLinePoint') || [];
|
||||||
|
const outerLinePoints = outerLines.map((line) => ({x:line.left, y:line.top}))
|
||||||
|
|
||||||
|
const hipLines = canvas.getObjects().filter((object) => object.name === 'hip' && object.parentId === roofId) || [];
|
||||||
|
const ridgeLines = canvas.getObjects().filter((object) => object.name === 'ridge' && object.parentId === roofId) || [];
|
||||||
|
|
||||||
//const skeletonLines = [];
|
//const skeletonLines = [];
|
||||||
// 1. 지붕 폴리곤 좌표 전처리
|
// 1. 지붕 폴리곤 좌표 전처리
|
||||||
@ -197,22 +278,22 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
|
|
||||||
let points = roof.points;
|
let points = roof.points;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//마루이동
|
//마루이동
|
||||||
if (moveFlowLine !== 0) {
|
if (moveFlowLine !== 0) {
|
||||||
|
|
||||||
|
|
||||||
points = movingRidgeFromSkeleton(roofId, canvas)
|
points = movingRidgeFromSkeleton(roofId, canvas)
|
||||||
|
|
||||||
const movePoints = {
|
|
||||||
points: points,
|
|
||||||
roofId: roofId,
|
|
||||||
}
|
|
||||||
canvas.set("movePoints", movePoints)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//처마
|
//처마
|
||||||
if(moveUpDown !== 0) {
|
if(moveUpDown !== 0) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
console.log('points:', points);
|
||||||
const geoJSONPolygon = toGeoJSON(points)
|
const geoJSONPolygon = toGeoJSON(points)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -232,7 +313,6 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
canvas.skeletonStates[roofId] = true
|
canvas.skeletonStates[roofId] = true
|
||||||
canvas.skeletonLines = [];
|
canvas.skeletonLines = [];
|
||||||
canvas.skeletonLines.push(...roof.innerLines)
|
canvas.skeletonLines.push(...roof.innerLines)
|
||||||
canvas.set("skeletonLines", canvas.skeletonLines)
|
|
||||||
|
|
||||||
const cleanSkeleton = {
|
const cleanSkeleton = {
|
||||||
Edges: skeleton.Edges.map(edge => ({
|
Edges: skeleton.Edges.map(edge => ({
|
||||||
@ -249,10 +329,10 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
};
|
};
|
||||||
canvas.skeleton = [];
|
canvas.skeleton = [];
|
||||||
canvas.skeleton = cleanSkeleton
|
canvas.skeleton = cleanSkeleton
|
||||||
|
canvas.skeleton.lastPoints = points
|
||||||
canvas.set("skeleton", cleanSkeleton);
|
canvas.set("skeleton", cleanSkeleton);
|
||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
|
console.log('skeleton rendered.', canvas);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('스켈레톤 생성 중 오류 발생:', e)
|
console.error('스켈레톤 생성 중 오류 발생:', e)
|
||||||
if (canvas.skeletonStates) {
|
if (canvas.skeletonStates) {
|
||||||
@ -281,23 +361,8 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
// 1. 모든 Edge를 순회하며 기본 스켈레톤 선(용마루)을 수집합니다.
|
// 1. 모든 Edge를 순회하며 기본 스켈레톤 선(용마루)을 수집합니다.
|
||||||
|
|
||||||
skeleton.Edges.forEach((edgeResult, index) => {
|
skeleton.Edges.forEach((edgeResult, index) => {
|
||||||
// const { Begin, End } = edgeResult.Edge;
|
|
||||||
// let outerLine = roof.lines.find(line =>
|
|
||||||
// line.attributes.type === 'eaves' && isSameLine(Begin.X, Begin.Y, End.X, End.Y, line)
|
|
||||||
// );
|
|
||||||
// if(!outerLine){
|
|
||||||
//
|
|
||||||
// for (const line of canvas.skeletonLines) {
|
|
||||||
// if (line.lineName === 'hip' && line.attributes.hipIndex === index)
|
|
||||||
// {
|
|
||||||
// outerLine = line;
|
|
||||||
// break; // Found the matching line, exit the loop
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// const pitch = outerLine.attributes?.pitch??0
|
|
||||||
// console.log("pitch", pitch)
|
|
||||||
processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines);
|
processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -365,6 +430,7 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
const innerLines = [];
|
const innerLines = [];
|
||||||
const existingLines = new Set(); // 이미 추가된 라인을 추적하기 위한 Set
|
const existingLines = new Set(); // 이미 추가된 라인을 추적하기 위한 Set
|
||||||
|
|
||||||
|
|
||||||
skeletonLines.forEach(line => {
|
skeletonLines.forEach(line => {
|
||||||
const { p1, p2, attributes, lineStyle } = line;
|
const { p1, p2, attributes, lineStyle } = line;
|
||||||
|
|
||||||
@ -379,6 +445,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
return; // 이미 있는 라인이면 스킵
|
return; // 이미 있는 라인이면 스킵
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const direction = getLineDirection(
|
||||||
|
{ x: line.p1.x, y: line.p1.y },
|
||||||
|
{ x: line.p2.x, y: line.p2.y }
|
||||||
|
);
|
||||||
|
|
||||||
const innerLine = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
const innerLine = new QLine([p1.x, p1.y, p2.x, p2.y], {
|
||||||
parentId: roof.id,
|
parentId: roof.id,
|
||||||
fontSize: roof.fontSize,
|
fontSize: roof.fontSize,
|
||||||
@ -386,10 +457,11 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
strokeWidth: lineStyle.width,
|
strokeWidth: lineStyle.width,
|
||||||
name: (line.attributes.isOuterEdge)?'eaves': attributes.type,
|
name: (line.attributes.isOuterEdge)?'eaves': attributes.type,
|
||||||
attributes: attributes,
|
attributes: attributes,
|
||||||
|
direction: direction,
|
||||||
isBaseLine: line.attributes.isOuterEdge,
|
isBaseLine: line.attributes.isOuterEdge,
|
||||||
lineName: (line.attributes.isOuterEdge)?'outerLine': attributes.type,
|
lineName: (line.attributes.isOuterEdge)?'outerLine': attributes.type,
|
||||||
selectable:(!line.attributes.isOuterEdge),
|
selectable:(!line.attributes.isOuterEdge),
|
||||||
roofId: roofId
|
roofId: roofId,
|
||||||
});
|
});
|
||||||
|
|
||||||
//skeleton 라인에서 처마선은 삭제
|
//skeleton 라인에서 처마선은 삭제
|
||||||
@ -397,6 +469,23 @@ const createInnerLinesFromSkeleton = (roofId, canvas, skeleton, textMode) => {
|
|||||||
canvas.add(innerLine);
|
canvas.add(innerLine);
|
||||||
innerLine.bringToFront();
|
innerLine.bringToFront();
|
||||||
existingLines.add(lineKey); // 추가된 라인을 추적
|
existingLines.add(lineKey); // 추가된 라인을 추적
|
||||||
|
}else{
|
||||||
|
const coordinateText = new fabric.Text(`(${Math.round(p1.x)}, ${Math.round(p1.y)})`, {
|
||||||
|
left: p1.x + 5, // 좌표점에서 약간 오른쪽으로 이동
|
||||||
|
top: p1.y - 20, // 좌표점에서 약간 위로 이동
|
||||||
|
fontSize: 13,
|
||||||
|
fill: 'red',
|
||||||
|
fontFamily: 'Arial',
|
||||||
|
selectable: true,
|
||||||
|
lockMovementX: false,
|
||||||
|
lockMovementY: false,
|
||||||
|
lockRotation: true,
|
||||||
|
lockScalingX: true,
|
||||||
|
lockScalingY: true,
|
||||||
|
name: 'lengthText'
|
||||||
|
})
|
||||||
|
|
||||||
|
canvas?.add(coordinateText)
|
||||||
}
|
}
|
||||||
innerLines.push(innerLine)
|
innerLines.push(innerLine)
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
@ -421,6 +510,7 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) {
|
|||||||
const { Begin, End } = edgeResult.Edge;
|
const { Begin, End } = edgeResult.Edge;
|
||||||
let outerLine = roof.lines.find(line =>
|
let outerLine = roof.lines.find(line =>
|
||||||
line.attributes.type === 'eaves' && isSameLine(Begin.X, Begin.Y, End.X, End.Y, line)
|
line.attributes.type === 'eaves' && isSameLine(Begin.X, Begin.Y, End.X, End.Y, line)
|
||||||
|
|
||||||
);
|
);
|
||||||
if(!outerLine) {
|
if(!outerLine) {
|
||||||
outerLine = findMatchingLine(edgeResult.Polygon, roof, roof.points);
|
outerLine = findMatchingLine(edgeResult.Polygon, roof, roof.points);
|
||||||
@ -429,6 +519,25 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) {
|
|||||||
let pitch = outerLine?.attributes?.pitch??0
|
let pitch = outerLine?.attributes?.pitch??0
|
||||||
|
|
||||||
|
|
||||||
|
const convertedPolygon = edgeResult.Polygon?.map(point => ({
|
||||||
|
x: typeof point.X === 'number' ? parseFloat(point.X) : 0,
|
||||||
|
y: typeof point.Y === 'number' ? parseFloat(point.Y) : 0
|
||||||
|
})).filter(point => point.x !== 0 || point.y !== 0) || [];
|
||||||
|
|
||||||
|
if (convertedPolygon.length > 0) {
|
||||||
|
const skeletonPolygon = new QPolygon(convertedPolygon, {
|
||||||
|
type: POLYGON_TYPE.ROOF,
|
||||||
|
fill: false,
|
||||||
|
stroke: 'blue',
|
||||||
|
strokeWidth: 8,
|
||||||
|
skeletonType: 'polygon',
|
||||||
|
polygonName: '',
|
||||||
|
parentId: roof.id,
|
||||||
|
});
|
||||||
|
//canvas?.add(skeletonPolygon)
|
||||||
|
//canvas.renderAll()
|
||||||
|
}
|
||||||
|
|
||||||
let eavesLines = []
|
let eavesLines = []
|
||||||
for (let i = 0; i < polygonPoints.length; i++) {
|
for (let i = 0; i < polygonPoints.length; i++) {
|
||||||
const p1 = polygonPoints[i];
|
const p1 = polygonPoints[i];
|
||||||
@ -439,9 +548,9 @@ function processEavesEdge(roofId, canvas, skeleton, edgeResult, skeletonLines) {
|
|||||||
//외벽선 밖으로 나간 선을 정리한다(roof.line의 교점까지 정리한다)
|
//외벽선 밖으로 나간 선을 정리한다(roof.line의 교점까지 정리한다)
|
||||||
// 지붕 경계선과 교차 확인 및 클리핑
|
// 지붕 경계선과 교차 확인 및 클리핑
|
||||||
const clippedLine = clipLineToRoofBoundary(p1, p2, roof.lines);
|
const clippedLine = clipLineToRoofBoundary(p1, p2, roof.lines);
|
||||||
console.log('clipped line', clippedLine.p1, clippedLine.p2);
|
//console.log('clipped line', clippedLine.p1, clippedLine.p2);
|
||||||
const isOuterLine = isOuterEdge(p1, p2, [edgeResult.Edge])
|
const isOuterLine = isOuterEdge(clippedLine.p1, clippedLine.p2, [edgeResult.Edge])
|
||||||
addRawLine(roof.id, skeletonLines, p1, p2, 'ridge', '#FF0000', 3, pitch, isOuterLine);
|
addRawLine(roof.id, skeletonLines, clippedLine.p1, clippedLine.p2, 'ridge', 'red', 5, pitch, isOuterLine);
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -627,6 +736,7 @@ const preprocessPolygonCoordinates = (initialPoints) => {
|
|||||||
if (coordinates.length > 1 && coordinates[0][0] === coordinates[coordinates.length - 1][0] && coordinates[0][1] === coordinates[coordinates.length - 1][1]) {
|
if (coordinates.length > 1 && coordinates[0][0] === coordinates[coordinates.length - 1][0] && coordinates[0][1] === coordinates[coordinates.length - 1][1]) {
|
||||||
coordinates.pop();
|
coordinates.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
return coordinates.reverse();
|
return coordinates.reverse();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1275,19 +1385,29 @@ function findOppositeLine(edges, startPoint, endPoint, points) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getLinePosition(line, referenceLine) {
|
function getLinePosition(line, referenceLine) {
|
||||||
|
// 대상선의 중점
|
||||||
const lineMidX = (line.start.x + line.end.x) / 2;
|
const lineMidX = (line.start.x + line.end.x) / 2;
|
||||||
const lineMidY = (line.start.y + line.end.y) / 2;
|
const lineMidY = (line.start.y + line.end.y) / 2;
|
||||||
|
|
||||||
|
// 참조선의 중점
|
||||||
const refMidX = (referenceLine.start.x + referenceLine.end.x) / 2;
|
const refMidX = (referenceLine.start.x + referenceLine.end.x) / 2;
|
||||||
const refMidY = (referenceLine.start.y + referenceLine.end.y) / 2;
|
const refMidY = (referenceLine.start.y + referenceLine.end.y) / 2;
|
||||||
|
|
||||||
// Y축 차이가 더 크면 위/아래로 판단
|
// 단순히 좌표 차이로 판단
|
||||||
// Y축 차이가 더 크면 위/아래로 판단
|
const deltaX = lineMidX - refMidX;
|
||||||
if (Math.abs(lineMidY - refMidY) > Math.abs(lineMidX - refMidX)) {
|
const deltaY = lineMidY - refMidY;
|
||||||
return lineMidY > refMidY ? 'bottom' : 'top';
|
|
||||||
}
|
// 참조선의 기울기
|
||||||
// X축 차이가 더 크면 왼쪽/오른쪽으로 판단
|
const refDeltaX = referenceLine.end.x - referenceLine.start.x;
|
||||||
else {
|
const refDeltaY = referenceLine.end.y - referenceLine.start.y;
|
||||||
return lineMidX > refMidX ? 'right' : 'left';
|
|
||||||
|
// 참조선이 더 수평인지 수직인지 판단
|
||||||
|
if (Math.abs(refDeltaX) > Math.abs(refDeltaY)) {
|
||||||
|
// 수평선에 가까운 경우 - Y 좌표로 판단
|
||||||
|
return deltaY > 0 ? 'bottom' : 'top';
|
||||||
|
} else {
|
||||||
|
// 수직선에 가까운 경우 - X 좌표로 판단
|
||||||
|
return deltaX > 0 ? 'right' : 'left';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1347,54 +1467,171 @@ function findPolygonsContainingLine(edges, p1, p2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* roof.lines와 교차하는 선분(p1, p2)을 찾아 교차점에서 자릅니다.
|
* roof.lines로 만들어진 다각형 내부에만 선분이 존재하도록 클리핑합니다.
|
||||||
* @param {Object} p1 - 선분의 시작점 {x, y}
|
* @param {Object} p1 - 선분의 시작점 {x, y}
|
||||||
* @param {Object} p2 - 선분의 끝점 {x, y}
|
* @param {Object} p2 - 선분의 끝점 {x, y}
|
||||||
* @param {Array} roofLines - 지붕 경계선 배열 (QLine 객체의 배열)
|
* @param {Array} roofLines - 지붕 경계선 배열 (QLine 객체의 배열)
|
||||||
* @returns {Object} {p1: {x, y}, p2: {x, y}} - 교차점에서 자른 선분 또는 원래 선분
|
* @returns {Object} {p1: {x, y}, p2: {x, y}} - 다각형 내부로 클리핑된 선분
|
||||||
*/
|
*/
|
||||||
function clipLineToRoofBoundary(p1, p2, roofLines) {
|
function clipLineToRoofBoundary(p1, p2, roofLines) {
|
||||||
if (!roofLines || !roofLines.length) return { p1, p2 };
|
if (!roofLines || !roofLines.length) {
|
||||||
|
return { p1: { ...p1 }, p2: { ...p2 } };
|
||||||
|
}
|
||||||
|
|
||||||
let closestIntersection = null;
|
// 기본값으로 원본 좌표 설정
|
||||||
let minDistSq = Infinity;
|
let clippedP1 = { x: p1.x, y: p1.y };
|
||||||
const originalP1 = { ...p1 };
|
let clippedP2 = { x: p2.x, y: p2.y };
|
||||||
const originalP2 = { ...p2 };
|
|
||||||
|
// p1이 다각형 내부에 있는지 확인
|
||||||
|
const p1Inside = isPointInsidePolygon(p1, roofLines);
|
||||||
|
|
||||||
|
// p2가 다각형 내부에 있는지 확인
|
||||||
|
const p2Inside = isPointInsidePolygon(p2, roofLines);
|
||||||
|
|
||||||
|
console.log('p1Inside:', p1Inside, 'p2Inside:', p2Inside);
|
||||||
|
|
||||||
|
// 두 점 모두 내부에 있으면 그대로 반환
|
||||||
|
if (p1Inside && p2Inside) {
|
||||||
|
return { p1: clippedP1, p2: clippedP2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 선분과 다각형 경계선의 교차점들을 찾음
|
||||||
|
const intersections = [];
|
||||||
|
|
||||||
// 모든 지붕 경계선과의 교차점을 찾음
|
|
||||||
for (const line of roofLines) {
|
for (const line of roofLines) {
|
||||||
const lineP1 = { x: line.x1, y: line.y1 };
|
const lineP1 = { x: line.x1, y: line.y1 };
|
||||||
const lineP2 = { x: line.x2, y: line.y2 };
|
const lineP2 = { x: line.x2, y: line.y2 };
|
||||||
|
|
||||||
const intersection = getLineIntersection(
|
const intersection = getLineIntersection(p1, p2, lineP1, lineP2);
|
||||||
p1, p2,
|
|
||||||
lineP1, lineP2
|
|
||||||
);
|
|
||||||
|
|
||||||
if (intersection) {
|
if (intersection) {
|
||||||
// 교차점과 p1 사이의 거리 계산
|
// 교차점이 선분 위에 있는지 확인
|
||||||
const dx = intersection.x - p1.x;
|
const t = getParameterT(p1, p2, intersection);
|
||||||
const dy = intersection.y - p1.y;
|
if (t >= 0 && t <= 1) {
|
||||||
const distSq = dx * dx + dy * dy;
|
intersections.push({
|
||||||
|
point: intersection,
|
||||||
// p1에 가장 가까운 교차점 찾기
|
t: t
|
||||||
if (distSq < minDistSq) {
|
});
|
||||||
minDistSq = distSq;
|
|
||||||
closestIntersection = intersection;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 교차점이 있으면 p2를 가장 가까운 교차점으로 업데이트
|
console.log('Found intersections:', intersections.length);
|
||||||
if (closestIntersection) {
|
|
||||||
return {
|
// 교차점들을 t 값으로 정렬
|
||||||
p1: originalP1,
|
intersections.sort((a, b) => a.t - b.t);
|
||||||
p2: closestIntersection
|
|
||||||
};
|
if (!p1Inside && !p2Inside) {
|
||||||
|
// 두 점 모두 외부에 있는 경우
|
||||||
|
if (intersections.length >= 2) {
|
||||||
|
console.log('Both outside, using intersection points');
|
||||||
|
clippedP1.x = intersections[0].point.x;
|
||||||
|
clippedP1.y = intersections[0].point.y;
|
||||||
|
clippedP2.x = intersections[1].point.x;
|
||||||
|
clippedP2.y = intersections[1].point.y;
|
||||||
|
} else {
|
||||||
|
console.log('Both outside, no valid intersections - returning original');
|
||||||
|
// 교차점이 충분하지 않으면 원본 반환
|
||||||
|
return { p1: clippedP1, p2: clippedP2 };
|
||||||
|
}
|
||||||
|
} else if (!p1Inside && p2Inside) {
|
||||||
|
// p1이 외부, p2가 내부
|
||||||
|
if (intersections.length > 0) {
|
||||||
|
console.log('p1 outside, p2 inside - moving p1 to intersection');
|
||||||
|
clippedP1.x = intersections[0].point.x;
|
||||||
|
clippedP1.y = intersections[0].point.y;
|
||||||
|
// p2는 이미 내부에 있으므로 원본 유지
|
||||||
|
clippedP2.x = p2.x;
|
||||||
|
clippedP2.y = p2.y;
|
||||||
|
}
|
||||||
|
} else if (p1Inside && !p2Inside) {
|
||||||
|
// p1이 내부, p2가 외부
|
||||||
|
if (intersections.length > 0) {
|
||||||
|
console.log('p1 inside, p2 outside - moving p2 to intersection');
|
||||||
|
// p1은 이미 내부에 있으므로 원본 유지
|
||||||
|
clippedP1.x = p1.x;
|
||||||
|
clippedP1.y = p1.y;
|
||||||
|
clippedP2.x = intersections[0].point.x;
|
||||||
|
clippedP2.y = intersections[0].point.y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 교차점이 없으면 원래 선분 반환
|
return { p1: clippedP1, p2: clippedP2 };
|
||||||
return { p1: originalP1, p2: originalP2 };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 getLineIntersection 함수를 사용하거나, 없으면 아래 구현 사용
|
/**
|
||||||
|
* 점이 다각형 내부에 있는지 확인합니다 (Ray Casting 알고리즘 사용).
|
||||||
|
* @param {Object} point - 확인할 점 {x, y}
|
||||||
|
* @param {Array} roofLines - 다각형을 구성하는 선분들
|
||||||
|
* @returns {boolean} 점이 다각형 내부에 있으면 true
|
||||||
|
*/
|
||||||
|
function isPointInsidePolygon(point, roofLines) {
|
||||||
|
let inside = false;
|
||||||
|
const x = point.x;
|
||||||
|
const y = point.y;
|
||||||
|
|
||||||
|
for (const line of roofLines) {
|
||||||
|
const x1 = line.x1;
|
||||||
|
const y1 = line.y1;
|
||||||
|
const x2 = line.x2;
|
||||||
|
const y2 = line.y2;
|
||||||
|
|
||||||
|
// Ray casting: 점에서 오른쪽으로 수평선을 그었을 때 다각형 경계와 교차하는 횟수 확인
|
||||||
|
if (((y1 > y) !== (y2 > y)) && (x < (x2 - x1) * (y - y1) / (y2 - y1) + x1)) {
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 선분 위의 점에 대한 매개변수 t를 계산합니다.
|
||||||
|
* p = p1 + t * (p2 - p1)에서 t 값을 구합니다.
|
||||||
|
* @param {Object} p1 - 선분의 시작점
|
||||||
|
* @param {Object} p2 - 선분의 끝점
|
||||||
|
* @param {Object} point - 선분 위의 점
|
||||||
|
* @returns {number} 매개변수 t (0이면 p1, 1이면 p2)
|
||||||
|
*/
|
||||||
|
function getParameterT(p1, p2, point) {
|
||||||
|
const dx = p2.x - p1.x;
|
||||||
|
const dy = p2.y - p1.y;
|
||||||
|
|
||||||
|
// x 좌표가 더 큰 변화를 보이면 x로 계산, 아니면 y로 계산
|
||||||
|
if (Math.abs(dx) > Math.abs(dy)) {
|
||||||
|
return dx === 0 ? 0 : (point.x - p1.x) / dx;
|
||||||
|
} else {
|
||||||
|
return dy === 0 ? 0 : (point.y - p1.y) / dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const convertBaseLinesToPoints = (baseLines) => {
|
||||||
|
const points = [];
|
||||||
|
const pointSet = new Set();
|
||||||
|
|
||||||
|
baseLines.forEach((line) => {
|
||||||
|
[
|
||||||
|
{ x: line.x1, y: line.y1 },
|
||||||
|
{ x: line.x2, y: line.y2 }
|
||||||
|
].forEach(point => {
|
||||||
|
const key = `${point.x},${point.y}`;
|
||||||
|
if (!pointSet.has(key)) {
|
||||||
|
pointSet.add(key);
|
||||||
|
points.push(point);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return points;
|
||||||
|
};
|
||||||
|
|
||||||
|
function getLineDirection(p1, p2) {
|
||||||
|
const dx = p2.x - p1.x;
|
||||||
|
const dy = p2.y - p1.y;
|
||||||
|
const angle = Math.atan2(dy, dx) * 180 / Math.PI;
|
||||||
|
|
||||||
|
// 각도 범위에 따라 방향 반환
|
||||||
|
if ((angle >= -45 && angle < 45)) return 'right';
|
||||||
|
if ((angle >= 45 && angle < 135)) return 'bottom';
|
||||||
|
if ((angle >= 135 || angle < -135)) return 'left';
|
||||||
|
return 'top'; // (-135 ~ -45)
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user