This commit is contained in:
hyojun.choi 2024-07-19 19:00:07 +09:00
parent 2f4560c2c9
commit 3d126dbfc7
6 changed files with 158 additions and 101 deletions

View File

@ -7,11 +7,11 @@ 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 { canvasListState, canvasSizeState, fontSizeState, sortedPolygonArray } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
export default function Roof2() { export default function Roof2() {
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage } = useCanvas('canvas') const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas, changeCanvas } = useCanvas('canvas')
//canvas //canvas
const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState) const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState)
@ -29,6 +29,9 @@ export default function Roof2() {
const [showControl, setShowControl] = useState(false) const [showControl, setShowControl] = useState(false)
const [canvasIdx, setCanvasIdx] = useState(0)
const canvasList = useRecoilValue(canvasListState)
const { mode, changeMode, handleClear, fillCellInPolygon, zoomIn, zoomOut, zoom, togglePolygonLine, handleOuterlinesTest2, applyTemplateB } = const { mode, changeMode, handleClear, fillCellInPolygon, zoomIn, zoomOut, zoom, togglePolygonLine, handleOuterlinesTest2, applyTemplateB } =
useMode() useMode()
@ -180,6 +183,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)
@ -200,17 +205,19 @@ export default function Roof2() {
const makeQLine = () => { const makeQLine = () => {
if (canvas) { if (canvas) {
const line = new QLine( const line = new fabric.QLine(
[50, 50, 200, 50], [200, 200, 500, 500],
{ {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
fontSize: fontSize, fontSize: fontSize,
selectable: true,
}, },
50, 50,
) )
canvas?.add(line) canvas?.add(line)
console.log(line)
} }
} }
@ -240,6 +247,12 @@ export default function Roof2() {
setShowControl(!showControl) setShowControl(!showControl)
} }
const nextCanvas = () => {
const nextCanvasNumber = canvasIdx < canvasList.length - 1 ? canvasIdx + 1 : 0
setCanvasIdx(nextCanvasNumber)
changeCanvas(nextCanvasNumber)
}
return ( return (
<> <>
{canvas && ( {canvas && (
@ -333,6 +346,12 @@ 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={addCanvas}>
캔버스 추가
</Button>
<Button className="m-1 p-2" onClick={nextCanvas}>
다음 캔버스
</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

@ -16,7 +16,7 @@ export class QLine extends fabric.Group {
isAlreadyInterSection = false isAlreadyInterSection = false
interSectionPoints = [] interSectionPoints = []
#lengthTxt = 0 lengthTxt = 0
constructor(points, option = { isActiveLengthText: true }, lengthTxt) { constructor(points, option = { isActiveLengthText: true }, lengthTxt) {
// 소수점 전부 제거 // 소수점 전부 제거
@ -44,24 +44,24 @@ export class QLine extends fabric.Group {
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', () => {
@ -73,19 +73,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
@ -115,4 +115,37 @@ export class QLine extends fabric.Group {
this.text.set({ fontSize }) this.text.set({ fontSize })
this.addWithUpdate() this.addWithUpdate()
} }
fromObject(object, callback) {
fabric.util.enlivenObjects(object.objects, function (enlivenedObjects) {
delete object.objects
callback && callback(new fabric.Frindle(enlivenedObjects, object))
})
}
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,
})
}
} }

View File

@ -28,6 +28,7 @@ export default class QPolygon extends fabric.Group {
helpLines = [] helpLines = []
wall wall
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.')
@ -51,13 +52,13 @@ export default class QPolygon extends fabric.Group {
this.points = sortPoints this.points = sortPoints
this.polygon = polygon this.polygon = polygon
this.name = options.name this.name = options.name
this.#init() this.init()
this.#addEvent() this.addEvent()
this.#initLines() 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], {
@ -71,13 +72,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 () {
@ -110,7 +111,7 @@ export default class QPolygon extends fabric.Group {
this.addWithUpdate() this.addWithUpdate()
} }
#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)
@ -142,7 +143,7 @@ export default class QPolygon extends fabric.Group {
this.canvas.renderAll() this.canvas.renderAll()
} }
#updateLengthText() { updateLengthText() {
const points = this.getCurrentPoints() const points = this.getCurrentPoints()
points.forEach((start, i) => { points.forEach((start, i) => {
@ -188,7 +189,7 @@ 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.addWithUpdate(rect)
@ -245,7 +246,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
@ -317,13 +318,13 @@ 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 || this.lines.length === 8) { } else if (this.lines.length === 6 || this.lines.length === 8) {
// TODO : 6각형 // TODO : 6각형
this.#drawHelpLineInHexagon2(chon) this.drawHelpLineInHexagon2(chon)
} /* else if (this.lines.length === 8) { } /* else if (this.lines.length === 8) {
// TODO : 8각형 // TODO : 8각형
this.#drawHelpLineInOctagon(chon) this.drawHelpLineInOctagon(chon)
}*/ }*/
} }
@ -370,7 +371,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
@ -526,7 +527,7 @@ export default class QPolygon extends fabric.Group {
this.canvas.add(ridge) this.canvas.add(ridge)
} }
} }
#drawHelpLineInHexagon2(chon) { drawHelpLineInHexagon2(chon) {
const oneSideLines = [...this.lines].map((line) => { const oneSideLines = [...this.lines].map((line) => {
let newX1, newY1, newX2, newY2 let newX1, newY1, newX2, newY2
if (line.direction === 'top') { if (line.direction === 'top') {
@ -742,7 +743,7 @@ export default class QPolygon extends fabric.Group {
this.canvas.renderAll() this.canvas.renderAll()
}) })
} }
#drawHelpLineInHexagon(chon) { drawHelpLineInHexagon(chon) {
const historyLines = [] const historyLines = []
const helpPoints = [] const helpPoints = []
const notInterSectionLines = [] const notInterSectionLines = []
@ -884,5 +885,9 @@ export default class QPolygon extends fabric.Group {
this.canvas.renderAll() this.canvas.renderAll()
} }
#drawHelpLineInOctagon(chon) {} drawHelpLineInOctagon(chon) {}
getObject() {
return this
}
} }

View File

@ -1,13 +1,9 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { import { actionHandler, anchorWrapper, getDirectionByPoint, polygonPositionHandler, sortedPoints } 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 { canvasListState, canvasSizeState, fontSizeState } from '@/store/canvasAtom'
import QPolygon from '@/components/fabric/QPolygon' import QPolygon from '@/components/fabric/QPolygon'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import QRect from '@/components/fabric/QRect' import QRect from '@/components/fabric/QRect'
@ -20,6 +16,8 @@ export function useCanvas(id) {
const [fontSize] = useRecoilState(fontSizeState) const [fontSize] = useRecoilState(fontSizeState)
const points = useRef([]) const points = useRef([])
const [canvasList, setCanvasList] = useRecoilState(canvasListState)
/** /**
* 처음 셋팅 * 처음 셋팅
*/ */
@ -31,17 +29,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 +44,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 +98,26 @@ 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 = QLine
// addEventOnCanvas()
} }
/** /**
@ -176,26 +153,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 +296,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 +449,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 +538,33 @@ export function useCanvas(id) {
}) })
} }
const addCanvas = () => {
const canvasState = canvas?.getObjects()
canvasState.forEach((state) => {
console.log(state)
})
const str = JSON.stringify(canvasState)
canvas?.clear()
JSON.parse(str).forEach((state) => {
canvas?.add(state)
})
/*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 +579,7 @@ export function useCanvas(id) {
saveImage, saveImage,
handleFlip, handleFlip,
setCanvasBackgroundWithDots, setCanvasBackgroundWithDots,
addCanvas,
changeCanvas,
} }
} }

View File

@ -718,8 +718,6 @@ export function useMode() {
const handleOuterlinesTest = (polygon, offset = 71) => { const handleOuterlinesTest = (polygon, offset = 71) => {
var offsetPoints = [] var offsetPoints = []
debugger
const sortedIndex = getStartIndex(polygon.lines) const sortedIndex = getStartIndex(polygon.lines)
let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex) let tmpArraySorted = rearrangeArray(polygon.lines, sortedIndex)

View File

@ -35,3 +35,9 @@ export const wallState = atom({
default: {}, default: {},
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })
export const canvasListState = atom({
key: 'canvas',
default: [],
dangerouslyAllowMutability: true,
})