Merge branch 'dev'
This commit is contained in:
commit
bfa51bd24d
@ -1,6 +1,6 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
// reactStrictMode: false,
|
||||
reactStrictMode: false,
|
||||
webpack: (config) => {
|
||||
config.externals.push({
|
||||
"utf-8-validate": "commonjs utf-8-validate",
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"postcss": "^8",
|
||||
"prettier": "^3.3.3",
|
||||
"prisma": "^5.17.0",
|
||||
"tailwindcss": "^3.4.1"
|
||||
}
|
||||
|
||||
23
src/app/404.js
Normal file
23
src/app/404.js
Normal file
@ -0,0 +1,23 @@
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<section className="bg-white dark:bg-gray-900">
|
||||
<div className="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||
<div className="mx-auto max-w-screen-sm text-center">
|
||||
<h1 className="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-primary-600 dark:text-primary-500">404</h1>
|
||||
<p className="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white">Something's missing.</p>
|
||||
<p className="mb-4 text-lg font-light text-gray-500 dark:text-gray-400">
|
||||
Sorry, we can't find that page. You'll find lots to explore on the home page.{' '}
|
||||
</p>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex text-white bg-primary-600 hover:bg-primary-800 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:focus:ring-primary-900 my-4"
|
||||
>
|
||||
Back to Homepage
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
13
src/app/500.js
Normal file
13
src/app/500.js
Normal file
@ -0,0 +1,13 @@
|
||||
export default function ServerError() {
|
||||
return (
|
||||
<section className="bg-white dark:bg-gray-900">
|
||||
<div className="py-8 px-4 mx-auto max-w-screen-xl lg:py-16 lg:px-6">
|
||||
<div className="mx-auto max-w-screen-sm text-center">
|
||||
<h1 className="mb-4 text-7xl tracking-tight font-extrabold lg:text-9xl text-primary-600 dark:text-primary-500">500</h1>
|
||||
<p className="mb-4 text-3xl tracking-tight font-bold text-gray-900 md:text-4xl dark:text-white">Internal Server Error.</p>
|
||||
<p className="mb-4 text-lg font-light text-gray-500 dark:text-gray-400">We are already working to solve the problem. </p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
@ -3,14 +3,14 @@ import { useEffect, useState } from 'react'
|
||||
import { Mode, useMode } from '@/hooks/useMode'
|
||||
import { Button } from '@nextui-org/react'
|
||||
import QRect from '@/components/fabric/QRect'
|
||||
import QPolygon from '@/components/fabric/QPolygon'
|
||||
|
||||
import RangeSlider from './ui/RangeSlider'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { canvasAtom, canvasListState, canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
|
||||
import { canvasSizeState, fontSizeState, roofState, sortedPolygonArray } from '@/store/canvasAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas'
|
||||
import { calculateIntersection2 } from '@/util/canvas-util'
|
||||
import { getCanvasState, insertCanvasState } from '@/lib/canvas'
|
||||
import { calculateIntersection } from '@/util/canvas-util'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
|
||||
export default function Roof2() {
|
||||
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas')
|
||||
@ -31,6 +31,8 @@ export default function Roof2() {
|
||||
|
||||
const [showControl, setShowControl] = useState(false)
|
||||
|
||||
const roof = useRecoilValue(roofState)
|
||||
|
||||
const {
|
||||
mode,
|
||||
changeMode,
|
||||
@ -99,12 +101,10 @@ export default function Roof2() {
|
||||
selectable: true,
|
||||
fontSize: fontSize,
|
||||
},
|
||||
canvas,
|
||||
)
|
||||
|
||||
canvas?.add(polygon)
|
||||
|
||||
polygon.fillCell({ width: 50, height: 30, padding: 10 })
|
||||
// polygon.fillCell({ width: 50, height: 30, padding: 10 })
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,14 +186,14 @@ export default function Roof2() {
|
||||
]
|
||||
|
||||
const eightPoint = [
|
||||
{ x: 240, y: 130 },
|
||||
{ x: 240, y: 630 },
|
||||
{ x: 640, y: 630 },
|
||||
{ x: 640, y: 480 },
|
||||
{ x: 440, y: 480 },
|
||||
{ x: 440, y: 280 },
|
||||
{ x: 740, y: 280 },
|
||||
{ x: 740, y: 130 },
|
||||
{ x: 240.1111, y: 130.1111 },
|
||||
{ x: 240.1111, y: 630.1111 },
|
||||
{ x: 640.1111, y: 630.1111 },
|
||||
{ x: 640.1111, y: 480.1111 },
|
||||
{ x: 440.1111, y: 480.1111 },
|
||||
{ x: 440.1111, y: 280.1111 },
|
||||
{ x: 740.1111, y: 280.1111 },
|
||||
{ x: 740.1111, y: 130.1111 },
|
||||
]
|
||||
|
||||
const eightPoint2 = [
|
||||
@ -207,25 +207,75 @@ export default function Roof2() {
|
||||
{ x: 897, y: 215 },
|
||||
]
|
||||
|
||||
if (canvas) {
|
||||
const polygon = new QPolygon(eightPoint, {
|
||||
fill: 'transparent',
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
fontSize: fontSize,
|
||||
name: 'QPolygon1',
|
||||
})
|
||||
const eightPoint3 = [
|
||||
{ x: 190, y: 147 },
|
||||
{ x: 190, y: 747 },
|
||||
{ x: 490, y: 747 },
|
||||
{ x: 490, y: 497 },
|
||||
{ x: 640, y: 497 },
|
||||
{ x: 640, y: 747 },
|
||||
{ x: 1090, y: 747 },
|
||||
{ x: 1090, y: 147 },
|
||||
]
|
||||
|
||||
canvas?.add(polygon)
|
||||
const eightPoint4 = [
|
||||
{ x: 228, y: 92 },
|
||||
{ x: 228, y: 592 },
|
||||
{ x: 478, y: 592 },
|
||||
{ x: 478, y: 342 },
|
||||
{ x: 728, y: 342 },
|
||||
{ x: 728, y: 592 },
|
||||
{ x: 1078, y: 592 },
|
||||
{ x: 1078, y: 92 },
|
||||
]
|
||||
|
||||
console.log(polygon)
|
||||
const twelvePoint = [
|
||||
{ x: 195, y: 166 },
|
||||
{ x: 195, y: 466 },
|
||||
{ x: 395, y: 466 },
|
||||
{ x: 395, y: 766 },
|
||||
{ x: 545, y: 766 },
|
||||
{ x: 545, y: 466 },
|
||||
{ x: 695, y: 466 },
|
||||
{ x: 695, y: 666 },
|
||||
{ x: 845, y: 666 },
|
||||
{ x: 845, y: 466 },
|
||||
{ x: 995, y: 466 },
|
||||
{ x: 995, y: 166 },
|
||||
]
|
||||
|
||||
handleOuterlinesTest2(polygon)
|
||||
const complicatedType = [
|
||||
{ x: 100, y: 100 },
|
||||
{ x: 100, y: 1100 },
|
||||
{ x: 400, y: 1100 },
|
||||
{ x: 400, y: 800 },
|
||||
{ x: 700, y: 800 },
|
||||
{ x: 700, y: 1100 },
|
||||
{ x: 1000, y: 1100 },
|
||||
{ x: 1000, y: 600 },
|
||||
{ x: 700, y: 600 },
|
||||
{ x: 700, y: 300 },
|
||||
{ x: 1000, y: 300 },
|
||||
{ x: 1000, y: 100 },
|
||||
]
|
||||
|
||||
// const lines = togglePolygonLine(polygon)
|
||||
// togglePolygonLine(lines[0])
|
||||
}
|
||||
const types = [type1, type2, type3, type4, type1A, type1B, eightPoint, eightPoint2, eightPoint3, eightPoint4, twelvePoint]
|
||||
|
||||
const polygon = new QPolygon(type1B, {
|
||||
fill: 'transparent',
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
fontSize: fontSize,
|
||||
name: 'QPolygon1',
|
||||
})
|
||||
|
||||
canvas?.add(polygon)
|
||||
|
||||
handleOuterlinesTest2(polygon)
|
||||
|
||||
// const lines = togglePolygonLine(polygon)
|
||||
// togglePolygonLine(lines[0])
|
||||
}
|
||||
|
||||
const rotateShape = () => {
|
||||
@ -241,36 +291,26 @@ export default function Roof2() {
|
||||
|
||||
const makeQLine = () => {
|
||||
if (canvas) {
|
||||
const line = new QLine(
|
||||
[50, 250, 900, 250],
|
||||
{
|
||||
stroke: 'black',
|
||||
strokeWidth: 5,
|
||||
fontSize: fontSize,
|
||||
selectable: true,
|
||||
},
|
||||
50,
|
||||
)
|
||||
const line = new QLine([50, 250, 900, 250], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 5,
|
||||
fontSize: fontSize,
|
||||
selectable: true,
|
||||
})
|
||||
|
||||
const line2 = new QLine(
|
||||
[450, 450, 821, 78],
|
||||
{
|
||||
stroke: 'black',
|
||||
strokeWidth: 5,
|
||||
fontSize: fontSize,
|
||||
selectable: true,
|
||||
},
|
||||
50,
|
||||
)
|
||||
const line2 = new QLine([450, 450, 821, 78], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 5,
|
||||
fontSize: fontSize,
|
||||
selectable: true,
|
||||
})
|
||||
|
||||
canvas?.add(line)
|
||||
canvas?.add(line2)
|
||||
|
||||
const interSectionPoint = calculateIntersection2(line, line2)
|
||||
const interSectionPoint = calculateIntersection(line, line2)
|
||||
|
||||
if (interSectionPoint) {
|
||||
console.log(interSectionPoint)
|
||||
|
||||
const circle = new fabric.Circle({
|
||||
radius: 5,
|
||||
fill: 'red',
|
||||
@ -376,6 +416,13 @@ export default function Roof2() {
|
||||
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => drawRoofPatterns(2)}>
|
||||
지붕패턴2
|
||||
</Button>
|
||||
<Button
|
||||
className="m-1 p-2"
|
||||
color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`}
|
||||
onClick={() => changeMode(canvas, Mode.ROOT_TRESTLE)}
|
||||
>
|
||||
지붕가대생성
|
||||
</Button>
|
||||
<Button className="m-1 p-2" color={`${mode === Mode.TEXTBOX ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.TEXTBOX)}>
|
||||
텍스트박스 모드
|
||||
</Button>
|
||||
|
||||
@ -1,112 +1,87 @@
|
||||
import { fabric } from 'fabric'
|
||||
import { getDirection, getDirectionByPoint } from '@/util/canvas-util'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { getDirection } from '@/util/canvas-util'
|
||||
|
||||
export class QLine extends fabric.Group {
|
||||
line
|
||||
text
|
||||
fontSize
|
||||
length = 0
|
||||
x1
|
||||
y1
|
||||
x2
|
||||
y2
|
||||
direction
|
||||
idx
|
||||
type = 'QLine'
|
||||
parent
|
||||
isAlreadyInterSection = false
|
||||
|
||||
interSectionPoints = []
|
||||
lengthTxt = 0
|
||||
|
||||
initPoints
|
||||
initOption
|
||||
initLengthTxt
|
||||
|
||||
constructor(points, option = { isActiveLengthText: true }, lengthTxt) {
|
||||
export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
type: 'QLine',
|
||||
text: null,
|
||||
id: null,
|
||||
line: null,
|
||||
length: 0,
|
||||
direction: null,
|
||||
idx: 0,
|
||||
initialize: function (points, options, canvas) {
|
||||
this.callSuper('initialize', points, options)
|
||||
if (options.id) {
|
||||
this.id = options.id
|
||||
} else {
|
||||
this.id = uuidv4()
|
||||
}
|
||||
this.line = this
|
||||
// 소수점 전부 제거
|
||||
points.forEach((point) => {
|
||||
point = Math.round(point)
|
||||
})
|
||||
|
||||
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], {})
|
||||
|
||||
this.initPoints = points
|
||||
this.initOption = option
|
||||
this.initLengthTxt = lengthTxt
|
||||
|
||||
this.x1 = x1
|
||||
this.y1 = y1
|
||||
this.x2 = x2
|
||||
this.y2 = y2
|
||||
this.line = line
|
||||
this.fontSize = option.fontSize
|
||||
this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 })
|
||||
this.parent = option.parent
|
||||
this.idx = option.idx
|
||||
|
||||
if (lengthTxt > 0) {
|
||||
this.lengthTxt = Number(lengthTxt)
|
||||
}
|
||||
|
||||
option.isActiveLengthText ?? this.init()
|
||||
this.addControl()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.addLengthText(true)
|
||||
}
|
||||
|
||||
addControl() {
|
||||
this.on('moving', () => {
|
||||
this.addLengthText(false)
|
||||
})
|
||||
|
||||
this.on('modified', (e) => {
|
||||
this.addLengthText(false)
|
||||
})
|
||||
|
||||
this.on('selected', () => {
|
||||
console.log(this)
|
||||
Object.keys(this.controls).forEach((controlKey) => {
|
||||
if (controlKey !== 'ml' && controlKey !== 'mr') {
|
||||
this.setControlVisible(controlKey, false)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
addLengthText(isFirst) {
|
||||
if (this.text) {
|
||||
this.removeWithUpdate(this.text)
|
||||
this.text = null
|
||||
}
|
||||
|
||||
if (isFirst && this.lengthTxt > 0) {
|
||||
const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), {
|
||||
left: (this.x1 + this.x2) / 2,
|
||||
top: (this.y1 + this.y2) / 2,
|
||||
fontSize: this.fontSize,
|
||||
})
|
||||
this.length = this.lengthTxt
|
||||
this.text = text
|
||||
this.addWithUpdate(text)
|
||||
return
|
||||
}
|
||||
|
||||
const scaleX = this.scaleX
|
||||
const scaleY = this.scaleY
|
||||
const x1 = this.left
|
||||
const y1 = this.top
|
||||
const x2 = this.left + this.width * scaleX
|
||||
const y2 = this.top + this.height * scaleY
|
||||
this.idx = options.idx ?? 0
|
||||
const dx = x2 - x1
|
||||
const dy = y2 - y1
|
||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0))
|
||||
|
||||
this.direction = options.direction ?? getDirection({ x: this.x1, y: this.y1 }, { x: this.x2, y: this.y2 })
|
||||
|
||||
if (canvas) {
|
||||
this.canvas = canvas
|
||||
}
|
||||
},
|
||||
|
||||
toObject: function (propertiesToInclude) {
|
||||
return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), {
|
||||
type: this.type,
|
||||
text: this.text,
|
||||
})
|
||||
},
|
||||
init: function () {
|
||||
this.addLengthText()
|
||||
|
||||
this.on('moving', () => {
|
||||
this.addLengthText()
|
||||
})
|
||||
|
||||
this.on('modified', (e) => {
|
||||
this.addLengthText()
|
||||
})
|
||||
|
||||
this.on('removed', () => {
|
||||
const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id)
|
||||
if (thisText) {
|
||||
this.canvas.remove(thisText)
|
||||
}
|
||||
this.text = null
|
||||
})
|
||||
},
|
||||
|
||||
addLengthText() {
|
||||
const thisText = this.canvas.getObjects().find((obj) => obj.name === 'lengthText' && obj.parentId === this.id)
|
||||
|
||||
const scaleX = this.scaleX
|
||||
const scaleY = this.scaleY
|
||||
const x1 = this.left
|
||||
const y1 = this.top
|
||||
const x2 = this.left + this.width * scaleX
|
||||
const y2 = this.top + this.height * scaleY
|
||||
|
||||
if (thisText) {
|
||||
thisText.set({ text: this.length.toFixed(0).toString(), left: (x1 + x2) / 2, top: (y1 + y2) / 2 })
|
||||
return
|
||||
}
|
||||
|
||||
const dx = x2 - x1
|
||||
const dy = y2 - y1
|
||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0))
|
||||
@ -115,51 +90,27 @@ export class QLine extends fabric.Group {
|
||||
left: (x1 + x2) / 2,
|
||||
top: (y1 + y2) / 2,
|
||||
fontSize: this.fontSize,
|
||||
selectable: false,
|
||||
parentId: this.id,
|
||||
name: 'lengthText',
|
||||
})
|
||||
this.text = text
|
||||
this.addWithUpdate(text)
|
||||
}
|
||||
|
||||
this.text = text
|
||||
this.canvas.add(text)
|
||||
},
|
||||
setFontSize(fontSize) {
|
||||
this.fontSize = fontSize
|
||||
this.text.set({ fontSize })
|
||||
this.addWithUpdate()
|
||||
}
|
||||
|
||||
fromObject(object, callback) {
|
||||
console.log('fromObject', object, callback)
|
||||
}
|
||||
|
||||
async = true
|
||||
|
||||
toObject(propertiesToInclude) {
|
||||
return fabric.util.object.extend(this.callSuper('toObject'), {
|
||||
length: this.length,
|
||||
line: this.line,
|
||||
text: this.text,
|
||||
fontSize: this.fontSize,
|
||||
x1: this.x1,
|
||||
y1: this.y1,
|
||||
x2: this.x2,
|
||||
y2: this.y2,
|
||||
direction: this.direction,
|
||||
idx: this.idx,
|
||||
type: this.type,
|
||||
parent: this.parent,
|
||||
isAlreadyInterSection: this.isAlreadyInterSection,
|
||||
interSectionPoints: this.interSectionPoints,
|
||||
lengthTxt: this.lengthTxt,
|
||||
setFontSize: this.setFontSize,
|
||||
addLengthText: this.addLengthText,
|
||||
init: this.init,
|
||||
addControl: this.addControl,
|
||||
initPoints: this.initPoints,
|
||||
initOption: this.initOption,
|
||||
initLengthTxt: this.initLengthTxt,
|
||||
})
|
||||
}
|
||||
|
||||
_set(key, value) {
|
||||
},
|
||||
_render: function (ctx) {
|
||||
this.callSuper('_render', ctx)
|
||||
this.init()
|
||||
},
|
||||
_set: function (key, value) {
|
||||
this.callSuper('_set', key, value)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setCanvas(canvas) {
|
||||
this.canvas = canvas
|
||||
},
|
||||
})
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,9 +4,9 @@ import { actionHandler, anchorWrapper, polygonPositionHandler } from '@/util/can
|
||||
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { canvasSizeState, fontSizeState } from '@/store/canvasAtom'
|
||||
import QPolygon from '@/components/fabric/QPolygon'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import QRect from '@/components/fabric/QRect'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
|
||||
export function useCanvas(id) {
|
||||
const [canvas, setCanvas] = useState()
|
||||
@ -110,28 +110,25 @@ export function useCanvas(id) {
|
||||
fabric.Object.prototype.cornerStyle = 'rect'
|
||||
fabric.Object.prototype.cornerStrokeColor = '#2BEBC8'
|
||||
fabric.Object.prototype.cornerSize = 6
|
||||
|
||||
fabric.QLine = QLine
|
||||
fabric.QPolygon = QPolygon
|
||||
QPolygon.prototype.canvas = canvas
|
||||
QLine.prototype.canvas = canvas
|
||||
QRect.prototype.canvas = canvas
|
||||
|
||||
fabric.QLine = fabric.util.createClass(fabric.Group, {})
|
||||
fabric.QPolygon = fabric.util.createClass(fabric.Group, {})
|
||||
|
||||
// fromObject 메서드를 QLine 클래스에 직접 추가
|
||||
fabric.QLine.fromObject = function (object, callback) {
|
||||
const { initOption, initPoints, initLengthTxt } = object
|
||||
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
|
||||
return callback(new QLine(initPoints, initOption, initLengthTxt))
|
||||
})
|
||||
function _callback(instance) {
|
||||
delete instance.points
|
||||
callback && callback(instance)
|
||||
}
|
||||
const options = fabric.util.object.clone(object, true)
|
||||
options.points = [object.x1, object.y1, object.x2, object.y2]
|
||||
|
||||
fabric.Object._fromObject('QLine', options, _callback, 'points')
|
||||
}
|
||||
|
||||
// fromObject 메서드를 QLine 클래스에 직접 추가
|
||||
fabric.QPolygon.fromObject = function (object, callback) {
|
||||
const { initOption, initPoints, initLengthTxt } = object
|
||||
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
|
||||
return callback(new QPolygon(initPoints, initOption, initLengthTxt))
|
||||
})
|
||||
fabric.Object._fromObject('QPolygon', object, callback, 'points')
|
||||
}
|
||||
}
|
||||
|
||||
@ -556,44 +553,19 @@ export function useCanvas(id) {
|
||||
const addCanvas = () => {
|
||||
// const canvasState = canvas
|
||||
|
||||
const objs = canvas
|
||||
const objs = canvas?.toJSON(['selectable', 'name', 'parentId', 'id', 'length', 'idx', 'direction', 'lines', 'points'])
|
||||
|
||||
console.log(objs)
|
||||
const str = JSON.stringify(objs)
|
||||
|
||||
canvas?.clear()
|
||||
|
||||
console.log(str)
|
||||
console.log(JSON.parse(str))
|
||||
|
||||
// 역직렬화하여 캔버스에 객체를 다시 추가합니다.
|
||||
setTimeout(() => {
|
||||
canvas?.loadFromJSON(
|
||||
JSON.parse(str),
|
||||
function () {
|
||||
// 모든 객체가 로드되고 캔버스에 추가된 후 호출됩니다.
|
||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||
console.log('Objects are reloaded and rendered on canvas.')
|
||||
},
|
||||
function (o, object) {
|
||||
// 각 객체가 로드될 때마다 호출됩니다.
|
||||
console.log('Object loaded: ', o, object)
|
||||
|
||||
canvas?.add(object)
|
||||
canvas?.renderAll()
|
||||
},
|
||||
)
|
||||
// 역직렬화하여 캔버스에 객체를 다시 추가합니다.
|
||||
canvas?.loadFromJSON(JSON.parse(str), function () {
|
||||
// 모든 객체가 로드되고 캔버스에 추가된 후 호출됩니다.
|
||||
canvas?.renderAll() // 캔버스를 다시 그립니다.
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
/*canvas?.loadFromJSON(JSON.parse(str), () => {
|
||||
console.log('load done')
|
||||
})*/
|
||||
|
||||
/*const stateArr = canvasState.map((state) => state.getObject().getObjects())
|
||||
|
||||
const newCanvasList = [...canvasList, JSON.stringify(stateArr)]
|
||||
|
||||
setCanvasList(newCanvasList)*/
|
||||
}
|
||||
|
||||
const changeCanvas = (idx) => {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -41,3 +41,15 @@ export const roofPolygonPatternArrayState = atom({
|
||||
default: {}, //object ex) big, mid, sht = {point : [{x1, y1}, {x2, y1}], direction : left or right or top or bottom}
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const roofPolygonArrayState = atom({
|
||||
key: 'roofPolygonArray',
|
||||
default: [],
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const templateTypeState = atom({
|
||||
key: 'templateType',
|
||||
default: 1, //1:모임지붕, 2:A타입, 3:B타입
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
@ -292,56 +292,6 @@ export const getDirectionByPoint = (a, b) => {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 두 선분의 교차점을 찾는 함수입니다. 이 함수는 두 선분이 실제로 교차하는 지점 내에서 교차하는지 확인합니다.
|
||||
* @param {Object} line1 첫 번째 선분, {x1, y1, x2, y2} 형태의 객체
|
||||
* @param {Object} line2 두 번째 선분, {x1, y1, x2, y2} 형태의 객체
|
||||
* @returns {{x: number, y: number}|null} 교차점의 좌표를 반환하거나, 교차점이 없으면 null을 반환합니다.
|
||||
*/
|
||||
export function calculateIntersection(line1, line2) {
|
||||
const { x1: x1_1, y1: y1_1, x2: x2_1, y2: y2_1 } = line1
|
||||
const { x1: x1_2, y1: y1_2, x2: x2_2, y2: y2_2 } = line2
|
||||
|
||||
const denominator = (x1_1 - x2_1) * (y1_2 - y2_2) - (y1_1 - y2_1) * (x1_2 - x2_2)
|
||||
if (denominator === 0) return null // 선분이 평행하거나 일치하는 경우
|
||||
|
||||
const t = ((x1_1 - x1_2) * (y1_2 - y2_2) - (y1_1 - y1_2) * (x1_2 - x2_2)) / denominator
|
||||
const u = -((x1_1 - x2_1) * (y1_1 - y1_2) - (y1_1 - y2_1) * (x1_1 - x1_2)) / denominator
|
||||
|
||||
// t와 u가 모두 0과 1 사이에 있을 때, 선분 내에서 교차
|
||||
if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
|
||||
const intersectionX = x1_1 + t * (x2_1 - x1_1)
|
||||
const intersectionY = y1_1 + t * (y2_1 - y1_1)
|
||||
|
||||
// Determine the min and max for line1 and line2 for both x and y
|
||||
const line1MinX = Math.min(line1.x1, line1.x2)
|
||||
const line1MaxX = Math.max(line1.x1, line1.x2)
|
||||
const line2MinX = Math.min(line2.x1, line2.x2)
|
||||
const line2MaxX = Math.max(line2.x1, line2.x2)
|
||||
|
||||
const line1MinY = Math.min(line1.y1, line1.y2)
|
||||
const line1MaxY = Math.max(line1.y1, line1.y2)
|
||||
const line2MinY = Math.min(line2.y1, line2.y2)
|
||||
const line2MaxY = Math.max(line2.y1, line2.y2)
|
||||
|
||||
// 교차점이 선분의 범위 내에 있는지 확인
|
||||
if (
|
||||
intersectionX >= line1MinX &&
|
||||
intersectionX <= line1MaxX &&
|
||||
intersectionX >= line2MinX &&
|
||||
intersectionX <= line2MaxX &&
|
||||
intersectionY >= line1MinY &&
|
||||
intersectionY <= line1MaxY &&
|
||||
intersectionY >= line2MinY &&
|
||||
intersectionY <= line2MaxY
|
||||
) {
|
||||
return { x: Math.round(intersectionX), y: Math.round(intersectionY) }
|
||||
}
|
||||
}
|
||||
|
||||
return null // 교차점이 선분의 범위 내에 없음
|
||||
}
|
||||
|
||||
export const findIntersection1 = (line1, line2) => {
|
||||
const { x1, y1, x2, y2 } = line1 // 첫 번째 선의 두 점
|
||||
|
||||
@ -372,7 +322,7 @@ export const findIntersection1 = (line1, line2) => {
|
||||
return { x, y }
|
||||
}
|
||||
|
||||
export const calculateIntersection2 = (line1, line2) => {
|
||||
export const calculateIntersection = (line1, line2) => {
|
||||
const result = intersect([line1.x1, line1.y1], [line1.x2, line1.y2], [line2.x1, line2.y1], [line2.x2, line2.y2])
|
||||
|
||||
if (!result) {
|
||||
@ -424,6 +374,55 @@ export function findOrthogonalPoint(line1, line2) {
|
||||
* @param points
|
||||
*/
|
||||
export const sortedPoints = (points) => {
|
||||
const copyPoints = [...points]
|
||||
copyPoints.forEach((point) => {
|
||||
point.x1 = point.x
|
||||
point.y1 = point.y
|
||||
const nextPoint = copyPoints[(copyPoints.indexOf(point) + 1) % copyPoints.length]
|
||||
point.x2 = nextPoint.x
|
||||
point.y2 = nextPoint.y
|
||||
})
|
||||
|
||||
// copyPoint에서 x1, y1 값을 기준으로 시작 인덱스
|
||||
const startIndex = getStartIndex(copyPoints)
|
||||
const startDirection = getDirectionByPoint(
|
||||
{ x: copyPoints[startIndex].x1, y: copyPoints[startIndex].y1 },
|
||||
{ x: copyPoints[startIndex].x2, y: copyPoints[startIndex].y2 },
|
||||
)
|
||||
|
||||
const resultPoints = [copyPoints[startIndex]]
|
||||
|
||||
let currentPoint = copyPoints[startIndex]
|
||||
|
||||
switch (startDirection) {
|
||||
case 'right': {
|
||||
copyPoints.forEach((point, index) => {
|
||||
if (index === startIndex) return
|
||||
|
||||
const nextPoint = copyPoints.find((p) => p.x2 === currentPoint.x && p.y2 === currentPoint.y)
|
||||
resultPoints.push(nextPoint)
|
||||
currentPoint = nextPoint
|
||||
})
|
||||
break
|
||||
}
|
||||
case 'bottom': {
|
||||
copyPoints.forEach((point, index) => {
|
||||
if (index === startIndex) return
|
||||
|
||||
const nextPoint = copyPoints.find((p) => p.x1 === currentPoint.x2 && p.y1 === currentPoint.y2)
|
||||
resultPoints.push(nextPoint)
|
||||
currentPoint = nextPoint
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return resultPoints.map((point) => {
|
||||
return { x: point.x, y: point.y }
|
||||
})
|
||||
}
|
||||
|
||||
export const sortedPointLessEightPoint = (points) => {
|
||||
const copyPoints = [...points]
|
||||
//points를 x,y좌표를 기준으로 정렬합니다.
|
||||
copyPoints.sort((a, b) => {
|
||||
@ -433,10 +432,6 @@ export const sortedPoints = (points) => {
|
||||
return a.x - b.x
|
||||
})
|
||||
|
||||
// 이때 copyPoints를 순회하며 최초엔 x값을 비교하여 같은 점을 찾는다. 이때 이 점이 2번째 점이 된다.
|
||||
// 그 다음점은 2번째 점과 y값이 같은 점이 된다.
|
||||
// 또 그다음 점은 3번째 점과 x값이 같은 점이 된다.
|
||||
// 이를 반복하여 copyPoints를 재배열한다.
|
||||
const resultPoints = [copyPoints[0]]
|
||||
let index = 1
|
||||
let currentPoint = { ...copyPoints[0] }
|
||||
@ -649,3 +644,26 @@ export function removeDuplicatePoints(points) {
|
||||
|
||||
return uniquePoints
|
||||
}
|
||||
|
||||
/**
|
||||
* x,y가 다르면서 가장 가까운 점
|
||||
* @param targetPoint
|
||||
* @param points
|
||||
* @returns {null}
|
||||
*/
|
||||
export function findClosestPointWithDifferentXY(targetPoint, points) {
|
||||
let closestPoint = null
|
||||
let smallestDistance = Infinity
|
||||
|
||||
points.forEach((point) => {
|
||||
if (point.x !== targetPoint.x && point.y !== targetPoint.y) {
|
||||
const distance = Math.sqrt(Math.pow(point.x - targetPoint.x, 2) + Math.pow(point.y - targetPoint.y, 2))
|
||||
if (distance < smallestDistance) {
|
||||
smallestDistance = distance
|
||||
closestPoint = point
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return closestPoint
|
||||
}
|
||||
|
||||
@ -2,11 +2,8 @@ import { fabric } from 'fabric'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
|
||||
export const defineQLine = () => {
|
||||
fabric.QLine = fabric.util.createClass(fabric.Group, {})
|
||||
fabric.QLine.fromObject = function (object, callback) {
|
||||
const { initOption, initPoints, initLengthTxt } = object
|
||||
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
|
||||
return callback(new QLine(initPoints, initOption, initLengthTxt))
|
||||
})
|
||||
}
|
||||
/*fabric.QLine = QLine
|
||||
fabric.QLine.fromObject = (object, callback) => {
|
||||
return new fabric.QLine([object.x1, object.y1, object.x2, object.y2], object)
|
||||
}*/
|
||||
}
|
||||
|
||||
@ -1,21 +1,312 @@
|
||||
import { fabric } from 'fabric'
|
||||
import QPolygon from '@/components/fabric/QPolygon'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { calculateIntersection, calculateIntersection2, distanceBetweenPoints, findIntersection1, removeDuplicatePoints } from '@/util/canvas-util'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
|
||||
|
||||
export const defineQPloygon = () => {
|
||||
fabric.QPolygon = fabric.util.createClass(fabric.Group, {})
|
||||
// fromObject 메서드를 QLine 클래스에 직접 추가
|
||||
fabric.QPolygon.fromObject = function (object, callback) {
|
||||
const { initOption, initPoints, initLengthTxt } = object
|
||||
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
|
||||
return callback(new QPolygon(initPoints, initOption, initLengthTxt))
|
||||
})
|
||||
fabric.Object._fromObject('QPolygon', object, callback, 'points')
|
||||
}
|
||||
}
|
||||
|
||||
export const drawHelpLineInHexagon2 = (polygon, chon) => {
|
||||
const oneSideLines = [...polygon.lines].map((line) => {
|
||||
export const drawHelpLineInHexagon = (polygon, chon) => {
|
||||
const centerLines = drawCenterLines(polygon)
|
||||
|
||||
let helpLines = []
|
||||
|
||||
const interSectionPoints = []
|
||||
const tempInterSectionPoints = []
|
||||
|
||||
const ridgeStartPoints = []
|
||||
const ridgeEndPoints = []
|
||||
|
||||
const centerInterSectionPoints = []
|
||||
|
||||
// polygon.lines = polygon.lines.sort((a, b) => a.length - b.length)
|
||||
polygon.wall.lines = getOneSideLines(polygon.wall)
|
||||
|
||||
const maxLength = Math.max(...polygon.lines.map((line) => line.length))
|
||||
|
||||
polygon.points.forEach((point, index) => {
|
||||
const wallPoint = polygon.wall.points[index]
|
||||
|
||||
const angle = Math.atan2(wallPoint.y - point.y, wallPoint.x - point.x)
|
||||
|
||||
const degree = fabric.util.radiansToDegrees(angle)
|
||||
|
||||
const newX2 = Math.floor(point.x + maxLength * Math.cos(angle))
|
||||
const newY2 = Math.floor(point.y + maxLength * Math.sin(angle))
|
||||
|
||||
const helpLine = new QLine([point.x, point.y, newX2, newY2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'green',
|
||||
startPoint: point,
|
||||
degree: degree,
|
||||
idx: index,
|
||||
})
|
||||
|
||||
// polygon.canvas?.add(helpLine)
|
||||
|
||||
helpLines.push(helpLine)
|
||||
})
|
||||
|
||||
helpLines.forEach((line, index) => {
|
||||
for (let i = index + 1; i < helpLines.length; i++) {
|
||||
const nextLine = helpLines[i]
|
||||
if (!line.connectedPoint) {
|
||||
line.connectedPoint = null
|
||||
line.connectedPoints = []
|
||||
}
|
||||
if (!nextLine.connectedPoint) {
|
||||
nextLine.connectedPoint = null
|
||||
nextLine.connectedPoints = []
|
||||
}
|
||||
|
||||
const interSectionPoint = calculateIntersection(line, nextLine)
|
||||
|
||||
if (
|
||||
interSectionPoint &&
|
||||
polygon.inPolygon(interSectionPoint) &&
|
||||
polygon.wall.inPolygon(interSectionPoint) &&
|
||||
Math.abs(distanceBetweenPoints(line.startPoint, interSectionPoint) - distanceBetweenPoints(nextLine.startPoint, interSectionPoint)) < 2
|
||||
) {
|
||||
const area = calculateTriangleArea(line.startPoint, nextLine.startPoint, interSectionPoint)
|
||||
const currentLineConnectedPoint = line.connectedPoint
|
||||
const nextLineConnectedPoint = nextLine.connectedPoint
|
||||
|
||||
if (area <= 1) {
|
||||
return
|
||||
}
|
||||
|
||||
if (currentLineConnectedPoint && currentLineConnectedPoint.area < area) {
|
||||
return
|
||||
}
|
||||
|
||||
//startPoint는 line의 startPoint와 nextLine의 startPoint를 비교하여 x가 같은경우 y가 더 작은 값, y가 같은경우 x가 더 작은 값을 선택한다.
|
||||
const startPoint =
|
||||
line.startPoint.x === nextLine.startPoint.x
|
||||
? line.startPoint.y < nextLine.startPoint.y
|
||||
? line.startPoint
|
||||
: nextLine.startPoint
|
||||
: line.startPoint.x < nextLine.startPoint.x
|
||||
? line.startPoint
|
||||
: nextLine.startPoint
|
||||
|
||||
const endPoint =
|
||||
line.startPoint.x === nextLine.startPoint.x
|
||||
? line.startPoint.y > nextLine.startPoint.y
|
||||
? line.startPoint
|
||||
: nextLine.startPoint
|
||||
: line.startPoint.x > nextLine.startPoint.x
|
||||
? line.startPoint
|
||||
: nextLine.startPoint
|
||||
|
||||
line.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
|
||||
line.connectedPoints.push(interSectionPoint)
|
||||
nextLine.connectedPoint = { interSectionPoint, area, startPoint, endPoint }
|
||||
nextLine.connectedPoints.push(interSectionPoint)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
helpLines.forEach((line) => {
|
||||
if (line.connectedPoint) {
|
||||
tempInterSectionPoints.push(line.connectedPoint)
|
||||
}
|
||||
})
|
||||
|
||||
// interSectionPoints에서 interSectionPoint가 중복인 값이 있는 경우만 선택한다.
|
||||
tempInterSectionPoints.forEach((point) => {
|
||||
// intersectionPoint가 중복인 경우
|
||||
const isDuplicated =
|
||||
tempInterSectionPoints.filter((p) => p.interSectionPoint.x === point.interSectionPoint.x && p.interSectionPoint.y === point.interSectionPoint.y)
|
||||
.length > 1
|
||||
if (isDuplicated) {
|
||||
interSectionPoints.push(point)
|
||||
}
|
||||
})
|
||||
|
||||
// interSectionPoints에서 interSectionPoint 기준으로 중복을 제거한다.
|
||||
const uniqueInterSectionPoints = Array.from(
|
||||
new Set(interSectionPoints.map((point) => `${point.interSectionPoint.x},${point.interSectionPoint.y}`)),
|
||||
).map((key) => {
|
||||
const { interSectionPoint, area, startPoint, endPoint } = interSectionPoints.find(
|
||||
(point) => `${point.interSectionPoint.x},${point.interSectionPoint.y}` === key,
|
||||
)
|
||||
return { interSectionPoint, area, startPoint, endPoint }
|
||||
})
|
||||
|
||||
uniqueInterSectionPoints.forEach((point) => {
|
||||
ridgeStartPoints.push(point.interSectionPoint)
|
||||
|
||||
const line = new QLine([point.startPoint.x, point.startPoint.y, point.interSectionPoint.x, point.interSectionPoint.y], {
|
||||
stroke: 'purple',
|
||||
fontSize: polygon.fontSize,
|
||||
name: 'hip',
|
||||
})
|
||||
|
||||
const line2 = new QLine([point.endPoint.x, point.endPoint.y, point.interSectionPoint.x, point.interSectionPoint.y], {
|
||||
stroke: 'purple',
|
||||
fontSize: polygon.fontSize,
|
||||
name: 'hip',
|
||||
})
|
||||
|
||||
polygon.hips.push(line)
|
||||
polygon.hips.push(line2)
|
||||
|
||||
polygon.canvas.add(line)
|
||||
polygon.canvas.add(line2)
|
||||
})
|
||||
|
||||
const removedIdx = []
|
||||
|
||||
helpLines.forEach((line) => {
|
||||
const connectedPoints = line.connectedPoints
|
||||
connectedPoints.forEach((connectedPoint) => {
|
||||
uniqueInterSectionPoints.forEach((point) => {
|
||||
const interSectionPoint = point.interSectionPoint
|
||||
|
||||
if (connectedPoint.x === interSectionPoint.x && connectedPoint.y === interSectionPoint.y) {
|
||||
removedIdx.push(line.idx)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const notIntersectedLines = helpLines.filter((line) => !removedIdx.includes(line.idx))
|
||||
|
||||
notIntersectedLines.forEach((line) => {
|
||||
centerLines.forEach((centerLine) => {
|
||||
const interSectionPoint = calculateIntersection(line, centerLine)
|
||||
|
||||
if (interSectionPoint && polygon.inPolygon(interSectionPoint) && polygon.wall.inPolygon(interSectionPoint)) {
|
||||
centerInterSectionPoints.push(interSectionPoint)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// centerInterSectionPoints에서 ridgeStartPoints와 x가 같거나 y가 같은것중 가장 가까운 점들을 찾는다.
|
||||
ridgeStartPoints.forEach((point) => {
|
||||
const xPoints = centerInterSectionPoints.filter((centerPoint) => Math.abs(centerPoint.x - point.x) < 2)
|
||||
const yPoints = centerInterSectionPoints.filter((centerPoint) => Math.abs(centerPoint.y - point.y) < 2)
|
||||
let closestPoint
|
||||
if (xPoints.length === 0) {
|
||||
closestPoint = findClosestPoint(point, yPoints)
|
||||
} else {
|
||||
closestPoint = findClosestPoint(point, xPoints)
|
||||
}
|
||||
|
||||
if (closestPoint) {
|
||||
const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], {
|
||||
stroke: 'purple',
|
||||
fontSize: polygon.fontSize,
|
||||
name: 'ridge',
|
||||
})
|
||||
polygon.ridges.push(line)
|
||||
polygon.canvas.add(line)
|
||||
ridgeEndPoints.push(closestPoint)
|
||||
}
|
||||
})
|
||||
|
||||
// ridgeEndPoints끼리 이어준다.
|
||||
const remainingPoints = ridgeEndPoints
|
||||
|
||||
remainingPoints.forEach((ridgePoint) => {
|
||||
polygon.points.forEach((point) => {
|
||||
const degree = calculateAngle(ridgePoint, point)
|
||||
|
||||
if (Math.abs(degree) % 45 < 1) {
|
||||
const line = new QLine([ridgePoint.x, ridgePoint.y, point.x, point.y], {
|
||||
stroke: 'purple',
|
||||
fontSize: polygon.fontSize,
|
||||
name: 'hip',
|
||||
})
|
||||
|
||||
polygon.hips.push(line)
|
||||
polygon.canvas.add(line)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
while (remainingPoints.length > 0) {
|
||||
const point = remainingPoints.shift()
|
||||
const closestPoint = findClosestPoint(point, remainingPoints)
|
||||
if (!closestPoint) continue
|
||||
// 마루끼리 연결
|
||||
const line = new QLine([point.x, point.y, closestPoint.x, closestPoint.y], {
|
||||
stroke: 'purple',
|
||||
fontSize: polygon.fontSize,
|
||||
name: 'connectRidge',
|
||||
})
|
||||
polygon.connectRidges.push(line)
|
||||
|
||||
polygon.canvas.add(line)
|
||||
}
|
||||
}
|
||||
|
||||
export const drawCenterLines = (polygon) => {
|
||||
const centerLines = []
|
||||
|
||||
const oneSideLines = getOneSideLines(polygon)
|
||||
|
||||
const horizontalLines = oneSideLines.filter((line) => line.direction === 'right')
|
||||
const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom')
|
||||
// horizontalLines 를 y1 좌표 기준으로 정렬한다.
|
||||
horizontalLines.sort((a, b) => a.y1 - b.y1)
|
||||
// verticalLines 를 x1 좌표 기준으로 정렬한다.
|
||||
verticalLines.sort((a, b) => a.x1 - b.x1)
|
||||
|
||||
let maxHorizontalLineLength = 0
|
||||
let maxVerticalLineLength = 0
|
||||
// 모든 가로선의 중심선을 긋는다.
|
||||
horizontalLines.forEach((line, index) => {
|
||||
const nextLine = horizontalLines[(index + 1) % horizontalLines.length]
|
||||
|
||||
line.set({ strokeWidth: 5 })
|
||||
nextLine.set({ strokeWidth: 5 })
|
||||
|
||||
polygon.canvas.renderAll()
|
||||
|
||||
const startCenterX = Math.min(line.x1, nextLine.x1)
|
||||
const startCenterY = (line.y1 + nextLine.y1) / 2
|
||||
|
||||
const endCenterX = line.x2 > nextLine.x2 ? line.x2 : nextLine.x2
|
||||
const endCenterY = startCenterY
|
||||
|
||||
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
direction: 'horizontal',
|
||||
})
|
||||
|
||||
centerLines.push(centerLine)
|
||||
})
|
||||
|
||||
// 모든 세로선의 중심선을 긋는다.
|
||||
verticalLines.forEach((line, index) => {
|
||||
const nextLine = verticalLines[(index + 1) % verticalLines.length]
|
||||
|
||||
const startCenterX = (line.x1 + nextLine.x1) / 2
|
||||
const startCenterY = Math.min(line.y1, nextLine.y1)
|
||||
|
||||
const endCenterX = startCenterX
|
||||
let endCenterY = line.y2 > nextLine.y2 ? line.y2 : nextLine.y2
|
||||
|
||||
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
direction: 'vertical',
|
||||
})
|
||||
|
||||
centerLines.push(centerLine)
|
||||
})
|
||||
|
||||
return centerLines
|
||||
}
|
||||
|
||||
const getOneSideLines = (polygon) => {
|
||||
return [...polygon.lines].map((line) => {
|
||||
let newX1, newY1, newX2, newY2
|
||||
if (line.direction === 'top') {
|
||||
newX1 = line.x2
|
||||
@ -42,288 +333,45 @@ export const drawHelpLineInHexagon2 = (polygon, chon) => {
|
||||
}
|
||||
return line
|
||||
})
|
||||
|
||||
const centerLines = []
|
||||
const helpLines = []
|
||||
const ridgeStartPoints = []
|
||||
|
||||
const horizontalLines = oneSideLines.filter((line) => line.direction === 'right')
|
||||
const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom')
|
||||
// horizontalLines 를 y1 좌표 기준으로 정렬한다.
|
||||
horizontalLines.sort((a, b) => a.y1 - b.y1)
|
||||
// verticalLines 를 x1 좌표 기준으로 정렬한다.
|
||||
verticalLines.sort((a, b) => a.x1 - b.x1)
|
||||
|
||||
const maxHorizontalLineLength = horizontalLines.reduce((prev, current) => (prev.length > current.length ? prev.length : current.length))
|
||||
const maxVerticalLineLength = verticalLines.reduce((prev, current) => (prev.length > current.length ? prev.length : current.length))
|
||||
|
||||
// 모든 가로선의 중심선을 긋는다.
|
||||
horizontalLines.forEach((line, index) => {
|
||||
const nextLine = horizontalLines[(index + 1) % horizontalLines.length]
|
||||
|
||||
line.line.set({ strokeWidth: 5 })
|
||||
nextLine.line.set({ strokeWidth: 5 })
|
||||
|
||||
polygon.canvas.renderAll()
|
||||
|
||||
const startCenterX = Math.min(line.x1, nextLine.x1)
|
||||
const startCenterY = (line.y1 + nextLine.y1) / 2
|
||||
|
||||
const endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length
|
||||
const endCenterY = startCenterY
|
||||
|
||||
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
direction: 'horizontal',
|
||||
})
|
||||
|
||||
/*polygon.canvas.add(centerLine)
|
||||
polygon.canvas.renderAll()*/
|
||||
|
||||
centerLines.push(centerLine)
|
||||
})
|
||||
|
||||
// 모든 세로선의 중심선을 긋는다.
|
||||
verticalLines.forEach((line, index) => {
|
||||
const nextLine = verticalLines[(index + 1) % verticalLines.length]
|
||||
|
||||
const startCenterX = (line.x1 + nextLine.x1) / 2
|
||||
const startCenterY = Math.min(line.y1, nextLine.y1)
|
||||
|
||||
const endCenterX = startCenterX
|
||||
let endCenterY = line.length >= nextLine.length ? startCenterY + line.length : startCenterY + nextLine.length
|
||||
|
||||
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
direction: 'vertical',
|
||||
})
|
||||
|
||||
/*polygon.canvas.add(centerLine)
|
||||
polygon.canvas.renderAll()*/
|
||||
centerLines.push(centerLine)
|
||||
})
|
||||
|
||||
polygon.points.forEach((point, index) => {
|
||||
const wallPoint = polygon.wall.points[index]
|
||||
// 두 점의 좌표
|
||||
const x1 = point.x
|
||||
const y1 = point.y
|
||||
const x2 = wallPoint.x
|
||||
const y2 = wallPoint.y
|
||||
|
||||
let newX2, newY2
|
||||
|
||||
// x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
|
||||
const angle = Math.atan2(y2 - y1, x2 - x1)
|
||||
|
||||
let previousIndex = index === 0 ? polygon.lines.length - 1 : index - 1
|
||||
const maxLength = Math.max(polygon.lines[index].length, polygon.lines[previousIndex].length)
|
||||
|
||||
newX2 = Math.floor(x1 + (maxLength / 2 + polygon.points.length * 10) * Math.cos(angle))
|
||||
newY2 = Math.floor(y1 + (maxLength / 2 + polygon.points.length * 10) * Math.sin(angle))
|
||||
|
||||
const line = new QLine([x1, y1, newX2, newY2], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'green',
|
||||
idx: index,
|
||||
})
|
||||
line.set({ degree: fabric.util.radiansToDegrees(angle) })
|
||||
polygon.canvas.add(line)
|
||||
helpLines.push(line)
|
||||
polygon.canvas.renderAll()
|
||||
})
|
||||
|
||||
helpLines.forEach((line, index) => {
|
||||
for (let i = index + 1; i < helpLines.length; i++) {
|
||||
const nextLine = helpLines[i]
|
||||
|
||||
if (line.isAlreadyInterSection || nextLine.isAlreadyInterSection) {
|
||||
continue
|
||||
}
|
||||
|
||||
let intersectionPoint = calculateIntersection(line, nextLine)
|
||||
|
||||
if (!intersectionPoint) {
|
||||
continue
|
||||
}
|
||||
|
||||
const circle = new fabric.Circle({
|
||||
radius: 3,
|
||||
fill: 'red',
|
||||
left: intersectionPoint.x - 3,
|
||||
top: intersectionPoint.y - 3,
|
||||
})
|
||||
|
||||
polygon.canvas.add(circle)
|
||||
|
||||
line.set({ isAlreadyInterSection: true })
|
||||
nextLine.set({ isAlreadyInterSection: true })
|
||||
|
||||
const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'skyblue',
|
||||
})
|
||||
|
||||
const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'skyblue',
|
||||
})
|
||||
|
||||
ridgeStartPoints.push(intersectionPoint)
|
||||
polygon.canvas.add(helpLine1)
|
||||
polygon.canvas.add(helpLine2)
|
||||
polygon.canvas.remove(nextLine)
|
||||
polygon.canvas.remove(line)
|
||||
polygon.canvas.renderAll()
|
||||
}
|
||||
})
|
||||
|
||||
// 안만나는 선들
|
||||
const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection)
|
||||
const ridgeEndPoints = []
|
||||
let interSectionPoints = []
|
||||
|
||||
notInterSectionLines.forEach((line, index) => {
|
||||
let subCenterLines
|
||||
if (Math.abs(line.degree) < 90) {
|
||||
subCenterLines = centerLines.filter((centerLine) => centerLine.direction === 'vertical')
|
||||
} else {
|
||||
subCenterLines = centerLines.filter((centerLine) => centerLine.direction === 'horizontal')
|
||||
}
|
||||
|
||||
centerLines.forEach((centerLine) => {
|
||||
const interSectionPoint = calculateIntersection2(line, centerLine)
|
||||
|
||||
if (!interSectionPoint) {
|
||||
return
|
||||
}
|
||||
|
||||
ridgeStartPoints.forEach((point) => {
|
||||
line.interSectionPoints.push(interSectionPoint)
|
||||
interSectionPoints.push(interSectionPoint)
|
||||
|
||||
const newLine = new QLine([line.x1, line.y1, interSectionPoint.x, interSectionPoint.y], {
|
||||
stroke: 'black',
|
||||
fontSize: polygon.fontSize,
|
||||
})
|
||||
|
||||
const circle = new fabric.Circle({
|
||||
radius: 3,
|
||||
fill: 'blue',
|
||||
left: interSectionPoint.x - 3,
|
||||
top: interSectionPoint.y - 3,
|
||||
})
|
||||
polygon.canvas.add(circle)
|
||||
polygon.canvas.add(newLine)
|
||||
|
||||
polygon.canvas.remove(line)
|
||||
|
||||
line.set({ isAlreadyInterSection: true })
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
interSectionPoints = removeDuplicatePoints(interSectionPoints)
|
||||
|
||||
const startRidgePoint = ridgeStartPoints[0]
|
||||
|
||||
const endRidgePoint = ridgeStartPoints[ridgeStartPoints.length - 1]
|
||||
|
||||
let step = 0
|
||||
|
||||
while (true) {
|
||||
if (step % 2 === 0) {
|
||||
const nextPoint = interSectionPoints.find((point) => point.x === startRidgePoint.x || point.y === startRidgePoint.y)
|
||||
|
||||
if (nextPoint) {
|
||||
const ridge = new QLine([startRidgePoint.x, startRidgePoint.y, nextPoint.x, nextPoint.y], {
|
||||
stroke: 'green',
|
||||
fontSize: polygon.fontSize,
|
||||
})
|
||||
polygon.canvas.add(ridge)
|
||||
polygon.canvas.renderAll()
|
||||
}
|
||||
|
||||
console.log('nextPoint', nextPoint)
|
||||
console.log('startRidgePoint', startRidgePoint)
|
||||
} else {
|
||||
}
|
||||
|
||||
step++
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
/*ridgeStartPoints.forEach((point, index) => {
|
||||
for (let i = index + 1; i < ridgeStartPoints.length; i++) {
|
||||
const currentPoint = ridgeStartPoints[index]
|
||||
const nextPoint = ridgeStartPoints[i]
|
||||
|
||||
if (currentPoint.x === nextPoint.x || currentPoint.y === nextPoint.y) {
|
||||
const ridge = new QLine([currentPoint.x, currentPoint.y, nextPoint.x, nextPoint.y], {
|
||||
stroke: 'black',
|
||||
fontSize: polygon.fontSize,
|
||||
})
|
||||
polygon.canvas.add(ridge)
|
||||
polygon.canvas.renderAll()
|
||||
break
|
||||
}
|
||||
}
|
||||
})*/
|
||||
|
||||
/*ridgeStartPoints.forEach((point, index) => {
|
||||
let arrivalPoint
|
||||
let distance = Infinity
|
||||
let startPoint
|
||||
interSectionPoints.forEach((interSectionPoint) => {
|
||||
if (Math.abs(point.x - interSectionPoint.x) < 1 || Math.abs(point.y - interSectionPoint.y) < 1) {
|
||||
if (distanceBetweenPoints(point, interSectionPoint) < distance) {
|
||||
startPoint = point
|
||||
distance = distanceBetweenPoints(point, interSectionPoint)
|
||||
arrivalPoint = interSectionPoint
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (arrivalPoint) {
|
||||
const line = notInterSectionLines.filter((line) => line.interSectionPoints.includes(arrivalPoint))[0]
|
||||
|
||||
const ridge = new QLine([startPoint.x, startPoint.y, arrivalPoint.x, arrivalPoint.y], {
|
||||
stroke: 'black',
|
||||
fontSize: polygon.fontSize,
|
||||
})
|
||||
|
||||
const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], {
|
||||
stroke: 'red',
|
||||
fontSize: polygon.fontSize,
|
||||
})
|
||||
|
||||
ridgeEndPoints.push(arrivalPoint)
|
||||
polygon.canvas.add(ridge)
|
||||
polygon.canvas.add(helpLine)
|
||||
// polygon.canvas.remove(line)
|
||||
polygon.canvas.renderAll()
|
||||
debugger
|
||||
}
|
||||
})*/
|
||||
|
||||
/*for (let i = 0; i < ridgeEndPoints.length; i = i + 2) {
|
||||
const currentRidgeEndPoint = ridgeEndPoints[i]
|
||||
const nextRidgeEndPoint = ridgeEndPoints[(i + 1) % ridgeEndPoints.length]
|
||||
const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
|
||||
fontSize: polygon.fontSize,
|
||||
stroke: 'green',
|
||||
})
|
||||
|
||||
polygon.canvas.add(ridgeConnectLine)
|
||||
polygon.canvas.renderAll()
|
||||
}*/
|
||||
}
|
||||
const calculateAngle = (point1, point2) => {
|
||||
const deltaX = point2.x - point1.x
|
||||
const deltaY = point2.y - point1.y
|
||||
const angleInRadians = Math.atan2(deltaY, deltaX)
|
||||
return angleInRadians * (180 / Math.PI)
|
||||
}
|
||||
|
||||
export const drawHelpLineInHexagon = (polygon, chon) => {
|
||||
// 가장 긴라인을 기준으로 centerLine을 그린다.
|
||||
/**
|
||||
* 3개의 점을 이용해 직각 이등변 삼각형인지 확인
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @param point3
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isRightIsoscelesTriangle = (point1, point2, point3) => {
|
||||
const distance = (p1, p2) => Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2))
|
||||
|
||||
const d1 = distance(point1, point2)
|
||||
const d2 = distance(point2, point3)
|
||||
const d3 = distance(point3, point1)
|
||||
|
||||
const distances = [d1, d2, d3].sort((a, b) => a - b)
|
||||
|
||||
// Check if the two smaller distances are equal and the largest distance is the hypotenuse
|
||||
return distances[0] === distances[1] && Math.abs(Math.pow(distances[0], 2) * 2 - Math.pow(distances[2], 2)) < 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 세개의 점으로 삼각형의 넓이를 구한다.
|
||||
* @param point1
|
||||
* @param point2
|
||||
* @param point3
|
||||
* @returns {number}
|
||||
*/
|
||||
const calculateTriangleArea = (point1, point2, point3) => {
|
||||
const { x: x1, y: y1 } = point1
|
||||
const { x: x2, y: y2 } = point2
|
||||
const { x: x3, y: y3 } = point3
|
||||
|
||||
return Math.abs(x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2
|
||||
}
|
||||
|
||||
@ -3221,6 +3221,11 @@ postcss@^8, postcss@^8.4.23:
|
||||
picocolors "^1.0.0"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
prettier@^3.3.3:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.3.tgz#30c54fe0be0d8d12e6ae61dbb10109ea00d53105"
|
||||
integrity sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==
|
||||
|
||||
prisma@^5.17.0:
|
||||
version "5.17.0"
|
||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.17.0.tgz#267b43921ab94805b010537cffa5ccaf530fa066"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user