Merge branch 'dev' into dev-test2

This commit is contained in:
changkyu choi 2024-08-23 14:27:17 +09:00
commit 693a80a061
5 changed files with 111 additions and 22 deletions

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useCanvas } from '@/hooks/useCanvas' import { useCanvas } from '@/hooks/useCanvas'
import { useEffect, useState, useRef } from 'react' import { useEffect, useRef, useState } from 'react'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
@ -59,6 +59,8 @@ export default function Roof2(props) {
const wall = useRecoilValue(wallState) const wall = useRecoilValue(wallState)
const [scale, setScale] = useState(1)
//canvas //canvas
const [thumbnails, setThumbnails] = useState([]) const [thumbnails, setThumbnails] = useState([])
const thumbnailProps = { const thumbnailProps = {
@ -547,6 +549,16 @@ export default function Roof2(props) {
setCompass(degree) setCompass(degree)
} }
const changeLength = (e) => {
setScale(e)
const polygon = canvas?.getActiveObject()
if (polygon.type !== 'QPolygon') {
return
}
canvas?.renderAll()
}
return ( return (
<> <>
{canvas && ( {canvas && (
@ -643,7 +655,7 @@ export default function Roof2(props) {
<Button <Button
className="m-1 p-2" className="m-1 p-2"
onClick={() => { onClick={() => {
setCanvasBackgroundWithDots(canvas, 20) setCanvasBackgroundWithDots(canvas, 200)
}} }}
> >
점선 추가 점선 추가
@ -721,6 +733,9 @@ export default function Roof2(props) {
<div className="m-2 p-2 w-80"> <div className="m-2 p-2 w-80">
<RangeSlider title={`글자 크기${fontSize}`} initValue={fontSize} onchange={setFontSize} /> <RangeSlider title={`글자 크기${fontSize}`} initValue={fontSize} onchange={setFontSize} />
</div> </div>
<div className="m-2 p-2 w-80">
<RangeSlider title={`선택한 obj 가로 늘리기${scale}`} initValue={scale} min={0.01} max={2.0} step={0.01} onchange={changeLength} />
</div>
</div> </div>
</> </>
)} )}

View File

@ -1,10 +1,10 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useState } from 'react'
import { fabric } from 'fabric' 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 { useRecoilState, useSetRecoilState } from 'recoil'
import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' import { canvasSizeState, fontSizeState, guidePointModeState } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import { defineQLine } from '@/util/qline-utils' import { defineQLine } from '@/util/qline-utils'
@ -20,6 +20,7 @@ export function useCanvas(id) {
const [canvasSize] = useRecoilState(canvasSizeState) const [canvasSize] = useRecoilState(canvasSizeState)
const [fontSize] = useRecoilState(fontSizeState) const [fontSize] = useRecoilState(fontSizeState)
const { setCanvasForEvent, attachDefaultEventOnCanvas } = useCanvasEvent() const { setCanvasForEvent, attachDefaultEventOnCanvas } = useCanvasEvent()
const setGuidePointMode = useSetRecoilState(guidePointModeState)
/** /**
* 처음 셋팅 * 처음 셋팅
@ -400,6 +401,7 @@ export function useCanvas(id) {
} }
const setCanvasBackgroundWithDots = (canvas, gap) => { const setCanvasBackgroundWithDots = (canvas, gap) => {
setGuidePointMode(true)
// Create a new canvas and fill it with dots // Create a new canvas and fill it with dots
const tempCanvas = new fabric.StaticCanvas() const tempCanvas = new fabric.StaticCanvas()
tempCanvas.setDimensions({ tempCanvas.setDimensions({

View File

@ -18,9 +18,6 @@ export function useCanvasEvent() {
canvas?.on('object:added', () => { canvas?.on('object:added', () => {
document.addEventListener('keydown', handleKeyDown) document.addEventListener('keydown', handleKeyDown)
}) })
canvas?.on('mouse:move', drawMouseLines)
canvas?.on('mouse:out', removeMouseLines)
} }
const onChange = (e) => { const onChange = (e) => {

View File

@ -7,23 +7,22 @@ import {
getDirection, getDirection,
getStartIndex, getStartIndex,
rearrangeArray, rearrangeArray,
getRoofHeight,
getDegreeByChon,
} from '@/util/canvas-util' } from '@/util/canvas-util'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { import {
canvasSizeState, canvasSizeState,
compassState,
drewRoofCellsState, drewRoofCellsState,
fontSizeState, fontSizeState,
guidePointModeState,
modeState,
roofPolygonArrayState, roofPolygonArrayState,
roofPolygonPatternArrayState, roofPolygonPatternArrayState,
roofState, roofState,
sortedPolygonArray, sortedPolygonArray,
templateTypeState, templateTypeState,
wallState, wallState,
compassState,
modeState,
} from '@/store/canvasAtom' } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { fabric } from 'fabric' import { fabric } from 'fabric'
@ -46,6 +45,7 @@ export function useMode() {
const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray) const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray)
const [roof, setRoof] = useRecoilState(roofState) const [roof, setRoof] = useRecoilState(roofState)
const [wall, setWall] = useRecoilState(wallState) const [wall, setWall] = useRecoilState(wallState)
const isGuidePointMode = useRecoilValue(guidePointModeState)
const [endPoint, setEndPoint] = useState(null) const [endPoint, setEndPoint] = useState(null)
@ -67,11 +67,11 @@ export function useMode() {
useEffect(() => { useEffect(() => {
// 이벤트 리스너 추가 // 이벤트 리스너 추가
// if (!canvas) { // if (!canvas) {
canvas?.setZoom(0.8) // canvas?.setZoom(0.8)
// return // return
// } // }
document.addEventListener('keydown', handleKeyDown) document.addEventListener('keydown', handleKeyDown)
canvas?.on('mouse:move', drawMouseLines)
// 컴포넌트가 언마운트될 때 이벤트 리스너 제거 // 컴포넌트가 언마운트될 때 이벤트 리스너 제거
return () => { return () => {
document.removeEventListener('keydown', handleKeyDown) document.removeEventListener('keydown', handleKeyDown)
@ -94,6 +94,15 @@ export function useMode() {
canvas?.on('mouse:move', (e) => addLineEndPointToMousePoint(e, endPoint)) canvas?.on('mouse:move', (e) => addLineEndPointToMousePoint(e, endPoint))
}, [endPoint]) }, [endPoint])
useEffect(() => {
canvas?.off('mouse:move')
canvas?.on('mouse:move', drawMouseLines)
if (mode === Mode.EDIT) {
canvas?.off('mouse:down')
canvas?.on('mouse:down', mouseEvent.editMode)
}
}, [mode, isGuidePointMode])
const drawMouseLines = (e) => { const drawMouseLines = (e) => {
// 현재 마우스 포인터의 위치를 가져옵니다. // 현재 마우스 포인터의 위치를 가져옵니다.
const pointer = canvas?.getPointer(e.e) const pointer = canvas?.getPointer(e.e)
@ -104,9 +113,17 @@ export function useMode() {
if (canvas?.getActiveObject()) { if (canvas?.getActiveObject()) {
return return
} }
let newX, newY
if (isGuidePointMode && mode === Mode.EDIT) {
newX = Math.round(pointer.x / 200) * 200
newY = Math.round(pointer.y / 200) * 200
} else {
newX = pointer.x
newY = pointer.y
}
// 가로선을 그립니다. // 가로선을 그립니다.
const horizontalLine = new fabric.Line([0, pointer.y, canvasSize.horizontal, pointer.y], { const horizontalLine = new fabric.Line([0, newY, canvasSize.horizontal, newY], {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
@ -114,7 +131,7 @@ export function useMode() {
}) })
// 세로선을 그립니다. // 세로선을 그립니다.
const verticalLine = new fabric.Line([pointer.x, 0, pointer.x, canvasSize.vertical], { const verticalLine = new fabric.Line([newX, 0, newX, canvasSize.vertical], {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
@ -206,8 +223,17 @@ export function useMode() {
const pointer = canvas?.getPointer(e.e) const pointer = canvas?.getPointer(e.e)
let newX, newY
if (isGuidePointMode && mode === Mode.EDIT) {
newX = Math.round(pointer.x / 200) * 200
newY = Math.round(pointer.y / 200) * 200
} else {
newX = pointer.x
newY = pointer.y
}
// 마우스 포인터 위치랑 endPoint를 연결하는 line 생성 // 마우스 포인터 위치랑 endPoint를 연결하는 line 생성
const line = new fabric.Line([endPoint.left, endPoint.top, pointer.x, pointer.y], { const line = new fabric.Line([endPoint.left, endPoint.top, newX, newY], {
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: false, selectable: false,
@ -529,13 +555,18 @@ export function useMode() {
canvas?.renderAll() canvas?.renderAll()
}, },
editMode: (options) => { editMode: (options) => {
const pointer = canvas?.getPointer(options.e) // const pointer = canvas?.getPointer(options.e)
const mouseLines = canvas?._objects.filter((obj) => obj.name === 'mouseLine')
const pointer = calculateIntersection(mouseLines[0], mouseLines[1])
const circle = new fabric.Circle({ const circle = new fabric.Circle({
radius: 5, radius: 5,
fill: 'transparent', // 원 안을 비웁니다. fill: 'transparent', // 원 안을 비웁니다.
stroke: 'red', // 원 테두리 색상을 검은색으로 설정합니다. stroke: 'red', // 원 테두리 색상을 검은색으로 설정합니다.
left: pointer.x - 5, left: pointer.x,
top: pointer.y - 5, top: pointer.y,
originX: 'center', originX: 'center',
originY: 'center', originY: 'center',
selectable: false, selectable: false,
@ -544,14 +575,48 @@ export function useMode() {
startPoint.current = circle startPoint.current = circle
pointCount.current = pointCount.current + 1 pointCount.current = pointCount.current + 1
} }
let prevEndPoint
setEndPoint(circle) setEndPoint((prev) => {
prevEndPoint = prev
return circle
})
historyPoints.current.push(circle) historyPoints.current.push(circle)
points.current.push(circle) points.current.push(circle)
canvas?.add(circle) canvas?.add(circle)
if (points.current.length === 2) { if (points.current.length === 2) {
if (isGuidePointMode) {
const vector = {
x: points.current[1].left - points.current[0].left,
y: points.current[1].top - points.current[0].top,
}
const slope = Math.abs(vector.y / vector.x) // 기울기 계산
let scaledVector
if (slope >= 1) {
// 기울기가 1 이상이면 x축 방향으로 그림
scaledVector = {
x: 0,
y: pointer.y - prevEndPoint?.top,
}
} else {
// 기울기가 1 미만이면 y축 방향으로 그림
scaledVector = {
x: pointer.x - prevEndPoint?.left,
y: 0,
}
}
const verticalLength = scaledVector.y
const horizontalLength = scaledVector.x
drawCircleAndLine(verticalLength, horizontalLength)
canvas?.renderAll()
return
}
const length = Number(prompt('길이를 입력하세요:')) const length = Number(prompt('길이를 입력하세요:'))
// length 값이 숫자가 아닌 경우 // length 값이 숫자가 아닌 경우
@ -561,7 +626,7 @@ export function useMode() {
const lastPoint = historyPoints.current[historyPoints.current.length - 1] const lastPoint = historyPoints.current[historyPoints.current.length - 1]
canvas?.remove(lastPoint) canvas?.remove(lastPoint)
setEndPoint(prevEndPoint)
historyPoints.current.pop() historyPoints.current.pop()
points.current.pop() points.current.pop()
return return

View File

@ -10,6 +10,16 @@ export const modeState = atom({
default: 'default', default: 'default',
}) })
export const guidePointModeState = atom({
key: 'guidePointModeState',
default: false,
})
export const guideModeLineState = atom({
key: 'guideLineModeState',
default: false,
})
export const fontSizeState = atom({ export const fontSizeState = atom({
key: 'fontSizeState', key: 'fontSizeState',
default: 16, default: 16,