diff --git a/src/app/intro/page.jsx b/src/app/intro/page.jsx deleted file mode 100644 index ebf1081b..00000000 --- a/src/app/intro/page.jsx +++ /dev/null @@ -1,12 +0,0 @@ -import Intro from '@/components/Intro' -import { initCheck } from '@/util/session-util' - -export default async function IntroPage() { - return ( - <> -
- -
- - ) -} diff --git a/src/common/common.js b/src/common/common.js index cf60295b..d0016af1 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -159,6 +159,8 @@ export const SAVE_KEY = [ 'groupName', 'lineDirection', 'groupId', + 'planeSize', + 'actualSize', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype] diff --git a/src/components/Intro.jsx b/src/components/Intro.jsx deleted file mode 100644 index a6825af7..00000000 --- a/src/components/Intro.jsx +++ /dev/null @@ -1,142 +0,0 @@ -'use client' - -import { useEffect, useState } from 'react' - -import Link from 'next/link' - -import { useRecoilState } from 'recoil' -import { modalContent, modalState } from '@/store/modalAtom' - -import { useAxios } from '@/hooks/useAxios' - -import { Button } from '@nextui-org/react' - -import SingleDatePicker from './common/datepicker/SingleDatePicker' -import RangeDatePicker from './common/datepicker/RangeDatePicker' -import QGrid from './common/grid/QGrid' -import { useSwal } from '@/hooks/useSwal' - -export default function Intro() { - const { get } = useAxios() - const { swalFire } = useSwal() - - // const [open, setOpen] = useState(false) - const [startDate, setStartDate] = useState(new Date()) - const singleDatePickerProps = { - startDate, - setStartDate, - } - - const [dateRange, setDateRange] = useState([null, null]) - const [startRangeDate, endRangeDate] = dateRange - const rangeDatePickerProps = { - startRangeDate, - endRangeDate, - setDateRange, - } - - // const gridProps = { - // isPageable: false, - // } - const [gridProps, setGridProps] = useState({ - gridData: [], - isPageable: false, - }) - - const [open, setOpen] = useRecoilState(modalState) - const [contents, setContent] = useRecoilState(modalContent) - - const modelProps = { - open, - setOpen, - } - - const ipsum = ( - <> -

title

-

- 저작자·발명가·과학기술자와 예술가의 권리는 법률로써 보호한다. 이 헌법은 1988년 2월 25일부터 시행한다. 다만, 이 헌법을 시행하기 위하여 필요한 - 법률의 제정·개정과 이 헌법에 의한 대통령 및 국회의원의 선거 기타 이 헌법시행에 관한 준비는 이 헌법시행 전에 할 수 있다. -

-

- 국가는 주택개발정책등을 통하여 모든 국민이 쾌적한 주거생활을 할 수 있도록 노력하여야 한다. 통신·방송의 시설기준과 신문의 기능을 보장하기 - 위하여 필요한 사항은 법률로 정한다. -

-

- 국회에서 의결된 법률안은 정부에 이송되어 15일 이내에 대통령이 공포한다. 선거에 관한 경비는 법률이 정하는 경우를 제외하고는 정당 또는 - 후보자에게 부담시킬 수 없다. -

- - ) - - useEffect(() => { - async function fetchData() { - // const response = await fetch('https://www.ag-grid.com/example-assets/space-mission-data.json') - // const data = await response.json() - const data = await get({ url: 'https://www.ag-grid.com/example-assets/space-mission-data.json' }) - setGridProps({ ...gridProps, gridData: data }) - } - fetchData() - }, []) - - return ( - <> -
- - - -
-
-
Single Date Picker
-
- -
-
-
-
Range Date Picker
-
- -
-
-
-
QGrid
-
- -
-
-
-
QModal
-
- {/* - {ipsum} */} - -
-
-
-
QToast
-
- -
-
- - ) -} diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx index 5cd4588c..a908ba0f 100644 --- a/src/components/Playground.jsx +++ b/src/components/Playground.jsx @@ -39,7 +39,7 @@ export default function Playground() { const [color, setColor] = useState('#ff0000') const [textInput, setTextInput] = useState('') - const [numberInput, setNumberInput] = useState(null) + const [numberInput, setNumberInput] = useState('') const [radioInput, setRadioInput] = useState('') const [checkboxInput, setCheckboxInput] = useState([]) const [selectedValue, setSelectedValue] = useState('') @@ -171,7 +171,7 @@ export default function Playground() { diff --git a/src/components/common/context-menu/QContextMenu.jsx b/src/components/common/context-menu/QContextMenu.jsx index 9d3eb4f3..f6665930 100644 --- a/src/components/common/context-menu/QContextMenu.jsx +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -1,16 +1,20 @@ 'use client' import { useEffect } from 'react' import '@/styles/contents.scss' -import { useRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue } from 'recoil' import { contextMenuListState, contextMenuState } from '@/store/contextMenu' import { useTempGrid } from '@/hooks/useTempGrid' +import { useContextMenu } from '@/hooks/useContextMenu' +import { useEvent } from '@/hooks/useEvent' export default function QContextMenu(props) { - const { contextRef, canvasProps, handleKeyup } = props + const { contextRef, canvasProps } = props const [contextMenu, setContextMenu] = useRecoilState(contextMenuState) - const [contextMenuList, setContextMenuList] = useRecoilState(contextMenuListState) + const contextMenuList = useRecoilValue(contextMenuListState) const activeObject = canvasProps?.getActiveObject() //액티브된 객체를 가져옴 const { tempGridMode, setTempGridMode } = useTempGrid() + const { handleKeyup } = useContextMenu() + const { addDocumentEventListener, removeDocumentEvent } = useEvent() let contextType = '' @@ -22,13 +26,13 @@ export default function QContextMenu(props) { } } } - const getYPosition = (e) => { const contextLength = contextMenuList.reduce((acc, cur, index) => { return acc + cur.length }, 0) return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17) } + useEffect(() => { if (!contextRef.current) return @@ -40,7 +44,7 @@ export default function QContextMenu(props) { y: window.innerHeight / 2 < e.pageY ? getYPosition(e) : e.pageY, } setContextMenu({ visible: true, ...position }) - document.addEventListener('keyup', (e) => handleKeyup(e)) + addDocumentEventListener('keyup', document, handleKeyup) canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제 } @@ -51,8 +55,9 @@ export default function QContextMenu(props) { const handleOutsideClick = (e) => { // e.preventDefault() - if (contextMenu.visible && !ref.current.contains(e.target)) { + if (contextMenu.visible) { setContextMenu({ ...contextMenu, visible: false }) + removeDocumentEvent('keyup') } } @@ -61,10 +66,11 @@ export default function QContextMenu(props) { document.addEventListener('click', handleOutsideClick) return () => { + removeDocumentEvent('keyup') document.removeEventListener('click', handleClick) document.removeEventListener('click', handleOutsideClick) } - }, [contextRef, contextMenu]) + }, [contextRef, contextMenuList]) const handleObjectMove = () => { activeObject.set({ diff --git a/src/components/common/draggable/WithDraggable.jsx b/src/components/common/draggable/WithDraggable.jsx index 29952981..76656837 100644 --- a/src/components/common/draggable/WithDraggable.jsx +++ b/src/components/common/draggable/WithDraggable.jsx @@ -1,18 +1,18 @@ 'use client' -import { useEffect, useState } from 'react' +import { useState } from 'react' import Draggable from 'react-draggable' -export default function WithDraggable({ isShow, children, pos, handle = '' }) { - const [position, setPosition] = useState({ x: 0, y: 0 }) +export default function WithDraggable({ isShow, children, pos = { x: 0, y: 0 }, handle = '' }) { + const [position, setPosition] = useState(pos) const handleOnDrag = (e, data) => { e.stopPropagation() setPosition({ x: data.x, y: data.y }) } - useEffect(() => { - setPosition({ ...pos }) - }, []) + // useEffect(() => { + // setPosition({ ...pos }) + // }, []) return ( <> diff --git a/src/components/common/input/QInput.jsx b/src/components/common/input/QInput.jsx index 3c1046af..83e16657 100644 --- a/src/components/common/input/QInput.jsx +++ b/src/components/common/input/QInput.jsx @@ -1,8 +1,9 @@ 'use client' -import { useCallback } from 'react' +import { useCallback, useState } from 'react' export default function QInput({ type, className, ref, id, readOnly = false, options = [], placeholder, value, onChange }) { + const [prevNum, setPrevNum] = useState('') // options = options || [ // { // id: 'one', @@ -35,51 +36,59 @@ export default function QInput({ type, className, ref, id, readOnly = false, opt const handleTextNumberChange = useCallback( (e) => { - if (type === 'text') { - onChange(e.target.value) - } else if (type === 'number') { - onChange(Number(e.target.value)) - } + onChange(e.target.value) }, - [type, onChange], + [onChange], ) // type=number 정수 부호, 소수점 검사, 일부 키 허용 + // const checkInputNumber = (e) => { + // const value = e.target.value + // const key = e.key + // const allowKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Tab', 'Enter', 'Control'] // 'ArrowUp', 'ArrowDown', + // if (key >= '0' && key <= '9') { + // return + // } + // if (key === '.' && !value.includes('.') && value.length > 0 && !isNaN(Number(value))) { + // return + // } + // if (key === '-' && !value.includes('-') && value.length > 0) { + // return + // } + // if (allowKeys.includes(key)) { + // return + // } + // if (key === 'a' || key === 'c' || key === 'v' || key === 'x' || key === 'z') { + // return + // } + // e.preventDefault() + // } + // type=number 정수 부호, 소수점 검사 const checkInputNumber = (e) => { - const value = e.target.value - const key = e.key - const allowKeys = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'Tab', 'Enter', 'Control'] // 'ArrowUp', 'ArrowDown', - if (key >= '0' && key <= '9') { - return + const value = e.target.defaultValue + if (value === '') return + + const regex = /^-?([1-9]\d*|\d)(\.\d*)?$/ + if (regex.test(value)) { + setPrevNum(value) + } else { + onChange(prevNum) } - if (key === '.' && !value.includes('.') && value.length > 0 && !isNaN(Number(value))) { - return - } - if (key === '-' && !value.includes('-') && value.length > 0) { - return - } - if (allowKeys.includes(key)) { - return - } - if (key === 'a' || key === 'c' || key === 'v' || key === 'x' || key === 'z') { - return - } - e.preventDefault() } // input type : text, number const inputTextNumber = () => { return ( ) } diff --git a/src/components/common/popupManager/PopupManager.jsx b/src/components/common/popupManager/PopupManager.jsx index f24a8526..e84ee610 100644 --- a/src/components/common/popupManager/PopupManager.jsx +++ b/src/components/common/popupManager/PopupManager.jsx @@ -1,10 +1,10 @@ 'use client' -import { useRecoilState } from 'recoil' +import { useRecoilValue } from 'recoil' import { popupState } from '@/store/popupAtom' import { Fragment } from 'react' export default function PopupManager() { - const [popup, setPopup] = useRecoilState(popupState) + const popup = useRecoilValue(popupState) return [ ...popup?.config.map((child) => {child.component}), diff --git a/src/components/estimate/Estimate.jsx b/src/components/estimate/Estimate.jsx index 813962b3..f794cb50 100644 --- a/src/components/estimate/Estimate.jsx +++ b/src/components/estimate/Estimate.jsx @@ -1,9 +1,8 @@ 'use client' -import { useEffect, useState, useRef } from 'react' +import { useEffect, useState, useContext } from 'react' import { useRecoilValue } from 'recoil' import { floorPlanObjectState } from '@/store/floorPlanObjectAtom' -import { sessionStore } from '@/store/commonAtom' import { useMessage } from '@/hooks/useMessage' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import SingleDatePicker from '../common/datepicker/SingleDatePicker' @@ -15,15 +14,15 @@ import dayjs from 'dayjs' import { useCommonCode } from '@/hooks/common/useCommonCode' import Select from 'react-select' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' +import { SessionContext } from '@/app/SessionProvider' export default function Estimate({ params }) { + const { session } = useContext(SessionContext) const [objectNo, setObjectNo] = useState('') //물건번호 const [planNo, setPlanNo] = useState('') //플랜번호 - const [files, setFiles] = useState([]) // 보내는 첨부파일 - //체크박스 - const [checkItems, setCheckItems] = useState(new Set()) - const [checkedList, setCheckedList] = useState([]) + const [files, setFiles] = useState([]) // 보내는 첨부파일 + const [originFiles, setOriginFiles] = useState([]) //기존 첨부파일 const [showContentCode, setShowContentCode] = useState('ATTR001') @@ -40,7 +39,6 @@ export default function Estimate({ params }) { setStartDate, } - const sessionState = useRecoilValue(sessionStore) const objectRecoil = useRecoilValue(floorPlanObjectState) //견적서 상세데이터 @@ -52,16 +50,14 @@ export default function Estimate({ params }) { const [specialNoteList, setSpecialNoteList] = useState([]) const globalLocaleState = useRecoilValue(globalLocaleStore) - const { get, post } = useAxios(globalLocaleState) + const { get, promisePost } = useAxios(globalLocaleState) const { getMessage } = useMessage() const { setMenuNumber } = useCanvasMenu() + //새로 추가한 첨부파일 props const fileUploadProps = { - // objectNo: '', - // planNo: params.pid, - // category: '10', uploadFiles: files, setUploadFiles: setFiles, } @@ -127,11 +123,42 @@ export default function Estimate({ params }) { event.stopPropagation() } + // 추가한 첨부파일 state에 넣기 + useEffect(() => { + if (isNotEmptyArray(files)) { + files.map((row) => { + setState({ fileList: row.data }) + }) + } else { + setState({ fileList: [] }) + } + }, [files]) + + //상세에서 내려온 첨부파일 set 만들기 + useEffect(() => { + if (isNotEmptyArray(state.fileList)) { + setOriginFiles(state.fileList) + } + }, [state?.fileList]) + + // 기존첨부파일 삭제 + const deleteOriginFile = async (objectNo, no) => { + const delParams = { + userId: session.userId, + objectNo: objectNo, + no: no, + } + await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => { + if (res.status === 204) { + setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no)) + } + }) + } + return (
{/* 물건번호, 견적서번호, 등록일, 변경일시 시작 */} - {/*
*/}
@@ -206,7 +233,6 @@ export default function Estimate({ params }) { defaultValue={state?.charger} onChange={(e) => { //담당자 charger - // console.log('담당자:::::', e.target.value) setState({ charger: e.target.value }) }} /> @@ -227,7 +253,6 @@ export default function Estimate({ params }) { defaultValue={state?.objectName} onChange={(e) => { //안건명 objectName - // console.log('안건명::::', e.target.value) setState({ objectName: e.target.value }) }} /> @@ -244,7 +269,6 @@ export default function Estimate({ params }) { if (isObjectNotEmpty(e)) { setState({ objectNameOmit: e.clCodeNm }) } else { - // console.log('XXX') setState({ objectNameOmit: '' }) } }} @@ -342,7 +366,6 @@ export default function Estimate({ params }) { defaultValue={state?.remarks} onChange={(e) => { //비고 - // console.log('비고:::::', e.target.value) setState({ remarks: e.target.value }) }} /> @@ -390,6 +413,23 @@ export default function Estimate({ params }) { {getMessage('estimate.detail.header.fileList2')} + +
+
    + {isNotEmptyArray(originFiles) && + originFiles.map((originFile) => { + return ( +
  • + + {originFile.faileName} + + +
  • + ) + })} +
+
+ @@ -439,7 +479,7 @@ export default function Estimate({ params }) { ) })}
- {/* 견적특이사항 선택한 내용?영역시작 */} + {/* 견적특이사항 선택한 내용 영역시작 */}
{specialNoteList.map((row) => { if (row.code === showContentCode) { @@ -570,7 +610,6 @@ export default function Estimate({ params }) {
{/* 기본정보끝 */} - {/* */}
) diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 7df9d495..d1f97e0e 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -1,8 +1,15 @@ import { fabric } from 'fabric' import { v4 as uuidv4 } from 'uuid' import { QLine } from '@/components/fabric/QLine' -import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util' -import { calculateAngle, drawRidgeRoof, drawHippedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils' +import { + distanceBetweenPoints, + findTopTwoIndexesByDistance, + getAllRelatedObjects, + getDirectionByPoint, + sortedPointLessEightPoint, + sortedPoints, +} from '@/util/canvas-util' +import { calculateAngle, drawRidgeRoof, drawShedRoof, inPolygon, toGeoJSON } from '@/util/qpolygon-utils' import * as turf from '@turf/turf' import { LINE_TYPE } from '@/common/common' @@ -131,7 +138,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { }) this.on('removed', () => { - const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id) + // const children = getAllRelatedObjects(this.id, this.canvas) + const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id && obj.name === 'lengthText') children.forEach((child) => { this.canvas.remove(child) }) @@ -171,6 +179,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { offset: 0, }, parent: this, + parentId: this.id, direction: getDirectionByPoint(point, nextPoint), idx: i + 1, }) @@ -189,7 +198,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE] const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD] - const isEaves = types.every((type) => eavesType.includes(type)) + // const isEaves = types.every((type) => eavesType.includes(type)) const gableOdd = types.filter((type, i) => i % 2 === 0) const gableEven = types.filter((type, i) => i % 2 === 1) const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED) @@ -201,18 +210,40 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { ) { console.log('박공 지붕') } else if (hasShed) { - //편류지붕 - let shedIndex = 0 - types.forEach((type, i) => { - if (type === LINE_TYPE.WALLLINE.SHED) { - shedIndex = i - } + const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED) + const areLinesParallel = function (line1, line2) { + const angle1 = calculateAngle(line1.startPoint, line1.endPoint) + const angle2 = calculateAngle(line2.startPoint, line2.endPoint) + return angle1 === angle2 + } + + let isShedRoof = true + sheds.forEach((shed, i) => { + isShedRoof = areLinesParallel(shed, sheds[(i + 1) % sheds.length]) }) - const shedOdd = types.filter((type, i) => i % 2 === shedIndex % 2).filter((type) => type !== LINE_TYPE.WALLLINE.SHED) - const shedEven = types.filter((type, i) => i % 2 !== shedIndex % 2) - types.forEach((type, i) => console.log(type, i, i % 2, shedIndex % 2, i % 2 === shedIndex % 2)) - if (shedOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && shedEven.every((type) => type === LINE_TYPE.WALLLINE.GABLE)) { - console.log('편류지붕') + if (isShedRoof) { + const eaves = this.lines + .filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.EAVES) + .filter((line) => { + const angle1 = calculateAngle(sheds[0].startPoint, sheds[0].endPoint) + const angle2 = calculateAngle(line.startPoint, line.endPoint) + if (Math.abs(angle1 - angle2) === 180) { + return line + } + }) + if (eaves.length > 0) { + const gables = this.lines.filter((line) => sheds.includes(line) === false && eaves.includes(line) === false) + const isGable = gables.every((line) => gableType.includes(line.attributes.type)) + if (isGable) { + drawShedRoof(this.id, this.canvas) + } else { + drawRidgeRoof(this.id, this.canvas) + } + } else { + drawRidgeRoof(this.id, this.canvas) + } + } else { + drawRidgeRoof(this.id, this.canvas) } } else { drawRidgeRoof(this.id, this.canvas) diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index b0881b6e..ad10485c 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -19,7 +19,7 @@ export default function CanvasFrame() { const { canvas } = useCanvas('canvas') const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() const currentMenu = useRecoilValue(currentMenuState) - const { contextMenu, handleClick, handleKeyup } = useContextMenu() + const { contextMenu, handleClick } = useContextMenu() const { selectedPlan, modifiedPlanFlag, checkCanvasObjectEvent, resetModifiedPlans } = usePlan() useEvent() @@ -57,7 +57,7 @@ export default function CanvasFrame() {
- + {contextMenu?.map((menus, index) => (
    {menus.map((menu) => ( diff --git a/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx b/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx index c22d2248..431e0f85 100644 --- a/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx +++ b/src/components/floor-plan/modal/placementSurface/PlacementSurfaceSetting.jsx @@ -240,7 +240,7 @@ export default function PlacementSurfaceSetting({ id, pos = { x: 50, y: 230 } }) }, []) return ( - +

    {getMessage('plan.menu.placement.surface.arrangement')}

    diff --git a/src/components/floor-plan/modal/setting01/SettingModal01.jsx b/src/components/floor-plan/modal/setting01/SettingModal01.jsx index e1bef1f0..222226c1 100644 --- a/src/components/floor-plan/modal/setting01/SettingModal01.jsx +++ b/src/components/floor-plan/modal/setting01/SettingModal01.jsx @@ -11,12 +11,12 @@ import { useRecoilValue } from 'recoil' import { usePopup } from '@/hooks/usePopup' export default function SettingModal01(props) { - const { setShowDotLineGridModal, setShowFontSettingModal, id, isConfig } = props - console.log(props) + const { id } = props const [buttonAct, setButtonAct] = useState(1) const { getMessage } = useMessage() const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor) - const { addPopup, closePopup } = usePopup() + const { closePopup } = usePopup() + const handleBtnClick = (num) => { setButtonAct(num) } diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 2603cc04..58b1c002 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -5,21 +5,19 @@ import { useRouter, usePathname } from 'next/navigation' import { useAxios } from '@/hooks/useAxios' import { useMessage } from '@/hooks/useMessage' import StuffQGrid from './StuffQGrid' -import { useRecoilValue, useRecoilState, useSetRecoilState } from 'recoil' +import { useRecoilValue, useRecoilState, useSetRecoilState, useResetRecoilState } from 'recoil' import { stuffSearchState } from '@/store/stuffAtom' import { queryStringFormatter, isEmptyArray } from '@/util/common-utils' import dayjs from 'dayjs' -import { isObjectNotEmpty } from '@/util/common-utils' import { convertNumberToPriceDecimal } from '@/util/common-utils' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import KO from '@/locales/ko.json' import JA from '@/locales/ja.json' import QPagination from '../common/pagination/QPagination' -import { sessionStore } from '@/store/commonAtom' import { SessionContext } from '@/app/SessionProvider' export default function Stuff() { - const sessionState = useRecoilValue(sessionStore) + const resetStuffRecoil = useResetRecoilState(stuffSearchState) const { session } = useContext(SessionContext) const setAppMessageState = useSetRecoilState(appMessageStore) const stuffSearchParams = useRecoilValue(stuffSearchState) @@ -34,9 +32,6 @@ export default function Stuff() { const { get } = useAxios(globalLocaleState) const gridRef = useRef() - // const [selectedRowData, setSelectedRowData] = useState([]) - // const [selectedRowDataCount, setSelectedRowDataCount] = useState(0) - const router = useRouter() const pathname = usePathname() @@ -67,10 +62,6 @@ export default function Stuff() { field: 'lastEditDatetime', minWidth: 200, headerName: getMessage('stuff.gridHeader.lastEditDatetime'), - // headerCheckboxSelection: true, - // headerCheckboxSelectionCurrentPageOnly: true, //페이징시 현재 페이지만 체크되도록 - // checkboxSelection: true, - // showDisabledCheckboxes: true, cellStyle: { justifyContent: 'center' }, valueFormatter: function (params) { if (params.value) { @@ -169,77 +160,69 @@ export default function Stuff() { // 진입시 그리드 데이터 조회 useEffect(() => { - if (isObjectNotEmpty(session)) { - if (stuffSearchParams?.code === 'S') { - const params = { - // saleStoreId: stuffSearchParams.schSelSaleStoreId, - saleStoreId: session.storeId, - schObjectNo: stuffSearchParams?.schObjectNo, - schAddress: stuffSearchParams?.schAddress, - schObjectName: stuffSearchParams?.schObjectName, - schSaleStoreName: stuffSearchParams?.schSaleStoreName, - schReceiveUser: stuffSearchParams?.schReceiveUser, - schDispCompanyName: stuffSearchParams?.schDispCompanyName, - schDateType: stuffSearchParams.schDateType, - schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), - schToDt: dayjs(new Date()).format('YYYY-MM-DD'), - startRow: (pageNo - 1) * pageSize + 1, - endRow: pageNo * pageSize, - schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId - ? stuffSearchParams.schOtherSelSaleStoreId - : stuffSearchParams.schSelSaleStoreId, - schSortType: stuffSearchParams.schSortType, - } - async function fetchData() { - const apiUrl = `/api/object/list?${queryStringFormatter(params)}` - await get({ - url: apiUrl, - }).then((res) => { - if (!isEmptyArray(res)) { - setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) - setTotalCount(res[0].totCnt) - } - }) - } - if (stuffSearchParams?.schSelSaleStoreId !== '') { - fetchData() - } - } else if (stuffSearchParams?.code === 'M') { - //메인화면에서 진입 - const params = { - saleStoreId: session?.storeId, - schObjectNo: stuffSearchParams.schObjectNo, - schAddress: '', - schObjectName: '', - schSaleStoreName: '', - schReceiveUser: '', - schDispCompanyName: '', - schDateType: 'U', - schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), - schToDt: dayjs(new Date()).format('YYYY-MM-DD'), - startRow: (pageNo - 1) * pageSize + 1, - endRow: pageNo * pageSize, - schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId - ? stuffSearchParams.schOtherSelSaleStoreId - : stuffSearchParams.schSelSaleStoreId, - schSortType: 'R', - } - setStuffSearch({ - ...params, + if (stuffSearchParams?.code === 'S') { + const params = { + saleStoreId: session.storeId, + schObjectNo: stuffSearchParams?.schObjectNo, + schAddress: stuffSearchParams?.schAddress, + schObjectName: stuffSearchParams?.schObjectName, + schSaleStoreName: stuffSearchParams?.schSaleStoreName, + schReceiveUser: stuffSearchParams?.schReceiveUser, + schDispCompanyName: stuffSearchParams?.schDispCompanyName, + schDateType: stuffSearchParams.schDateType, + schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), + schToDt: dayjs(new Date()).format('YYYY-MM-DD'), + startRow: (pageNo - 1) * pageSize + 1, + endRow: pageNo * pageSize, + schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, + schSortType: stuffSearchParams.schSortType, + } + + async function fetchData() { + const apiUrl = `/api/object/list?${queryStringFormatter(params)}` + await get({ + url: apiUrl, + }).then((res) => { + if (!isEmptyArray(res)) { + setGridProps({ ...gridProps, gridData: res, count: res[0].totCnt }) + setTotalCount(res[0].totCnt) + } }) } - } - }, [pageNo, session, stuffSearchParams]) - useEffect(() => { - if (stuffSearchParams?.code === 'E') { + if (stuffSearch.schSelSaleStoreId !== '') { + fetchData() + } + } else if (stuffSearchParams?.code === 'M') { + const params = { + saleStoreId: session?.storeId, + schObjectNo: stuffSearchParams.schObjectNo, + schAddress: '', + schObjectName: '', + schSaleStoreName: '', + schReceiveUser: '', + schDispCompanyName: '', + schDateType: 'U', + schFromDt: dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), + schToDt: dayjs(new Date()).format('YYYY-MM-DD'), + startRow: (pageNo - 1) * pageSize + 1, + endRow: pageNo * pageSize, + schSelSaleStoreId: stuffSearchParams?.schOtherSelSaleStoreId ? stuffSearchParams.schOtherSelSaleStoreId : stuffSearchParams.schSelSaleStoreId, + schSortType: 'R', + } + setStuffSearch({ + ...params, + }) + } else if (stuffSearchParams?.code === 'E') { stuffSearchParams.startRow = 1 stuffSearchParams.endRow = 1 * pageSize stuffSearchParams.schSortType = defaultSortType - setPageNo(1) + setStuffSearch({ + ...stuffSearch, + code: 'FINISH', + }) - //조회를 눌렀을때 async function fetchData() { const apiUrl = `/api/object/list?saleStoreId=${session?.storeId}&${queryStringFormatter(stuffSearchParams)}` await get({ url: apiUrl }).then((res) => { @@ -252,7 +235,10 @@ export default function Stuff() { } }) } + fetchData() + } else if (stuffSearchParams?.code === 'C') { + resetStuffRecoil() } }, [stuffSearchParams]) diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 9a455216..61cdc1df 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -11,7 +11,6 @@ import { isEmptyArray, isNotEmptyArray, isObjectNotEmpty } from '@/util/common-u import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' import { useRecoilValue, useSetRecoilState } from 'recoil' -import { sessionStore } from '@/store/commonAtom' import { SessionContext } from '@/app/SessionProvider' import FindAddressPop from './popup/FindAddressPop' import PlanRequestPop from './popup/PlanRequestPop' @@ -28,7 +27,6 @@ export default function StuffDetail() { const [selOptions, setSelOptions] = useState('') //선택한 1차점 const [otherSelOptions, setOtherSelOptions] = useState('') //선택한 1차점외 - const sessionState = useRecoilValue(sessionStore) const { session } = useContext(SessionContext) const router = useRouter() @@ -320,12 +318,11 @@ export default function StuffDetail() { let firstList let otherList let favList - // if (sessionState?.storeId === 'T01') { if (session?.storeId === 'T01') { url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}` } else { if (session.storeLvl === '1') { - url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}` + url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` } else { url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` } @@ -400,7 +397,6 @@ export default function StuffDetail() { } }) } - // }, [objectNo, sessionState]) }, [objectNo, session]) useEffect(() => { @@ -1279,9 +1275,7 @@ export default function StuffDetail() { //1차점 or 2차점 안고르고 임시저장하면 if (params.saleStoreId == '') { - // params.saleStoreId = sessionState.storeId params.saleStoreId = session.storeId - // params.saleStoreLevel = sessionState.storeLvl params.saleStoreLevel = session.storeLvl } @@ -1445,7 +1439,6 @@ export default function StuffDetail() {
    - {/* {sessionState?.storeId === 'T01' && ( */} {session?.storeId === 'T01' && ( <>
    @@ -1479,7 +1472,6 @@ export default function StuffDetail() { )} - {/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl === '1' && ( */} {session?.storeId !== 'T01' && session?.storeLvl === '1' && ( <>
    @@ -1511,7 +1503,6 @@ export default function StuffDetail() {
    )} - {/* {sessionState?.storeId !== 'T01' && sessionState?.storeLvl !== '1' && ( */} {session?.storeId !== 'T01' && session?.storeLvl !== '1' && ( <>
    diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 659ab696..7bd8f456 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -12,7 +12,6 @@ import { isEmptyArray } from '@/util/common-utils' import dayjs from 'dayjs' import Link from 'next/link' import SingleDatePicker from '../common/datepicker/SingleDatePicker' -import { sessionStore } from '@/store/commonAtom' import { useMessage } from '@/hooks/useMessage' import { isObjectNotEmpty } from '@/util/common-utils' @@ -20,7 +19,6 @@ import { SessionContext } from '@/app/SessionProvider' export default function StuffSearchCondition() { const { session } = useContext(SessionContext) - const sessionState = useRecoilValue(sessionStore) const setAppMessageState = useSetRecoilState(appMessageStore) const globalLocaleState = useRecoilValue(globalLocaleStore) const { getMessage } = useMessage() @@ -94,13 +92,13 @@ export default function StuffSearchCondition() { }) } else { setStuffSearch({ - schObjectNo: objectNo ? objectNo : '', - schSaleStoreName: saleStoreName ? saleStoreName : '', - schAddress: address ? address : '', - schObjectName: objectName ? objectName : '', - schDispCompanyName: dispCompanyName ? dispCompanyName : '', + schObjectNo: objectNo, + schSaleStoreName: saleStoreName, + schAddress: address, + schObjectName: objectName, + schDispCompanyName: dispCompanyName, schSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId ? stuffSearch.schOtherSelSaleStoreId : stuffSearch.schSelSaleStoreId, - schReceiveUser: receiveUser ? receiveUser : '', + schReceiveUser: receiveUser, schDateType: dateType, schFromDt: dayjs(startDate).format('YYYY-MM-DD'), schToDt: dayjs(endDate).format('YYYY-MM-DD'), @@ -131,14 +129,12 @@ export default function StuffSearchCondition() { setDateType('U') setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) - // if (sessionState?.storeId === 'T01') { if (session?.storeId === 'T01') { setSchSelSaleStoreId('') handleClear1() //판매대리점선택 자동완성 클리어 resetStuffRecoil() setStuffSearch({ ...stuffSearch, - code: 'C', schSelSaleStoreId: '', schOtherSelSaleStoreId: '', }) @@ -156,23 +152,17 @@ export default function StuffSearchCondition() { } useEffect(() => { - // if (isObjectNotEmpty(sessionState)) { if (isObjectNotEmpty(session)) { // storeId가 T01 이거나 storeLvl이 1차점일때만 판매대리점 선택 활성화 let url - // if (sessionState?.storeId === 'T01') { if (session?.storeId === 'T01') { //T01일떄 - // url = `/api/object/saleStore/${sessionState?.storeId}/firstList?userId=${sessionState?.userId}` url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}` } else { - // if (sessionState.storeLvl === '1') { if (session.storeLvl === '1') { //T01아닌 1차점일때 - // url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}` - url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}` + url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` } else { - // url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=1&userId=${sessionState?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}` } } @@ -187,7 +177,6 @@ export default function StuffSearchCondition() { let allList let favList let otherList - // if (sessionState?.storeId === 'T01') { if (session?.storeId === 'T01') { allList = res allList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId) @@ -195,17 +184,14 @@ export default function StuffSearchCondition() { setSchSelSaleStoreList(allList) setFavoriteStoreList(favList) setShowSaleStoreList(favList) - // setSchSelSaleStoreId(sessionState?.storeId) setSchSelSaleStoreId(session?.storeId) setStuffSearch({ ...stuffSearch, code: 'S', - // schSelSaleStoreId: sessionState?.storeId, schSelSaleStoreId: session?.storeId, }) //T01일때 2차 판매점 호출하기 디폴트로 1차점을 본인으로 셋팅해서 세션storeId사용 - // url = `/api/object/saleStore/${sessionState?.storeId}/list?firstFlg=0&userId=${sessionState?.userId}` url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}` get({ url: url }).then((res) => { @@ -222,7 +208,6 @@ export default function StuffSearchCondition() { } }) } else { - // if (sessionState?.storeLvl === '1') { if (session?.storeLvl === '1') { allList = res favList = res.filter((row) => row.priority !== 'B') @@ -250,7 +235,6 @@ export default function StuffSearchCondition() { setOtherSaleStoreList(otherList) //선택한 2차점 세션으로 자동셋팅 - // setOtherSaleStoreId(sessionState?.storeId) setOtherSaleStoreId(session?.storeId) setStuffSearch({ ...stuffSearch, @@ -262,7 +246,6 @@ export default function StuffSearchCondition() { } }) } - // }, [sessionState]) }, [session]) //초기화 눌렀을 때 1차판매점 자동완성도.. @@ -296,7 +279,6 @@ export default function StuffSearchCondition() { stuffSearch.schSelSaleStoreId = key.saleStoreId //T01아닌 1차점은 본인으로 디폴트셋팅이고 수정할수없어서 여기안옴 //고른 1차점의 saleStoreId로 2차점 API호출하기 - // let url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${sessionState?.userId}` let url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}` let otherList get({ url: url }).then((res) => { @@ -339,6 +321,12 @@ export default function StuffSearchCondition() { useEffect(() => { setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD')) + setObjectNo(stuffSearch.schObjectNo ? stuffSearch.schObjectNo : objectNo) + setSaleStoreName(stuffSearch.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName) + setAddress(stuffSearch.schAddress ? stuffSearch.schAddress : address) + setobjectName(stuffSearch.schObjectName ? stuffSearch.schObjectName : objectName) + setDispCompanyName(stuffSearch.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName) + setReceiveUser(stuffSearch.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser) }, [stuffSearch]) useEffect(() => { @@ -401,7 +389,7 @@ export default function StuffSearchCondition() { ref={objectNoRef} className="input-light" defaultValue={stuffSearch?.schObjectNo ? stuffSearch.schObjectNo : objectNo} - onChange={(e) => { + onChange={() => { setObjectNo(objectNoRef.current.value) }} onKeyUp={handleByOnKeyUp} @@ -416,7 +404,7 @@ export default function StuffSearchCondition() { ref={saleStoreNameRef} className="input-light" defaultValue={stuffSearch?.schSaleStoreName ? stuffSearch.schSaleStoreName : saleStoreName} - onChange={(e) => { + onChange={() => { setSaleStoreName(saleStoreNameRef.current.value) }} onKeyUp={handleByOnKeyUp} @@ -431,7 +419,7 @@ export default function StuffSearchCondition() { ref={addressRef} className="input-light" defaultValue={stuffSearch?.schAddress ? stuffSearch.schAddress : address} - onChange={(e) => { + onChange={() => { setAddress(addressRef.current.value) }} onKeyUp={handleByOnKeyUp} @@ -446,7 +434,7 @@ export default function StuffSearchCondition() { ref={dispCompanyNameRef} className="input-light" defaultValue={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName} - onChange={(e) => { + onChange={() => { setDispCompanyName(dispCompanyNameRef.current.value) }} onKeyUp={handleByOnKeyUp} @@ -463,7 +451,7 @@ export default function StuffSearchCondition() { ref={objectNameRef} className="input-light" defaultValue={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName} - onChange={(e) => { + onChange={() => { setobjectName(objectNameRef.current.value) }} onKeyUp={handleByOnKeyUp} @@ -478,7 +466,7 @@ export default function StuffSearchCondition() { className="input-light" ref={receiveUserRef} defaultValue={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser} - onChange={(e) => { + onChange={() => { setReceiveUser(receiveUserRef.current.value) }} onKeyUp={handleByOnKeyUp} @@ -620,7 +608,6 @@ export default function StuffSearchCondition() { value={'U'} onChange={(e) => { setDateType(e.target.value) - //setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) }} /> @@ -634,7 +621,6 @@ export default function StuffSearchCondition() { value={'R'} onChange={(e) => { setDateType(e.target.value) - //setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) }} /> diff --git a/src/hooks/floorPlan/estimate/useEstimateController.js b/src/hooks/floorPlan/estimate/useEstimateController.js index 87fa15ca..2b92a14a 100644 --- a/src/hooks/floorPlan/estimate/useEstimateController.js +++ b/src/hooks/floorPlan/estimate/useEstimateController.js @@ -1,9 +1,11 @@ import { useAxios } from '@/hooks/useAxios' -import { useEffect, useReducer, useState } from 'react' +import { useContext, useEffect, useReducer, useState } from 'react' import { useRecoilState, useRecoilValue } from 'recoil' import { globalLocaleStore } from '@/store/localeAtom' import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom' import { isObjectNotEmpty } from '@/util/common-utils' +import { SessionContext } from '@/app/SessionProvider' + const reducer = (prevState, nextState) => { return { ...prevState, ...nextState } } @@ -41,6 +43,7 @@ const defaultEstimateData = { unit: '', }, ], + fileList: [], } // Helper functions @@ -51,14 +54,14 @@ const updateItemInList = (itemList, itemId, updates) => { } export const useEstimateController = (planNo) => { + const { session } = useContext(SessionContext) const globalLocaleState = useRecoilValue(globalLocaleStore) const objectRecoil = useRecoilValue(floorPlanObjectState) const [estimateData, setEstimateData] = useRecoilState(estimateState) - const { get, post } = useAxios(globalLocaleState) + const { get, post, promisePost } = useAxios(globalLocaleState) const [isLoading, setIsLoading] = useState(false) - const { promisePost } = useAxios() const [state, setState] = useReducer(reducer, defaultEstimateData) useEffect(() => { @@ -123,12 +126,32 @@ export const useEstimateController = (planNo) => { } useEffect(() => { - setEstimateData({ ...state }) + setEstimateData({ ...state, userId: session.userId }) + //sapSalesStoreCd 추가예정 필수값 + // setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd : session.sapSalesStoreCd }) }, [state]) //견적서 저장 const handleEstimateSubmit = async () => { console.log('::담긴 estimateData:::', estimateData) + //1. 첨부파일 저장 + const formData = new FormData() + formData.append('file', estimateData.fileList) + formData.append('objectNo', estimateData.objectNo) + formData.append('planNo', estimateData.planNo) + formData.append('category', '10') + formData.append('userId', estimateData.userId) + for (const value of formData.values()) { + console.log('formData::', value) + } + + await promisePost({ url: '/api/file/fileUpload', data: formData }).then((res) => { + console.log('파일저장::::::::::', res) + }) + + //2. 상세데이터 저장 + + console.log('상세저장시작!!') return try { const result = await promisePost({ diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index e9aa7d93..6e95e79c 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -815,7 +815,7 @@ export function useAuxiliaryDrawing(id) { roofBase.lines.some((line) => isPointOnLine(line, { x: line.x2, y: line.y2 })) if (inPolygon1 && inPolygon2) { - line.attributes = { ...line.attributes, roofId: roofBase.id } + line.attributes = { ...line.attributes, roofId: roofBase.id, actualSize: 0, planeSize: line.getLength() } return true } }) diff --git a/src/hooks/roofcover/useRoofAllocationSetting.js b/src/hooks/roofcover/useRoofAllocationSetting.js index c229549e..921e737a 100644 --- a/src/hooks/roofcover/useRoofAllocationSetting.js +++ b/src/hooks/roofcover/useRoofAllocationSetting.js @@ -2,7 +2,6 @@ import { useRecoilValue } from 'recoil' import { canvasState } from '@/store/canvasAtom' import { useEffect, useState } from 'react' import { setSurfaceShapePattern } from '@/util/canvas-util' -import { splitPolygonWithLines } from '@/util/qpolygon-utils' import { useSwal } from '@/hooks/useSwal' import { usePolygon } from '@/hooks/usePolygon' import { roofDisplaySelector } from '@/store/settingAtom' @@ -13,7 +12,7 @@ import { POLYGON_TYPE } from '@/common/common' export function useRoofAllocationSetting(id) { const canvas = useRecoilValue(canvasState) const roofDisplay = useRecoilValue(roofDisplaySelector) - const { drawDirectionArrow } = usePolygon() + const { drawDirectionArrow, addLengthText, splitPolygonWithLines } = usePolygon() const { closePopup } = usePopup() const { swalFire } = useSwal() diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 2823415d..21948de0 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -170,6 +170,7 @@ export function useRoofShapeSetting(id) { //기존 wallLine 삭제 let outerLines + let direction switch (shapeNum) { case 1: { @@ -196,6 +197,7 @@ export function useRoofShapeSetting(id) { // 서쪽 initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + direction = 'west' outerLines.forEach((line) => { setWestAndEastRoof(line) @@ -240,6 +242,7 @@ export function useRoofShapeSetting(id) { case 6: { initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + direction = 'east' outerLines.forEach((line) => { setWestAndEastRoof(line) @@ -285,6 +288,7 @@ export function useRoofShapeSetting(id) { case 7: { initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + direction = 'south' outerLines.forEach((line) => { setSouthAndNorthRoof(line) @@ -329,6 +333,7 @@ export function useRoofShapeSetting(id) { case 8: { initLineSetting() outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + direction = 'north' outerLines.forEach((line) => { setSouthAndNorthRoof(line) @@ -389,7 +394,7 @@ export function useRoofShapeSetting(id) { canvas.remove(obj) }) - const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL }) + const polygon = addPolygonByLines(outerLines, { name: POLYGON_TYPE.WALL, direction }) polygon.lines = [...outerLines] addPitchTextsByOuterLines() diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index 4bab1fd6..42266c8a 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react' import { useRecoilState, useRecoilValue } from 'recoil' import { v4 as uuidv4 } from 'uuid' -import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom' +import { canvasSizeState, canvasState, canvasZoomState, currentObjectState } from '@/store/canvasAtom' import { QPolygon } from '@/components/fabric/QPolygon' import { usePlan } from '@/hooks/usePlan' import { fontSelector } from '@/store/fontAtom' @@ -12,8 +12,6 @@ export function useCanvasEvent() { const [canvasForEvent, setCanvasForEvent] = useState(null) const [currentObject, setCurrentObject] = useRecoilState(currentObjectState) const canvasSize = useRecoilValue(canvasSizeState) - const fontSize = useRecoilValue(fontSizeState) - const fontFamily = useRecoilValue(fontFamilyState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) const lengthTextOption = useRecoilValue(fontSelector('lengthText')) const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan() @@ -209,13 +207,52 @@ export function useCanvasEvent() { created: (e) => { const target = e.selected[0] setCurrentObject(target) + const { selected } = e + + if (selected?.length > 0) { + selected.forEach((obj) => { + if (obj.type === 'QPolygon') { + obj.set({ stroke: 'red' }) + } + }) + canvas.renderAll() + } }, cleared: (e) => { setCurrentObject(null) + const { deselected } = e + + if (deselected?.length > 0) { + deselected.forEach((obj) => { + if (obj.type === 'QPolygon') { + obj.set({ stroke: 'black' }) + } + }) + } + canvas.renderAll() }, updated: (e) => { const target = e.selected[0] setCurrentObject(target) + const { selected, deselected } = e + + if (deselected?.length > 0) { + deselected.forEach((obj) => { + if (obj.type === 'QPolygon') { + obj.set({ stroke: 'black' }) + } + }) + } + + if (selected?.length > 0) { + selected.forEach((obj) => { + if (obj.type === 'QPolygon') { + obj.set({ stroke: 'red' }) + } + }) + } + + canvas.renderAll() }, } diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 34f5a952..64892dce 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -231,12 +231,12 @@ export function useContextMenu() { y: 180, }) setCurrentContextMenu(menu) + currentMenuSetting() setQContextMenu({ ...qContextMenu, visible: false }) } const handleKeyup = (e) => { let menu = null - for (let i = 0; i < contextMenu.length; i++) { const temp = contextMenu[i].filter((menu) => { return menu.shortcut?.includes(e.key) @@ -259,7 +259,6 @@ export function useContextMenu() { useEffect(() => { console.log('currentObject', currentObject) if (currentObject?.name) { - console.log(currentObject?.name) switch (currentObject.name) { case 'triangleDormer': case 'pentagonDormer': diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index a84dea38..891ad101 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -1,12 +1,11 @@ import { useEffect, useRef } from 'react' -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { useRecoilValue, useSetRecoilState } from 'recoil' import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom' import { fabric } from 'fabric' -import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint, polygonToTurfPolygon } from '@/util/canvas-util' +import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useDotLineGrid } from '@/hooks/useDotLineGrid' import { useTempGrid } from '@/hooks/useTempGrid' -import { gridDisplaySelector } from '@/store/settingAtom' export function useEvent() { const canvas = useRecoilValue(canvasState) @@ -100,7 +99,13 @@ export function useEvent() { const distance = calculateDistance(pointer, closestLine) if (distance < adsorptionRange) { - arrivalPoint = closestLine.direction === 'vertical' ? { x: closestLine.x1, y: pointer.y } : { x: pointer.x, y: closestLine.y1 } + arrivalPoint = + closestLine.direction === 'vertical' + ? { x: closestLine.x1, y: pointer.y } + : { + x: pointer.x, + y: closestLine.y1, + } } } } @@ -261,6 +266,7 @@ export function useEvent() { addCanvasMouseEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, + removeDocumentEvent, removeMouseEvent, removeMouseLine, initEvent, diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 486bc8e9..84c4fac6 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1515,9 +1515,17 @@ export function useMode() { pitch: 4, sleeve: true, } - /*if (index === 1 || index === 3) { + /*if (index === 1) { line.attributes = { - type: LINE_TYPE.WALLLINE.WALL, + type: LINE_TYPE.WALLLINE.SHED, + offset: 30, //출폭 + width: 30, //폭 + pitch: 4, //구배 + sleeve: true, //소매 + } + } else if (index === 5 || index === 3) { + line.attributes = { + type: LINE_TYPE.WALLLINE.EAVES, offset: 50, //출폭 width: 30, //폭 pitch: 4, //구배 @@ -1525,8 +1533,8 @@ export function useMode() { } } else { line.attributes = { - type: LINE_TYPE.WALLLINE.EAVES, - offset: 40, + type: LINE_TYPE.WALLLINE.GABLE, + offset: 20, width: 50, pitch: 4, sleeve: true, @@ -1746,12 +1754,20 @@ export function useMode() { return { x1: point.x, y1: point.y } }), ) + if (wall.direction) { + roof.direction = wall.direction + } roof.name = POLYGON_TYPE.ROOF roof.setWall(wall) roof.lines.forEach((line, index) => { + const lineLength = Math.sqrt( + Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2), + ) line.attributes = { roofId: roof.id, + planeSize: lineLength, + actualSize: lineLength, wallLine: wall.lines[index].id, type: wall.lines[index].attributes.type, offset: wall.lines[index].attributes.offset, @@ -1770,9 +1786,6 @@ export function useMode() { line.attributes.currentRoof = roof.lines[index].id }) - console.log('drawRoofPolygon roof : ', roof) - console.log('drawRoofPolygon wall : ', wall) - setRoof(roof) setWall(wall) diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index 5a7d3062..9ea8b3b7 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -1,11 +1,13 @@ import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' -import { getDegreeByChon, getDirectionByPoint } from '@/util/canvas-util' +import { getDegreeByChon, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' -import { isSamePoint } from '@/util/qpolygon-utils' +import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils' import { flowDisplaySelector } from '@/store/settingAtom' import { fontSelector } from '@/store/fontAtom' +import { QLine } from '@/components/fabric/QLine' +import { POLYGON_TYPE } from '@/common/common' export const usePolygon = () => { const canvas = useRecoilValue(canvasState) @@ -25,6 +27,7 @@ export const usePolygon = () => { }) canvas?.add(polygon) + addLengthText(polygon) return polygon } @@ -40,7 +43,64 @@ export const usePolygon = () => { } const addLengthText = (polygon) => { - const points = polygon.get('points') + const lengthTexts = canvas.getObjects().filter((obj) => obj.name === 'lengthText' && obj.parentId === polygon.id) + lengthTexts.forEach((text) => { + canvas.remove(text) + }) + const lines = polygon.lines + + lines.forEach((line, i) => { + const length = line.getLength() + const { planeSize, actualSize } = line.attributes + const scaleX = line.scaleX + const scaleY = line.scaleY + const x1 = line.left + const y1 = line.top + const x2 = line.left + line.width * scaleX + const y2 = line.top + line.height * scaleY + + let left, top + + if (line.direction === 'left' || line.direction === 'right') { + left = (x1 + x2) / 2 + top = (y1 + y2) / 2 + 10 + } else if (line.direction === 'top' || line.direction === 'bottom') { + left = (x1 + x2) / 2 + 10 + top = (y1 + y2) / 2 + } + + const minX = line.left + const maxX = line.left + line.width + const minY = line.top + const maxY = line.top + line.length + const degree = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI + + const text = new fabric.Textbox(actualSize ? actualSize.toString() : planeSize ? planeSize.toString() : length.toString(), { + left: left, + top: top, + fontSize: lengthTextFontOptions.fontSize.value, + minX, + maxX, + minY, + maxY, + parentDirection: line.direction, + parentDegree: degree, + parentId: polygon.id, + planeSize, + actualSize, + editable: false, + selectable: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + parent: polygon, + name: 'lengthText', + }) + + canvas.add(text) + }) + + /*const points = polygon.get('points') points.forEach((start, i) => { const end = points[(i + 1) % points.length] const dx = end.x - start.x @@ -71,12 +131,12 @@ export const usePolygon = () => { lockScalingY: true, idx: i, name: 'lengthText', - parent: this, + parent: polygon, }) // this.texts.push(text) canvas.add(text) - }) + })*/ canvas.renderAll() } @@ -409,7 +469,8 @@ export const usePolygon = () => { const addTextByArrows = (arrows, txt, canvas) => { arrows.forEach((arrow, index) => { - const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})` + // const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})` + const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})` const text = new fabric.Text(`${textStr}`, { fontSize: flowFontOptions.fontSize.value, @@ -432,10 +493,272 @@ export const usePolygon = () => { }) } + const splitPolygonWithLines = (polygon) => { + polygon.set({ visible: false }) + let innerLines = [...polygon.innerLines] + let polygonLines = [...polygon.lines] + const roofs = [] + + let delIndexs = [] + let newLines = [] + + polygonLines.forEach((line, index) => { + line.tempIndex = index + innerLines.forEach((innerLine) => { + let newLine1, newLine2 + if (isPointOnLine(line, innerLine.startPoint)) { + // 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다. + newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], { + fontSize: polygon.fontSize, + stroke: 'black', + strokeWidth: 3, + }) + + newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], { + fontSize: polygon.fontSize, + stroke: 'black', + strokeWidth: 3, + }) + delIndexs.push(polygonLines.indexOf(line)) + canvas.remove(polygonLines[polygonLines.indexOf(line)]) + if (newLine1.length / 10 > 10) { + newLines.push(newLine1) + } + if (newLine2.length / 10 > 10) { + newLines.push(newLine2) + } + } + if (isPointOnLine(line, innerLine.endPoint)) { + newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], { + fontSize: polygon.fontSize, + stroke: 'black', + strokeWidth: 3, + }) + + newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], { + fontSize: polygon.fontSize, + stroke: 'black', + strokeWidth: 3, + }) + delIndexs.push(polygonLines.indexOf(line)) + canvas.remove(polygonLines[polygonLines.indexOf(line)]) + if (newLine1.length / 10 > 10) { + newLines.push(newLine1) + } + if (newLine2.length / 10 > 10) { + newLines.push(newLine2) + } + } + }) + }) + polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex)) + polygonLines = [...polygonLines, ...newLines] + + const allLines = [...polygonLines, ...innerLines] + + /** + * 왼쪽 상단을 startPoint로 전부 변경 + */ + allLines.forEach((line) => { + let startPoint // 시작점 + let endPoint // 끝점 + if (line.x1 < line.x2) { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } else if (line.x1 > line.x2) { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } else { + if (line.y1 < line.y2) { + startPoint = { x: line.x1, y: line.y1 } + endPoint = { x: line.x2, y: line.y2 } + } else { + startPoint = { x: line.x2, y: line.y2 } + endPoint = { x: line.x1, y: line.y1 } + } + } + + line.startPoint = startPoint + line.endPoint = endPoint + }) + + polygonLines.forEach((line) => { + const startPoint = line.startPoint // 시작점 + let arrivalPoint = line.endPoint // 도착점 + + let currentPoint = startPoint + const roofPoints = [startPoint] + + const startLine = line + const visitPoints = [startPoint] + const visitLines = [startLine] + let cnt = 0 + + while (!isSamePoint(currentPoint, arrivalPoint)) { + let nextLines = allLines.filter( + (line2) => + (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) && + line !== line2 && + innerLines.includes(line2) && + !visitLines.includes(line2), + ) + + if (nextLines.length === 0) { + nextLines = allLines.filter( + (line2) => + (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) && + line !== line2 && + !visitLines.includes(line2), + ) + } + + if (!nextLines) { + break + } + + let comparisonPoints = [] + + nextLines.forEach((nextLine) => { + if (isSamePoint(nextLine.startPoint, currentPoint)) { + comparisonPoints.push(nextLine.endPoint) + } else { + comparisonPoints.push(nextLine.startPoint) + } + }) + + comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point))) + comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint)) + + const minDistancePoint = comparisonPoints.reduce((prev, current) => { + const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2)) + const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2)) + + return prevDistance < currentDistance ? prev : current + }, comparisonPoints[0]) + + nextLines.forEach((nextLine) => { + if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) { + visitLines.push(nextLine) + } + }) + + currentPoint = { ...minDistancePoint } + roofPoints.push(currentPoint) + cnt++ + if (cnt > 100) { + throw new Error('무한루프') + } + } + roofs.push(roofPoints) + }) + + const newRoofs = removeDuplicatePolygons(roofs) + + newRoofs.forEach((roofPoint, index) => { + let defense, pitch + const polygonLines = [...polygon.lines] + + let representLines = [] + let representLine + + // 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다. + polygonLines.forEach((line) => { + let startFlag = false + let endFlag = false + const startPoint = line.startPoint + const endPoint = line.endPoint + roofPoint.forEach((point, index) => { + if (isSamePoint(point, startPoint)) { + startFlag = true + } + if (isSamePoint(point, endPoint)) { + endFlag = true + } + }) + + if (startFlag && endFlag) { + if (!representLines.includes(line)) { + representLines.push(line) + } + } + }) + + // representLines중 가장 긴 line을 찾는다. + representLines.forEach((line) => { + if (!representLine) { + representLine = line + } else { + if (representLine.length < line.length) { + representLine = line + } + } + }) + + const direction = newRoofs.length === 1 ? polygon.direction : representLine.direction + const polygonDirection = polygon.direction + + switch (direction) { + case 'top': + defense = 'east' + break + case 'right': + defense = 'south' + break + case 'bottom': + defense = 'west' + break + case 'left': + defense = 'north' + break + } + pitch = polygon.lines[index].attributes?.pitch ?? 0 + + const roof = new QPolygon(roofPoint, { + fontSize: polygon.fontSize, + stroke: 'black', + fill: 'transparent', + strokeWidth: 3, + name: POLYGON_TYPE.ROOF, + originX: 'center', + originY: 'center', + selectable: true, + defense: defense, + direction: newRoofs.length === 1 ? polygonDirection : defense, + pitch: pitch, + }) + + //allLines중 생성된 roof와 관련있는 line을 찾는다. + + roof.lines = [...polygon.lines, ...polygon.innerLines].filter((line) => { + let startFlag = false + let endFlag = false + const startPoint = line.startPoint + const endPoint = line.endPoint + roofPoint.forEach((point, index) => { + if (isSamePoint(point, startPoint)) { + startFlag = true + } + if (isSamePoint(point, endPoint)) { + endFlag = true + } + }) + + return startFlag && endFlag + }) + + canvas.add(roof) + addLengthText(roof) + canvas.remove(polygon) + canvas.renderAll() + }) + } + return { addPolygon, addPolygonByLines, removePolygon, drawDirectionArrow, + addLengthText, + splitPolygonWithLines, } } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 1a78f991..a78beb15 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -925,3 +925,32 @@ export function checkLineOrientation(line) { return 'diagonal' // 대각선 } } + +// 최상위 parentId를 통해 모든 하위 객체를 찾는 함수 +export const getAllRelatedObjects = (id, canvas) => { + const result = [] + const map = new Map() + + // Create a map of objects by their id + canvas.getObjects().forEach((obj) => { + map.set(obj.id, obj) + }) + + // Helper function to recursively find all related objects + function findRelatedObjects(id) { + const obj = map.get(id) + if (obj) { + result.push(obj) + canvas.getObjects().forEach((o) => { + if (o.parentId === id) { + findRelatedObjects(o.id) + } + }) + } + } + + // Start the search with the given parentId + findRelatedObjects(id) + + return result +} diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index e373df8d..d84330e8 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1177,244 +1177,6 @@ export default function offsetPolygon(vertices, offset) { polygon.canvas.renderAll() }) }*/ -export const splitPolygonWithLines = (polygon) => { - const canvas = polygon.canvas - polygon.set({ visible: false }) - let innerLines = [...polygon.innerLines] - let polygonLines = [...polygon.lines] - const roofs = [] - - let delIndexs = [] - let newLines = [] - - polygonLines.forEach((line, index) => { - line.tempIndex = index - innerLines.forEach((innerLine) => { - let newLine1, newLine2 - if (isPointOnLine(line, innerLine.startPoint)) { - // 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다. - newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - - newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - delIndexs.push(polygonLines.indexOf(line)) - canvas.remove(polygonLines[polygonLines.indexOf(line)]) - if (newLine1.length / 10 > 10) { - newLines.push(newLine1) - } - if (newLine2.length / 10 > 10) { - newLines.push(newLine2) - } - } - if (isPointOnLine(line, innerLine.endPoint)) { - newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - - newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], { - fontSize: polygon.fontSize, - stroke: 'black', - strokeWidth: 3, - }) - delIndexs.push(polygonLines.indexOf(line)) - canvas.remove(polygonLines[polygonLines.indexOf(line)]) - if (newLine1.length / 10 > 10) { - newLines.push(newLine1) - } - if (newLine2.length / 10 > 10) { - newLines.push(newLine2) - } - } - }) - }) - polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex)) - polygonLines = [...polygonLines, ...newLines] - - const allLines = [...polygonLines, ...innerLines] - - /** - * 왼쪽 상단을 startPoint로 전부 변경 - */ - allLines.forEach((line) => { - let startPoint // 시작점 - let endPoint // 끝점 - if (line.x1 < line.x2) { - startPoint = { x: line.x1, y: line.y1 } - endPoint = { x: line.x2, y: line.y2 } - } else if (line.x1 > line.x2) { - startPoint = { x: line.x2, y: line.y2 } - endPoint = { x: line.x1, y: line.y1 } - } else { - if (line.y1 < line.y2) { - startPoint = { x: line.x1, y: line.y1 } - endPoint = { x: line.x2, y: line.y2 } - } else { - startPoint = { x: line.x2, y: line.y2 } - endPoint = { x: line.x1, y: line.y1 } - } - } - - line.startPoint = startPoint - line.endPoint = endPoint - }) - - polygonLines.forEach((line) => { - const startPoint = line.startPoint // 시작점 - let arrivalPoint = line.endPoint // 도착점 - - let currentPoint = startPoint - const roofPoints = [startPoint] - - const startLine = line - const visitPoints = [startPoint] - const visitLines = [startLine] - let cnt = 0 - - while (!isSamePoint(currentPoint, arrivalPoint)) { - line.set({ stroke: 'red' }) - canvas.renderAll() - let nextLines = allLines.filter( - (line2) => - (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) && - line !== line2 && - innerLines.includes(line2) && - !visitLines.includes(line2), - ) - - if (nextLines.length === 0) { - nextLines = allLines.filter( - (line2) => - (isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) && - line !== line2 && - !visitLines.includes(line2), - ) - } - - if (!nextLines) { - break - } - - let comparisonPoints = [] - - nextLines.forEach((nextLine) => { - if (isSamePoint(nextLine.startPoint, currentPoint)) { - comparisonPoints.push(nextLine.endPoint) - } else { - comparisonPoints.push(nextLine.startPoint) - } - }) - - comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point))) - comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint)) - - const minDistancePoint = comparisonPoints.reduce((prev, current) => { - const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2)) - const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2)) - - return prevDistance < currentDistance ? prev : current - }, comparisonPoints[0]) - - nextLines.forEach((nextLine) => { - if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) { - visitLines.push(nextLine) - } - }) - - currentPoint = { ...minDistancePoint } - roofPoints.push(currentPoint) - cnt++ - if (cnt > 100) { - throw new Error('무한루프') - } - } - roofs.push(roofPoints) - }) - - const newRoofs = removeDuplicatePolygons(roofs) - newRoofs.forEach((roofPoint, index) => { - let defense, pitch - const polygonLines = [...polygon.lines] - - let representLines = [] - let representLine - - // 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다. - polygonLines.forEach((line) => { - let startFlag = false - let endFlag = false - const startPoint = line.startPoint - const endPoint = line.endPoint - roofPoint.forEach((point, index) => { - if (isSamePoint(point, startPoint)) { - startFlag = true - } - if (isSamePoint(point, endPoint)) { - endFlag = true - } - }) - - if (startFlag && endFlag) { - representLines.push(line) - } - }) - - // representLines중 가장 긴 line을 찾는다. - representLines.forEach((line) => { - if (!representLine) { - representLine = line - } else { - if (representLine.length < line.length) { - representLine = line - } - } - }) - - const direction = representLine.direction - - switch (direction) { - case 'top': - defense = 'east' - break - case 'right': - defense = 'south' - break - case 'bottom': - defense = 'west' - break - case 'left': - defense = 'north' - break - } - pitch = polygon.lines[index].attributes?.pitch ?? 0 - - const roof = new QPolygon(roofPoint, { - fontSize: polygon.fontSize, - stroke: 'black', - fill: 'transparent', - strokeWidth: 3, - name: POLYGON_TYPE.ROOF, - originX: 'center', - originY: 'center', - selectable: true, - defense: defense, - direction: defense, - pitch: pitch, - }) - - polygon.canvas.add(roof) - canvas.remove(polygon) - polygon.canvas.renderAll() - }) -} function normalizePoint(point) { return { @@ -1432,7 +1194,7 @@ function arePolygonsEqual(polygon1, polygon2) { return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index])) } -function removeDuplicatePolygons(polygons) { +export function removeDuplicatePolygons(polygons) { const uniquePolygons = [] polygons.forEach((polygon) => { @@ -1477,6 +1239,42 @@ function calculateAngleBetweenLines(line1, line2) { return (angleInRadians * 180) / Math.PI } +/** + * 한쪽흐름 지붕 + * @param roofId + * @param canvas + */ +export const drawShedRoof = (roofId, canvas) => { + const roof = canvas?.getObjects().find((object) => object.id === roofId) + const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2) + if (hasNonParallelLines.length > 0) { + alert('대각선이 존재합니다.') + return + } + + const sheds = roof.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED) + const eaves = roof.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.EAVES) + const gables = roof.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.GABLE) + + console.log('gable', gables) + + let shedDegree = sheds[0].attributes.degree || 0 + const shedChon = sheds[0].attributes.pitch || 0 + + if (shedDegree === 0) { + shedDegree = getDegreeByChon(shedChon) + } + const getHeight = function (adjust, degree) { + return Math.tan(degree * (Math.PI / 180)) * adjust + } + + gables.forEach((gable) => { + const adjust = gable.attributes.planeSize + const height = getHeight(adjust, shedDegree) + gable.attributes.actualSize = Math.round(Math.sqrt(Math.pow(adjust, 2) + Math.pow(height, 2))) + }) +} + export const drawRidgeRoof = (roofId, canvas) => { const roof = canvas?.getObjects().find((object) => object.id === roofId) const hasNonParallelLines = roof.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2) @@ -1769,6 +1567,9 @@ const drawRidge = (roof, canvas) => { attributes: { roofId: roof.id }, }, ) + ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + canvas.add(ridge) roof.ridges.push(ridge) roof.innerLines.push(ridge) @@ -1809,6 +1610,8 @@ const drawRidge = (roof, canvas) => { roof.ridges = roof.ridges.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2)) roof.innerLines = roof.innerLines.filter((r) => !(ridge.x1 === r.x1 && ridge.y1 === r.y1 && ridge.x2 === r.x2 && ridge.y2 === r.y2)) roof.innerLines = roof.innerLines.filter((r) => !(ridge2.x1 === r.x1 && ridge2.y1 === r.y1 && ridge2.x2 === r.x2 && ridge2.y2 === r.y2)) + newRidge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + newRidge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) canvas.add(newRidge) roof.ridges.push(newRidge) roof.innerLines.push(newRidge) @@ -1884,7 +1687,7 @@ const drawHips = (roof, canvas) => { stroke: 'red', strokeWidth: 1, name: LINE_TYPE.SUBLINE.HIP, - attributes: { roofId: roof.id, currentRoof: currentRoof.id }, + attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 }, }) canvas.add(hip1) const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10 @@ -1901,12 +1704,7 @@ const drawHips = (roof, canvas) => { stroke: 'red', strokeWidth: 1, name: LINE_TYPE.SUBLINE.HIP, - attributes: { - roofId: roof.id, - currentRoof: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, - }, + attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 }, }) canvas.add(hip2) const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10 @@ -1977,12 +1775,7 @@ const drawHips = (roof, canvas) => { stroke: 'red', strokeWidth: 1, name: LINE_TYPE.SUBLINE.HIP, - attributes: { - roofId: roof.id, - currentRoof: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, - }, + attributes: { roofId: roof.id, currentRoof: currentRoof.id, actualSize: 0 }, }) canvas.add(hip) const hipBase = ((Math.abs(hip.x1 - hip.x2) + Math.abs(hip.y1 - hip.y2)) / 2) * 10 @@ -2040,92 +1833,6 @@ const checkValley = (polygon, line1, line2) => { return isValley } -const getPointInPolygon = (polygon, point, isInclude = false) => { - let inside = false - let minX = Math.min(polygon[0].x, polygon[1].x, polygon[2].x, polygon[3].x), - maxX = Math.max(polygon[0].x, polygon[1].x, polygon[2].x, polygon[3].x), - minY = Math.min(polygon[0].y, polygon[1].y, polygon[2].y, polygon[3].y), - maxY = Math.max(polygon[0].y, polygon[1].y, polygon[2].y, polygon[3].y) - if (!isInclude && minX < point.x && point.x < maxX && minY < point.y && point.y < maxY) { - inside = true - } - if (isInclude && minX <= point.x && point.x <= maxX && minY <= point.y && point.y <= maxY) { - inside = true - } - return inside -} - -/** - * 라인과 마주하는 다른 라인과의 가장 가까운 거리를 구한다. - * @param polygon - * @param currentLine 현재 라인 - * @param dVector 현재 라인의 방향 - * @returns {*[]|null} - */ -const getAcrossLine = (polygon, currentLine, dVector) => { - let acrossLine - switch (dVector) { - case 45: - acrossLine = polygon.lines - .filter((line) => line.x1 > currentLine.x1 && line.y1 <= currentLine.y1) - .reduce((prev, current) => { - if (prev.length > 0) { - return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev - } else { - return current - } - }, []) - break - case 135: - acrossLine = polygon.lines - .filter((line) => line.x1 > currentLine.x1 && line.y1 >= currentLine.y1) - .reduce((prev, current) => { - if (prev.length > 0) { - return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev - } else { - return current - } - }, []) - break - case 225: - acrossLine = polygon.lines - .filter((line) => line.x1 < currentLine.x1 && line.y1 >= currentLine.y1) - .reduce((prev, current) => { - if (prev.length > 0) { - return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev - } else { - return current - } - }, []) - break - case 315: - acrossLine = polygon.lines - .filter((line) => line.x1 < currentLine.x1 && line.y1 <= currentLine.y1) - .reduce((prev, current) => { - if (prev.length > 0) { - return Math.abs(currentLine.x1 - current.x1) < Math.abs(currentLine.x1 - prev.x1) ? current : prev - } else { - return current - } - }, []) - break - } - return acrossLine -} - -/* - 추녀마루(hip) 중복방지를 위해 마루와 함께 그려진 추녀마루를 확인한다 - */ -const isAlreadyHip = (polygon, line) => { - let isAlreadyHip = false - polygon.hips.forEach((hip) => { - if (line.x1 === hip.x1 && line.y1 === hip.y1) { - isAlreadyHip = true - } - }) - return isAlreadyHip -} - /* 3개 이상 이어지지 않은 라인 포인트 계산 모임지붕에서 point는 3개 이상의 라인과 접해야 함. @@ -2216,6 +1923,8 @@ const connectLinePoint = (polygon) => { stroke: 'purple', strokeWidth: 1, }) + line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) / 10 + line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) / 10 polygon.canvas.add(line) polygon.innerLines.push(line) }) @@ -2272,6 +1981,8 @@ const connectLinePoint = (polygon) => { stroke: 'purple', strokeWidth: 1, }) + line.attributes.planeSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) + line.attributes.actualSize = Math.round(Math.sqrt(Math.pow(line.x1 - line.x2, 2) + Math.pow(line.y1 - line.y2, 2)) * 10) polygon.canvas.add(line) polygon.innerLines.push(line) }) @@ -2576,6 +2287,8 @@ const changeEavesRoof = (currentRoof, canvas) => { hipX2 = midX - addHipX2 hipY2 = midY - addHipY2 } + ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) } hipLines.forEach((hip) => { @@ -2753,6 +2466,8 @@ const changeGableRoof = (currentRoof, canvas) => { }) currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 } } + ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) let hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX, midY], { fontSize: roof.fontSize, @@ -2814,6 +2529,14 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { if (wallLine.length > 0) { wallLine = wallLine[0] } + let prevRoof, nextRoof + roof.lines.forEach((r, index) => { + if (r.id === currentRoof.id) { + currentRoof = r + prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1] + nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1] + } + }) const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심 const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심 @@ -2927,6 +2650,8 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { }) currentRoof.attributes.ridgeCoordinate = { x1: ridge.x2, y1: ridge.y2 } } + ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) } const hip1 = new QLine([currentRoof.x1, currentRoof.y1, midX + hipX2, midY + hipY2], { @@ -2937,10 +2662,15 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoof: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, }, }) + const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree + const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree + + const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10 + const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180)) + hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10 + hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2))) canvas?.add(hip1) roof.innerLines.push(hip1) @@ -2956,6 +2686,10 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { actualSize: currentRoof.length, }, }) + const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10 + const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180)) + hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10 + hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) canvas?.add(hip2) roof.innerLines.push(hip2) @@ -2974,7 +2708,7 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { }) }) - hipLines.forEach((hip) => { + hipLines.forEach((hip, i) => { const gableLine = new QLine([hip.x2, hip.y2, currentRoof.attributes.ridgeCoordinate.x1, currentRoof.attributes.ridgeCoordinate.y1], { fontSize: roof.fontSize, stroke: 'red', @@ -2983,10 +2717,15 @@ const changeHipAndGableRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoof: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, + actualSize: 0, }, }) + const gableDegree = i === 0 ? prevDegree : nextDegree + const gableBase = ((Math.abs(gableLine.x1 - gableLine.x2) + Math.abs(gableLine.y1 - gableLine.y2)) / 2) * 10 + const gableHeight = Math.round(gableBase / Math.tan(((90 - gableDegree) * Math.PI) / 180)) + gableLine.attributes.planeSize = + Math.round(Math.sqrt(Math.pow(gableLine.x1 - gableLine.x2, 2) + Math.pow(gableLine.y1 - gableLine.y2, 2))) * 10 + gableLine.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gableLine.attributes.planeSize, 2) + Math.pow(gableHeight, 2))) canvas?.add(gableLine) roof.innerLines.push(gableLine) }) @@ -3016,6 +2755,18 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { wallLine = wallLine[0] } + let prevRoof, nextRoof + roof.lines.forEach((r, index) => { + if (r.id === currentRoof.id) { + currentRoof = r + prevRoof = roof.lines[index === 0 ? roof.lines.length - 1 : index - 1] + nextRoof = roof.lines[index === roof.lines.length - 1 ? 0 : index + 1] + } + }) + + const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree + const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree + const midX = (currentRoof.x1 + currentRoof.x2) / 2 // 지붕의 X 중심 const midY = (currentRoof.y1 + currentRoof.y2) / 2 // 지붕의 Y 중심 const midWallX = (wallLine.x1 + wallLine.x2) / 2 // 벽의 X 중심 @@ -3137,6 +2888,8 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { hipX2 = midX - xWidth hipY2 = midY - yWidth } + ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) } let hipX1 = (Math.sign(currentRoof.x1 - midX) * currentRoof.attributes.width) / 2 @@ -3150,10 +2903,14 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoofId: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, + actualSize: 0, }, }) + const gableDegree = currentRoof.attributes.degree > 0 ? currentRoof.attributes.degree : getDegreeByChon(currentRoof.attributes.pitch) + const gable1Base = ((Math.abs(gable1.x1 - gable1.x2) + Math.abs(gable1.y1 - gable1.y2)) / 2) * 10 + const gable1Height = Math.round(gable1Base / Math.tan(((90 - gableDegree) * Math.PI) / 180)) + gable1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable1.x1 - gable1.x2, 2) + Math.pow(gable1.y1 - gable1.y2, 2))) * 10 + gable1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable1.attributes.planeSize, 2) + Math.pow(gable1Height, 2))) canvas?.add(gable1) roof.innerLines.push(gable1) @@ -3168,10 +2925,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoofId: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, + actualSize: 0, }, }) + const gable2Base = ((Math.abs(gable2.x1 - gable2.x2) + Math.abs(gable2.y1 - gable2.y2)) / 2) * 10 + const gable2Height = Math.round(gable2Base / Math.tan(((90 - gableDegree) * Math.PI) / 180)) + gable2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable2.x1 - gable2.x2, 2) + Math.pow(gable2.y1 - gable2.y2, 2))) * 10 + gable2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable2.attributes.planeSize, 2) + Math.pow(gable2Height, 2))) canvas?.add(gable2) roof.innerLines.push(gable2) @@ -3183,10 +2943,11 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoofId: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, + actualSize: 0, }, }) + gable3.attributes.planeSize = Math.round(Math.sqrt(Math.pow(gable3.x1 - gable3.x2, 2) + Math.pow(gable3.y1 - gable3.y2, 2))) * 10 + gable3.attributes.actualSize = Math.round(Math.sqrt(Math.pow(gable3.x1 - gable3.x2, 2) + Math.pow(gable3.y1 - gable3.y2, 2))) * 10 canvas?.add(gable3) roof.innerLines.push(gable3) @@ -3198,10 +2959,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoofId: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, + actualSize: 0, }, }) + const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10 + const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180)) + hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10 + hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2))) canvas?.add(hip1) roof.innerLines.push(hip1) @@ -3213,10 +2977,13 @@ const changeJerkInHeadRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoofId: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, + actualSize: 0, }, }) + const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10 + const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180)) + hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10 + hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) canvas?.add(hip2) roof.innerLines.push(hip2) } @@ -3312,6 +3079,9 @@ const changeWallRoof = (currentRoof, canvas) => { canvas?.remove(line) }) + const prevDegree = prevRoof.attributes.pitch > 0 ? getDegreeByChon(prevRoof.attributes.pitch) : prevRoof.attributes.degree + const nextDegree = nextRoof.attributes.pitch > 0 ? getDegreeByChon(nextRoof.attributes.pitch) : nextRoof.attributes.degree + if (currentRoof.attributes.sleeve && currentRoof.attributes.width > 0 && prevRoof.attributes.offset > 0 && nextRoof.attributes.offset > 0) { const prevSignX = Math.sign(prevRoof.x1 - prevRoof.x2) const prevSignY = Math.sign(prevRoof.y1 - prevRoof.y2) @@ -3417,6 +3187,8 @@ const changeWallRoof = (currentRoof, canvas) => { y2: ridge.y2 - diffY, }) } + ridge.attributes.planeSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) + ridge.attributes.actualSize = Math.round(Math.sqrt(Math.pow(ridge.x1 - ridge.x2, 2) + Math.pow(ridge.y1 - ridge.y2, 2)) * 10) let hip1 = new QLine([currentRoof.x1, currentRoof.y1, wallMidX, wallMidY], { fontSize: roof.fontSize, @@ -3426,10 +3198,14 @@ const changeWallRoof = (currentRoof, canvas) => { attributes: { roofId: roof.id, currentRoofId: currentRoof.id, - planeSize: currentRoof.length, - actualSize: currentRoof.length, + actualSize: 0, }, }) + const hip1Base = ((Math.abs(hip1.x1 - hip1.x2) + Math.abs(hip1.y1 - hip1.y2)) / 2) * 10 + const hip1Height = Math.round(hip1Base / Math.tan(((90 - prevDegree) * Math.PI) / 180)) + hip1.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip1.x1 - hip1.x2, 2) + Math.pow(hip1.y1 - hip1.y2, 2))) * 10 + hip1.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip1.attributes.planeSize, 2) + Math.pow(hip1Height, 2))) + let hip2 = new QLine([currentRoof.x2, currentRoof.y2, wallMidX, wallMidY], { fontSize: roof.fontSize, stroke: 'red', @@ -3442,6 +3218,10 @@ const changeWallRoof = (currentRoof, canvas) => { actualSize: currentRoof.length, }, }) + const hip2Base = ((Math.abs(hip2.x1 - hip2.x2) + Math.abs(hip2.y1 - hip2.y2)) / 2) * 10 + const hip2Height = Math.round(hip2Base / Math.tan(((90 - nextDegree) * Math.PI) / 180)) + hip2.attributes.planeSize = Math.round(Math.sqrt(Math.pow(hip2.x1 - hip2.x2, 2) + Math.pow(hip2.y1 - hip2.y2, 2))) * 10 + hip2.attributes.actualSize = Math.round(Math.sqrt(Math.pow(hip2.attributes.planeSize, 2) + Math.pow(hip2Height, 2))) canvas?.add(hip1) canvas?.add(hip2) roof.innerLines.push(hip1) @@ -3505,8 +3285,13 @@ export const changeCurrentRoof = (currentRoof, canvas) => { newRoof.setWall(wall) newRoof.lines.forEach((line, index) => { + const lineLength = Math.sqrt( + Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2), + ) line.attributes = { roofId: newRoof.id, + planeSize: lineLength, + actualSize: lineLength, wallLine: wall.lines[index].id, type: wall.lines[index].attributes.type, offset: wall.lines[index].attributes.offset, @@ -3561,6 +3346,19 @@ const reDrawPolygon = (polygon, canvas) => { line.attributes = l.attributes } }) + const lineLength = Math.sqrt( + Math.pow(Math.round(Math.abs(line.x1 - line.x2) * 10), 2) + Math.pow(Math.round(Math.abs(line.y1 - line.y2) * 10), 2), + ) + if (line.attributes !== undefined) { + line.attributes.planeSize = lineLength + line.attributes.actualSize = line + } else { + line.attributes = { + roofId: newPolygon.id, + planeSize: lineLength, + actualSize: lineLength, + } + } }) canvas?.add(newPolygon) diff --git a/src/util/session-util.js b/src/util/session-util.js deleted file mode 100644 index 6f818e43..00000000 --- a/src/util/session-util.js +++ /dev/null @@ -1,12 +0,0 @@ -import { checkSession } from '@/lib/user' -import { redirect } from 'next/navigation' - -export const initCheck = async () => { - const { session } = await checkSession() - - if (!session.isLoggedIn) { - redirect('/login') - } - - return session -}