템플릿A,B 중간 저장
This commit is contained in:
parent
eb5bb77f1f
commit
d7bdfa4892
@ -5,7 +5,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { useMode } from '@/hooks/useMode'
|
import { useMode } from '@/hooks/useMode'
|
||||||
import { LINE_TYPE, Mode } from '@/common/common'
|
import { LINE_TYPE, Mode } from '@/common/common'
|
||||||
import { Button, Input } from '@nextui-org/react'
|
import { Button } from '@nextui-org/react'
|
||||||
import RangeSlider from './ui/RangeSlider'
|
import RangeSlider from './ui/RangeSlider'
|
||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import {
|
import {
|
||||||
@ -410,10 +410,10 @@ export default function Roof2(props) {
|
|||||||
]
|
]
|
||||||
|
|
||||||
const rectangleType1 = [
|
const rectangleType1 = [
|
||||||
{ x: 100, y: 100 },
|
{ x: 500, y: 100 },
|
||||||
{ x: 100, y: 600 },
|
{ x: 500, y: 800 },
|
||||||
{ x: 300, y: 600 },
|
{ x: 900, y: 800 },
|
||||||
{ x: 300, y: 100 },
|
{ x: 900, y: 100 },
|
||||||
]
|
]
|
||||||
|
|
||||||
const rectangleType2 = [
|
const rectangleType2 = [
|
||||||
@ -460,6 +460,15 @@ export default function Roof2(props) {
|
|||||||
{ x: 200, y: 100 },
|
{ x: 200, y: 100 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const test4 = [
|
||||||
|
{ x: 100, y: 100 },
|
||||||
|
{ x: 100, y: 1000 },
|
||||||
|
{ x: 1100, y: 1000 },
|
||||||
|
{ x: 1100, y: 550 },
|
||||||
|
{ x: 500, y: 550 },
|
||||||
|
{ x: 500, y: 100 },
|
||||||
|
]
|
||||||
|
|
||||||
const polygon = new QPolygon(eightPoint4, {
|
const polygon = new QPolygon(eightPoint4, {
|
||||||
fill: 'transparent',
|
fill: 'transparent',
|
||||||
stroke: 'green',
|
stroke: 'green',
|
||||||
@ -1021,7 +1030,7 @@ export default function Roof2(props) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<ThumbnailList {...thumbnailProps} />
|
<ThumbnailList {...thumbnailProps} />
|
||||||
<div className={'flex'}>
|
{/* <div className={'flex'}>
|
||||||
<p className={'m-1 p-3'}>각도 입력(0~360) 후 방향설정 클릭</p>
|
<p className={'m-1 p-3'}>각도 입력(0~360) 후 방향설정 클릭</p>
|
||||||
<Input
|
<Input
|
||||||
className="m-1 p-3"
|
className="m-1 p-3"
|
||||||
@ -1039,14 +1048,14 @@ export default function Roof2(props) {
|
|||||||
<Button className="m-1 p-3" onClick={setDirectionStringToArrow}>
|
<Button className="m-1 p-3" onClick={setDirectionStringToArrow}>
|
||||||
방향 설정
|
방향 설정
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>*/}
|
||||||
<div className="relative w-48 h-48 flex justify-center items-center border-2 border-gray-300 rounded-full">
|
{/* <div className="relative w-48 h-48 flex justify-center items-center border-2 border-gray-300 rounded-full">
|
||||||
{/* Compass Circle */}
|
Compass Circle
|
||||||
<div
|
<div
|
||||||
className="absolute w-full h-full border-2 border-gray-300 rounded-full flex justify-center items-center"
|
className="absolute w-full h-full border-2 border-gray-300 rounded-full flex justify-center items-center"
|
||||||
style={{ rotate: `${globalCampass}deg` }}
|
style={{ rotate: `${globalCampass}deg` }}
|
||||||
>
|
>
|
||||||
{/* N, S, E, W Labels */}
|
N, S, E, W Labels
|
||||||
<div className="absolute top-2 text-lg font-bold">N</div>
|
<div className="absolute top-2 text-lg font-bold">N</div>
|
||||||
<div className="absolute bottom-2 text-lg font-bold">S</div>
|
<div className="absolute bottom-2 text-lg font-bold">S</div>
|
||||||
<div className="absolute right-2 text-lg font-bold" style={{ rotate: '90deg' }}>
|
<div className="absolute right-2 text-lg font-bold" style={{ rotate: '90deg' }}>
|
||||||
@ -1057,9 +1066,9 @@ export default function Roof2(props) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Compass Pointer */}
|
Compass Pointer
|
||||||
<div className="relative w-10 h-10">
|
<div className="relative w-10 h-10">
|
||||||
{/* Red Upper Triangle */}
|
Red Upper Triangle
|
||||||
<div
|
<div
|
||||||
className="absolute top-0"
|
className="absolute top-0"
|
||||||
style={{
|
style={{
|
||||||
@ -1072,7 +1081,7 @@ export default function Roof2(props) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>*/}
|
||||||
<div className="flex justify-start my-8 mx-2 w-full">
|
<div className="flex justify-start my-8 mx-2 w-full">
|
||||||
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
|
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
|
||||||
{!canvas ? null : mode === Mode.DRAW_LINE ? (
|
{!canvas ? null : mode === Mode.DRAW_LINE ? (
|
||||||
|
|||||||
@ -1,15 +1,8 @@
|
|||||||
import { fabric } from 'fabric'
|
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 {
|
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
|
||||||
distanceBetweenPoints,
|
import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
|
||||||
findTopTwoIndexesByDistance,
|
|
||||||
getAllRelatedObjects,
|
|
||||||
getDirectionByPoint,
|
|
||||||
sortedPointLessEightPoint,
|
|
||||||
sortedPoints,
|
|
||||||
} from '@/util/canvas-util'
|
|
||||||
import { calculateAngle, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils'
|
|
||||||
import * as turf from '@turf/turf'
|
import * as turf from '@turf/turf'
|
||||||
import { LINE_TYPE } from '@/common/common'
|
import { LINE_TYPE } from '@/common/common'
|
||||||
|
|
||||||
@ -217,7 +210,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
|||||||
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
|
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
|
||||||
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
|
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
|
||||||
) {
|
) {
|
||||||
console.log('박공 지붕')
|
drawGabledRoof(this.id, this.canvas)
|
||||||
} else if (hasShed) {
|
} else if (hasShed) {
|
||||||
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
|
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
|
||||||
const areLinesParallel = function (line1, line2) {
|
const areLinesParallel = function (line1, line2) {
|
||||||
|
|||||||
@ -1508,55 +1508,14 @@ export function useMode() {
|
|||||||
const handleOuterlinesTest2 = (polygon, offset = 50) => {
|
const handleOuterlinesTest2 = (polygon, offset = 50) => {
|
||||||
// TODO [ljyoung] : offset 입력 처리 후 제거 해야함.
|
// TODO [ljyoung] : offset 입력 처리 후 제거 해야함.
|
||||||
polygon.lines.forEach((line, index) => {
|
polygon.lines.forEach((line, index) => {
|
||||||
line.attributes = {
|
/*line.attributes = {
|
||||||
type: LINE_TYPE.WALLLINE.EAVES,
|
type: LINE_TYPE.WALLLINE.EAVES,
|
||||||
offset: 50,
|
offset: 50,
|
||||||
width: 50,
|
width: 50,
|
||||||
pitch: 4,
|
pitch: 4,
|
||||||
sleeve: true,
|
sleeve: true,
|
||||||
}
|
|
||||||
/*if (index === 1) {
|
|
||||||
line.attributes = {
|
|
||||||
type: LINE_TYPE.WALLLINE.SHED,
|
|
||||||
offset: 30, //출폭
|
|
||||||
width: 30, //폭
|
|
||||||
pitch: 4, //구배
|
|
||||||
sleeve: true, //소매
|
|
||||||
}
|
|
||||||
} else if (index === 5 || index === 3) {
|
|
||||||
line.attributes = {
|
|
||||||
type: LINE_TYPE.WALLLINE.EAVES,
|
|
||||||
offset: 50, //출폭
|
|
||||||
width: 30, //폭
|
|
||||||
pitch: 4, //구배
|
|
||||||
sleeve: true, //소매
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
line.attributes = {
|
|
||||||
type: LINE_TYPE.WALLLINE.GABLE,
|
|
||||||
offset: 20,
|
|
||||||
width: 50,
|
|
||||||
pitch: 4,
|
|
||||||
sleeve: true,
|
|
||||||
}
|
|
||||||
}*/
|
}*/
|
||||||
/* if (index === 1) {
|
if (index % 2 !== 0) {
|
||||||
line.attributes = {
|
|
||||||
type: LINE_TYPE.WALLLINE.SHED,
|
|
||||||
offset: 20, //출폭
|
|
||||||
width: 30, //폭
|
|
||||||
pitch: 4, //구배
|
|
||||||
sleeve: true, //소매
|
|
||||||
}
|
|
||||||
} else if (index === 3) {
|
|
||||||
line.attributes = {
|
|
||||||
type: LINE_TYPE.WALLLINE.EAVES,
|
|
||||||
offset: 50, //출폭
|
|
||||||
width: 30, //폭
|
|
||||||
pitch: 4, //구배
|
|
||||||
sleeve: true, //소매
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
line.attributes = {
|
line.attributes = {
|
||||||
type: LINE_TYPE.WALLLINE.GABLE,
|
type: LINE_TYPE.WALLLINE.GABLE,
|
||||||
offset: 30,
|
offset: 30,
|
||||||
@ -1564,7 +1523,15 @@ export function useMode() {
|
|||||||
pitch: 4,
|
pitch: 4,
|
||||||
sleeve: true,
|
sleeve: true,
|
||||||
}
|
}
|
||||||
}*/
|
} else {
|
||||||
|
line.attributes = {
|
||||||
|
type: LINE_TYPE.WALLLINE.EAVES,
|
||||||
|
offset: 50,
|
||||||
|
width: 50,
|
||||||
|
pitch: 4,
|
||||||
|
sleeve: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const roof = drawRoofPolygon(polygon) //지붕 그리기
|
const roof = drawRoofPolygon(polygon) //지붕 그리기
|
||||||
|
|||||||
@ -1232,6 +1232,367 @@ function calculateAngleBetweenLines(line1, line2) {
|
|||||||
return (angleInRadians * 180) / Math.PI
|
return (angleInRadians * 180) / Math.PI
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 박공지붕(templateA, templateB)을 그린다.
|
||||||
|
* @param roofId
|
||||||
|
* @param canvas
|
||||||
|
*/
|
||||||
|
export const drawGabledRoof = (roofId, canvas) => {
|
||||||
|
const roof = canvas?.getObjects().find((object) => object.id === roofId)
|
||||||
|
const roofLines = roof.lines
|
||||||
|
const wallLines = canvas?.getObjects().find((object) => object.name === POLYGON_TYPE.WALL && object.attributes.roofId === roof.id).lines
|
||||||
|
const hasNonParallelLines = roofLines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
||||||
|
if (hasNonParallelLines.length > 0) {
|
||||||
|
alert('대각선이 존재합니다.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const roofPoints = roof.points
|
||||||
|
const minX = Math.min(...roofPoints.map((point) => point.x))
|
||||||
|
const maxX = Math.max(...roofPoints.map((point) => point.x))
|
||||||
|
const minY = Math.min(...roofPoints.map((point) => point.y))
|
||||||
|
const maxY = Math.max(...roofPoints.map((point) => point.y))
|
||||||
|
|
||||||
|
const checkLength = Math.abs(Math.sqrt(Math.pow(maxX - minX, 2) + Math.pow(maxY - minY, 2)))
|
||||||
|
|
||||||
|
wallLines
|
||||||
|
.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.GABLE)
|
||||||
|
.forEach((gable) => {
|
||||||
|
const gableLine = new QLine([gable.x1, gable.y1, gable.x2, gable.y2], {
|
||||||
|
fontSize: roof.fontSize,
|
||||||
|
stroke: 'cyan',
|
||||||
|
strokeWidth: 2,
|
||||||
|
})
|
||||||
|
canvas.add(gableLine)
|
||||||
|
canvas.renderAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
const eaves = []
|
||||||
|
roofLines.forEach((currentRoof, index) => {
|
||||||
|
if (currentRoof.attributes !== undefined && currentRoof.attributes.type === LINE_TYPE.WALLLINE.EAVES) {
|
||||||
|
eaves.push({ index: index, roof: currentRoof, length: currentRoof.attributes.planeSize })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const ridgeCount = eaves.length - 1
|
||||||
|
const ridges = []
|
||||||
|
eaves.sort((a, b) => a.length - b.length)
|
||||||
|
|
||||||
|
eaves.forEach((eave, index) => {
|
||||||
|
if (ridges.length < ridgeCount) {
|
||||||
|
const index = eave.index,
|
||||||
|
currentRoof = eave.roof
|
||||||
|
const currentWall = wallLines[index]
|
||||||
|
const prevRoof = index === 0 ? roofLines[wallLines.length - 1] : roofLines[index - 1]
|
||||||
|
const nextRoof = index === roofLines.length - 1 ? roofLines[0] : index === roofLines.length ? roofLines[1] : roofLines[index + 1]
|
||||||
|
|
||||||
|
const midX = Math.round(((currentRoof.x1 + currentRoof.x2) / 2) * 10) / 10
|
||||||
|
const midY = Math.round(((currentRoof.y1 + currentRoof.y2) / 2) * 10) / 10
|
||||||
|
const deltaX = currentWall.x2 - currentWall.x1
|
||||||
|
const deltaY = currentWall.y2 - currentWall.y1
|
||||||
|
const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
|
||||||
|
// currentWall과 직각인 기울기 계산
|
||||||
|
const perpendicularDeltaX = deltaY / length
|
||||||
|
const perpendicularDeltaY = -deltaX / length
|
||||||
|
// midX와 midY를 기준으로 직각 기울기를 사용하여 midWallX와 midWallY 계산
|
||||||
|
const midWallX = midX + perpendicularDeltaX * length
|
||||||
|
const midWallY = midY + perpendicularDeltaY * length
|
||||||
|
|
||||||
|
const signX = Math.sign(midX - midWallX)
|
||||||
|
const signY = Math.sign(midY - midWallY)
|
||||||
|
const checkX = Math.round((midX - signX * checkLength) * 10) / 10
|
||||||
|
const checkY = Math.round((midY - signY * checkLength) * 10) / 10
|
||||||
|
|
||||||
|
const intersectLines = []
|
||||||
|
roofLines
|
||||||
|
.filter((line) => line !== currentRoof)
|
||||||
|
.forEach((line) => {
|
||||||
|
const intersect = edgesIntersection(
|
||||||
|
{ vertex1: { x: midX, y: midY }, vertex2: { x: checkX, y: checkY } },
|
||||||
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
||||||
|
)
|
||||||
|
if (intersect && !intersect.isIntersectionOutside) {
|
||||||
|
intersectLines.push({ x: intersect.x, y: intersect.y, line: line })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const intersect = intersectLines.reduce((prev, current) => {
|
||||||
|
if (prev !== undefined) {
|
||||||
|
const prevDistance = Math.sqrt(Math.pow(prev.x - midX, 2) + Math.pow(prev.y - midY, 2))
|
||||||
|
const currentDistance = Math.sqrt(Math.pow(current.x - midX, 2) + Math.pow(current.y - midY, 2))
|
||||||
|
return prevDistance >= currentDistance ? current : prev
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}, undefined)
|
||||||
|
|
||||||
|
const linesCenterX = Math.round(((midX + intersect.x) / 2) * 10) / 10
|
||||||
|
const linesCenterY = Math.round(((midY + intersect.y) / 2) * 10) / 10
|
||||||
|
|
||||||
|
let addLength = currentRoof.attributes.planeSize / 2 / 10
|
||||||
|
|
||||||
|
let centerLineX1 = Math.sign(currentRoof.x1 - currentRoof.x2) * addLength + linesCenterX
|
||||||
|
let centerLineY1 = Math.sign(currentRoof.y1 - currentRoof.y2) * addLength + linesCenterY
|
||||||
|
let centerLineX2 = Math.sign(currentRoof.x2 - currentRoof.x1) * addLength + linesCenterX
|
||||||
|
let centerLineY2 = Math.sign(currentRoof.y2 - currentRoof.y1) * addLength + linesCenterY
|
||||||
|
|
||||||
|
const point1Intersect = []
|
||||||
|
const point2Intersect = []
|
||||||
|
roofLines.forEach((line) => {
|
||||||
|
const point1 = edgesIntersection(
|
||||||
|
{ vertex1: { x: linesCenterX, y: linesCenterY }, vertex2: { x: centerLineX1, y: centerLineY1 } },
|
||||||
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
||||||
|
)
|
||||||
|
if (point1 && !point1.isIntersectionOutside) {
|
||||||
|
point1Intersect.push({ x: point1.x, y: point1.y })
|
||||||
|
}
|
||||||
|
const point2 = edgesIntersection(
|
||||||
|
{ vertex1: { x: linesCenterX, y: linesCenterY }, vertex2: { x: centerLineX2, y: centerLineY2 } },
|
||||||
|
{ vertex1: { x: line.x1, y: line.y1 }, vertex2: { x: line.x2, y: line.y2 } },
|
||||||
|
)
|
||||||
|
if (point2 && !point2.isIntersectionOutside) {
|
||||||
|
point2Intersect.push({ x: point2.x, y: point2.y })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
point1Intersect.reduce((prev, current) => {
|
||||||
|
if (prev !== undefined) {
|
||||||
|
const prevDistance = Math.sqrt(Math.pow(prev.x - linesCenterX, 2) + Math.pow(prev.y - linesCenterY, 2))
|
||||||
|
const currentDistance = Math.sqrt(Math.pow(current.x - linesCenterX, 2) + Math.pow(current.y - linesCenterY, 2))
|
||||||
|
return prevDistance >= currentDistance ? current : prev
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}, undefined)
|
||||||
|
point2Intersect.reduce((prev, current) => {
|
||||||
|
if (prev !== undefined) {
|
||||||
|
const prevDistance = Math.sqrt(Math.pow(prev.x - linesCenterX, 2) + Math.pow(prev.y - linesCenterY, 2))
|
||||||
|
const currentDistance = Math.sqrt(Math.pow(current.x - linesCenterX, 2) + Math.pow(current.y - linesCenterY, 2))
|
||||||
|
return prevDistance >= currentDistance ? current : prev
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}, undefined)
|
||||||
|
|
||||||
|
if (point1Intersect.length > 0) {
|
||||||
|
centerLineX1 = point1Intersect[0].x
|
||||||
|
centerLineY1 = point1Intersect[0].y
|
||||||
|
}
|
||||||
|
if (point2Intersect.length > 0) {
|
||||||
|
centerLineX2 = point2Intersect[0].x
|
||||||
|
centerLineY2 = point2Intersect[0].y
|
||||||
|
}
|
||||||
|
|
||||||
|
const ridge = new QLine([centerLineX1, centerLineY1, centerLineX2, centerLineY2], {
|
||||||
|
fontSize: roof.fontSize,
|
||||||
|
stroke: 'blue',
|
||||||
|
strokeWidth: 1,
|
||||||
|
name: LINE_TYPE.SUBLINE.RIDGE,
|
||||||
|
attributes: { roofId: roof.id, currentRoof: currentRoof.id },
|
||||||
|
})
|
||||||
|
canvas.add(ridge)
|
||||||
|
canvas.renderAll()
|
||||||
|
ridges.push(ridge)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
eaves.forEach((eave) => {
|
||||||
|
const index = eave.index,
|
||||||
|
currentRoof = eave.roof
|
||||||
|
const currentWall = wallLines[index]
|
||||||
|
const prevRoof = index === 0 ? roofLines[wallLines.length - 1] : roofLines[index - 1]
|
||||||
|
const nextRoof = index === roofLines.length - 1 ? roofLines[0] : index === roofLines.length ? roofLines[1] : roofLines[index + 1]
|
||||||
|
|
||||||
|
/*const midX = Math.round(((currentRoof.x1 + currentRoof.x2) / 2) * 10) / 10
|
||||||
|
const midY = Math.round(((currentRoof.y1 + currentRoof.y2) / 2) * 10) / 10
|
||||||
|
const midWallX = Math.round(((currentWall.x1 + currentWall.x2) / 2) * 10) / 10
|
||||||
|
const midWallY = Math.round(((currentWall.y1 + currentWall.y2) / 2) * 10) / 10
|
||||||
|
const signX = Math.sign(midX - midWallX)
|
||||||
|
const signY = Math.sign(midY - midWallY)
|
||||||
|
*/
|
||||||
|
const midX = Math.round(((currentRoof.x1 + currentRoof.x2) / 2) * 10) / 10
|
||||||
|
const midY = Math.round(((currentRoof.y1 + currentRoof.y2) / 2) * 10) / 10
|
||||||
|
const deltaX = currentWall.x2 - currentWall.x1
|
||||||
|
const deltaY = currentWall.y2 - currentWall.y1
|
||||||
|
const length = Math.sqrt(deltaX * deltaX + deltaY * deltaY)
|
||||||
|
// currentWall과 직각인 기울기 계산
|
||||||
|
const perpendicularDeltaX = deltaY / length
|
||||||
|
const perpendicularDeltaY = -deltaX / length
|
||||||
|
// midX와 midY를 기준으로 직각 기울기를 사용하여 midWallX와 midWallY 계산
|
||||||
|
const midWallX = midX + perpendicularDeltaX * length
|
||||||
|
const midWallY = midY + perpendicularDeltaY * length
|
||||||
|
const signX = Math.sign(midX - midWallX)
|
||||||
|
const signY = Math.sign(midY - midWallY)
|
||||||
|
|
||||||
|
let points = []
|
||||||
|
const intersectRidge = []
|
||||||
|
// 현재 roof가 wall보다 작을때 이전, 다음 지붕의 offset만큼 포인트를 조정한다.
|
||||||
|
if (currentRoof.attributes.planeSize >= currentWall.attributes.planeSize) {
|
||||||
|
points.push({ x: currentRoof.x1, y: currentRoof.y1 }, { x: currentRoof.x2, y: currentRoof.y2 })
|
||||||
|
} else {
|
||||||
|
const deltaX = currentRoof.x2 - currentRoof.x1
|
||||||
|
const deltaY = currentRoof.y2 - currentRoof.y1
|
||||||
|
points.push(
|
||||||
|
{ x: currentRoof.x1 - Math.sign(deltaX) * prevRoof.attributes.offset, y: currentRoof.y1 - Math.sign(deltaY) * prevRoof.attributes.offset },
|
||||||
|
{ x: currentRoof.x2 + Math.sign(deltaX) * nextRoof.attributes.offset, y: currentRoof.y2 + Math.sign(deltaY) * nextRoof.attributes.offset },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
ridges.forEach((ridge) => {
|
||||||
|
const ridgeMidX = (ridge.x1 + ridge.x2) / 2
|
||||||
|
const ridgeMidY = (ridge.y1 + ridge.y2) / 2
|
||||||
|
if (midX === ridgeMidX || midY === ridgeMidY) {
|
||||||
|
const intersection = edgesIntersection(
|
||||||
|
{ vertex1: { x: midX, y: midY }, vertex2: { x: ridgeMidX, y: ridgeMidY } },
|
||||||
|
{ vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } },
|
||||||
|
)
|
||||||
|
if (intersection && !intersection.isIntersectionOutside) {
|
||||||
|
intersectRidge.push({ line: ridge, name: 'mid' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log('signX : ', signX, 'signY : ', signY)
|
||||||
|
if (Math.sign(midX - ridgeMidX) === signX || Math.sign(midY - ridgeMidY) === signY) {
|
||||||
|
const prevIntersect = edgesIntersection(
|
||||||
|
{ vertex1: { x: prevRoof.x1, y: prevRoof.y1 }, vertex2: { x: prevRoof.x2, y: prevRoof.y2 } },
|
||||||
|
{ vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } },
|
||||||
|
)
|
||||||
|
const nextIntersect = edgesIntersection(
|
||||||
|
{ vertex1: { x: nextRoof.x1, y: nextRoof.y1 }, vertex2: { x: nextRoof.x2, y: nextRoof.y2 } },
|
||||||
|
{ vertex1: { x: ridge.x1, y: ridge.y1 }, vertex2: { x: ridge.x2, y: ridge.y2 } },
|
||||||
|
)
|
||||||
|
if (prevIntersect && !prevIntersect.isIntersectionOutside) {
|
||||||
|
intersectRidge.push({ line: ridge, name: 'prev' })
|
||||||
|
}
|
||||||
|
if (nextIntersect && !nextIntersect.isIntersectionOutside) {
|
||||||
|
intersectRidge.push({ line: ridge, name: 'next' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
intersectRidge.forEach((intersect) => {
|
||||||
|
const line = intersect.line
|
||||||
|
if (currentRoof.attributes.planeSize >= currentWall.attributes.planeSize) {
|
||||||
|
points.push({ x: line.x1, y: line.y1 }, { x: line.x2, y: line.y2 })
|
||||||
|
} else {
|
||||||
|
if (intersect.name === 'mid') {
|
||||||
|
let lineX1 = line.x1,
|
||||||
|
lineY1 = line.y1,
|
||||||
|
lineX2 = line.x2,
|
||||||
|
lineY2 = line.y2
|
||||||
|
const prevCheck1 = Math.sqrt(Math.pow(Math.round(lineX1 - currentRoof.x1), 2) + Math.pow(Math.round(lineY1 - currentRoof.y1), 2))
|
||||||
|
const prevCheck2 = Math.sqrt(Math.pow(Math.round(lineX2 - currentRoof.x1), 2) + Math.pow(Math.round(lineY2 - currentRoof.y1), 2))
|
||||||
|
const deltaX = currentRoof.x2 - currentRoof.x1
|
||||||
|
const deltaY = currentRoof.y2 - currentRoof.y1
|
||||||
|
console.log('deltaX : ', deltaX, 'deltaY : ', deltaY)
|
||||||
|
if (prevCheck1 < prevCheck2) {
|
||||||
|
lineX1 = lineX1 - Math.sign(deltaX) * prevRoof.attributes.offset
|
||||||
|
lineY1 = lineY1 - Math.sign(deltaY) * prevRoof.attributes.offset
|
||||||
|
lineX2 = lineX2 + Math.sign(deltaX) * nextRoof.attributes.offset
|
||||||
|
lineY2 = lineY2 + Math.sign(deltaY) * nextRoof.attributes.offset
|
||||||
|
} else {
|
||||||
|
lineX1 = lineX1 + Math.sign(deltaX) * prevRoof.attributes.offset
|
||||||
|
lineY1 = lineY1 + Math.sign(deltaY) * prevRoof.attributes.offset
|
||||||
|
lineX2 = lineX2 - Math.sign(deltaX) * nextRoof.attributes.offset
|
||||||
|
lineY2 = lineY2 - Math.sign(deltaY) * nextRoof.attributes.offset
|
||||||
|
}
|
||||||
|
points.push({ x: lineX1, y: lineY1 }, { x: lineX2, y: lineY2 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
points = points.filter((point, index, self) => index === self.findIndex((p) => p.x === point.x && p.y === point.y))
|
||||||
|
|
||||||
|
const startPoint = points
|
||||||
|
.filter((point) => point.x === Math.min(...points.map((point) => point.x)))
|
||||||
|
.reduce((prev, current) => {
|
||||||
|
return prev.y < current.y ? prev : current
|
||||||
|
})
|
||||||
|
const sortedPoints = [startPoint]
|
||||||
|
points.forEach((p, index) => {
|
||||||
|
const lastPoint = sortedPoints[sortedPoints.length - 1]
|
||||||
|
if (index === 0) {
|
||||||
|
const underStartPoint = points.filter((point) => point.y > startPoint.y)
|
||||||
|
const nextPoint = underStartPoint
|
||||||
|
.filter((point) => point.x === startPoint.x)
|
||||||
|
.reduce((prev, current) => {
|
||||||
|
return Math.abs(prev.y - startPoint.y) < Math.abs(current.y - startPoint.y) ? prev : current
|
||||||
|
})
|
||||||
|
if (nextPoint) {
|
||||||
|
sortedPoints.push(nextPoint)
|
||||||
|
} else {
|
||||||
|
const nextPoint = underStartPoint.reduce((prev, current) => {
|
||||||
|
const prevHypos = Math.sqrt(Math.abs(Math.pow(prev.x - startPoint.x, 2)) + Math.abs(Math.pow(prev.y - startPoint.y, 2)))
|
||||||
|
const currentHypos = Math.sqrt(Math.abs(Math.pow(current.x - startPoint.x, 2)) + Math.abs(Math.pow(current.y - startPoint.y, 2)))
|
||||||
|
return prevHypos < currentHypos ? prev : current
|
||||||
|
})
|
||||||
|
sortedPoints.push(nextPoint)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const prevPoint = sortedPoints[sortedPoints.length - 2]
|
||||||
|
const otherPoints = points.filter((point) => sortedPoints.includes(point) === false)
|
||||||
|
const nextPoint = otherPoints.reduce((prev, current) => {
|
||||||
|
if (prev === undefined) {
|
||||||
|
const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))))
|
||||||
|
const adjacent = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
|
||||||
|
const hypotenuse = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))))
|
||||||
|
|
||||||
|
const angle = Math.round(
|
||||||
|
Math.acos((Math.pow(adjacent, 2) + Math.pow(height, 2) - Math.pow(hypotenuse, 2)) / (2 * adjacent * height)) * (180 / Math.PI),
|
||||||
|
)
|
||||||
|
if (angle === 90) {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
}, undefined)
|
||||||
|
if (nextPoint) {
|
||||||
|
sortedPoints.push(nextPoint)
|
||||||
|
} else {
|
||||||
|
const nextPoint = otherPoints.reduce((prev, current) => {
|
||||||
|
if (prev !== undefined) {
|
||||||
|
const height = Math.abs(Math.sqrt(Math.abs(Math.pow(prevPoint.x - lastPoint.x, 2)) + Math.abs(Math.pow(prevPoint.y - lastPoint.y, 2))))
|
||||||
|
const adjacentC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - lastPoint.x, 2)) + Math.abs(Math.pow(current.y - lastPoint.y, 2))))
|
||||||
|
const hypotenuseC = Math.abs(Math.sqrt(Math.abs(Math.pow(current.x - prevPoint.x, 2)) + Math.abs(Math.pow(current.y - prevPoint.y, 2))))
|
||||||
|
|
||||||
|
const angleC = Math.round(
|
||||||
|
Math.acos((Math.pow(adjacentC, 2) + Math.pow(height, 2) - Math.pow(hypotenuseC, 2)) / (2 * adjacentC * height)) * (180 / Math.PI),
|
||||||
|
)
|
||||||
|
|
||||||
|
const adjacentP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - lastPoint.x, 2)) + Math.abs(Math.pow(prev.y - lastPoint.y, 2))))
|
||||||
|
const hypotenuseP = Math.abs(Math.sqrt(Math.abs(Math.pow(prev.x - prevPoint.x, 2)) + Math.abs(Math.pow(prev.y - prevPoint.y, 2))))
|
||||||
|
|
||||||
|
const angleP = Math.round(
|
||||||
|
Math.acos((Math.pow(adjacentP, 2) + Math.pow(height, 2) - Math.pow(hypotenuseP, 2)) / (2 * adjacentP * height)) * (180 / Math.PI),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (Math.abs(90 - angleC) < Math.abs(90 - angleP)) {
|
||||||
|
return current
|
||||||
|
} else {
|
||||||
|
return prev
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return current
|
||||||
|
}
|
||||||
|
}, undefined)
|
||||||
|
if (nextPoint) {
|
||||||
|
sortedPoints.push(nextPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
console.log('sortedPoints : ', sortedPoints)
|
||||||
|
if (sortedPoints.length > 0) {
|
||||||
|
const roofPolygon = new QPolygon(sortedPoints, {
|
||||||
|
fill: 'transparent',
|
||||||
|
stroke: 'blue',
|
||||||
|
strokeWidth: 1,
|
||||||
|
selectable: false,
|
||||||
|
fontSize: roof.fontSize,
|
||||||
|
name: 'roofPolygon',
|
||||||
|
attributes: { roofId: roof.id, currentRoof: currentRoof.id },
|
||||||
|
})
|
||||||
|
canvas.add(roofPolygon)
|
||||||
|
canvas.renderAll()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 한쪽흐름 지붕
|
* 한쪽흐름 지붕
|
||||||
* @param roofId
|
* @param roofId
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user