템플릿 사각형 수정
This commit is contained in:
parent
c44fa88ed8
commit
aebf4e0c42
@ -155,7 +155,6 @@ export default function Roof2() {
|
||||
|
||||
canvas?.add(polygon)
|
||||
|
||||
polygon.drawRoof(-50)
|
||||
addBackgroundInPolygon(polygon)
|
||||
|
||||
const lines = togglePolygonLine(polygon)
|
||||
@ -176,11 +175,15 @@ export default function Roof2() {
|
||||
|
||||
const makeQLine = () => {
|
||||
if (canvas) {
|
||||
const line = new QLine([50, 50, 200, 50], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
fontSize: fontSize,
|
||||
})
|
||||
const line = new QLine(
|
||||
[50, 50, 200, 50],
|
||||
{
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
fontSize: fontSize,
|
||||
},
|
||||
50,
|
||||
)
|
||||
|
||||
canvas?.add(line)
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ export class QLine extends fabric.Group {
|
||||
line
|
||||
text
|
||||
fontSize
|
||||
length
|
||||
length = 0
|
||||
x1
|
||||
y1
|
||||
x2
|
||||
@ -13,12 +13,13 @@ export class QLine extends fabric.Group {
|
||||
type = 'QLine'
|
||||
parent
|
||||
|
||||
constructor(points, option) {
|
||||
constructor(points, option, lengthTxt) {
|
||||
const [x1, y1, x2, y2] = points
|
||||
|
||||
if (!option.fontSize) {
|
||||
throw new Error('Font size is required.')
|
||||
}
|
||||
|
||||
const line = new fabric.Line(points, { ...option, strokeWidth: 1 })
|
||||
super([line], {})
|
||||
|
||||
@ -31,6 +32,10 @@ export class QLine extends fabric.Group {
|
||||
this.direction = option.direction
|
||||
this.parent = option.parent
|
||||
|
||||
if (lengthTxt > 0) {
|
||||
this.length = Number(lengthTxt)
|
||||
}
|
||||
|
||||
this.#init()
|
||||
this.#addControl()
|
||||
}
|
||||
@ -66,7 +71,16 @@ export class QLine extends fabric.Group {
|
||||
this.removeWithUpdate(this.text)
|
||||
this.text = null
|
||||
}
|
||||
|
||||
if (this.length > 0) {
|
||||
const text = new fabric.Textbox(this.length.toFixed(0).toString(), {
|
||||
left: (this.x1 + this.x2) / 2,
|
||||
top: (this.y1 + this.y2) / 2,
|
||||
fontSize: this.fontSize,
|
||||
})
|
||||
this.text = text
|
||||
this.addWithUpdate(text)
|
||||
return
|
||||
}
|
||||
const scaleX = this.scaleX
|
||||
const scaleY = this.scaleY
|
||||
const x1 = this.left
|
||||
@ -77,7 +91,7 @@ export class QLine extends fabric.Group {
|
||||
const dy = y2 - y1
|
||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0))
|
||||
|
||||
const text = new fabric.Textbox(this.length.toString(), {
|
||||
const text = new fabric.Textbox(this.length.toFixed(0).toString(), {
|
||||
left: (x1 + x2) / 2,
|
||||
top: (y1 + y2) / 2,
|
||||
fontSize: this.fontSize,
|
||||
|
||||
@ -1,8 +1,12 @@
|
||||
import { fabric } from 'fabric'
|
||||
import {
|
||||
calculateIntersection,
|
||||
distanceBetweenPoints,
|
||||
getDegreeByChon,
|
||||
getDirection,
|
||||
getDirectionByPoint,
|
||||
getRoofHeight,
|
||||
getRoofHypotenuse,
|
||||
} from '@/util/canvas-util'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
|
||||
@ -15,7 +19,6 @@ export default class QPolygon extends fabric.Group {
|
||||
canvas
|
||||
fontSize
|
||||
qCells = []
|
||||
roof
|
||||
name
|
||||
constructor(points, options, canvas) {
|
||||
if (!options.fontSize) {
|
||||
@ -43,8 +46,8 @@ export default class QPolygon extends fabric.Group {
|
||||
this.points.forEach((point, i) => {
|
||||
const nextPoint = this.points[(i + 1) % this.points.length]
|
||||
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
stroke: this.stroke,
|
||||
strokeWidth: this.strokeWidth,
|
||||
fontSize: this.fontSize,
|
||||
direction: getDirectionByPoint(point, nextPoint),
|
||||
})
|
||||
@ -88,7 +91,7 @@ export default class QPolygon extends fabric.Group {
|
||||
})
|
||||
this.texts = []
|
||||
}
|
||||
const points = this.getCurrentPoints()
|
||||
const points = this.points
|
||||
|
||||
points.forEach((start, i) => {
|
||||
const end = points[(i + 1) % points.length]
|
||||
@ -281,77 +284,170 @@ export default class QPolygon extends fabric.Group {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 지붕 그리기
|
||||
* @param offset
|
||||
*/
|
||||
drawRoof(offset = -10) {
|
||||
const points = this.getCurrentPoints()
|
||||
const offsetPoints = []
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
const prev = points[(i - 1 + points.length) % points.length]
|
||||
const current = points[i]
|
||||
const next = points[(i + 1) % points.length]
|
||||
|
||||
// 두 벡터 계산 (prev -> current, current -> next)
|
||||
const vector1 = { x: current.x - prev.x, y: current.y - prev.y }
|
||||
const vector2 = { x: next.x - current.x, y: next.y - current.y }
|
||||
|
||||
// 벡터의 길이 계산
|
||||
const length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y)
|
||||
const length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y)
|
||||
|
||||
// 벡터를 단위 벡터로 정규화
|
||||
const unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 }
|
||||
const unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 }
|
||||
|
||||
// 법선 벡터 계산 (왼쪽 방향)
|
||||
const normal1 = { x: -unitVector1.y, y: unitVector1.x }
|
||||
const normal2 = { x: -unitVector2.y, y: unitVector2.x }
|
||||
|
||||
// 법선 벡터 평균 계산
|
||||
const averageNormal = {
|
||||
x: (normal1.x + normal2.x) / 2,
|
||||
y: (normal1.y + normal2.y) / 2,
|
||||
}
|
||||
|
||||
// 평균 법선 벡터를 단위 벡터로 정규화
|
||||
const lengthNormal = Math.sqrt(
|
||||
averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y,
|
||||
)
|
||||
const unitNormal = {
|
||||
x: averageNormal.x / lengthNormal,
|
||||
y: averageNormal.y / lengthNormal,
|
||||
}
|
||||
|
||||
// 오프셋 적용
|
||||
const offsetPoint = {
|
||||
x: current.x + unitNormal.x * offset,
|
||||
y: current.y + unitNormal.y * offset,
|
||||
}
|
||||
|
||||
offsetPoints.push(offsetPoint)
|
||||
}
|
||||
|
||||
const roof = new QPolygon(
|
||||
offsetPoints,
|
||||
{
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
fill: 'transparent',
|
||||
fontSize: this.fontSize,
|
||||
},
|
||||
this.canvas,
|
||||
)
|
||||
|
||||
this.roof = roof
|
||||
|
||||
this.addWithUpdate(outPolygon)
|
||||
this.canvas.renderAll()
|
||||
}
|
||||
|
||||
fillBackground(pattern) {
|
||||
this.polygon.set({ fill: pattern })
|
||||
this.canvas.renderAll()
|
||||
this.canvas.requestRenderAll()
|
||||
}
|
||||
|
||||
// 보조선 그리기 사각형에서만
|
||||
drawHelpLine(chon) {
|
||||
let type
|
||||
let smallestLength = Infinity
|
||||
let maxLength = 0
|
||||
|
||||
this.lines.forEach((line) => {
|
||||
if (line.length < smallestLength) {
|
||||
smallestLength = line.length
|
||||
}
|
||||
if (line.length > maxLength) {
|
||||
maxLength = line.length
|
||||
}
|
||||
})
|
||||
|
||||
const smallestLines = this.lines.filter(
|
||||
(line) => line.length === smallestLength,
|
||||
)
|
||||
|
||||
let needPlusLine
|
||||
let needMinusLine
|
||||
|
||||
const direction = smallestLines[0].direction
|
||||
|
||||
if (direction === 'top' || direction === 'bottom') {
|
||||
needPlusLine =
|
||||
smallestLines[0].x1 < smallestLines[1].x1
|
||||
? smallestLines[0]
|
||||
: smallestLines[1]
|
||||
needMinusLine =
|
||||
needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0]
|
||||
|
||||
type = 1 // 가로가 긴 사각형
|
||||
}
|
||||
|
||||
if (direction === 'left' || direction === 'right') {
|
||||
needPlusLine =
|
||||
smallestLines[0].y1 < smallestLines[1].y1
|
||||
? smallestLines[0]
|
||||
: smallestLines[1]
|
||||
needMinusLine =
|
||||
needPlusLine === smallestLines[0] ? smallestLines[1] : smallestLines[0]
|
||||
|
||||
type = 2 // 세로가 긴 사각형
|
||||
}
|
||||
|
||||
let point1
|
||||
let point2
|
||||
|
||||
if (type === 1) {
|
||||
point1 = {
|
||||
x: needPlusLine.x1 + smallestLength / 2,
|
||||
y:
|
||||
needPlusLine.y1 > needPlusLine.y2
|
||||
? needPlusLine.y1 - smallestLength / 2
|
||||
: needPlusLine.y2 - smallestLength / 2,
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: needMinusLine.x1 - smallestLength / 2,
|
||||
y:
|
||||
needMinusLine.y1 > needMinusLine.y2
|
||||
? needMinusLine.y1 - smallestLength / 2
|
||||
: needMinusLine.y2 - smallestLength / 2,
|
||||
}
|
||||
} else if (type === 2) {
|
||||
point1 = {
|
||||
x:
|
||||
needPlusLine.x1 > needPlusLine.x2
|
||||
? needPlusLine.x1 - smallestLength / 2
|
||||
: needPlusLine.x2 - smallestLength / 2,
|
||||
y: needPlusLine.y1 + smallestLength / 2,
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x:
|
||||
needMinusLine.x1 > needMinusLine.x2
|
||||
? needMinusLine.x1 - smallestLength / 2
|
||||
: needMinusLine.x2 - smallestLength / 2,
|
||||
y: needMinusLine.y1 - smallestLength / 2,
|
||||
}
|
||||
}
|
||||
|
||||
// 빗변1
|
||||
const realLine1 = new QLine(
|
||||
[needPlusLine.x1, needPlusLine.y1, point1.x, point1.y],
|
||||
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
|
||||
getRoofHypotenuse(smallestLength / 2),
|
||||
)
|
||||
|
||||
// 빗변2
|
||||
const realLine2 = new QLine(
|
||||
[needPlusLine.x2, needPlusLine.y2, point1.x, point1.y],
|
||||
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
|
||||
getRoofHypotenuse(smallestLength / 2),
|
||||
)
|
||||
|
||||
// 빗변3
|
||||
const realLine3 = new QLine(
|
||||
[needMinusLine.x1, needMinusLine.y1, point2.x, point2.y],
|
||||
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
|
||||
getRoofHypotenuse(smallestLength / 2),
|
||||
)
|
||||
|
||||
// 빗변4
|
||||
const realLine4 = new QLine(
|
||||
[needMinusLine.x2, needMinusLine.y2, point2.x, point2.y],
|
||||
{ fontSize: this.fontSize, stroke: 'black', strokeWidth: 1 },
|
||||
getRoofHypotenuse(smallestLength / 2),
|
||||
)
|
||||
|
||||
let centerPoint1
|
||||
let centerPoint2
|
||||
|
||||
if (type === 1) {
|
||||
centerPoint1 = { x: point1.x - smallestLength / 2, y: point1.y }
|
||||
centerPoint2 = { x: point2.x + smallestLength / 2, y: point2.y }
|
||||
} else if (type === 2) {
|
||||
centerPoint1 = { x: point1.x, y: point1.y - smallestLength / 2 }
|
||||
centerPoint2 = { x: point2.x, y: point2.y + smallestLength / 2 }
|
||||
}
|
||||
|
||||
// 옆으로 누워있는 지붕의 높이
|
||||
const realLine5 = new QLine(
|
||||
[point1.x, point1.y, centerPoint1.x, centerPoint1.y],
|
||||
{
|
||||
fontSize: this.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [5, 5],
|
||||
},
|
||||
getRoofHeight(smallestLength / 2, getDegreeByChon(4)),
|
||||
)
|
||||
|
||||
// 옆으로 누워있는 지붕의 높이
|
||||
const realLine6 = new QLine(
|
||||
[point2.x, point2.y, centerPoint2.x, centerPoint2.y],
|
||||
{
|
||||
fontSize: this.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [5, 5],
|
||||
},
|
||||
getRoofHeight(smallestLength / 2, getDegreeByChon(4)),
|
||||
)
|
||||
|
||||
// 용마루
|
||||
const ridge = new QLine([point1.x, point1.y, point2.x, point2.y], {
|
||||
fontSize: this.fontSize,
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
})
|
||||
|
||||
this.addWithUpdate(realLine1)
|
||||
this.addWithUpdate(realLine2)
|
||||
this.addWithUpdate(realLine3)
|
||||
this.addWithUpdate(realLine4)
|
||||
this.addWithUpdate(realLine5)
|
||||
this.addWithUpdate(realLine6)
|
||||
this.addWithUpdate(ridge)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,8 +7,13 @@ import {
|
||||
findTopTwoIndexesByDistance,
|
||||
getDirection,
|
||||
} from '@/util/canvas-util'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil'
|
||||
import {
|
||||
fontSizeState,
|
||||
roofState,
|
||||
sortedPolygonArray,
|
||||
wallState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
|
||||
export const Mode = {
|
||||
@ -30,6 +35,8 @@ export function useMode() {
|
||||
const [fontSize] = useRecoilState(fontSizeState)
|
||||
const [shape, setShape] = useState(0)
|
||||
const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray)
|
||||
const [roof, setRoof] = useRecoilState(roofState)
|
||||
const [wall, setWall] = useRecoilState(wallState)
|
||||
|
||||
const addEvent = (mode) => {
|
||||
switch (mode) {
|
||||
@ -210,7 +217,8 @@ export function useMode() {
|
||||
// handleOuterlines()
|
||||
handleOuterlinesTest() //외곽선 그리기 테스트
|
||||
|
||||
makePolygon()
|
||||
const wall = makePolygon()
|
||||
setWall(wall)
|
||||
}
|
||||
}
|
||||
|
||||
@ -398,11 +406,13 @@ export function useMode() {
|
||||
|
||||
// 캔버스를 다시 그립니다.
|
||||
if (!otherLines) {
|
||||
polygon.fillCell()
|
||||
// polygon.fillCell()
|
||||
canvas.renderAll()
|
||||
polygon.setViewLengthText(false)
|
||||
setMode(Mode.DEFAULT)
|
||||
}
|
||||
|
||||
return polygon
|
||||
}
|
||||
|
||||
/**
|
||||
@ -714,7 +724,6 @@ export function useMode() {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(newOuterlines)
|
||||
makePolygon(newOuterlines)
|
||||
}
|
||||
|
||||
@ -780,7 +789,9 @@ export function useMode() {
|
||||
offsetPoints.push(offsetPoint)
|
||||
}
|
||||
|
||||
makePolygon(offsetPoints)
|
||||
const roof = makePolygon(offsetPoints)
|
||||
setRoof(roof)
|
||||
roof.drawHelpLine()
|
||||
}
|
||||
|
||||
const togglePolygonLine = (obj) => {
|
||||
|
||||
@ -20,6 +20,18 @@ export const canvasSizeState = atom({
|
||||
|
||||
export const sortedPolygonArray = atom({
|
||||
key: 'sortedArray',
|
||||
default : [],
|
||||
default: [],
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const roofState = atom({
|
||||
key: 'roof',
|
||||
default: {},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const wallState = atom({
|
||||
key: 'wall',
|
||||
default: {},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
@ -264,3 +264,34 @@ export const getDirectionByPoint = (a, b) => {
|
||||
return vector.y > 0 ? 'bottom' : 'top'
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateIntersection(line1, line2) {
|
||||
const x1 = line1.x1,
|
||||
y1 = line1.y1,
|
||||
x2 = line1.x2,
|
||||
y2 = line1.y2
|
||||
const x3 = line2.x1,
|
||||
y3 = line2.y1,
|
||||
x4 = line2.x2,
|
||||
y4 = line2.y2
|
||||
|
||||
const denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
|
||||
if (denom === 0) return null // 선분이 평행하거나 일치
|
||||
|
||||
const intersectX =
|
||||
((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom
|
||||
const intersectY =
|
||||
((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom
|
||||
|
||||
// 교차점이 두 선분의 x 좌표 범위 내에 있는지 확인
|
||||
if (
|
||||
intersectX < Math.min(x1, x2) ||
|
||||
intersectX > Math.max(x1, x2) ||
|
||||
intersectX < Math.min(x3, x4) ||
|
||||
intersectX > Math.max(x3, x4)
|
||||
) {
|
||||
return null // 교차점이 선분 범위 밖에 있음
|
||||
}
|
||||
|
||||
return { x: intersectX, y: intersectY }
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user