Merge branch 'dev'

This commit is contained in:
yoosangwook 2024-08-14 09:54:06 +09:00
commit ec084d46d2
19 changed files with 377 additions and 374 deletions

View File

@ -1,14 +1,12 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Archive from '@/components/community/Archive' import Archive from '@/components/community/Archive'
export default function CommunityArchivePage() { export default function CommunityArchivePage() {
return ( return (
<> <>
<Hero title="자료 다운로드"/> <Hero title="자료 다운로드" />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border"> <div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Archive/> <Archive />
</div> </div>
</> </>
) )

View File

@ -1,14 +1,12 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Faq from '@/components/community/Faq' import Faq from '@/components/community/Faq'
export default function CommunityFaqPage() { export default function CommunityFaqPage() {
return ( return (
<> <>
<Hero title="FAQ"/> <Hero title="FAQ" />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border"> <div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Faq/> <Faq />
</div> </div>
</> </>
) )

View File

@ -1,14 +1,12 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Notice from '@/components/community/Notice' import Notice from '@/components/community/Notice'
export default function CommunityNoticePage() { export default function CommunityNoticePage() {
return ( return (
<> <>
<Hero title="공지사항"/> <Hero title="공지사항" />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border"> <div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Notice/> <Notice />
</div> </div>
</> </>
) )

View File

@ -1,14 +1,12 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Plan from '@/components/management/Plan' import Plan from '@/components/management/Plan'
export default function ManagementPlanPage() { export default function ManagementPlanPage() {
return ( return (
<> <>
<Hero title="도면관리"/> <Hero title="도면관리" />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border"> <div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Plan/> <Plan />
</div> </div>
</> </>
) )

View File

@ -1,5 +1,3 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Stuff from '@/components/management/Stuff' import Stuff from '@/components/management/Stuff'
@ -8,7 +6,7 @@ export default function ManagementStuffPage() {
<> <>
<Hero title="물건관리" /> <Hero title="물건관리" />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border"> <div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Stuff/> <Stuff />
</div> </div>
</> </>
) )

View File

@ -1,14 +1,12 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Company from '@/components/master/Company' import Company from '@/components/master/Company'
export default function MasterCompanyPage() { export default function MasterCompanyPage() {
return ( return (
<> <>
<Hero title="회사정보 조회"/> <Hero title="회사정보 조회" />
<div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border"> <div className="container flex flex-wrap items-center justify-between mx-auto p-4 m-4 border">
<Company/> <Company />
</div> </div>
</> </>
) )

View File

@ -1,5 +1,3 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Price from '@/components/master/Price' import Price from '@/components/master/Price'

View File

@ -0,0 +1,9 @@
import Playground from '@/components/Playground'
export default function PlaygroundPage() {
return (
<>
<Playground />
</>
)
}

View File

@ -1,5 +1,3 @@
'use client'
import Hero from '@/components/Hero' import Hero from '@/components/Hero'
import Roof from '@/components/Roof' import Roof from '@/components/Roof'

View File

@ -9,7 +9,7 @@ export default function Headers() {
</Link> </Link>
<div className="space-x-4 text-xl"> <div className="space-x-4 text-xl">
<Link href="/intro">Intro</Link> <Link href="/intro">Intro</Link>
<Link href="/changelog">Changelog</Link> <Link href="/playground">Playground</Link>
<Link href="/roof">Roof</Link> <Link href="/roof">Roof</Link>
<Link href="/roof2">Roof2</Link> <Link href="/roof2">Roof2</Link>
</div> </div>

View File

@ -1,13 +1,13 @@
'use client' 'use client'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { modalContent, modalState } from '@/store/modalAtom' import { modalContent, modalState } from '@/store/modalAtom'
import { AxiosType, useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
@ -72,7 +72,7 @@ export default function Intro() {
async function fetchData() { async function fetchData() {
// const response = await fetch('https://www.ag-grid.com/example-assets/space-mission-data.json') // const response = await fetch('https://www.ag-grid.com/example-assets/space-mission-data.json')
// const data = await response.json() // const data = await response.json()
const data = await get({ type: AxiosType.EXTERNAL, url: 'https://www.ag-grid.com/example-assets/space-mission-data.json' }) const data = await get({ url: 'https://www.ag-grid.com/example-assets/space-mission-data.json' })
setGridProps({ ...gridProps, gridData: data }) setGridProps({ ...gridProps, gridData: data })
} }
fetchData() fetchData()

View File

@ -2,21 +2,20 @@
import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react'
import { AxiosType, useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
// import { get } from '@/lib/Axios' // import { get } from '@/lib/Axios'
import QSelect from '@/components/ui/QSelect' import QSelect from '@/components/ui/QSelect'
import styles from './changelog.module.css' import styles from './playground.module.css'
export default function changelogPage() { export default function Playground() {
const { get } = useAxios() const { get } = useAxios()
const testVar = process.env.NEXT_PUBLIC_TEST const testVar = process.env.NEXT_PUBLIC_TEST
const handleUsers = async () => { const handleUsers = async () => {
// const users = await get('/api/user/find-all') // const users = await get('/api/user/find-all')
const params = { const params = {
type: AxiosType.INTERNAL,
url: '/api/user/find-all', url: '/api/user/find-all',
} }
const users = await get(params) const users = await get(params)
@ -47,24 +46,6 @@ export default function changelogPage() {
<> <>
<div className="container mx-auto p-4 m-4 border"> <div className="container mx-auto p-4 m-4 border">
<div className={styles.test}> 영역은 테스트입니다.</div> <div className={styles.test}> 영역은 테스트입니다.</div>
<div>
<Table isStriped>
<TableHeader>
<TableColumn>DATE</TableColumn>
<TableColumn>NAME</TableColumn>
<TableColumn>CONTENTS</TableColumn>
</TableHeader>
<TableBody>
{data.map((item) => (
<TableRow key={item.id}>
<TableCell>{item.date}</TableCell>
<TableCell>{item.author}</TableCell>
<TableCell>{item.contents}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="m-2"> <div className="m-2">
<QSelect /> <QSelect />
</div> </div>

View File

@ -4,13 +4,20 @@ import { Mode, useMode } from '@/hooks/useMode'
import { Button } from '@nextui-org/react' import { Button } from '@nextui-org/react'
import RangeSlider from './ui/RangeSlider' import RangeSlider from './ui/RangeSlider'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { canvasSizeState, fontSizeState, roofMaterialState, sortedPolygonArray, templateTypeState, compassState } from '@/store/canvasAtom' import {
canvasSizeState,
compassState,
fontSizeState,
roofMaterialState,
roofState,
sortedPolygonArray,
templateTypeState,
wallState,
} from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
import { getCanvasState, insertCanvasState } from '@/lib/canvas' import { getCanvasState, insertCanvasState } from '@/lib/canvas'
import { calculateIntersection } from '@/util/canvas-util' import { calculateIntersection } from '@/util/canvas-util'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import * as turf from '@turf/turf'
import { toGeoJSON } from '@/util/qpolygon-utils'
export default function Roof2() { export default function Roof2() {
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas')
@ -38,6 +45,10 @@ export default function Roof2() {
const [compass, setCompass] = useRecoilState(compassState) const [compass, setCompass] = useRecoilState(compassState)
const roof = useRecoilValue(roofState)
const wall = useRecoilValue(wallState)
const { const {
mode, mode,
setMode, setMode,
@ -55,6 +66,8 @@ export default function Roof2() {
createRoofRack, createRoofRack,
drawRoofPolygon, drawRoofPolygon,
drawCellInTrestle, drawCellInTrestle,
setDirectionTrestles,
cutHelpLines,
} = useMode() } = useMode()
// const [canvasState, setCanvasState] = useRecoilState(canvasAtom) // const [canvasState, setCanvasState] = useRecoilState(canvasAtom)
@ -553,12 +566,19 @@ export default function Roof2() {
<Button className="m-1 p-2" color={`${mode === Mode.TEXTBOX ? 'primary' : 'default'}`} onClick={() => setMode(Mode.TEXTBOX)}> <Button className="m-1 p-2" color={`${mode === Mode.TEXTBOX ? 'primary' : 'default'}`} onClick={() => setMode(Mode.TEXTBOX)}>
텍스트박스 모드 텍스트박스 모드
</Button> </Button>
{/*<Button className="m-1 p-2" onClick={handleUndo}> <Button
className="m-1 p-2"
color={`${mode === Mode.DRAW_HELP_LINE ? 'primary' : 'default'}`}
onClick={() => setMode(Mode.DRAW_HELP_LINE)}
>
보조선 연결 모드
</Button>
<Button className="m-1 p-2" onClick={handleUndo}>
Undo Undo
</Button> </Button>
<Button className="m-1 p-2" onClick={handleRedo}> <Button className="m-1 p-2" onClick={handleRedo}>
Redo Redo
</Button>*/} </Button>
<Button className="m-1 p-2" onClick={handleClear}> <Button className="m-1 p-2" onClick={handleClear}>
clear clear
</Button> </Button>
@ -614,6 +634,9 @@ export default function Roof2() {
<Button className="m-1 p-2" onClick={addCanvas}> <Button className="m-1 p-2" onClick={addCanvas}>
캔버스 추가 캔버스 추가
</Button>*/} </Button>*/}
<Button className="m-1 p-2" onClick={cutHelpLines}>
보조선 절삭
</Button>
{templateType === 1 && ( {templateType === 1 && (
<> <>
<Button className="m-1 p-2" onClick={drawRoofMaterial}> <Button className="m-1 p-2" onClick={drawRoofMaterial}>
@ -622,6 +645,9 @@ export default function Roof2() {
<Button className="m-1 p-2" onClick={createRoofRack}> <Button className="m-1 p-2" onClick={createRoofRack}>
지붕가대설치 지붕가대설치
</Button> </Button>
<Button className="m-1 p-2" onClick={setDirectionTrestles}>
가대 방향 설정
</Button>
<Button className="m-1 p-2" onClick={drawCellInTrestle}> <Button className="m-1 p-2" onClick={drawCellInTrestle}>
선택한 가대 셀채우기 선택한 가대 셀채우기
</Button> </Button>

View File

@ -1,12 +1,15 @@
import axios from 'axios' import axios, { Axios } from 'axios'
export const AxiosType = { const AxiosType = {
INTERNAL: 'Internal', INTERNAL: 'Internal',
EXTERNAL: 'External', EXTERNAL: 'External',
} }
export function useAxios() { export function useAxios() {
const getInstances = (type) => { const getInstances = (url) => {
let type = AxiosType.INTERNAL
url.startsWith('http') ? (type = AxiosType.EXTERNAL) : ''
return axios.create({ return axios.create({
baseURL: type === AxiosType.INTERNAL ? process.env.NEXT_PUBLIC_API_SERVER_PATH : '', baseURL: type === AxiosType.INTERNAL ? process.env.NEXT_PUBLIC_API_SERVER_PATH : '',
headers: { headers: {
@ -28,36 +31,36 @@ export function useAxios() {
// } // }
}) })
const get = async ({ type, url }) => { const get = async ({ url }) => {
return await getInstances(type) return await getInstances(url)
.get(url) .get(url)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const post = async ({ type, url, data }) => { const post = async ({ url, data }) => {
return await getInstances(type) return await getInstances(url)
.post(url, data) .post(url, data)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const put = async ({ type, url, data }) => { const put = async ({ url, data }) => {
return await getInstances(type) return await getInstances(url)
.put(url, data) .put(url, data)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const patch = async ({ type, url, data }) => { const patch = async ({ url, data }) => {
return await getInstances(type) return await getInstances(url)
.patch(url, data) .patch(url, data)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)
} }
const del = async ({ type, url }) => { const del = async ({ url }) => {
return await getInstances(type) return await getInstances(url)
.delete(url) .delete(url)
.then((res) => res.data) .then((res) => res.data)
.catch(console.error) .catch(console.error)

View File

@ -1,6 +1,6 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { actionHandler, anchorWrapper, polygonPositionHandler } from '@/util/canvas-util' import { actionHandler, anchorWrapper, calculateIntersection, distanceBetweenPoints, polygonPositionHandler } from '@/util/canvas-util'
import { useRecoilState } from 'recoil' import { useRecoilState } from 'recoil'
import { canvasSizeState, fontSizeState } from '@/store/canvasAtom' import { canvasSizeState, fontSizeState } from '@/store/canvasAtom'
@ -9,6 +9,8 @@ import { QPolygon } from '@/components/fabric/QPolygon'
import { defineQLine } from '@/util/qline-utils' import { defineQLine } from '@/util/qline-utils'
import { defineQPloygon } from '@/util/qpolygon-utils' import { defineQPloygon } from '@/util/qpolygon-utils'
import * as turf from '@turf/turf'
export function useCanvas(id) { export function useCanvas(id) {
const [canvas, setCanvas] = useState() const [canvas, setCanvas] = useState()
const [isLocked, setIsLocked] = useState(false) const [isLocked, setIsLocked] = useState(false)
@ -65,6 +67,7 @@ export function useCanvas(id) {
initialize() initialize()
canvas?.on('object:added', onChange) canvas?.on('object:added', onChange)
canvas?.on('object:added', addEventOnObject) canvas?.on('object:added', addEventOnObject)
canvas?.on('object:modified', onChange) canvas?.on('object:modified', onChange)
canvas?.on('object:removed', onChange) canvas?.on('object:removed', onChange)
canvas?.on('mouse:move', drawMouseLines) canvas?.on('mouse:move', drawMouseLines)
@ -110,6 +113,10 @@ export function useCanvas(id) {
if (target.name === 'trestle') { if (target.name === 'trestle') {
target.on('mousedown', () => { target.on('mousedown', () => {
if (target.defense === 'north') {
alert('북쪽은 선택 불가합니다.')
return
}
if (target.get('selected')) { if (target.get('selected')) {
target.set({ strokeWidth: 1 }) target.set({ strokeWidth: 1 })
target.set({ strokeDashArray: [5, 5] }) target.set({ strokeDashArray: [5, 5] })

View File

@ -1,5 +1,13 @@
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { findTopTwoIndexesByDistance, getCenterPoint, getDirection, getStartIndex, rearrangeArray } from '@/util/canvas-util' import {
calculateIntersection,
distanceBetweenPoints,
findTopTwoIndexesByDistance,
getCenterPoint,
getDirection,
getStartIndex,
rearrangeArray,
} from '@/util/canvas-util'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { import {
@ -18,6 +26,8 @@ import { QLine } from '@/components/fabric/QLine'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { QPolygon } from '@/components/fabric/QPolygon' import { QPolygon } from '@/components/fabric/QPolygon'
import offsetPolygon from '@/util/qpolygon-utils' import offsetPolygon from '@/util/qpolygon-utils'
import { isObjectNotEmpty } from '@/util/common-utils'
import * as turf from '@turf/turf'
export const Mode = { export const Mode = {
DRAW_LINE: 'drawLine', // 기준선 긋기모드` DRAW_LINE: 'drawLine', // 기준선 긋기모드`
@ -31,6 +41,7 @@ export const Mode = {
ROOF_TRESTLE: 'roofTrestle', //지붕가대 모드 ROOF_TRESTLE: 'roofTrestle', //지붕가대 모드
FILL_CELLS: 'fillCells', //태양광셀 모드 FILL_CELLS: 'fillCells', //태양광셀 모드
CELL_POWERCON: 'cellPowercon', //파워콘 CELL_POWERCON: 'cellPowercon', //파워콘
DRAW_HELP_LINE: 'drawHelpLine', // 보조선 그리기 모드 지붕 존재해야함
DEFAULT: 'default', DEFAULT: 'default',
} }
@ -254,6 +265,14 @@ export function useMode() {
case 'cellPowercon': case 'cellPowercon':
makeCellPowercon() makeCellPowercon()
break break
case 'drawHelpLine':
canvas?.off('selection:created', addSelectCreatedEvent)
canvas?.off('selection:cleared', addSelectClearedEvent)
canvas?.on('selection:created', addSelectCreatedEvent)
canvas?.on('selection:cleared', addSelectClearedEvent)
drawHelpLineMode()
break
case 'default': case 'default':
canvas?.off('mouse:down') canvas?.off('mouse:down')
break break
@ -340,6 +359,81 @@ export function useMode() {
canvas?.renderAll() canvas?.renderAll()
} }
const addSelectCreatedEvent = (e) => {
const target = e.selected[0]
if (target.name === 'helpPoint') {
canvas?.on('mouse:move', helpPointEvent.mouseMove)
}
}
const helpPointEvent = {
mouseMove: (e) => {
const target = canvas?.getActiveObject()
const pointer = canvas?.getPointer(e.e)
const point = { x: target.left + target.radius, y: target.top + target.radius }
const angle = Math.atan2(pointer.y - point.y, pointer.x - point.x)
const degree = fabric.util.radiansToDegrees(angle)
const min = [0, 45, 90, -0, -90, -45, 135, -135, 180, -180].reduce((prev, curr) => {
return Math.abs(curr - degree) < Math.abs(prev - degree) ? curr : prev
})
// Calculate the center point of the target object
const centerX = target.left + target.width / 2
const centerY = target.top + target.height / 2
const length = distanceBetweenPoints(point, { x: pointer.x, y: pointer.y })
// min의 각도와 pointer의 위치를 이용하여 새로운 점을 구한다.
const newPoint = {
x: centerX + length * Math.cos(fabric.util.degreesToRadians(min)),
y: centerY + length * Math.sin(fabric.util.degreesToRadians(min)),
}
const line = new fabric.Line([point.x, point.y, newPoint.x, newPoint.y], {
stroke: 'black',
strokeWidth: 1,
selectable: false,
name: 'beforeHelpLine',
helpPoint: target,
})
const helpLines = canvas?._objects.filter((obj) => obj.name === 'beforeHelpLine')
helpLines.forEach((item) => canvas?.remove(item))
canvas?.add(line)
},
}
const addSelectClearedEvent = (e) => {
const target = e.deselected[0]
if (target.name === 'helpPoint') {
const beforeHelpLines = canvas?._objects.filter((obj) => obj.name === 'beforeHelpLine' && obj.helpPoint === target)
const helpLines = canvas?._objects.filter((obj) => obj.name === 'helpLine' && obj.helpPoint === target)
beforeHelpLines.forEach((item) => canvas?.remove(item))
helpLines.forEach((item) => canvas?.remove(item))
const newPoint = { x: beforeHelpLines[0].x2, y: beforeHelpLines[0].y2 }
const helpLine = new fabric.Line([target.left + target.radius, target.top + target.radius, newPoint.x, newPoint.y], {
stroke: 'red',
strokeWidth: 1,
selectable: false,
name: 'helpLine',
helpPoint: target,
})
canvas?.add(helpLine)
canvas?.renderAll()
canvas?.off('mouse:move', helpPointEvent.mouseMove)
}
}
const handleKeyDown = (e) => { const handleKeyDown = (e) => {
switch (e.key) { switch (e.key) {
case 'ArrowDown': { case 'ArrowDown': {
@ -531,7 +625,7 @@ export function useMode() {
// handleOuterlines() // handleOuterlines()
const wall = makePolygon(null, sort) const wall = makePolygon(null, sort)
wall.set({ name: 'wall' }) wall.set({ name: 'wall' })
setWall(wall) console.log('wall', wall)
return wall return wall
} }
@ -695,6 +789,7 @@ export function useMode() {
viewLengthText: true, viewLengthText: true,
fontSize: fontSize, fontSize: fontSize,
sort: sort, sort: sort,
selectable: false,
}, },
canvas, canvas,
) )
@ -738,296 +833,6 @@ export function useMode() {
setZoom(Math.ceil(zoom - 10)) setZoom(Math.ceil(zoom - 10))
} }
const handleOuterlines = () => {
const newOuterlines = []
for (let i = 0; i < historyLines.current.length; i++) {
const next = historyLines.current[i + 1]
const prev = historyLines.current[i - 1] ?? historyLines.current[historyLines.current.length - 1]
if (next) {
if (next.direction === 'right') {
// 다름 라인이 오른쪽으로 이동
if (historyLines.current[i].direction === 'top') {
if (prev.direction !== 'right') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
}
} else {
// bottom
if (prev?.direction !== 'right') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
}
}
} else if (next.direction === 'left') {
if (historyLines.current[i].direction === 'top') {
if (prev?.direction !== 'left') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
}
} else {
// bottom
if (prev?.direction !== 'left') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 - 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 - 50,
})
}
}
} else if (next.direction === 'top') {
if (historyLines.current[i].direction === 'right') {
if (prev?.direction !== 'top') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 - 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
}
} else {
// left
if (prev?.direction !== 'top') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
}
}
} else if (next.direction === 'bottom') {
if (historyLines.current[i].direction === 'right') {
if (prev?.direction !== 'bottom') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 + 50,
})
}
} else {
// left
if (prev.direction !== 'bottom') {
if (historyLines.current.length === 4) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 6) {
newOuterlines.push({
x1: historyLines.current[i].x1 + 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 - 50,
y2: historyLines.current[i].y2 - 50,
})
} else if (historyLines.current.length === 8) {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 + 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 + 50,
})
}
} else {
newOuterlines.push({
x1: historyLines.current[i].x1 - 50,
y1: historyLines.current[i].y1 - 50,
x2: historyLines.current[i].x2 + 50,
y2: historyLines.current[i].y2 - 50,
})
}
}
}
} else {
const tmp = newOuterlines[newOuterlines.length - 1]
newOuterlines.push({
x1: tmp.x2,
y1: tmp.y2,
x2: newOuterlines[0].x1,
y2: newOuterlines[0].y1,
})
}
}
makePolygon(newOuterlines)
}
/** /**
* 지붕 외곽선 생성 * 지붕 외곽선 생성
*/ */
@ -1115,9 +920,10 @@ export function useMode() {
) )
roof.setWall(polygon) roof.setWall(polygon)
setRoof(roof) setRoof(roof)
setWall(polygon)
roof.drawHelpLine() // roof.drawHelpLine()
roof.divideLine() // roof.divideLine()
} }
const drawRoofPolygon = (wall, offset = 50) => { const drawRoofPolygon = (wall, offset = 50) => {
@ -4465,6 +4271,7 @@ export function useMode() {
} }
const createRoofRack = () => { const createRoofRack = () => {
roof.divideLine()
const trestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle') const trestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle')
// 이미 만들어진 가대가 있을 경우 return // 이미 만들어진 가대가 있을 경우 return
if (trestlePolygons.length !== 0) { if (trestlePolygons.length !== 0) {
@ -4475,7 +4282,6 @@ export function useMode() {
canvas?.off('mouse:out') canvas?.off('mouse:out')
document.removeEventListener('keydown', handleKeyDown) document.removeEventListener('keydown', handleKeyDown)
const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof') const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof')
let roofCells = [] // roof에 적재된 cell들
roofs.forEach((roof, index) => { roofs.forEach((roof, index) => {
const offsetPolygonPoint = offsetPolygon(roof.points, -20) const offsetPolygonPoint = offsetPolygon(roof.points, -20)
@ -4487,6 +4293,7 @@ export function useMode() {
selectable: false, selectable: false,
fontSize: fontSize, fontSize: fontSize,
name: 'trestle', name: 'trestle',
defense: roof.defense,
lockMovementX: true, // X 축 이동 잠금 lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금 lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금 lockRotation: true, // 회전 잠금
@ -4498,8 +4305,7 @@ export function useMode() {
canvas?.add(trestlePoly) canvas?.add(trestlePoly)
}) })
removeHelpPointAndHelpLine()
setDrewRoofCells(roofCells)
} }
//배터리 셀 넣기 //배터리 셀 넣기
@ -4531,19 +4337,24 @@ export function useMode() {
let drawRoofCells let drawRoofCells
if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') {
drawRoofCells = trestle.fillCell({ width: 50, height: 100, padding: 0 }) drawRoofCells = trestle.fillCell({ width: 50, height: 100, padding: 10 })
trestle.direction = 'south' trestle.direction = 'south'
} else { } else {
drawRoofCells = trestle.fillCell({ width: 100, height: 50, padding: 0 }) drawRoofCells = trestle.fillCell({ width: 100, height: 50, padding: 10 })
trestle.direction = 'east' trestle.direction = 'east'
} }
drawRoofCells.forEach((cell) => { drawRoofCells.forEach((cell) => {
roofCells.push(cell) drawCellsArray.push(cell)
}) })
}) })
setDrewRoofCells(roofCells) setDrewRoofCells(drawCellsArray)
}
// 가대 방위 설정
const setDirectionTrestles = () => {
console.log('roof', roof)
} }
const makeCellPowercon = () => { const makeCellPowercon = () => {
@ -4610,6 +4421,156 @@ export function useMode() {
return { concaveIndices: concaveIndices, concavePointIndices: concavePointIndices } return { concaveIndices: concaveIndices, concavePointIndices: concavePointIndices }
} }
const drawHelpLineMode = () => {
if (!isObjectNotEmpty(roof)) {
alert('지붕을 먼저 그려주세요.')
setMode(Mode.DEFAULT)
return
}
const roofPoints = roof.points
const wallPoints = wall.points
roofPoints.forEach((roofPoint, index) => {
const circle = new fabric.Circle({
radius: 5,
fill: 'red',
left: roofPoint.x - 5,
top: roofPoint.y - 5,
selectable: true, // 선택 가능하게 설정
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
name: 'helpPoint',
})
canvas?.add(circle)
canvas?.renderAll()
})
}
const cutHelpLines = () => {
// 먼저 hip을 자른다.
canvas
?.getObjects()
.filter((obj) => obj.name === 'helpLine')
.forEach((line, index1) => {
canvas
?.getObjects()
.filter((obj) => obj.name === 'helpLine')
.forEach((line2, index2) => {
if (line === line2) {
return
}
const intersectionPoint = calculateIntersection(line, line2)
if (!intersectionPoint) {
return
}
canvas?.remove(line)
canvas?.remove(line2)
const hip1 = new QLine([line.x1, line.y1, intersectionPoint.x, intersectionPoint.y], {
stroke: 'black',
strokeWidth: 1,
selectable: false,
name: 'hip',
})
const hip2 = new QLine([line2.x1, line2.y1, intersectionPoint.x, intersectionPoint.y], {
stroke: 'black',
strokeWidth: 1,
selectable: false,
name: 'hip',
})
const interSectionCircle = new fabric.Circle({
radius: 5,
fill: 'red',
left: intersectionPoint.x - 5,
top: intersectionPoint.y - 5,
selectable: true, // 선택 가능하게 설정
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
lockScalingX: true, // X 축 크기 조정 잠금
lockScalingY: true, // Y 축 크기 조정 잠금
name: 'helpPoint',
})
canvas?.add(hip1)
canvas?.add(hip2)
roof.innerLines.push(hip1)
roof.innerLines.push(hip2)
canvas?.add(interSectionCircle)
canvas?.renderAll()
})
})
canvas
?.getObjects()
.filter((obj) => obj.name === 'helpLine')
.forEach((line) => {
const helpPoints = canvas?.getObjects().filter((obj) => obj.name === 'helpPoint')
let cnt = 0
let intersectionPoints = []
helpPoints.forEach((point) => {
if (cnt === 2) {
return
}
if (
turf.booleanPointOnLine(
turf.point([point.left + point.radius, point.top + point.radius]),
turf.lineString([
[line.x1, line.y1],
[line.x2, line.y2],
]),
)
) {
intersectionPoints.push(point)
cnt++
}
})
if (intersectionPoints.length === 2) {
const ridge = new QLine(
[
intersectionPoints[0].left + intersectionPoints[0].radius,
intersectionPoints[0].top + intersectionPoints[0].radius,
intersectionPoints[1].left + intersectionPoints[1].radius,
intersectionPoints[1].top + intersectionPoints[1].radius,
],
{
stroke: 'black',
strokeWidth: 1,
selectable: false,
name: 'ridge',
},
)
roof.innerLines.push(ridge)
canvas?.add(ridge)
canvas?.remove(line)
canvas?.renderAll()
}
})
}
const removeHelpPointAndHelpLine = () => {
const helpPoints = canvas?.getObjects().filter((obj) => obj.name === 'helpPoint')
helpPoints.forEach((point) => {
canvas?.remove(point)
})
const helpLines = canvas?.getObjects().filter((obj) => obj.name === 'helpLine')
helpLines.forEach((line) => {
canvas?.remove(line)
})
canvas?.renderAll()
}
return { return {
mode, mode,
setMode, setMode,
@ -4627,5 +4588,7 @@ export function useMode() {
createRoofRack, createRoofRack,
drawRoofPolygon, drawRoofPolygon,
drawCellInTrestle, drawCellInTrestle,
setDirectionTrestles,
cutHelpLines,
} }
} }

11
src/util/common-utils.js Normal file
View File

@ -0,0 +1,11 @@
/**
* Check if an object is not empty.
* @param {Object} obj - The object to check.
* @returns {boolean} - Returns true if the object is not empty, false otherwise.
*/
export const isObjectNotEmpty = (obj) => {
if (!obj) {
return false
}
return Object.keys(obj).length > 0
}

View File

@ -1127,7 +1127,25 @@ export const splitPolygonWithLines = (polygon) => {
}) })
}) })
roofs.forEach((roofPoint) => { roofs.forEach((roofPoint, index) => {
let defense
const direction = getDirectionByPoint(roofPoint[0], roofPoint[roofPoint.length - 1])
switch (direction) {
case 'top':
defense = 'east'
break
case 'right':
defense = 'south'
break
case 'bottom':
defense = 'west'
break
case 'left':
defense = 'north'
break
}
const roof = new QPolygon(roofPoint, { const roof = new QPolygon(roofPoint, {
fontSize: polygon.fontSize, fontSize: polygon.fontSize,
stroke: 'black', stroke: 'black',
@ -1135,6 +1153,7 @@ export const splitPolygonWithLines = (polygon) => {
strokeWidth: 3, strokeWidth: 3,
name: 'roof', name: 'roof',
selectable: false, selectable: false,
defense: defense,
}) })
polygon.canvas.add(roof) polygon.canvas.add(roof)