Merge branch 'feature/test' into feature/test-jy

# Conflicts:
#	src/components/Roof2.jsx
#	src/components/fabric/QPolygon.js
This commit is contained in:
Jaeyoung Lee 2024-07-24 14:48:53 +09:00
commit 1704efc9d5
14 changed files with 1554 additions and 256 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": {
"@nextui-org/react": "^2.4.2",
"@prisma/client": "^5.17.0",
"fabric": "^5.3.0",
"framer-motion": "^11.2.13",
"mathjs": "^13.0.2",
"mongodb": "^6.8.0",
"next": "14.2.3",
"react": "^18",
"react-dom": "^18",
@ -21,6 +23,7 @@
},
"devDependencies": {
"postcss": "^8",
"prisma": "^5.17.0",
"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

@ -6,12 +6,14 @@ import QRect from '@/components/fabric/QRect'
import QPolygon from '@/components/fabric/QPolygon'
import RangeSlider from './ui/RangeSlider'
import { useRecoilState } from 'recoil'
import { canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasAtom, canvasListState, canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine'
import { getTests, getCanvasState, insertCanvasState } from '@/lib/canvas'
import { calculateIntersection2 } from '@/util/canvas-util'
export default function Roof2() {
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage } = useCanvas('canvas')
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas')
//canvas
const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState)
@ -38,11 +40,14 @@ export default function Roof2() {
zoomOut,
zoom,
togglePolygonLine,
handleOuterlinesTest,
handleOuterlinesTest2,
applyTemplateB,
makeRoofPatternPolygon,
} = useMode()
// const [canvasState, setCanvasState] = useRecoilState(canvasAtom)
useEffect(() => {
if (!canvas) {
return
@ -190,6 +195,18 @@ export default function Roof2() {
{ 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) {
const polygon = new QPolygon(eightPoint, {
fill: 'transparent',
@ -202,6 +219,8 @@ export default function Roof2() {
canvas?.add(polygon)
console.log(polygon)
handleOuterlinesTest2(polygon)
// const lines = togglePolygonLine(polygon)
@ -223,16 +242,44 @@ export default function Roof2() {
const makeQLine = () => {
if (canvas) {
const line = new QLine(
[50, 50, 200, 50],
[50, 250, 900, 250],
{
stroke: 'black',
strokeWidth: 1,
strokeWidth: 5,
fontSize: fontSize,
selectable: true,
},
50,
)
const line2 = new QLine(
[450, 450, 821, 78],
{
stroke: 'black',
strokeWidth: 5,
fontSize: fontSize,
selectable: true,
},
50,
)
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)
}
}
}
@ -258,6 +305,36 @@ export default function Roof2() {
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 = () => {
setShowControl(!showControl)
}
@ -373,6 +450,15 @@ export default function Roof2() {
<Button className="m-1 p-2" onClick={PolygonToLine}>
PolygonToLine
</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}>
canvas 컨트롤러 {`${showControl ? '숨기기' : '보이기'}`}
</Button>

View File

@ -1,4 +1,5 @@
import { fabric } from 'fabric'
import { getDirection, getDirectionByPoint } from '@/util/canvas-util'
export class QLine extends fabric.Group {
line
@ -16,7 +17,11 @@ export class QLine extends fabric.Group {
isAlreadyInterSection = false
interSectionPoints = []
#lengthTxt = 0
lengthTxt = 0
initPoints
initOption
initLengthTxt
constructor(points, option = { isActiveLengthText: true }, lengthTxt) {
// 소수점 전부 제거
@ -33,38 +38,43 @@ export class QLine extends fabric.Group {
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
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)
this.lengthTxt = Number(lengthTxt)
}
option.isActiveLengthText ?? this.#init()
this.#addControl()
option.isActiveLengthText ?? this.init()
this.addControl()
}
#init() {
this.#addLengthText(true)
init() {
this.addLengthText(true)
}
#addControl() {
addControl() {
this.on('moving', () => {
this.#addLengthText(false)
this.addLengthText(false)
})
this.on('modified', (e) => {
this.#addLengthText(false)
this.addLengthText(false)
})
this.on('selected', () => {
console.log(this)
Object.keys(this.controls).forEach((controlKey) => {
if (controlKey !== 'ml' && controlKey !== 'mr') {
this.setControlVisible(controlKey, false)
@ -73,19 +83,19 @@ export class QLine extends fabric.Group {
})
}
#addLengthText(isFirst) {
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(), {
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.length = this.lengthTxt
this.text = text
this.addWithUpdate(text)
return
@ -115,4 +125,41 @@ export class QLine extends fabric.Group {
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) {
this.callSuper('_set', key, value)
}
}

View File

@ -12,6 +12,7 @@ import {
sortedPoints,
} from '@/util/canvas-util'
import { QLine } from '@/components/fabric/QLine'
import { drawHelpLineInHexagon2 } from '@/util/qpolygon-utils'
export default class QPolygon extends fabric.Group {
type = 'QPolygon'
@ -28,8 +29,9 @@ export default class QPolygon extends fabric.Group {
helpLines = []
wall
ridges = []
hips = []
initPoints
initOption
constructor(points, options, canvas) {
/*if (points.length !== 4 && points.length !== 6) {
@ -48,19 +50,23 @@ export default class QPolygon extends fabric.Group {
const sortPoints = sortedPoints(points)
const polygon = new fabric.Polygon(sortPoints, options)
super([polygon], {})
super([polygon], { selectable: false })
this.fontSize = options.fontSize
this.points = sortPoints
this.polygon = polygon
this.name = options.name
this.#init()
this.#addEvent()
this.#initLines()
this.initPoints = points
this.initOption = options
this.init()
this.addEvent()
this.initLines()
this.setShape()
}
#initLines() {
initLines() {
this.points.forEach((point, i) => {
const nextPoint = this.points[(i + 1) % this.points.length]
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
@ -74,13 +80,13 @@ export default class QPolygon extends fabric.Group {
})
}
#init() {
this.#addLengthText()
init() {
this.addLengthText()
}
#addEvent() {
addEvent() {
this.on('scaling', (e) => {
this.#updateLengthText()
this.updateLengthText()
})
this.on('selected', function() {
@ -110,10 +116,10 @@ export default class QPolygon extends fabric.Group {
}
})
this.addWithUpdate()
this.canvas.add()
}
#addLengthText() {
addLengthText() {
if (this.texts.length > 0) {
this.texts.forEach((text) => {
this.canvas.remove(text)
@ -139,13 +145,13 @@ export default class QPolygon extends fabric.Group {
})
this.texts.push(text)
this.addWithUpdate(text)
this.canvas.add(text)
})
this.canvas.renderAll()
}
#updateLengthText() {
updateLengthText() {
const points = this.getCurrentPoints()
points.forEach((start, i) => {
@ -191,10 +197,10 @@ export default class QPolygon extends fabric.Group {
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) {
this.addWithUpdate(rect)
this.canvas.add(rect)
}
}
}
@ -248,7 +254,7 @@ export default class QPolygon extends fabric.Group {
return intersects % 2 === 1
}
#distanceFromEdge(point) {
distanceFromEdge(point) {
const vertices = this.getCurrentPoints()
let minDistance = Infinity
@ -375,7 +381,7 @@ export default class QPolygon extends fabric.Group {
return this.shape
}
#drawHelpLineInRect(chon) {
drawHelpLineInRect(chon) {
let type = 1
let smallestLength = Infinity
let maxLength = 0
@ -520,19 +526,18 @@ export default class QPolygon extends fabric.Group {
strokeWidth: 1,
})
this.addWithUpdate(realLine1)
this.addWithUpdate(realLine2)
this.addWithUpdate(realLine3)
this.addWithUpdate(realLine4)
this.addWithUpdate(realLine5)
this.addWithUpdate(realLine6)
this.canvas.add(realLine1)
this.canvas.add(realLine2)
this.canvas.add(realLine3)
this.canvas.add(realLine4)
this.canvas.add(realLine5)
this.canvas.add(realLine6)
if (smallestLength !== maxLength) {
// 정사각형이 아닌경우에만 용마루를 추가한다.
this.canvas.add(ridge)
}
}
#drawHelpLineInHexagon2(chon) {
drawHelpLineInHexagon2(chon) {
const oneSideLines = [...this.lines].map((line) => {
let newX1, newY1, newX2, newY2
if (line.direction === 'top') {
@ -748,8 +753,7 @@ export default class QPolygon extends fabric.Group {
this.canvas.renderAll()
})
}
#drawHelpLineInHexagon(chon) {
drawHelpLineInHexagon(chon) {
const historyLines = []
const helpPoints = []
const notInterSectionLines = []
@ -762,133 +766,146 @@ export default class QPolygon extends fabric.Group {
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
let newX2, newY2
let newX2, newY2
// x1, y1을 기준으로 x2, y2와의 거리를 유지한 새로운 직선 생성
const angle = Math.atan2(y2 - y1, x2 - x1)
// 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)
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,
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.push(line)
this.addWithUpdate(line)
/**
* 삼각 지붕
*/
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()
})
/**
* 삼각 지붕
*/
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.addWithUpdate(helpLine1)
this.addWithUpdate(helpLine2)
this.removeWithUpdate(prevLine)
this.removeWithUpdate(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.addWithUpdate(ridge)
this.canvas.renderAll()
this.addWithUpdate(ridgeHelpLine)
this.removeWithUpdate(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.addWithUpdate(ridgeConnectLine)
this.canvas.renderAll()
}
this.canvas.renderAll()
}
#drawHelpLineInOctagon(chon) {

View File

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

View File

@ -1,9 +1,10 @@
import { useRef, useState } from 'react'
import { useEffect, useRef, useState } from 'react'
import QRect from '@/components/fabric/QRect'
import QPolygon from '@/components/fabric/QPolygon'
import { findTopTwoIndexesByDistance, getCenterPoint, getDirection, getStartIndex, rearrangeArray } from '@/util/canvas-util'
import { useRecoilState } from 'recoil'
import { fontSizeState, roofState, sortedPolygonArray, roofPolygonPatternArrayState, wallState } from '@/store/canvasAtom'
import { fontSizeState, roofPolygonPatternArrayState, roofState, sortedPolygonArray, wallState } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine'
export const Mode = {
@ -30,8 +31,22 @@ export function useMode() {
const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray)
const [roof, setRoof] = useRecoilState(roofState)
const [wall, setWall] = useRecoilState(wallState)
const [roofPolygonPattern, setRoofPolygonPattern] = useRecoilState(roofPolygonPatternArrayState)
useEffect(() => {
// 이벤트 리스너 추가
if (!canvas) {
return
}
document.addEventListener('keydown', handleKeyDown)
// 컴포넌트가 언마운트될 때 이벤트 리스너 제거
return () => {
document.removeEventListener('keydown', handleKeyDown)
}
}, [canvas]) // 빈 배열을 전달하여 컴포넌트가 마운트될 때만 실행되도록 함
const addEvent = (mode) => {
switch (mode) {
case 'default':
@ -64,6 +79,147 @@ export function useMode() {
}
}
const keyValid = () => {
if (points.current.length === 0) {
alert('시작점을 선택해주세요')
return false
}
return true
}
const drawCircleAndLine = (verticalLength, horizontalLength) => {
const circle = new fabric.Circle({
radius: 5,
fill: 'transparent', // 원 안을 비웁니다.
stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
left: points.current[points.current.length - 1].left + horizontalLength - 5,
top: points.current[points.current.length - 1].top + verticalLength - 5,
originX: 'center',
originY: 'center',
selectable: false,
})
historyPoints.current.push(circle)
points.current.push(circle)
canvas?.add(circle)
canvas?.renderAll()
// length 값이 숫자가 아닌 경우
if (isNaN(length) || Math.max(Math.abs(verticalLength), Math.abs(horizontalLength)) === 0) {
//마지막 추가 된 points 제거합니다.
const lastPoint = historyPoints.current[historyPoints.current.length - 1]
canvas?.remove(lastPoint)
historyPoints.current.pop()
points.current.pop()
return
}
const line = new QLine(
[points.current[0].left, points.current[0].top, points.current[0].left + horizontalLength, points.current[0].top + verticalLength],
{
stroke: 'black',
strokeWidth: 2,
selectable: false,
viewLengthText: true,
direction: getDirection(points.current[0], points.current[1]),
fontSize: fontSize,
},
)
pushHistoryLine(line)
// 라인의 끝에 점을 추가합니다.
const endPointCircle = new fabric.Circle({
radius: 1,
fill: 'transparent', // 원 안을 비웁니다.
stroke: 'black', // 원 테두리 색상을 검은색으로 설정합니다.
left: points.current[0].left + horizontalLength,
top: points.current[0].top + verticalLength,
originX: 'center',
originY: 'center',
selectable: false,
})
canvas?.add(endPointCircle)
historyPoints.current.push(endPointCircle)
points.current.forEach((point) => {
canvas?.remove(point)
})
points.current = [endPointCircle]
canvas.renderAll()
}
const handleKeyDown = (e) => {
switch (e.key) {
case 'ArrowDown': {
if (!keyValid()) {
return
}
const verticalLength = Number(prompt('길이를 입력하세요:'))
const horizontalLength = 0
drawCircleAndLine(verticalLength, horizontalLength)
break
}
case 'ArrowUp': {
if (!keyValid()) {
return
}
const verticalLength = -Number(prompt('길이를 입력하세요:'))
const horizontalLength = 0
drawCircleAndLine(verticalLength, horizontalLength)
break
}
case 'ArrowLeft': {
if (!keyValid()) {
return
}
const verticalLength = 0
const horizontalLength = -Number(prompt('길이를 입력하세요:'))
drawCircleAndLine(verticalLength, horizontalLength)
break
}
case 'ArrowRight': {
if (!keyValid()) {
return
}
const verticalLength = 0
const horizontalLength = Number(prompt('길이를 입력하세요:'))
drawCircleAndLine(verticalLength, horizontalLength)
break
}
case 'Enter': {
const result = prompt('입력하세요 (a(A패턴),b(B패턴),t(지붕))')
switch (result) {
case 'a':
applyTemplateA()
break
case 'b':
applyTemplateB()
break
case 't':
templateMode()
break
}
}
}
}
const changeMode = (canvas, mode) => {
setMode(mode)
// mode변경 시 이전 이벤트 제거
@ -720,10 +876,17 @@ export function useMode() {
/**
* 지붕 외곽선 생성
*/
const handleOuterlinesTest = (polygon, offset = 71) => {
var offsetPoints = []
const handleOuterlinesTest = (offsetInputX, offsetInputY = 0) => {
const polygon = drawWallPolygon()
debugger
let offsetPoints = []
const originalMax = 71
const transformedMax = 100
offsetInputY = offsetInputY !== 0 ? offsetInputY : offsetInputX
const offsetX = (offsetInputX / transformedMax) * originalMax * 2
const offsetY = (offsetInputY / transformedMax) * originalMax * 2
const sortedIndex = getStartIndex(polygon.lines)
let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)
@ -776,17 +939,14 @@ export function useMode() {
// 오프셋 적용
var offsetPoint = {
x1: current.x + unitNormal.x * offset,
y1: current.y + unitNormal.y * offset,
x1: current.x + unitNormal.x * offsetX,
y1: current.y + unitNormal.y * offsetY,
}
offsetPoints.push(offsetPoint)
}
const roof = makePolygon(offsetPoints)
setRoof(roof)
return roof
makePolygon(offsetPoints)
}
/**
@ -855,6 +1015,7 @@ export function useMode() {
const roof = makePolygon(offsetPoints)
roof.setWall(polygon)
setRoof(roof)
roof.drawHelpLine()
}
@ -905,6 +1066,8 @@ export function useMode() {
} else if (polygon.lines.length === 6) {
//6각형
handleOuterLineTemplateA6Points(polygon)
} else if (polygon.lines.length === 8) {
handleOuterLineTemplateA8Points(polygon)
}
}
@ -1721,6 +1884,214 @@ export function useMode() {
canvas.renderAll()
}
const handleOuterLineTemplateA8Points = (polygon, offsetInputX = 50, offsetInputY = 20) => {
let offsetPoints = []
const originalMax = 71
const transformedMax = 100
let lines = [] //내각라인
let outLines = [] //아웃라인
let halfLength = 0 //선길이
const dashedCenterLineOpt = {
stroke: 'black',
strokeWidth: 4,
property: 'centerLine',
strokeDashArray: [5, 5],
fontSize: 14,
}
const centerLineOpt = {
stroke: 'blue',
strokeWidth: 5,
property: 'bigHoriCenter',
fontSize: 14,
}
// 폴리곤의 각 변을 선으로 생성
for (let i = 0; i < polygon.points.length; i++) {
const start = polygon.points[i]
const end = polygon.points[(i + 1) % polygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로
const color = i % 2 === 0 ? '#A0D468' : 'skyblue'
const line = new QLine([start.x, start.y, end.x, end.y], {
stroke: color,
strokeWidth: 2,
property: 'normal',
fontSize: 14,
})
// 선을 배열에 추가
lines.push(line)
canvas.add(line)
}
offsetInputY = offsetInputY !== 0 ? offsetInputY : offsetInputX
const sortedIndex = getStartIndex(polygon.lines)
let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)
if (tmpArraySorted[0].direction === 'right') {
//시계방향
tmpArraySorted = tmpArraySorted.reverse() //그럼 배열을 거꾸로 만들어서 무조건 반시계방향으로 배열 보정
}
setSortedArray(tmpArraySorted) //recoil에 넣음
const points = tmpArraySorted.map((line) => ({
x: line.x1,
y: line.y1,
}))
// 외적을 계산하는 함수
function crossProduct(p1, p2, p3) {
const dx1 = p2.x - p1.x
const dy1 = p2.y - p1.y
const dx2 = p3.x - p2.x
const dy2 = p3.y - p2.y
return dx1 * dy2 - dy1 * dx2
}
let concaveIndices = [] //볼록한 부분 인덱스 배열
let concavePointIndices = [] //오목한 부분 인덱스 배열
// 오목한 부분 찾기
function findConcavePointIndices(points) {
let concaveIndices = []
for (let i = 0; i < points.length; i++) {
const p1 = points[i]
const p2 = points[(i + 1) % points.length]
const p3 = points[(i + 2) % points.length]
const cross = crossProduct(p1, p2, p3)
if (cross < 0) {
concaveIndices.push((i + 1) % points.length)
} else {
concavePointIndices.push((i + 1) % points.length)
}
}
return concaveIndices
}
// 오목한 부분 인덱스 찾기
concaveIndices = findConcavePointIndices(points) //오목한 부분을 제외한 인덱스
const concavePoints = concaveIndices.map((index) => points[index])
for (var i = 0; i < points.length; i++) {
var prev = points[(i - 1 + points.length) % points.length]
var current = points[i]
var next = points[(i + 1) % points.length]
// 두 벡터 계산 (prev -> current, current -> next)
var vector1 = { x: current.x - prev.x, y: current.y - prev.y }
var vector2 = { x: next.x - current.x, y: next.y - current.y }
// 벡터의 길이 계산
var length1 = Math.sqrt(vector1.x * vector1.x + vector1.y * vector1.y)
var length2 = Math.sqrt(vector2.x * vector2.x + vector2.y * vector2.y)
// 벡터를 단위 벡터로 정규화
var unitVector1 = { x: vector1.x / length1, y: vector1.y / length1 }
var unitVector2 = { x: vector2.x / length2, y: vector2.y / length2 }
// 법선 벡터 계산 (왼쪽 방향)
var normal1 = { x: -unitVector1.y, y: unitVector1.x }
var normal2 = { x: -unitVector2.y, y: unitVector2.x }
// 법선 벡터 평균 계산
var averageNormal = {
x: (normal1.x + normal2.x) / 2,
y: (normal1.y + normal2.y) / 2,
}
// 평균 법선 벡터를 단위 벡터로 정규화
var lengthNormal = Math.sqrt(averageNormal.x * averageNormal.x + averageNormal.y * averageNormal.y)
var unitNormal = {
x: averageNormal.x / lengthNormal,
y: averageNormal.y / lengthNormal,
}
let offsetX
let offsetY
if (concavePointIndices[0] === i || concavePointIndices[1] === i) {
// 인덱스가 배열이랑 같으면
if ((concavePointIndices[0] === 4 && concavePointIndices[1] === 5) || (concavePointIndices[0] === 2 && concavePointIndices[1] === 3)) {
offsetX = 1
offsetY = (offsetInputY / transformedMax) * originalMax * 2
} else {
offsetX = (offsetInputX / transformedMax) * originalMax * 2
offsetY = 1
}
} else {
offsetX = (offsetInputX / transformedMax) * originalMax * 2
offsetY = (offsetInputY / transformedMax) * originalMax * 2
}
// 오프셋 적용
var offsetPoint = {
x1: current.x + unitNormal.x * offsetX,
y1: current.y + unitNormal.y * offsetY,
}
offsetPoints.push(offsetPoint)
}
const outlinePolygon = makePolygon(offsetPoints)
// 아웃라인 폴리곤의 각 변을 선으로 생성
for (let i = 0; i < outlinePolygon.points.length; i++) {
const start = outlinePolygon.points[i]
const end = outlinePolygon.points[(i + 1) % outlinePolygon.points.length] // 다음 점, 마지막 점의 경우 첫 점으로
const line = new QLine([start.x, start.y, end.x, end.y], {
stroke: 'blue',
strokeWidth: 2,
property: 'normal',
fontSize: 14,
})
// 선을 배열에 추가
outLines.push(line)
canvas.add(line)
}
canvas?.remove(outlinePolygon) //임시 폴리곤을 삭제
canvas?.remove(outLines[concavePointIndices[0]]) //가운데 제외되는 선을 지운다
//라인들을 좌측에서 -> 우측으로 그리는거처럼 데이터 보정
outLines.forEach((outline, index) => {
let minX, minY, maxX, maxY
if (outline.x2 < outline.x1 || outline.y2 < outline.y1) {
outLines[index].x1 = outline.x2
outLines[index].y1 = outline.y2
outLines[index].x2 = outline.x1
outLines[index].y2 = outline.y1
outLines[index].line.x1 = minX
outLines[index].line.y1 = minY
outLines[index].line.x2 = maxX
outLines[index].line.y2 = maxY
}
})
// for (let i = 0; i < outLines.length; i++) {
// if (!i % 2 === 0) {
// if (i === concavePointIndices[0] - 1 || i === concavePointIndices[1] + 1) {
// //배열 3번이나 5번일때
// } else {
// }
// }
// }
let parallelLines = concavePointIndices[0] + 4 //들어간선에 무조건 평행하는 선 찾기
if (parallelLines > outLines.length) {
parallelLines = outLines[concavePointIndices[0] + 4 - outLines.length - 1]
}
canvas.renderAll()
}
/**
* 템플릿 B 적용
*/
@ -2217,6 +2588,7 @@ export function useMode() {
zoomOut,
zoom,
togglePolygonLine,
handleOuterlinesTest,
handleOuterlinesTest2,
makeRoofPatternPolygon,
}

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

@ -312,21 +312,109 @@ export function calculateIntersection(line1, line2) {
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)
return { x: Math.round(intersectionX), y: Math.round(intersectionY) }
// 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 calculateIntersection2 = (line1, line2) => {
const result = intersect([line1.x1, line1.y1], [line1.x2, line1.y2], [line2.x1, line2.y1], [line2.x2, line2.y2])
return { x: Math.round(result[0]), y: Math.round(result[1]) }
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 }
}
function findOrthogonalPoint(x1, y1, x2, y2, x3, y3, x4, y4) {
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
const intersectionX = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
const intersectionY = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / ((x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4))
const intersectionX =
((line1.x1 * line2.y1 - line1.y1 * line2.x1) * (line2.x2 - line2.x1) - (line1.x1 - line1.x2) * (line2.x2 * line2.y2 - line2.y1 * line2.x1)) /
((line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x2 - line2.x1))
const intersectionY =
((line1.x1 * line2.y1 - line1.y1 * line2.x1) * (line2.y2 - line2.y1) - (line1.y1 - line1.y2) * (line2.x2 * line2.y2 - line2.y1 * line2.x1)) /
((line1.x1 - line1.x2) * (line2.y1 - line2.y2) - (line1.y1 - line1.y2) * (line2.x2 - line2.x1))
return { x: intersectionX, y: intersectionY }
}
@ -338,7 +426,6 @@ function findOrthogonalPoint(x1, y1, x2, y2, x3, y3, x4, y4) {
export const sortedPoints = (points) => {
const copyPoints = [...points]
//points를 x,y좌표를 기준으로 정렬합니다.
copyPoints.sort((a, b) => {
if (a.x === b.x) {
return a.y - b.y
@ -354,7 +441,6 @@ export const sortedPoints = (points) => {
let index = 1
let currentPoint = { ...copyPoints[0] }
copyPoints.splice(0, 1)
while (index < points.length) {
if (index === points.length - 1) {
resultPoints.push(copyPoints[0])
@ -364,9 +450,15 @@ export const sortedPoints = (points) => {
// 짝수번째는 y값이 같은 점을 찾는다.
for (let i = 0; i < copyPoints.length; i++) {
// y값이 같은 point가 많은 경우 그 중 x값이 가장 큰걸 찾는다.
const temp = copyPoints.filter((point) => point.y === currentPoint.y)
// temp중 x값이 가장 큰 값 copyPoint보다 큰값
const min = temp.reduce((prev, current) => (prev.x <= current.x ? prev : current))
let temp = copyPoints.filter((point) => point.y === currentPoint.y)
if (temp.length === 0) {
// temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
temp = Array.of(findClosestPointByY(currentPoint, copyPoints))
}
// temp중 x값이 가장 가까운 값
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)
@ -377,9 +469,14 @@ export const sortedPoints = (points) => {
// 홀수번째는 x값이 같은 점을 찾는다.
for (let i = 0; i < copyPoints.length; i++) {
// x값이 같은 point가 많은 경우 그 중 y값이 가장 큰걸 찾는다.
const temp = copyPoints.filter((point) => point.x === currentPoint.x)
// temp중 y값이 가장 작은 값
const min = temp.reduce((prev, current) => (prev.y <= current.y ? prev : current))
let temp = copyPoints.filter((point) => point.x === currentPoint.x)
if (temp.length === 0) {
// temp가 비어있을 경우 copyPoints에서 가장 가까운 점을 찾는다.
temp = Array.of(findClosestPointByX(currentPoint, copyPoints))
}
// 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
@ -389,7 +486,6 @@ export const sortedPoints = (points) => {
}
}
}
return resultPoints
}
@ -430,6 +526,81 @@ export function findClosestLineToPoint(point, lines) {
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
@ -448,3 +619,33 @@ export function calculateDistance(point, line) {
const denominator = Math.sqrt(Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2))
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"
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":
version "14.2.3"
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"
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":
version "3.5.13"
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"
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:
version "2.0.6"
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"
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:
version "1.6.0"
resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
@ -2852,6 +2917,11 @@ mathjs@^13.0.2:
tiny-emitter "^2.1.0"
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:
version "1.4.1"
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"
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:
version "2.1.2"
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"
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:
version "1.9.0"
resolved "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz"
integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
punycode@^2.1.1:
punycode@^2.1.1, punycode@^2.3.0:
version "2.3.1"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
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"
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:
version "1.1.0"
resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
@ -3563,6 +3664,13 @@ tr46@^3.0.0:
dependencies:
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:
version "0.0.3"
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"
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:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"