Merge branch 'feature/test'

This commit is contained in:
yoosangwook 2024-07-24 12:50:59 +09:00
commit 8d944afb0c
15 changed files with 2759 additions and 324 deletions

12
.env Normal file
View File

@ -0,0 +1,12 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
# DATABASE_URL="postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public"
# DATABASE_URL="mongodb://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/sample_mflix?retryWrites=true&w=majority"
#DATABASE_URL = "mongodb%2Bsrv%3A%2F%2Fyoo32767%3AGuCtswjLGqUaNL0G%40cluster0.vsdtcnb.mongodb.net%2F%3FretryWrites%3Dtrue%26w%3Dmajority%26appName%3DCluster0"
# DATABASE_URL = "mongodb+srv://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/Cluster0?retryWrites=true&w=majority"
# DATABASE_URL="mongodb://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/sample_mflix?retryWrites=true&w=majority"
DATABASE_URL="mongodb+srv://yoo32767:GuCtswjLGqUaNL0G@cluster0.vsdtcnb.mongodb.net/mytest"

View File

@ -10,9 +10,11 @@
}, },
"dependencies": { "dependencies": {
"@nextui-org/react": "^2.4.2", "@nextui-org/react": "^2.4.2",
"@prisma/client": "^5.17.0",
"fabric": "^5.3.0", "fabric": "^5.3.0",
"framer-motion": "^11.2.13", "framer-motion": "^11.2.13",
"mathjs": "^13.0.2", "mathjs": "^13.0.2",
"mongodb": "^6.8.0",
"next": "14.2.3", "next": "14.2.3",
"react": "^18", "react": "^18",
"react-dom": "^18", "react-dom": "^18",
@ -21,6 +23,7 @@
}, },
"devDependencies": { "devDependencies": {
"postcss": "^8", "postcss": "^8",
"prisma": "^5.17.0",
"tailwindcss": "^3.4.1" "tailwindcss": "^3.4.1"
} }
} }

19
prisma/schema.prisma Normal file
View File

@ -0,0 +1,19 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mongodb"
url = env("DATABASE_URL")
}
model test {
id String @id @default(auto()) @map("_id") @db.ObjectId
content String
}
model canvas {
id String @id @default(auto()) @map("_id") @db.ObjectId
loginId String
canvas Json
}

View File

@ -7,11 +7,13 @@ import QPolygon from '@/components/fabric/QPolygon'
import RangeSlider from './ui/RangeSlider' import RangeSlider from './ui/RangeSlider'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom' import { canvasAtom, canvasListState, canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas'
import { calculateIntersection2 } from '@/util/canvas-util'
export default function Roof2() { export default function Roof2() {
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage } = useCanvas('canvas') const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas')
//canvas //canvas
const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState) const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState)
@ -29,8 +31,22 @@ export default function Roof2() {
const [showControl, setShowControl] = useState(false) const [showControl, setShowControl] = useState(false)
const { mode, changeMode, handleClear, fillCellInPolygon, zoomIn, zoomOut, zoom, togglePolygonLine, handleOuterlinesTest2, applyTemplateB } = const {
useMode() mode,
changeMode,
handleClear,
fillCellInPolygon,
zoomIn,
zoomOut,
zoom,
togglePolygonLine,
handleOuterlinesTest,
handleOuterlinesTest2,
applyTemplateB,
makeRoofPatternPolygon,
} = useMode()
// const [canvasState, setCanvasState] = useRecoilState(canvasAtom)
useEffect(() => { useEffect(() => {
if (!canvas) { if (!canvas) {
@ -168,8 +184,31 @@ export default function Roof2() {
{ x: 1088, y: 991 }, { x: 1088, y: 991 },
{ x: 1088, y: 42 }, { x: 1088, y: 42 },
] ]
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 },
]
const eightPoint2 = [
{ x: 197, y: 215 },
{ x: 197, y: 815 },
{ x: 397, y: 815 },
{ x: 397, y: 1115 },
{ x: 697, y: 1115 },
{ x: 697, y: 815 },
{ x: 897, y: 815 },
{ x: 897, y: 215 },
]
if (canvas) { if (canvas) {
const polygon = new QPolygon(type1, { const polygon = new QPolygon(eightPoint, {
fill: 'transparent', fill: 'transparent',
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
@ -180,6 +219,8 @@ export default function Roof2() {
canvas?.add(polygon) canvas?.add(polygon)
console.log(polygon)
handleOuterlinesTest2(polygon) handleOuterlinesTest2(polygon)
// const lines = togglePolygonLine(polygon) // const lines = togglePolygonLine(polygon)
@ -201,16 +242,44 @@ export default function Roof2() {
const makeQLine = () => { const makeQLine = () => {
if (canvas) { if (canvas) {
const line = new QLine( const line = new QLine(
[50, 50, 200, 50], [50, 250, 900, 250],
{ {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 5,
fontSize: fontSize, fontSize: fontSize,
selectable: true,
},
50,
)
const line2 = new QLine(
[450, 450, 821, 78],
{
stroke: 'black',
strokeWidth: 5,
fontSize: fontSize,
selectable: true,
}, },
50, 50,
) )
canvas?.add(line) canvas?.add(line)
canvas?.add(line2)
const interSectionPoint = calculateIntersection2(line, line2)
if (interSectionPoint) {
console.log(interSectionPoint)
const circle = new fabric.Circle({
radius: 5,
fill: 'red',
left: interSectionPoint.x - 5,
top: interSectionPoint.y - 5,
})
canvas?.add(circle)
}
} }
} }
@ -236,10 +305,44 @@ export default function Roof2() {
const lines = togglePolygonLine(polygon) const lines = togglePolygonLine(polygon)
} }
/**
* canvas 내용 저장하기
*/
const handleSaveCanvas = async () => {
// const jsonStr = JSON.stringify(canvas?.toDatalessJSON(['type', 'fontSize']))
const jsonObj = JSON.stringify(canvas?.toDatalessJSON(['type', 'fontSize', 'lines']))
console.log(jsonObj)
const param = {
loginId: 'test',
canvas: jsonObj,
}
console.log(param)
await insertCanvasState(param)
handleClear()
}
/**
* canvas 내용 불러오기
*/
const handleLoadCanvas = async () => {
const canvasStates = await getCanvasState()
console.log(JSON.parse(canvasStates.canvas))
canvas?.loadFromJSON(JSON.parse(canvasStates.canvas))
}
/**
* 컨트롤러 보이기/숨기기
*/
const handleShowController = () => { const handleShowController = () => {
setShowControl(!showControl) setShowControl(!showControl)
} }
const drawRoofPatterns = (roofStyle) => {
makeRoofPatternPolygon(roofStyle)
}
return ( return (
<> <>
{canvas && ( {canvas && (
@ -267,6 +370,12 @@ export default function Roof2() {
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.PATTERNB)}> <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.PATTERNB)}>
템플릿(B 패턴) 템플릿(B 패턴)
</Button> </Button>
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => drawRoofPatterns(1)}>
지붕패턴1
</Button>
<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.TEXTBOX ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.TEXTBOX)}> <Button className="m-1 p-2" color={`${mode === Mode.TEXTBOX ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.TEXTBOX)}>
텍스트박스 모드 텍스트박스 모드
</Button> </Button>
@ -333,6 +442,15 @@ export default function Roof2() {
<Button className="m-1 p-2" onClick={PolygonToLine}> <Button className="m-1 p-2" onClick={PolygonToLine}>
PolygonToLine PolygonToLine
</Button> </Button>
{/*<Button className="m-1 p-2" onClick={handleSaveCanvas}>
canvas 내용 저장하기
</Button>
<Button className="m-1 p-2" onClick={handleLoadCanvas}>
canvas 내용 불러오기
</Button>*/}
<Button className="m-1 p-2" onClick={addCanvas}>
캔버스 추가
</Button>
<Button className="m-1 p-2" color={`${showControl ? 'primary' : 'default'}`} onClick={handleShowController}> <Button className="m-1 p-2" color={`${showControl ? 'primary' : 'default'}`} onClick={handleShowController}>
canvas 컨트롤러 {`${showControl ? '숨기기' : '보이기'}`} canvas 컨트롤러 {`${showControl ? '숨기기' : '보이기'}`}
</Button> </Button>

View File

@ -1,4 +1,5 @@
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { getDirection, getDirectionByPoint } from '@/util/canvas-util'
export class QLine extends fabric.Group { export class QLine extends fabric.Group {
line line
@ -13,7 +14,14 @@ export class QLine extends fabric.Group {
idx idx
type = 'QLine' type = 'QLine'
parent parent
#lengthTxt = 0 isAlreadyInterSection = false
interSectionPoints = []
lengthTxt = 0
initPoints
initOption
initLengthTxt
constructor(points, option = { isActiveLengthText: true }, lengthTxt) { constructor(points, option = { isActiveLengthText: true }, lengthTxt) {
// 소수점 전부 제거 // 소수점 전부 제거
@ -30,38 +38,43 @@ export class QLine extends fabric.Group {
const line = new fabric.Line(points, { ...option, strokeWidth: 1 }) const line = new fabric.Line(points, { ...option, strokeWidth: 1 })
super([line], {}) super([line], {})
this.initPoints = points
this.initOption = option
this.initLengthTxt = lengthTxt
this.x1 = x1 this.x1 = x1
this.y1 = y1 this.y1 = y1
this.x2 = x2 this.x2 = x2
this.y2 = y2 this.y2 = y2
this.line = line this.line = line
this.fontSize = option.fontSize this.fontSize = option.fontSize
this.direction = option.direction this.direction = option.direction ?? getDirectionByPoint({ x: x1, y: y1 }, { x: x2, y: y2 })
this.parent = option.parent this.parent = option.parent
this.idx = option.idx this.idx = option.idx
if (lengthTxt > 0) { if (lengthTxt > 0) {
this.#lengthTxt = Number(lengthTxt) this.lengthTxt = Number(lengthTxt)
} }
option.isActiveLengthText ?? this.#init() option.isActiveLengthText ?? this.init()
this.#addControl() this.addControl()
} }
#init() { init() {
this.#addLengthText(true) this.addLengthText(true)
} }
#addControl() { addControl() {
this.on('moving', () => { this.on('moving', () => {
this.#addLengthText(false) this.addLengthText(false)
}) })
this.on('modified', (e) => { this.on('modified', (e) => {
this.#addLengthText(false) this.addLengthText(false)
}) })
this.on('selected', () => { this.on('selected', () => {
console.log(this)
Object.keys(this.controls).forEach((controlKey) => { Object.keys(this.controls).forEach((controlKey) => {
if (controlKey !== 'ml' && controlKey !== 'mr') { if (controlKey !== 'ml' && controlKey !== 'mr') {
this.setControlVisible(controlKey, false) this.setControlVisible(controlKey, false)
@ -70,19 +83,19 @@ export class QLine extends fabric.Group {
}) })
} }
#addLengthText(isFirst) { addLengthText(isFirst) {
if (this.text) { if (this.text) {
this.removeWithUpdate(this.text) this.removeWithUpdate(this.text)
this.text = null this.text = null
} }
if (isFirst && this.#lengthTxt > 0) { if (isFirst && this.lengthTxt > 0) {
const text = new fabric.Textbox(this.#lengthTxt.toFixed(0).toString(), { const text = new fabric.Textbox(this.lengthTxt.toFixed(0).toString(), {
left: (this.x1 + this.x2) / 2, left: (this.x1 + this.x2) / 2,
top: (this.y1 + this.y2) / 2, top: (this.y1 + this.y2) / 2,
fontSize: this.fontSize, fontSize: this.fontSize,
}) })
this.length = this.#lengthTxt this.length = this.lengthTxt
this.text = text this.text = text
this.addWithUpdate(text) this.addWithUpdate(text)
return return
@ -112,4 +125,41 @@ export class QLine extends fabric.Group {
this.text.set({ fontSize }) this.text.set({ fontSize })
this.addWithUpdate() 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) {
this.callSuper('_set', key, value)
}
} }

View File

@ -1,6 +1,7 @@
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { import {
calculateIntersection, calculateIntersection,
calculateIntersection2,
distanceBetweenPoints, distanceBetweenPoints,
findClosestLineToPoint, findClosestLineToPoint,
findTopTwoIndexesByDistance, findTopTwoIndexesByDistance,
@ -11,6 +12,7 @@ import {
sortedPoints, sortedPoints,
} from '@/util/canvas-util' } from '@/util/canvas-util'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { drawHelpLineInHexagon2 } from '@/util/qpolygon-utils'
export default class QPolygon extends fabric.Group { export default class QPolygon extends fabric.Group {
type = 'QPolygon' type = 'QPolygon'
@ -27,6 +29,10 @@ export default class QPolygon extends fabric.Group {
helpLines = [] helpLines = []
wall wall
initPoints
initOption
constructor(points, options, canvas) { constructor(points, options, canvas) {
/*if (points.length !== 4 && points.length !== 6) { /*if (points.length !== 4 && points.length !== 6) {
throw new Error('Points must be 4 or 6.') throw new Error('Points must be 4 or 6.')
@ -44,19 +50,23 @@ export default class QPolygon extends fabric.Group {
const sortPoints = sortedPoints(points) const sortPoints = sortedPoints(points)
const polygon = new fabric.Polygon(sortPoints, options) const polygon = new fabric.Polygon(sortPoints, options)
super([polygon], {}) super([polygon], { selectable: false })
this.fontSize = options.fontSize this.fontSize = options.fontSize
this.points = sortPoints this.points = sortPoints
this.polygon = polygon this.polygon = polygon
this.name = options.name this.name = options.name
this.#init()
this.#addEvent() this.initPoints = points
this.#initLines() this.initOption = options
this.init()
this.addEvent()
this.initLines()
this.setShape() this.setShape()
} }
#initLines() { initLines() {
this.points.forEach((point, i) => { this.points.forEach((point, i) => {
const nextPoint = this.points[(i + 1) % this.points.length] const nextPoint = this.points[(i + 1) % this.points.length]
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], { const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
@ -70,13 +80,13 @@ export default class QPolygon extends fabric.Group {
}) })
} }
#init() { init() {
this.#addLengthText() this.addLengthText()
} }
#addEvent() { addEvent() {
this.on('scaling', (e) => { this.on('scaling', (e) => {
this.#updateLengthText() this.updateLengthText()
}) })
this.on('selected', function () { this.on('selected', function () {
@ -106,10 +116,10 @@ export default class QPolygon extends fabric.Group {
} }
}) })
this.addWithUpdate() this.canvas.add()
} }
#addLengthText() { addLengthText() {
if (this.texts.length > 0) { if (this.texts.length > 0) {
this.texts.forEach((text) => { this.texts.forEach((text) => {
this.canvas.remove(text) this.canvas.remove(text)
@ -135,13 +145,13 @@ export default class QPolygon extends fabric.Group {
}) })
this.texts.push(text) this.texts.push(text)
this.addWithUpdate(text) this.canvas.add(text)
}) })
this.canvas.renderAll() this.canvas.renderAll()
} }
#updateLengthText() { updateLengthText() {
const points = this.getCurrentPoints() const points = this.getCurrentPoints()
points.forEach((start, i) => { points.forEach((start, i) => {
@ -187,10 +197,10 @@ export default class QPolygon extends fabric.Group {
new fabric.Point(rect.left + rect.width, rect.top + rect.height), new fabric.Point(rect.left + rect.width, rect.top + rect.height),
] ]
const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.#distanceFromEdge(rectPoint) >= cell.padding) const isInside = rectPoints.every((rectPoint) => this.inPolygon(rectPoint) && this.distanceFromEdge(rectPoint) >= cell.padding)
if (isInside) { if (isInside) {
this.addWithUpdate(rect) this.canvas.add(rect)
} }
} }
} }
@ -244,7 +254,7 @@ export default class QPolygon extends fabric.Group {
return intersects % 2 === 1 return intersects % 2 === 1
} }
#distanceFromEdge(point) { distanceFromEdge(point) {
const vertices = this.getCurrentPoints() const vertices = this.getCurrentPoints()
let minDistance = Infinity let minDistance = Infinity
@ -316,14 +326,14 @@ export default class QPolygon extends fabric.Group {
} }
if (this.lines.length === 4) { if (this.lines.length === 4) {
this.#drawHelpLineInRect(chon) this.drawHelpLineInRect(chon)
} else if (this.lines.length === 6) { } else if (this.lines.length === 6 || this.lines.length === 8) {
// TODO : 6각형 // TODO : 6각형
this.#drawHelpLineInHexagon(chon) drawHelpLineInHexagon2(this, chon)
} else if (this.lines.length === 8) { } /* else if (this.lines.length === 8) {
// TODO : 8각형 // TODO : 8각형
this.#drawHelpLineInOctagon(chon) this.drawHelpLineInOctagon(chon)
} }*/
} }
/** /**
@ -369,7 +379,7 @@ export default class QPolygon extends fabric.Group {
return this.shape return this.shape
} }
#drawHelpLineInRect(chon) { drawHelpLineInRect(chon) {
let type = 1 let type = 1
let smallestLength = Infinity let smallestLength = Infinity
let maxLength = 0 let maxLength = 0
@ -514,24 +524,108 @@ export default class QPolygon extends fabric.Group {
strokeWidth: 1, strokeWidth: 1,
}) })
this.addWithUpdate(realLine1) this.canvas.add(realLine1)
this.addWithUpdate(realLine2) this.canvas.add(realLine2)
this.addWithUpdate(realLine3) this.canvas.add(realLine3)
this.addWithUpdate(realLine4) this.canvas.add(realLine4)
this.addWithUpdate(realLine5) this.canvas.add(realLine5)
this.addWithUpdate(realLine6) this.canvas.add(realLine6)
if (smallestLength !== maxLength) { if (smallestLength !== maxLength) {
// 정사각형이 아닌경우에만 용마루를 추가한다. // 정사각형이 아닌경우에만 용마루를 추가한다.
this.canvas.add(ridge) this.canvas.add(ridge)
} }
} }
drawHelpLineInHexagon2(chon) {
const oneSideLines = [...this.lines].map((line) => {
let newX1, newY1, newX2, newY2
if (line.direction === 'top') {
newX1 = line.x2
newY1 = line.y2
newX2 = line.x1
newY2 = line.y1
line.x1 = newX1
line.y1 = newY1
line.x2 = newX2
line.y2 = newY2
line.direction = 'bottom'
} else if (line.direction === 'left') {
newX1 = line.x2
newY1 = line.y2
newX2 = line.x1
newY2 = line.y1
line.x1 = newX1
line.y1 = newY1
line.x2 = newX2
line.y2 = newY2
line.direction = 'right'
}
return line
})
const centerLines = []
const helpLines = []
const ridgeStartPoints = []
const horizontalLines = oneSideLines.filter((line) => line.direction === 'right')
const verticalLines = oneSideLines.filter((line) => line.direction === 'bottom')
const horizontalMaxLength = horizontalLines.reduce((max, obj) => Math.max(max, obj.length), 0)
const verticalMaxLength = verticalLines.reduce((max, obj) => Math.max(max, obj.length), 0)
// 모든 가로선의 중심선을 긋는다.
horizontalLines.forEach((line, index) => {
const nextLine = horizontalLines[(index + 1) % horizontalLines.length]
const startCenterX = Math.max(line.x1, nextLine.x1)
const startCenterY = (line.y1 + nextLine.y1) / 2
let endCenterX = line.length >= nextLine.length ? startCenterX + line.length : startCenterX + nextLine.length
const endCenterY = startCenterY
if (endCenterX > Math.max(line.x2, nextLine.x2)) {
endCenterX = Math.max(line.x2, nextLine.x2)
}
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
fontSize: this.fontSize,
stroke: 'red',
strokeWidth: 1,
direction: 'horizontal',
})
// this.addWithUpdate(centerLine)
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
if (endCenterY > Math.max(line.y2, nextLine.y2)) {
endCenterY = Math.max(line.y2, nextLine.y2)
}
const centerLine = new QLine([startCenterX, startCenterY, endCenterX, endCenterY], {
fontSize: this.fontSize,
stroke: 'blue',
strokeWidth: 1,
direction: 'vertical',
})
// this.addWithUpdate(centerLine)
centerLines.push(centerLine)
})
const maxLength = horizontalMaxLength < verticalMaxLength ? horizontalMaxLength : verticalMaxLength
#drawHelpLineInHexagon(chon) {
const historyLines = []
const helpPoints = []
const notInterSectionLines = []
const ridge = []
const maxLength = this.lines.reduce((max, obj) => Math.min(max, obj.length), 999999)
this.points.forEach((point, index) => { this.points.forEach((point, index) => {
const wallPoint = this.wall.points[index] const wallPoint = this.wall.points[index]
// 두 점의 좌표 // 두 점의 좌표
@ -545,128 +639,296 @@ export default class QPolygon extends fabric.Group {
// x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성 // x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
const angle = Math.atan2(y2 - y1, x2 - x1) const angle = Math.atan2(y2 - y1, x2 - x1)
newX2 = x1 + (maxLength + 50) * Math.cos(angle) newX2 = Math.floor(x1 + (maxLength / 2 + 50) * Math.cos(angle))
newY2 = y1 + (maxLength + 50) * Math.sin(angle) newY2 = Math.floor(y1 + (maxLength / 2 + 50) * Math.sin(angle))
const line = new QLine([x1, y1, newX2, newY2], { const line = new QLine([x1, y1, newX2, newY2], {
fontSize: this.fontSize, fontSize: this.fontSize,
stroke: 'blue', stroke: 'green',
idx: index, idx: index,
}) })
historyLines.push(line)
this.addWithUpdate(line) this.addWithUpdate(line)
helpLines.push(line)
this.canvas.renderAll() this.canvas.renderAll()
}) })
/** helpLines.forEach((line, index) => {
* 삼각 지붕 if (line.isAlreadyInterSection) {
*/ return
historyLines.forEach((line, index) => { }
const prevLine = historyLines[(index - 1 + historyLines.length) % historyLines.length] const nextLine = helpLines[(index + 1 + helpLines.length) % helpLines.length]
this.canvas.renderAll()
let intersectionPoint = calculateIntersection(line, prevLine)
let intersectionPoint = calculateIntersection(line, nextLine)
if (!intersectionPoint) { if (!intersectionPoint) {
notInterSectionLines.push(line)
return return
} }
const helpLine1 = new QLine([prevLine.x1, prevLine.y1, intersectionPoint.x, intersectionPoint.y], { line.set({ isAlreadyInterSection: true })
nextLine.set({ isAlreadyInterSection: true })
const helpLine1 = new QLine([nextLine.x1, nextLine.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize, fontSize: this.fontSize,
stroke: 'red', stroke: 'skyblue',
}) })
const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], { const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize, fontSize: this.fontSize,
stroke: 'red', stroke: 'skyblue',
}) })
notInterSectionLines.pop()
helpPoints.push(intersectionPoint)
ridgeStartPoints.push(intersectionPoint)
this.addWithUpdate(helpLine1) this.addWithUpdate(helpLine1)
this.addWithUpdate(helpLine2) this.addWithUpdate(helpLine2)
this.removeWithUpdate(prevLine) this.removeWithUpdate(nextLine)
this.removeWithUpdate(line) this.removeWithUpdate(line)
this.canvas.renderAll() this.canvas.renderAll()
}) })
// 용마루
const ridgePoint = [] // 안만나는 선들
const notInterSectionLines = helpLines.filter((line) => !line.isAlreadyInterSection)
const ridgeEndPoints = []
const interSectionPoints = []
helpPoints.forEach((helpPoint, index) => { notInterSectionLines.forEach((line, index) => {
const closestLine = findClosestLineToPoint(helpPoint, notInterSectionLines) line.line.set({ strokeWidth: (index + 1) * 5 })
// 가까운 선의 중심점 centerLines.forEach((centerLine) => {
const centerClosestLinePoint = { const interSectionPoint = calculateIntersection2(line, centerLine)
x: (closestLine.x1 + closestLine.x2) / 2,
y: (closestLine.y1 + closestLine.y2) / 2,
}
const direction = getDirectionByPoint(helpPoint, centerClosestLinePoint) if (!this.inPolygon(interSectionPoint) || !interSectionPoint) {
return
let newX, newY
switch (direction) {
case 'left': {
newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
newY = helpPoint.y
break
} }
case 'right': { line.interSectionPoints.push(interSectionPoint)
newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1 interSectionPoints.push(interSectionPoint)
newY = helpPoint.y
break
}
case 'top': {
newX = helpPoint.x
newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
break
}
case 'bottom': {
newX = helpPoint.x
newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
break
}
}
const ridgeHelpLine = new QLine([closestLine.x1, closestLine.y1, newX, newY], {
fontSize: this.fontSize,
stroke: 'purple',
strokeWidth: 5,
}) })
ridgePoint.push({ x: newX, y: newY })
const ridge = new QLine([helpPoint.x, helpPoint.y, newX, newY], {
fontSize: this.fontSize,
stroke: 'skyblue',
strokeWidth: 5,
})
this.addWithUpdate(ridge)
this.canvas.renderAll()
this.addWithUpdate(ridgeHelpLine)
this.removeWithUpdate(closestLine)
this.canvas.renderAll()
}) })
// 용마루 끼리 연결 ridgeStartPoints.forEach((point, index) => {
for (let i = 0; i < ridgePoint.length; i = i + 2) { let arrivalPoint
const currentRidgeEndPoint = ridgePoint[i] let distance = Infinity
const nextRidgeEndPoint = ridgePoint[(i + 1) % ridgePoint.length] let startPoint
interSectionPoints.forEach((interSectionPoint) => {
if (Math.abs(point.x - interSectionPoint.x) < 3 || Math.abs(point.y - interSectionPoint.y) < 3) {
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: this.fontSize,
})
const helpLine = new QLine([line.x1, line.y1, arrivalPoint.x, arrivalPoint.y], {
stroke: 'red',
fontSize: this.fontSize,
})
ridgeEndPoints.push(arrivalPoint)
this.addWithUpdate(ridge)
this.addWithUpdate(helpLine)
this.removeWithUpdate(line)
this.canvas.renderAll()
}
})
ridgeEndPoints.forEach((point, index) => {
const currentRidgeEndPoint = ridgeEndPoints[index]
const nextRidgeEndPoint = ridgeEndPoints[(index + 1) % ridgeEndPoints.length]
const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], { const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
fontSize: this.fontSize, fontSize: this.fontSize,
stroke: 'green', stroke: 'green',
strokeWidth: 5,
}) })
this.addWithUpdate(ridgeConnectLine) this.addWithUpdate(ridgeConnectLine)
this.canvas.renderAll() this.canvas.renderAll()
} })
}
drawHelpLineInHexagon(chon) {
const historyLines = []
const helpPoints = []
const notInterSectionLines = []
const ridge = []
const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0)
this.points.forEach((point, index) => {
const wallPoint = this.wall.points[index]
// 두 점의 좌표
const x1 = point.x
const y1 = point.y
const x2 = wallPoint.x
const y2 = wallPoint.y
const historyLines = []
const helpPoints = []
const notInterSectionLines = []
const ridge = []
const maxLength = this.lines.reduce((max, obj) => Math.max(max, obj.length), 0)
this.points.forEach((point, index) => {
const wallPoint = this.wall.points[index]
// 두 점의 좌표
const x1 = point.x
const y1 = point.y
const x2 = wallPoint.x
const y2 = wallPoint.y
this.canvas.renderAll() let newX2, newY2
// x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
const angle = Math.atan2(y2 - y1, x2 - x1)
newX2 = x1 + (maxLength / 2) * Math.cos(angle)
newY2 = y1 + (maxLength / 2) * Math.sin(angle)
const line = new QLine([x1, y1, newX2, newY2], {
fontSize: this.fontSize,
stroke: 'blue',
idx: index,
})
historyLines.push(line)
this.canvas.add(line)
this.canvas.renderAll()
})
/**
* 삼각 지붕
*/
historyLines.forEach((line, index) => {
const prevLine = historyLines[(index - 1 + historyLines.length) % historyLines.length]
let intersectionPoint = calculateIntersection(line, prevLine)
if (!intersectionPoint) {
notInterSectionLines.push(line)
return
}
const helpLine1 = new QLine([prevLine.x1, prevLine.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize,
stroke: 'red',
})
const helpLine2 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
fontSize: this.fontSize,
stroke: 'red',
})
notInterSectionLines.pop()
helpPoints.push(intersectionPoint)
this.canvas.add(helpLine1)
this.canvas.add(helpLine2)
this.canvas.remove(prevLine)
this.canvas.remove(line)
this.canvas.renderAll()
})
// 용마루
const ridgePoint = []
helpPoints.forEach((helpPoint, index) => {
const closestLine = findClosestLineToPoint(helpPoint, notInterSectionLines)
// 가까운 선의 중심점
const centerClosestLinePoint = {
x: (closestLine.x1 + closestLine.x2) / 2,
y: (closestLine.y1 + closestLine.y2) / 2,
}
const direction = getDirectionByPoint(helpPoint, centerClosestLinePoint)
let newX, newY
switch (direction) {
case 'left': {
newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
newY = helpPoint.y
break
}
case 'right': {
newX = ((closestLine.x2 - closestLine.x1) * (helpPoint.y - closestLine.y1)) / (closestLine.y2 - closestLine.y1) + closestLine.x1
newY = helpPoint.y
break
}
case 'top': {
newX = helpPoint.x
newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
break
}
case 'bottom': {
newX = helpPoint.x
newY = ((closestLine.y2 - closestLine.y1) * (helpPoint.x - closestLine.x1)) / (closestLine.x2 - closestLine.x1) + closestLine.y1
break
}
}
const ridgeHelpLine = new QLine([closestLine.x1, closestLine.y1, newX, newY], {
fontSize: this.fontSize,
stroke: 'purple',
strokeWidth: 5,
})
ridgePoint.push({ x: newX, y: newY })
const ridge = new QLine([helpPoint.x, helpPoint.y, newX, newY], {
fontSize: this.fontSize,
stroke: 'skyblue',
strokeWidth: 5,
})
this.canvas.add(ridge)
this.canvas.renderAll()
this.canvas.add(ridgeHelpLine)
this.canvas.remove(closestLine)
this.canvas.renderAll()
})
// 용마루 끼리 연결
for (let i = 0; i < ridgePoint.length; i = i + 2) {
const currentRidgeEndPoint = ridgePoint[i]
const nextRidgeEndPoint = ridgePoint[(i + 1) % ridgePoint.length]
const ridgeConnectLine = new QLine([currentRidgeEndPoint.x, currentRidgeEndPoint.y, nextRidgeEndPoint.x, nextRidgeEndPoint.y], {
fontSize: this.fontSize,
stroke: 'green',
strokeWidth: 5,
})
this.canvas.add(ridgeConnectLine)
this.canvas.renderAll()
}
this.canvas.renderAll()
})
} }
#drawHelpLineInOctagon(chon) {} drawHelpLineInOctagon(chon) {}
getObject() {
return this
}
sou
toObject(propertiesToInclude) {
return fabric.util.object.extend(this.callSuper('toObject'), {
points: this.points,
fontSize: this.fontSize,
name: this.name,
shape: this.shape,
texts: this.texts,
lines: this.lines,
wall: this.wall,
initPoints: this.initPoints,
initOption: this.initOption,
objects: this.getObjects().map((obj) => obj.toObject(propertiesToInclude)),
})
}
_set(key, value) {
this.callSuper('_set', key, value)
}
} }

View File

@ -1,10 +1,6 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { import { actionHandler, anchorWrapper, polygonPositionHandler } from '@/util/canvas-util'
actionHandler,
anchorWrapper,
polygonPositionHandler,
} from '@/util/canvas-util'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' import { canvasSizeState, fontSizeState } from '@/store/canvasAtom'
@ -31,17 +27,6 @@ export function useCanvas(id) {
selection: false, selection: false,
}) })
// settings for all canvas in the app
fabric.Object.prototype.transparentCorners = false
fabric.Object.prototype.cornerColor = '#2BEBC8'
fabric.Object.prototype.cornerStyle = 'rect'
fabric.Object.prototype.cornerStrokeColor = '#2BEBC8'
fabric.Object.prototype.cornerSize = 6
QPolygon.prototype.canvas = c
QLine.prototype.canvas = c
QRect.prototype.canvas = c
setCanvas(c) setCanvas(c)
return () => { return () => {
c.dispose() c.dispose()
@ -57,24 +42,14 @@ export function useCanvas(id) {
useEffect(() => { useEffect(() => {
canvas canvas
?.getObjects() ?.getObjects()
.filter( .filter((obj) => obj.type === 'textbox' || obj.type === 'text' || obj.type === 'i-text')
(obj) =>
obj.type === 'textbox' ||
obj.type === 'text' ||
obj.type === 'i-text',
)
.forEach((obj) => { .forEach((obj) => {
obj.set({ fontSize: fontSize }) obj.set({ fontSize: fontSize })
}) })
canvas canvas
?.getObjects() ?.getObjects()
.filter( .filter((obj) => obj.type === 'QLine' || obj.type === 'QPolygon' || obj.type === 'QRect')
(obj) =>
obj.type === 'QLine' ||
obj.type === 'QPolygon' ||
obj.type === 'QRect',
)
.forEach((obj) => { .forEach((obj) => {
obj.setFontSize(fontSize) obj.setFontSize(fontSize)
}) })
@ -121,26 +96,43 @@ export function useCanvas(id) {
*/ */
const removeMouseLines = () => { const removeMouseLines = () => {
if (canvas?._objects.length > 0) { if (canvas?._objects.length > 0) {
const mouseLines = canvas?._objects.filter( const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine')
(obj) => obj.name === 'mouseLine',
)
mouseLines.forEach((item) => canvas?.remove(item)) mouseLines.forEach((item) => canvas?.remove(item))
} }
canvas?.renderAll() canvas?.renderAll()
} }
/**
* 눈금 그리기
*/
const initialize = () => { const initialize = () => {
canvas?.clear() canvas?.clear()
// settings for all canvas in the app
fabric.Object.prototype.transparentCorners = false
fabric.Object.prototype.cornerColor = '#2BEBC8'
fabric.Object.prototype.cornerStyle = 'rect'
fabric.Object.prototype.cornerStrokeColor = '#2BEBC8'
fabric.Object.prototype.cornerSize = 6
// 기존 이벤트가 있을 경우 제거한다. QPolygon.prototype.canvas = canvas
// removeEventOnCanvas() QLine.prototype.canvas = canvas
QRect.prototype.canvas = canvas
// 작업 후에 event를 추가해준다. fabric.QLine = fabric.util.createClass(fabric.Group, {})
fabric.QPolygon = fabric.util.createClass(fabric.Group, {})
// addEventOnCanvas() // 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))
})
}
// 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))
})
}
} }
/** /**
@ -176,26 +168,20 @@ export function useCanvas(id) {
} }
// 가로선을 그립니다. // 가로선을 그립니다.
const horizontalLine = new fabric.Line( const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], {
[0, pointer.y, canvasSize.horizontal, pointer.y], stroke: 'black',
{ strokeWidth: 1,
stroke: 'black', selectable: false,
strokeWidth: 1, name: 'mouseLine',
selectable: false, })
name: 'mouseLine',
},
)
// 세로선을 그립니다. // 세로선을 그립니다.
const verticalLine = new fabric.Line( const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], {
[pointer.x, 0, pointer.x, canvasSize.vertical], stroke: 'black',
{ strokeWidth: 1,
stroke: 'black', selectable: false,
strokeWidth: 1, name: 'mouseLine',
selectable: false, })
name: 'mouseLine',
},
)
// 선들을 캔버스에 추가합니다. // 선들을 캔버스에 추가합니다.
canvas?.add(horizontalLine, verticalLine) canvas?.add(horizontalLine, verticalLine)
@ -325,7 +311,6 @@ export function useCanvas(id) {
} }
const jsonStr = JSON.stringify(canvas) const jsonStr = JSON.stringify(canvas)
localStorage.setItem('canvas', jsonStr) localStorage.setItem('canvas', jsonStr)
handleClear()
} }
/** /**
@ -479,10 +464,7 @@ export function useCanvas(id) {
poly.controls = poly.points.reduce(function (acc, point, index) { poly.controls = poly.points.reduce(function (acc, point, index) {
acc['p' + index] = new fabric.Control({ acc['p' + index] = new fabric.Control({
positionHandler: polygonPositionHandler, positionHandler: polygonPositionHandler,
actionHandler: anchorWrapper( actionHandler: anchorWrapper(index > 0 ? index - 1 : lastControl, actionHandler),
index > 0 ? index - 1 : lastControl,
actionHandler,
),
actionName: 'modifyPolygon', actionName: 'modifyPolygon',
pointIndex: index, pointIndex: index,
}) })
@ -571,6 +553,54 @@ export function useCanvas(id) {
}) })
} }
const addCanvas = () => {
// const canvasState = canvas
const objs = canvas
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()
},
)
}, 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) => {
canvas?.clear()
const canvasState = JSON.parse(canvasList[idx])
}
return { return {
canvas, canvas,
addShape, addShape,
@ -585,5 +615,7 @@ export function useCanvas(id) {
saveImage, saveImage,
handleFlip, handleFlip,
setCanvasBackgroundWithDots, setCanvasBackgroundWithDots,
addCanvas,
changeCanvas,
} }
} }

File diff suppressed because it is too large Load Diff

38
src/lib/canvas.js Normal file
View File

@ -0,0 +1,38 @@
'use server'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
export const getTests = () => {
return prisma.test.findMany()
}
export const insertTest = async (param) => {
return prisma.test.create({
data: {
content: param,
},
})
}
export const getCanvasStateAll = () => {
return prisma.canvas.findMany()
}
export const getCanvasState = () => {
return prisma.canvas.findFirst({
where: {
loginId: 'test',
},
orderBy: {
id: 'desc',
},
})
}
export const insertCanvasState = (param) => {
return prisma.canvas.create({
data: param,
})
}

14
src/lib/prisma.js Normal file
View File

@ -0,0 +1,14 @@
import { PrismaClient } from '@prisma/client'
let prisma
if (process.env.NODE_ENV === 'production') {
prisma = new PrismaClient()
} else {
if (!global.prisma) {
global.prisma = new PrismaClient()
}
prisma = global.prisma
}
export default prisma

View File

@ -13,8 +13,8 @@ export const fontSizeState = atom({
export const canvasSizeState = atom({ export const canvasSizeState = atom({
key: 'canvasSize', key: 'canvasSize',
default: { default: {
vertical: 1000, vertical: 1500,
horizontal: 1000, horizontal: 1500,
}, },
}) })
@ -35,3 +35,9 @@ export const wallState = atom({
default: {}, default: {},
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })
export const roofPolygonPatternArrayState = atom({
key: 'roofPolygonPattern',
default: {}, //object ex) big, mid, sht = {point : [{x1, y1}, {x2, y1}], direction : left or right or top or bottom}
dangerouslyAllowMutability: true,
})

View File

@ -1,3 +1,5 @@
import { intersect } from 'mathjs'
/** /**
* Collection of function to use on canvas * Collection of function to use on canvas
*/ */
@ -9,18 +11,14 @@ export function polygonPositionHandler(dim, finalMatrix, fabricObject) {
let y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y let y = fabricObject.points[this.pointIndex].y - fabricObject.pathOffset.y
return fabric.util.transformPoint( return fabric.util.transformPoint(
{ x, y }, { x, y },
fabric.util.multiplyTransformMatrices( fabric.util.multiplyTransformMatrices(fabricObject.canvas.viewportTransform, fabricObject.calcTransformMatrix()),
fabricObject.canvas.viewportTransform,
fabricObject.calcTransformMatrix(),
),
) )
} }
function getObjectSizeWithStroke(object) { function getObjectSizeWithStroke(object) {
let stroke = new fabric.Point( let stroke = new fabric.Point(object.strokeUniform ? 1 / object.scaleX : 1, object.strokeUniform ? 1 / object.scaleY : 1).multiply(
object.strokeUniform ? 1 / object.scaleX : 1, object.strokeWidth,
object.strokeUniform ? 1 / object.scaleY : 1, )
).multiply(object.strokeWidth)
return new fabric.Point(object.width + stroke.x, object.height + stroke.y) return new fabric.Point(object.width + stroke.x, object.height + stroke.y)
} }
@ -28,20 +26,12 @@ function getObjectSizeWithStroke(object) {
export function actionHandler(eventData, transform, x, y) { export function actionHandler(eventData, transform, x, y) {
let polygon = transform.target, let polygon = transform.target,
currentControl = polygon.controls[polygon.__corner], currentControl = polygon.controls[polygon.__corner],
mouseLocalPosition = polygon.toLocalPoint( mouseLocalPosition = polygon.toLocalPoint(new fabric.Point(x, y), 'center', 'center'),
new fabric.Point(x, y),
'center',
'center',
),
polygonBaseSize = getObjectSizeWithStroke(polygon), polygonBaseSize = getObjectSizeWithStroke(polygon),
size = polygon._getTransformedDimensions(0, 0) size = polygon._getTransformedDimensions(0, 0)
polygon.points[currentControl.pointIndex] = { polygon.points[currentControl.pointIndex] = {
x: x: (mouseLocalPosition.x * polygonBaseSize.x) / size.x + polygon.pathOffset.x,
(mouseLocalPosition.x * polygonBaseSize.x) / size.x + y: (mouseLocalPosition.y * polygonBaseSize.y) / size.y + polygon.pathOffset.y,
polygon.pathOffset.x,
y:
(mouseLocalPosition.y * polygonBaseSize.y) / size.y +
polygon.pathOffset.y,
} }
return true return true
} }
@ -50,8 +40,7 @@ export function actionHandler(eventData, transform, x, y) {
export function anchorWrapper(anchorIndex, fn) { export function anchorWrapper(anchorIndex, fn) {
return function (eventData, transform, x, y) { return function (eventData, transform, x, y) {
let fabricObject = transform.target let fabricObject = transform.target
let originX = let originX = fabricObject?.points[anchorIndex].x - fabricObject.pathOffset.x
fabricObject?.points[anchorIndex].x - fabricObject.pathOffset.x
let originY = fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y let originY = fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y
let absolutePoint = fabric.util.transformPoint( let absolutePoint = fabric.util.transformPoint(
{ {
@ -63,12 +52,8 @@ export function anchorWrapper(anchorIndex, fn) {
let actionPerformed = fn(eventData, transform, x, y) let actionPerformed = fn(eventData, transform, x, y)
let newDim = fabricObject._setPositionDimensions({}) let newDim = fabricObject._setPositionDimensions({})
let polygonBaseSize = getObjectSizeWithStroke(fabricObject) let polygonBaseSize = getObjectSizeWithStroke(fabricObject)
let newX = let newX = (fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / polygonBaseSize.x
(fabricObject.points[anchorIndex].x - fabricObject.pathOffset.x) / let newY = (fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) / polygonBaseSize.y
polygonBaseSize.x
let newY =
(fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y) /
polygonBaseSize.y
fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5) fabricObject.setPositionByOrigin(absolutePoint, newX + 0.5, newY + 0.5)
return actionPerformed return actionPerformed
} }
@ -104,9 +89,7 @@ export function addDistanceTextToPolygon(polygon) {
for (let i = 0; i < points.length; i++) { for (let i = 0; i < points.length; i++) {
const start = points[i] const start = points[i]
const end = points[(i + 1) % points.length] // 다음 점 (마지막 점의 경우 첫번째 점으로) const end = points[(i + 1) % points.length] // 다음 점 (마지막 점의 경우 첫번째 점으로)
const distance = Math.sqrt( const distance = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)) // 두 점 사이의 거리 계산
Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2),
) // 두 점 사이의 거리 계산
const text = new fabric.Textbox(distance.toFixed(2), { const text = new fabric.Textbox(distance.toFixed(2), {
// 소수 둘째자리까지 표시 // 소수 둘째자리까지 표시
@ -160,10 +143,7 @@ export const getStartIndex = (lines) => {
let smallestY1 = lines[0].y1 let smallestY1 = lines[0].y1
for (let i = 1; i < lines.length; i++) { for (let i = 1; i < lines.length; i++) {
if ( if (lines[i].x1 < smallestX1 || (lines[i].x1 === smallestX1 && lines[i].y1 < smallestY1)) {
lines[i].x1 < smallestX1 ||
(lines[i].x1 === smallestX1 && lines[i].y1 < smallestY1)
) {
smallestIndex = i smallestIndex = i
smallestX1 = lines[i].x1 smallestX1 = lines[i].x1
smallestY1 = lines[i].y1 smallestY1 = lines[i].y1
@ -184,10 +164,7 @@ export const getStartIndexPoint = (points) => {
let smallestY1 = points[0].y let smallestY1 = points[0].y
for (let i = 1; i < points.length; i++) { for (let i = 1; i < points.length; i++) {
if ( if (points[i].x < smallestX1 || (points[i].x === smallestX1 && points[i].y < smallestY1)) {
points[i].x < smallestX1 ||
(points[i].x === smallestX1 && points[i].y < smallestY1)
) {
smallestIndex = i smallestIndex = i
smallestX1 = points[i].x smallestX1 = points[i].x
smallestY1 = points[i].y smallestY1 = points[i].y
@ -316,50 +293,128 @@ export const getDirectionByPoint = (a, b) => {
} }
/** /**
* line을 두개를 이용해서 교차점을 찾는 함수 * 선분의 교차점을 찾는 함수입니다. 함수는 선분이 실제로 교차하는 지점 내에서 교차하는지 확인합니다.
* @param line1 * @param {Object} line1 번째 선분, {x1, y1, x2, y2} 형태의 객체
* @param line2 * @param {Object} line2 번째 선분, {x1, y1, x2, y2} 형태의 객체
* @returns {{x: number, y: number}|null} * @returns {{x: number, y: number}|null} 교차점의 좌표를 반환하거나, 교차점이 없으면 null을 반환합니다.
*/ */
export function calculateIntersection(line1, line2) { export function calculateIntersection(line1, line2) {
const x1 = line1.x1, const { x1: x1_1, y1: y1_1, x2: x2_1, y2: y2_1 } = line1
y1 = line1.y1, const { x1: x1_2, y1: y1_2, x2: x2_2, y2: y2_2 } = line2
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) const denominator = (x1_1 - x2_1) * (y1_2 - y2_2) - (y1_1 - y2_1) * (x1_2 - x2_2)
if (denom === 0) return null // 선분이 평행하거나 일치 if (denominator === 0) return null // 선분이 평행하거나 일치하는 경우
const intersectX = const t = ((x1_1 - x1_2) * (y1_2 - y2_2) - (y1_1 - y1_2) * (x1_2 - x2_2)) / denominator
((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denom const u = -((x1_1 - x2_1) * (y1_1 - y1_2) - (y1_1 - y2_1) * (x1_1 - x1_2)) / denominator
const intersectY =
((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denom
// 교차점이 두 선분의 x 좌표 범위 내에 있는지 확인 // t와 u가 모두 0과 1 사이에 있을 때, 선분 내에서 교차
if ( if (t >= 0 && t <= 1 && u >= 0 && u <= 1) {
intersectX < Math.min(x1, x2) || const intersectionX = x1_1 + t * (x2_1 - x1_1)
intersectX > Math.max(x1, x2) || const intersectionY = y1_1 + t * (y2_1 - y1_1)
intersectX < Math.min(x3, x4) ||
intersectX > Math.max(x3, x4) // Determine the min and max for line1 and line2 for both x and y
) { const line1MinX = Math.min(line1.x1, line1.x2)
return null // 교차점이 선분 범위 밖에 있음 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 { x: intersectX, y: intersectY } return null // 교차점이 선분의 범위 내에 없음
} }
function findOrthogonalPoint(x1, y1, x2, y2, x3, y3, x4, y4) { export const findIntersection1 = (line1, line2) => {
const { x1, y1, x2, y2 } = line1 // 첫 번째 선의 두 점
const x3 = line2.x1
const y3 = line2.y1
const x4 = line2.x2
const y4 = line2.y2
// 선의 방정식의 계수 계산
const A1 = y2 - y1
const B1 = x1 - x2
const C1 = A1 * x1 + B1 * y1
const A2 = y4 - y3
const B2 = x3 - x4
const C2 = A2 * x3 + B2 * y3
const determinant = A1 * B2 - A2 * B1
if (determinant === 0) {
// 두 선이 평행하거나 일직선일 경우
return null
}
const x = (B1 * C2 - B2 * C1) / determinant
const y = (A1 * C2 - A2 * C1) / determinant
return { x, y }
}
export const calculateIntersection2 = (line1, line2) => {
const result = intersect([line1.x1, line1.y1], [line1.x2, line1.y2], [line2.x1, line2.y1], [line2.x2, line2.y2])
if (!result) {
return null
}
// 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)
// Check if the intersection X and Y are within the range of both lines
if (
result[0] >= line1MinX &&
result[0] <= line1MaxX &&
result[0] >= line2MinX &&
result[0] <= line2MaxX &&
result[1] >= line1MinY &&
result[1] <= line1MaxY &&
result[1] >= line2MinY &&
result[1] <= line2MaxY
) {
return { x: Math.round(result[0]), y: Math.round(result[1]) }
} else {
return null // Intersection is out of range
}
}
export function findOrthogonalPoint(line1, line2) {
// Calculate the intersection point of two lines // Calculate the intersection point of two lines
const intersectionX = const intersectionX =
((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((line1.x1 * line2.y1 - line1.y1 * line2.x1) * (line2.x2 - line2.x1) - (line1.x1 - line1.x2) * (line2.x2 * line2.y2 - line2.y1 * line2.x1)) /
((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)) ((line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x2 - line2.x1))
const intersectionY = const intersectionY =
((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((line1.x1 * line2.y1 - line1.y1 * line2.x1) * (line2.y2 - line2.y1) - (line1.y1 - line1.y2) * (line2.x2 * line2.y2 - line2.y1 * line2.x1)) /
((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)) ((line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x2 - line2.x1))
return { x: intersectionX, y: intersectionY } return { x: intersectionX, y: intersectionY }
} }
@ -371,7 +426,6 @@ function findOrthogonalPoint(x1, y1, x2, y2, x3, y3, x4, y4) {
export const sortedPoints = (points) => { export const sortedPoints = (points) => {
const copyPoints = [...points] const copyPoints = [...points]
//points를 x,y좌표를 기준으로 정렬합니다. //points를 x,y좌표를 기준으로 정렬합니다.
copyPoints.sort((a, b) => { copyPoints.sort((a, b) => {
if (a.x === b.x) { if (a.x === b.x) {
return a.y - b.y return a.y - b.y
@ -387,7 +441,6 @@ export const sortedPoints = (points) => {
let index = 1 let index = 1
let currentPoint = { ...copyPoints[0] } let currentPoint = { ...copyPoints[0] }
copyPoints.splice(0, 1) copyPoints.splice(0, 1)
while (index < points.length) { while (index < points.length) {
if (index === points.length - 1) { if (index === points.length - 1) {
resultPoints.push(copyPoints[0]) resultPoints.push(copyPoints[0])
@ -397,14 +450,18 @@ export const sortedPoints = (points) => {
// 짝수번째는 y값이 같은 점을 찾는다. // 짝수번째는 y값이 같은 점을 찾는다.
for (let i = 0; i < copyPoints.length; i++) { for (let i = 0; i < copyPoints.length; i++) {
// y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다. // y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다.
const temp = copyPoints.filter((point) => point.y === currentPoint.y) let temp = copyPoints.filter((point) => point.y === currentPoint.y)
// temp중 x값이 가장 큰 값 if (temp.length === 0) {
const max = temp.reduce((prev, current) => // temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
prev.x >= current.x ? prev : current, temp = Array.of(findClosestPointByY(currentPoint, copyPoints))
) }
resultPoints.push(max) // temp중 x값이 가장 가까운 값
currentPoint = max
copyPoints.splice(copyPoints.indexOf(max), 1) const min = temp.reduce((prev, current) => (Math.abs(current.x - currentPoint.x) <= Math.abs(prev.x - currentPoint.x) ? current : prev))
resultPoints.push(min)
currentPoint = min
copyPoints.splice(copyPoints.indexOf(min), 1)
index++ index++
break break
} }
@ -412,21 +469,23 @@ export const sortedPoints = (points) => {
// 홀수번째는 x값이 같은 점을 찾는다. // 홀수번째는 x값이 같은 점을 찾는다.
for (let i = 0; i < copyPoints.length; i++) { for (let i = 0; i < copyPoints.length; i++) {
// x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다. // x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다.
const temp = copyPoints.filter((point) => point.x === currentPoint.x) let temp = copyPoints.filter((point) => point.x === currentPoint.x)
// temp중 y값이 가장 큰 값 if (temp.length === 0) {
const max = temp.reduce((prev, current) => // temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
prev.y >= current.y ? prev : current,
)
resultPoints.push(max) temp = Array.of(findClosestPointByX(currentPoint, copyPoints))
currentPoint = max }
copyPoints.splice(copyPoints.indexOf(max), 1) // temp중 y값이 가장 가까운 값
const min = temp.reduce((prev, current) => (Math.abs(current.y - currentPoint.y) <= Math.abs(prev.y - currentPoint.y) ? current : prev))
resultPoints.push(min)
currentPoint = min
copyPoints.splice(copyPoints.indexOf(min), 1)
index++ index++
break break
} }
} }
} }
return resultPoints return resultPoints
} }
@ -467,6 +526,81 @@ export function findClosestLineToPoint(point, lines) {
return closestLine return closestLine
} }
/**
* x값이 가장 가까운
* @param targetPoint
* @param points
* @returns {*|null}
*/
function findClosestPointByX(targetPoint, points) {
if (points.length === 0) {
return null // Return null if the points array is empty
}
let closestPoint = points[0]
let smallestDistance = Math.abs(targetPoint.x - points[0].x)
for (let i = 1; i < points.length; i++) {
const currentDistance = Math.abs(targetPoint.x - points[i].x)
if (currentDistance < smallestDistance) {
smallestDistance = currentDistance
closestPoint = points[i]
}
}
return closestPoint
}
/**
* y값이 가장 가까운
* @param targetPoint
* @param points
* @returns {*|null}
*/
function findClosestPointByY(targetPoint, points) {
if (points.length === 0) {
return null // Return null if the points array is empty
}
let closestPoint = points[0]
let smallestDistance = Math.abs(targetPoint.y - points[0].y)
for (let i = 1; i < points.length; i++) {
const currentDistance = Math.abs(targetPoint.y - points[i].y)
if (currentDistance < smallestDistance) {
smallestDistance = currentDistance
closestPoint = points[i]
}
}
return closestPoint
}
/**
* 주어진 점에서 points 배열 가장 가까운 점을 찾는 함수입니다.
* @param {Object} targetPoint - { x: number, y: number } 형태의 대상 객체
* @param {Array} points - { x: number, y: number } 형태의 객체들의 배열
* @returns {Object} targetPoint에 가장 가까운 객체
*/
export function findClosestPoint(targetPoint, points) {
if (points.length === 0) {
return null // points 배열이 비어있으면 null 반환
}
let closestPoint = points[0]
let smallestDistance = calculateDistancePoint(targetPoint, closestPoint)
for (let i = 1; i < points.length; i++) {
const currentDistance = calculateDistancePoint(targetPoint, points[i])
if (currentDistance < smallestDistance) {
smallestDistance = currentDistance
closestPoint = points[i]
}
}
return closestPoint
}
/** /**
* 점과 직선사이의 최단 거리 * 점과 직선사이의 최단 거리
* @param point * @param point
@ -481,9 +615,37 @@ export function calculateDistance(point, line) {
const x0 = point.x const x0 = point.x
const y0 = point.y const y0 = point.y
const numerator = Math.abs( const numerator = Math.abs((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1)
(y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1,
)
const denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2)) const denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2))
return numerator / denominator return numerator / denominator
} }
/**
* 사이의 거리를 계산하는 함수입니다.
* @param {Object} point1 - 번째 { x: number, y: number }
* @param {Object} point2 - 번째 { x: number, y: number }
* @returns {number} 사이의 거리
*/
function calculateDistancePoint(point1, point2) {
return Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.y - point1.y, 2))
}
/**
* {x, y} 형태의 배열을 받아 중복된 점을 제거하는 함수
* @param points
* @returns {*[]}
*/
export function removeDuplicatePoints(points) {
const uniquePoints = []
const seen = new Set()
points.forEach((point) => {
const identifier = `${point.x}:${point.y}`
if (!seen.has(identifier)) {
seen.add(identifier)
uniquePoints.push(point)
}
})
return uniquePoints
}

12
src/util/qline-utils.js Normal file
View File

@ -0,0 +1,12 @@
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))
})
}
}

329
src/util/qpolygon-utils.js Normal file
View File

@ -0,0 +1,329 @@
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'
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))
})
}
}
export const drawHelpLineInHexagon2 = (polygon, chon) => {
const oneSideLines = [...polygon.lines].map((line) => {
let newX1, newY1, newX2, newY2
if (line.direction === 'top') {
newX1 = line.x2
newY1 = line.y2
newX2 = line.x1
newY2 = line.y1
line.x1 = newX1
line.y1 = newY1
line.x2 = newX2
line.y2 = newY2
line.direction = 'bottom'
} else if (line.direction === 'left') {
newX1 = line.x2
newY1 = line.y2
newX2 = line.x1
newY2 = line.y1
line.x1 = newX1
line.y1 = newY1
line.x2 = newX2
line.y2 = newY2
line.direction = 'right'
}
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()
}*/
}
export const drawHelpLineInHexagon = (polygon, chon) => {
// 가장 긴라인을 기준으로 centerLine을 그린다.
}

118
yarn.lock
View File

@ -148,6 +148,13 @@
semver "^7.3.5" semver "^7.3.5"
tar "^6.1.11" tar "^6.1.11"
"@mongodb-js/saslprep@^1.1.5":
version "1.1.8"
resolved "https://registry.yarnpkg.com/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz#d39744540be8800d17749990b0da95b4271840d1"
integrity sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==
dependencies:
sparse-bitfield "^3.0.3"
"@next/env@14.2.3": "@next/env@14.2.3":
version "14.2.3" version "14.2.3"
resolved "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz" resolved "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz"
@ -1143,6 +1150,47 @@
resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz"
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
"@prisma/client@^5.17.0":
version "5.17.0"
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-5.17.0.tgz#9079947bd749689c2dabfb9ecc70a24ebefb1f43"
integrity sha512-N2tnyKayT0Zf7mHjwEyE8iG7FwTmXDHFZ1GnNhQp0pJUObsuel4ZZ1XwfuAYkq5mRIiC/Kot0kt0tGCfLJ70Jw==
"@prisma/debug@5.17.0":
version "5.17.0"
resolved "https://registry.yarnpkg.com/@prisma/debug/-/debug-5.17.0.tgz#a765105848993984535b6066f8ebc6e6ead26533"
integrity sha512-l7+AteR3P8FXiYyo496zkuoiJ5r9jLQEdUuxIxNCN1ud8rdbH3GTxm+f+dCyaSv9l9WY+29L9czaVRXz9mULfg==
"@prisma/engines-version@5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053":
version "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053"
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053.tgz#3c7cc1ef3ebc34cbd069e5873b9982f2aabf5acd"
integrity sha512-tUuxZZysZDcrk5oaNOdrBnnkoTtmNQPkzINFDjz7eG6vcs9AVDmA/F6K5Plsb2aQc/l5M2EnFqn3htng9FA4hg==
"@prisma/engines@5.17.0":
version "5.17.0"
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-5.17.0.tgz#74dd1aabb22675892760b3cf69a448e3aef4616b"
integrity sha512-+r+Nf+JP210Jur+/X8SIPLtz+uW9YA4QO5IXA+KcSOBe/shT47bCcRMTYCbOESw3FFYFTwe7vU6KTWHKPiwvtg==
dependencies:
"@prisma/debug" "5.17.0"
"@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053"
"@prisma/fetch-engine" "5.17.0"
"@prisma/get-platform" "5.17.0"
"@prisma/fetch-engine@5.17.0":
version "5.17.0"
resolved "https://registry.yarnpkg.com/@prisma/fetch-engine/-/fetch-engine-5.17.0.tgz#f718dc7426411d1ebeeee53e2d0d38652387f87c"
integrity sha512-ESxiOaHuC488ilLPnrv/tM2KrPhQB5TRris/IeIV4ZvUuKeaicCl4Xj/JCQeG9IlxqOgf1cCg5h5vAzlewN91Q==
dependencies:
"@prisma/debug" "5.17.0"
"@prisma/engines-version" "5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053"
"@prisma/get-platform" "5.17.0"
"@prisma/get-platform@5.17.0":
version "5.17.0"
resolved "https://registry.yarnpkg.com/@prisma/get-platform/-/get-platform-5.17.0.tgz#89fdcae2adddebbbf0e7bd0474a6c49d6023519b"
integrity sha512-UlDgbRozCP1rfJ5Tlkf3Cnftb6srGrEQ4Nm3og+1Se2gWmCZ0hmPIi+tQikGDUVLlvOWx3Gyi9LzgRP+HTXV9w==
dependencies:
"@prisma/debug" "5.17.0"
"@react-aria/breadcrumbs@3.5.13": "@react-aria/breadcrumbs@3.5.13":
version "3.5.13" version "3.5.13"
resolved "https://registry.yarnpkg.com/@react-aria/breadcrumbs/-/breadcrumbs-3.5.13.tgz#2686f7f460f20d67fe5cdfe185e32e3e78186962" resolved "https://registry.yarnpkg.com/@react-aria/breadcrumbs/-/breadcrumbs-3.5.13.tgz#2686f7f460f20d67fe5cdfe185e32e3e78186962"
@ -2023,6 +2071,18 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.6.tgz#193ced6a40c8006cfc1ca3f4553444fb38f0e543" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.6.tgz#193ced6a40c8006cfc1ca3f4553444fb38f0e543"
integrity sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA== integrity sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==
"@types/webidl-conversions@*":
version "7.0.3"
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz#1306dbfa53768bcbcfc95a1c8cde367975581859"
integrity sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==
"@types/whatwg-url@^11.0.2":
version "11.0.5"
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-11.0.5.tgz#aaa2546e60f0c99209ca13360c32c78caf2c409f"
integrity sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==
dependencies:
"@types/webidl-conversions" "*"
abab@^2.0.5, abab@^2.0.6: abab@^2.0.5, abab@^2.0.6:
version "2.0.6" version "2.0.6"
resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz" resolved "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz"
@ -2158,6 +2218,11 @@ browser-process-hrtime@^1.0.0:
resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz"
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
bson@^6.7.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/bson/-/bson-6.8.0.tgz#5063c41ba2437c2b8ff851b50d9e36cb7aaa7525"
integrity sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==
busboy@1.6.0: busboy@1.6.0:
version "1.6.0" version "1.6.0"
resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz" resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
@ -2852,6 +2917,11 @@ mathjs@^13.0.2:
tiny-emitter "^2.1.0" tiny-emitter "^2.1.0"
typed-function "^4.2.1" typed-function "^4.2.1"
memory-pager@^1.0.2:
version "1.5.0"
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
integrity sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==
merge2@^1.3.0: merge2@^1.3.0:
version "1.4.1" version "1.4.1"
resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz"
@ -2926,6 +2996,23 @@ mkdirp@^1.0.3:
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
mongodb-connection-string-url@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz#c13e6ac284ae401752ebafdb8cd7f16c6723b141"
integrity sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==
dependencies:
"@types/whatwg-url" "^11.0.2"
whatwg-url "^13.0.0"
mongodb@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-6.8.0.tgz#680450f113cdea6d2d9f7121fe57cd29111fd2ce"
integrity sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==
dependencies:
"@mongodb-js/saslprep" "^1.1.5"
bson "^6.7.0"
mongodb-connection-string-url "^3.0.0"
ms@2.1.2: ms@2.1.2:
version "2.1.2" version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
@ -3134,12 +3221,19 @@ postcss@^8, postcss@^8.4.23:
picocolors "^1.0.0" picocolors "^1.0.0"
source-map-js "^1.2.0" source-map-js "^1.2.0"
prisma@^5.17.0:
version "5.17.0"
resolved "https://registry.yarnpkg.com/prisma/-/prisma-5.17.0.tgz#267b43921ab94805b010537cffa5ccaf530fa066"
integrity sha512-m4UWkN5lBE6yevqeOxEvmepnL5cNPEjzMw2IqDB59AcEV6w7D8vGljDLd1gPFH+W6gUxw9x7/RmN5dCS/WTPxA==
dependencies:
"@prisma/engines" "5.17.0"
psl@^1.1.33: psl@^1.1.33:
version "1.9.0" version "1.9.0"
resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz" resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz"
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
punycode@^2.1.1: punycode@^2.1.1, punycode@^2.3.0:
version "2.3.1" version "2.3.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
@ -3378,6 +3472,13 @@ source-map@~0.6.1:
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
sparse-bitfield@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz#ff4ae6e68656056ba4b3e792ab3334d38273ca11"
integrity sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==
dependencies:
memory-pager "^1.0.2"
streamsearch@^1.1.0: streamsearch@^1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz" resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
@ -3563,6 +3664,13 @@ tr46@^3.0.0:
dependencies: dependencies:
punycode "^2.1.1" punycode "^2.1.1"
tr46@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-4.1.1.tgz#281a758dcc82aeb4fe38c7dfe4d11a395aac8469"
integrity sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==
dependencies:
punycode "^2.3.0"
tr46@~0.0.3: tr46@~0.0.3:
version "0.0.3" version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@ -3690,6 +3798,14 @@ whatwg-url@^11.0.0:
tr46 "^3.0.0" tr46 "^3.0.0"
webidl-conversions "^7.0.0" webidl-conversions "^7.0.0"
whatwg-url@^13.0.0:
version "13.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-13.0.0.tgz#b7b536aca48306394a34e44bda8e99f332410f8f"
integrity sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==
dependencies:
tr46 "^4.1.1"
webidl-conversions "^7.0.0"
whatwg-url@^5.0.0: whatwg-url@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"