Merge branch 'dev'

This commit is contained in:
yoosangwook 2024-08-01 17:32:48 +09:00
commit e793cba8e2
6 changed files with 178 additions and 76 deletions

View File

@ -1,3 +1,5 @@
'use client'
export default function ServerError() { export default function ServerError() {
return ( return (
<section className="bg-white dark:bg-gray-900"> <section className="bg-white dark:bg-gray-900">

View File

@ -1,3 +1,5 @@
'use client'
import Link from 'next/link' import Link from 'next/link'
export default function NotFound() { export default function NotFound() {

View File

@ -35,6 +35,7 @@ export default function Roof2() {
const { const {
mode, mode,
setMode,
changeMode, changeMode,
handleClear, handleClear,
fillCellInPolygon, fillCellInPolygon,
@ -108,28 +109,24 @@ export default function Roof2() {
} }
} }
/** useEffect(() => {
* canvas 사이즈 변경 함수 setCanvasSize({ ...canvasSize, vertical: parseInt(verticalSize), horizontal: parseInt(horizontalSize) })
*/ }, [verticalSize, horizontalSize])
const canvasSizeMode = () => {
if (canvas) {
canvas.setWidth(horizontalSize)
canvas.setHeight(verticalSize)
canvas.renderAll()
setCanvasSize(() => ({
vertical: verticalSize,
horizontal: horizontalSize,
}))
}
}
/** /**
* 변경시 * 변경시
*/ */
// useEffect(() => {
// canvasSizeMode()
// }, [verticalSize, horizontalSize])
useEffect(() => { useEffect(() => {
canvasSizeMode() const { vertical, horizontal } = canvasSize
}, [verticalSize, horizontalSize]) if (vertical !== verticalSize || horizontal !== horizontalSize) {
canvas?.setWidth(horizontalSize)
canvas?.setHeight(verticalSize)
canvas?.renderAll()
}
}, [canvasSize, canvas])
const makeQPolygon = () => { const makeQPolygon = () => {
const type1 = [ const type1 = [
@ -391,23 +388,19 @@ export default function Roof2() {
<Button className="m-1 p-2" color={`${mode === Mode.DEFAULT ? 'primary' : 'default'}`} onClick={fillCellInPolygon}> <Button className="m-1 p-2" color={`${mode === Mode.DEFAULT ? 'primary' : 'default'}`} onClick={fillCellInPolygon}>
모드 DEFAULT 모드 DEFAULT
</Button> </Button>
<Button <Button className="m-1 p-2" color={`${mode === Mode.DRAW_LINE ? 'primary' : 'default'}`} onClick={() => setMode(Mode.DRAW_LINE)}>
className="m-1 p-2"
color={`${mode === Mode.DRAW_LINE ? 'primary' : 'default'}`}
onClick={() => changeMode(canvas, Mode.DRAW_LINE)}
>
기준선 긋기 모드 기준선 긋기 모드
</Button> </Button>
<Button className="m-1 p-2" color={`${mode === Mode.EDIT ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.EDIT)}> <Button className="m-1 p-2" color={`${mode === Mode.EDIT ? 'primary' : 'default'}`} onClick={() => setMode(Mode.EDIT)}>
에디팅모드 에디팅모드
</Button> </Button>
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.TEMPLATE)}> <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => setMode(Mode.TEMPLATE)}>
템플릿(기둥) 템플릿(기둥)
</Button> </Button>
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.PATTERNA)}> <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => setMode(Mode.PATTERNA)}>
템플릿(A 패턴) 템플릿(A 패턴)
</Button> </Button>
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.PATTERNB)}> <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => setMode(Mode.PATTERNB)}>
템플릿(B 패턴) 템플릿(B 패턴)
</Button> </Button>
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => drawRoofPatterns(1)}> <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => drawRoofPatterns(1)}>
@ -416,21 +409,16 @@ export default function Roof2() {
<Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => drawRoofPatterns(2)}> <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => drawRoofPatterns(2)}>
지붕패턴2 지붕패턴2
</Button> </Button>
<Button <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => setMode(Mode.ROOF_TRESTLE)}>
className="m-1 p-2"
color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`}
onClick={() => changeMode(canvas, Mode.ROOT_TRESTLE)}
>
지붕가대생성 지붕가대생성
</Button> </Button>
<Button className="m-1 p-2" color={`${mode === Mode.TEXTBOX ? 'primary' : 'default'}`} onClick={() => changeMode(canvas, Mode.TEXTBOX)}> <Button className="m-1 p-2" color={`${mode === Mode.TEMPLATE ? 'primary' : 'default'}`} onClick={() => setMode(Mode.FILL_CELLS)}>
태양광셀생성
</Button>
<Button className="m-1 p-2" color={`${mode === Mode.TEXTBOX ? 'primary' : 'default'}`} onClick={() => setMode(Mode.TEXTBOX)}>
텍스트박스 모드 텍스트박스 모드
</Button> </Button>
<Button <Button className="m-1 p-2" color={`${mode === Mode.DRAW_RECT ? 'primary' : 'default'}`} onClick={() => setMode(Mode.DRAW_RECT)}>
className="m-1 p-2"
color={`${mode === Mode.DRAW_RECT ? 'primary' : 'default'}`}
onClick={() => changeMode(canvas, Mode.DRAW_RECT)}
>
사각형 생성 모드 사각형 생성 모드
</Button> </Button>
<Button className="m-1 p-2" onClick={handleUndo}> <Button className="m-1 p-2" onClick={handleUndo}>

View File

@ -181,6 +181,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
}, },
fillCell(cell = { width: 50, height: 100, padding: 10 }) { fillCell(cell = { width: 50, height: 100, padding: 10 }) {
const points = this.points const points = this.points
const minX = Math.min(...points.map((p) => p.x)) const minX = Math.min(...points.map((p) => p.x))
const maxX = Math.max(...points.map((p) => p.x)) const maxX = Math.max(...points.map((p) => p.x))
const minY = Math.min(...points.map((p) => p.y)) const minY = Math.min(...points.map((p) => p.y))
@ -195,10 +196,15 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
const cols = Math.floor((boundingBoxWidth + cell.padding) / (rectWidth + cell.padding)) const cols = Math.floor((boundingBoxWidth + cell.padding) / (rectWidth + cell.padding))
const rows = Math.floor((boundingBoxHeight + cell.padding) / (rectHeight + cell.padding)) const rows = Math.floor((boundingBoxHeight + cell.padding) / (rectHeight + cell.padding))
//전체 높이에서 패딩을 포함하고 rows를 곱해서 여백길이를 계산 후에 2로 나누면 반높이를 넣어서 중간으로 정렬
const tmpHeight = (boundingBoxHeight - (rectHeight + cell.padding) * rows) / 2
//센터 정렬시에 쓴다 체크박스가 존재함 TODO: if문 추가해서 정렬해야함
let tmpWidth = (boundingBoxWidth - (rectWidth + cell.padding) * cols) / 2
for (let i = 0; i < cols; i++) { for (let i = 0; i < cols; i++) {
for (let j = 0; j < rows; j++) { for (let j = 0; j < rows; j++) {
const rectLeft = minX + i * (rectWidth + cell.padding) const rectLeft = minX + i * (rectWidth + cell.padding) + tmpWidth
const rectTop = minY + j * (rectHeight + cell.padding) const rectTop = minY + j * (rectHeight + cell.padding) + tmpHeight
const rectPoints = [ const rectPoints = [
{ x: rectLeft, y: rectTop }, { x: rectLeft, y: rectTop },
@ -216,7 +222,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
width: rectWidth, width: rectWidth,
height: rectHeight, height: rectHeight,
fill: '#BFFD9F', fill: '#BFFD9F',
selectable: false, selectable: true, // 선택 가능하게 설정
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
opacity: 0.6, opacity: 0.6,
}) })
@ -224,8 +235,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
} }
} }
} }
this.canvas?.renderAll()
this.canvas.renderAll()
}, },
inPolygon(point) { inPolygon(point) {
const vertices = this.getCurrentPoints() const vertices = this.getCurrentPoints()

View File

@ -42,18 +42,18 @@ export function useCanvas(id) {
useEffect(() => { useEffect(() => {
canvas canvas
?.getObjects() ?.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) => { .forEach((obj) => {
obj.set({ fontSize: fontSize }) obj.set({ fontSize: fontSize })
}) })
canvas canvas
?.getObjects() ?.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) => { .forEach((obj) => {
obj.setFontSize(fontSize) obj.setFontSize(fontSize)
}) })
canvas?.renderAll() canvas?.getObjects().length > 0 && canvas?.renderAll()
}, [fontSize]) }, [fontSize])
/** /**
@ -103,7 +103,7 @@ export function useCanvas(id) {
} }
const initialize = () => { const initialize = () => {
canvas?.clear() canvas.getObjects().length > 0 && canvas?.clear()
// settings for all canvas in the app // settings for all canvas in the app
fabric.Object.prototype.transparentCorners = false fabric.Object.prototype.transparentCorners = false
fabric.Object.prototype.cornerColor = '#2BEBC8' fabric.Object.prototype.cornerColor = '#2BEBC8'
@ -121,6 +121,7 @@ export function useCanvas(id) {
delete instance.points delete instance.points
callback && callback(instance) callback && callback(instance)
} }
const options = fabric.util.object.clone(object, true) const options = fabric.util.object.clone(object, true)
options.points = [object.x1, object.y1, object.x2, object.y2] options.points = [object.x1, object.y1, object.x2, object.y2]
@ -526,7 +527,7 @@ export function useCanvas(id) {
} }
} }
canvas.renderAll() canvas?.renderAll()
} }
const setCanvasBackgroundWithDots = (canvas, gap) => { const setCanvasBackgroundWithDots = (canvas, gap) => {

View File

@ -27,7 +27,8 @@ export const Mode = {
DRAW_RECT: 'drawRect', DRAW_RECT: 'drawRect',
ROOF_PATTERN: 'roofPattern', ROOF_PATTERN: 'roofPattern',
MODULE: 'module', MODULE: 'module',
ROOT_TRESTLE: 'rootTrestle', ROOF_TRESTLE: 'roofTrestle',
FILL_CELLS: 'fillCells',
DEFAULT: 'default', DEFAULT: 'default',
} }
@ -55,6 +56,8 @@ export function useMode() {
const [canvasSize] = useRecoilState(canvasSizeState) const [canvasSize] = useRecoilState(canvasSizeState)
const [selectedCellRoofArray, setSelectedCellRoofArray] = useState([])
useEffect(() => { useEffect(() => {
// 이벤트 리스너 추가 // 이벤트 리스너 추가
if (!canvas) { if (!canvas) {
@ -211,9 +214,6 @@ export function useMode() {
const addEvent = (mode) => { const addEvent = (mode) => {
switch (mode) { switch (mode) {
case 'default':
canvas?.off('mouse:down')
break
case 'drawLine': case 'drawLine':
drawLineMode() drawLineMode()
break break
@ -238,9 +238,15 @@ export function useMode() {
case 'roofPattern': case 'roofPattern':
makeRoofPatternPolygon() makeRoofPatternPolygon()
break break
case 'rootTrestle': case 'roofTrestle':
makeRoofTrestle() makeRoofTrestle()
break break
case 'fillCells':
makeRoofFillCells()
break
case 'default':
canvas?.off('mouse:down')
break
} }
} }
@ -321,7 +327,7 @@ export function useMode() {
points.current = [endPointCircle] points.current = [endPointCircle]
canvas.renderAll() canvas?.renderAll()
} }
const handleKeyDown = (e) => { const handleKeyDown = (e) => {
@ -706,7 +712,7 @@ export function useMode() {
// 캔버스를 다시 그립니다. // 캔버스를 다시 그립니다.
if (!otherLines) { if (!otherLines) {
// polygon.fillCell() // polygon.fillCell()
canvas.renderAll() canvas?.renderAll()
// polygon.setViewLengthText(false) // polygon.setViewLengthText(false)
setMode(Mode.DEFAULT) setMode(Mode.DEFAULT)
} }
@ -1188,7 +1194,7 @@ export function useMode() {
rtnLines.push(line) rtnLines.push(line)
}) })
canvas.renderAll() canvas?.renderAll()
} }
if (obj.type === 'QLine') { if (obj.type === 'QLine') {
const parent = obj.parent const parent = obj.parent
@ -1201,7 +1207,7 @@ export function useMode() {
}) })
parent.visible = true parent.visible = true
canvas.renderAll() canvas?.renderAll()
} }
return rtnLines return rtnLines
} }
@ -2037,7 +2043,7 @@ export function useMode() {
roofPatternPolygonArray.push(smallRoofPolygon) //작은지붕폴리곤 roofPatternPolygonArray.push(smallRoofPolygon) //작은지붕폴리곤
setRoofPolygonPattern({ roofPatternPolygonArray, lines }) //모든 행을 저장 setRoofPolygonPattern({ roofPatternPolygonArray, lines }) //모든 행을 저장
canvas.renderAll() canvas?.renderAll()
} }
const handleOuterLineTemplateA8Points = (polygon, offsetInputX = 20, offsetInputY = 50) => { const handleOuterLineTemplateA8Points = (polygon, offsetInputX = 20, offsetInputY = 50) => {
@ -2640,7 +2646,7 @@ export function useMode() {
} }
setRoofPolygonPattern({ roofPatternPolygonArray, lines }) setRoofPolygonPattern({ roofPatternPolygonArray, lines })
} }
canvas.renderAll() canvas?.renderAll()
} }
/** /**
@ -2751,7 +2757,7 @@ export function useMode() {
setRoofPolygonPattern({ roofPatternPolygonArray, lines: polygon.lines }) setRoofPolygonPattern({ roofPatternPolygonArray, lines: polygon.lines })
} }
canvas.renderAll() canvas?.renderAll()
} }
/** /**
@ -3088,7 +3094,7 @@ export function useMode() {
setRoofPolygonPattern({ roofPatternPolygonArray, lines: polygon.lines }) setRoofPolygonPattern({ roofPatternPolygonArray, lines: polygon.lines })
// } // }
canvas.renderAll() canvas?.renderAll()
} }
/** /**
@ -3166,7 +3172,7 @@ export function useMode() {
}) })
canvas.add(overLine) canvas.add(overLine)
}) })
canvas.renderAll() canvas?.renderAll()
} }
const makeRoofPatternPolygon = (roofStyle) => { const makeRoofPatternPolygon = (roofStyle) => {
@ -3184,27 +3190,41 @@ export function useMode() {
var ratio = window.devicePixelRatio || 1 var ratio = window.devicePixelRatio || 1
let inputPatternSize = { width: 30, height: 20 } //임시 사이즈
let patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해
if (templateType === 2) {
//세로형이면 width height를 바꿈
;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width]
}
// 패턴 소스를 위한 임시 캔버스 생성 // 패턴 소스를 위한 임시 캔버스 생성
const patternSourceCanvas = document.createElement('canvas') const patternSourceCanvas = document.createElement('canvas')
patternSourceCanvas.width = 30 * ratio patternSourceCanvas.width = roofStyle === 2 ? patternSize.width * 2 * ratio : patternSize.width * ratio
patternSourceCanvas.height = 30 * ratio patternSourceCanvas.height = roofStyle === 2 ? patternSize.height * 2 * ratio : patternSize.height * ratio
const ctx = patternSourceCanvas.getContext('2d') const ctx = patternSourceCanvas.getContext('2d')
// 벽돌 패턴 그리기 // 벽돌 패턴 그리기
ctx.scale(ratio, ratio) ctx.scale(ratio, ratio)
ctx.strokeStyle = 'green' ctx.strokeStyle = 'green'
ctx.lineWidth = 0.2 ctx.lineWidth = 0.4
// 첫 번째 행 벽돌 // 첫 번째 행 벽돌
if (roofStyle === 2) { if (roofStyle === 2) {
patternSize.width = patternSize.width * 2
patternSize.height = patternSize.height * 2
//지그재그 //지그재그
// // 두 번째 행 벽돌 // // 두 번째 행 벽돌
ctx.strokeRect(0, 0, 30, 15) if (templateType === 2) {
ctx.strokeRect(30, 15, 30, 15) ctx.strokeRect(0, 0, patternSize.width / 2, patternSize.height)
ctx.strokeRect(-15, 15, 30, 15) ctx.strokeRect(patternSize.width / 2, patternSize.height / 2, patternSize.width / 2, patternSize.height)
ctx.strokeRect(0, 30, 30, 15) } else if (templateType === 3) {
ctx.strokeRect(0, 0, patternSize.width, patternSize.height / 2)
ctx.strokeRect(patternSize.width / 2, patternSize.height / 2, patternSize.width, patternSize.height / 2)
}
} else { } else {
ctx.strokeRect(0, 0, 30, 30) // 원패턴일때랑 지그재그일때랑은 다르게 들어가야함 ctx.strokeRect(0, 0, patternSize.width, patternSize.height) // 원패턴일때랑 지그재그일때랑은 다르게 들어가야함
} }
// 패턴 생성 // 패턴 생성
@ -3215,8 +3235,13 @@ export function useMode() {
const commonOption = { const commonOption = {
fill: pattern, fill: pattern,
selectable: true, selectable: false,
fontSize: 15, // fontSize는 필요에 따라 조정 fontSize: 15, // fontSize는 필요에 따라 조정
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingY: true,
lockScalingX: true,
} }
let polygonArray = [] let polygonArray = []
@ -3225,9 +3250,10 @@ export function useMode() {
const drawPolygon = new QPolygon(patternPolygon, commonOption) const drawPolygon = new QPolygon(patternPolygon, commonOption)
canvas.add(drawPolygon) canvas.add(drawPolygon)
drawPolygon.setViewLengthText(false) drawPolygon.setViewLengthText(false)
drawPolygon.set('customIndex', index)
polygonArray.push(drawPolygon) polygonArray.push(drawPolygon)
}) })
canvas.renderAll() canvas?.renderAll()
setRoofPolygonArray(polygonArray) setRoofPolygonArray(polygonArray)
} }
@ -3238,19 +3264,92 @@ export function useMode() {
return return
} }
// 오목한 부분 인덱스 찾기 const polygons = roofPolygonArray //리코일에 있는 패턴그린 폴리곤가져옴
const polygons = roofPolygonArray let selectedAreaArray = []
let concavePolygonObj = []
const defualtStrokeStyle = {
stroke: 'red',
strokeDashArray: [9, 5],
strokeWidth: 0.3,
}
const selectedStrokeStyle = {
stroke: 'red',
strokeWidth: 3,
}
function toggleSelection(polygon) {
if (polygon.strokeWidth === defualtStrokeStyle.strokeWidth) {
polygon.set({
stroke: selectedStrokeStyle.stroke,
strokeWidth: selectedStrokeStyle.strokeWidth,
strokeDashArray: [0],
})
canvas.discardActiveObject() // 객체의 활성 상태 해제
selectedAreaArray.push(polygon)
} else {
polygon.set({
stroke: defualtStrokeStyle.stroke,
strokeWidth: defualtStrokeStyle.strokeWidth,
strokeDashArray: defualtStrokeStyle.strokeDashArray,
})
canvas.discardActiveObject() // 객체의 활성 상태 해제
const removeIndex = polygon.customIndex
const removeArrayIndex = selectedAreaArray.findIndex((x) => x.customIndex === removeIndex)
selectedAreaArray.splice(removeArrayIndex, 1)
}
canvas?.renderAll()
}
polygons.forEach((polygon, index) => { polygons.forEach((polygon, index) => {
const trestlePolygon = handleOuterlinesTest(polygon, -12) const trestlePolygon = handleOuterlinesTest(polygon, -12)
trestlePolygon.set('stroke', 'red').set('strokeDashArray', [9, 5]).set('strokeWidth', 0.3).setViewLengthText(false) trestlePolygon.setViewLengthText(false) //얘는 set으로 안먹는다...
trestlePolygon.fillCell({ width: 100, height: 30, padding: 10 }) trestlePolygon.set({
stroke: 'red',
strokeDashArray: [9, 5],
strokeWidth: 0.3,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
bringToFront: true,
customIndex: polygon.customIndex,
})
trestlePolygon.on('mousedown', function () {
const customIndex = polygon.get('customIndex')
toggleSelection(trestlePolygon)
})
}) })
setSelectedCellRoofArray(selectedAreaArray)
}
const makeRoofFillCells = () => {
// const selectedCellRoofs = selectedCellRoofArray
if (selectedCellRoofArray.length === 0) {
alert('선택된 영역이 없습니다.')
setMode(Mode.DEFAULT) //default 모드로 변경
return
}
const inputCellSize = { width: 172, height: 113 }
const cellSize = { ...inputCellSize } //기본으로 가로형으로 넣고
if (templateType === 2) {
;[cellSize.width, cellSize.height] = [cellSize.height, cellSize.width]
}
selectedCellRoofArray.forEach((polygon, index) => {
polygon.fillCell({ width: cellSize.width, height: cellSize.height, padding: 10 })
})
setMode(Mode.DEFAULT) //default 모드로 변경
} }
return { return {
mode, mode,
setMode,
changeMode, changeMode,
setCanvas, setCanvas,
handleClear, handleClear,