diff --git a/public/static/images/canvas/allocation_delete.svg b/public/static/images/canvas/allocation_delete.svg new file mode 100644 index 00000000..711a241e --- /dev/null +++ b/public/static/images/canvas/allocation_delete.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/static/images/canvas/allocation_edit.svg b/public/static/images/canvas/allocation_edit.svg new file mode 100644 index 00000000..795d10f9 --- /dev/null +++ b/public/static/images/canvas/allocation_edit.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/static/images/canvas/allocation_icon01_black.svg b/public/static/images/canvas/allocation_icon01_black.svg new file mode 100644 index 00000000..01829a32 --- /dev/null +++ b/public/static/images/canvas/allocation_icon01_black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/allocation_icon01_white.svg b/public/static/images/canvas/allocation_icon01_white.svg new file mode 100644 index 00000000..5618848e --- /dev/null +++ b/public/static/images/canvas/allocation_icon01_white.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/allocation_icon02_black.svg b/public/static/images/canvas/allocation_icon02_black.svg new file mode 100644 index 00000000..57c6173b --- /dev/null +++ b/public/static/images/canvas/allocation_icon02_black.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/allocation_icon02_white.svg b/public/static/images/canvas/allocation_icon02_white.svg new file mode 100644 index 00000000..17211b04 --- /dev/null +++ b/public/static/images/canvas/allocation_icon02_white.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/static/images/canvas/eaves_icon01.svg b/public/static/images/canvas/eaves_icon01.svg new file mode 100644 index 00000000..da520069 --- /dev/null +++ b/public/static/images/canvas/eaves_icon01.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon02.svg b/public/static/images/canvas/eaves_icon02.svg new file mode 100644 index 00000000..0addbc04 --- /dev/null +++ b/public/static/images/canvas/eaves_icon02.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon03.svg b/public/static/images/canvas/eaves_icon03.svg new file mode 100644 index 00000000..b95b49fc --- /dev/null +++ b/public/static/images/canvas/eaves_icon03.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon04.svg b/public/static/images/canvas/eaves_icon04.svg new file mode 100644 index 00000000..ac08ce05 --- /dev/null +++ b/public/static/images/canvas/eaves_icon04.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon05.svg b/public/static/images/canvas/eaves_icon05.svg new file mode 100644 index 00000000..649a6058 --- /dev/null +++ b/public/static/images/canvas/eaves_icon05.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon06.svg b/public/static/images/canvas/eaves_icon06.svg new file mode 100644 index 00000000..a2f17801 --- /dev/null +++ b/public/static/images/canvas/eaves_icon06.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon07.svg b/public/static/images/canvas/eaves_icon07.svg new file mode 100644 index 00000000..1f101d56 --- /dev/null +++ b/public/static/images/canvas/eaves_icon07.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/eaves_icon08.svg b/public/static/images/canvas/eaves_icon08.svg new file mode 100644 index 00000000..8a2529de --- /dev/null +++ b/public/static/images/canvas/eaves_icon08.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu01.svg b/public/static/images/canvas/shape_menu01.svg new file mode 100644 index 00000000..6fbe44cc --- /dev/null +++ b/public/static/images/canvas/shape_menu01.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu02.svg b/public/static/images/canvas/shape_menu02.svg new file mode 100644 index 00000000..36a5eb41 --- /dev/null +++ b/public/static/images/canvas/shape_menu02.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu03.svg b/public/static/images/canvas/shape_menu03.svg new file mode 100644 index 00000000..ae7701c3 --- /dev/null +++ b/public/static/images/canvas/shape_menu03.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu04.svg b/public/static/images/canvas/shape_menu04.svg new file mode 100644 index 00000000..e23282c5 --- /dev/null +++ b/public/static/images/canvas/shape_menu04.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu05.svg b/public/static/images/canvas/shape_menu05.svg new file mode 100644 index 00000000..500d6bc6 --- /dev/null +++ b/public/static/images/canvas/shape_menu05.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu06.svg b/public/static/images/canvas/shape_menu06.svg new file mode 100644 index 00000000..a13e1b6c --- /dev/null +++ b/public/static/images/canvas/shape_menu06.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu07.svg b/public/static/images/canvas/shape_menu07.svg new file mode 100644 index 00000000..08119e5e --- /dev/null +++ b/public/static/images/canvas/shape_menu07.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/static/images/canvas/shape_menu08.svg b/public/static/images/canvas/shape_menu08.svg new file mode 100644 index 00000000..4588b3bb --- /dev/null +++ b/public/static/images/canvas/shape_menu08.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/app/management/stuff/detail/page.jsx b/src/app/management/stuff/detail/page.jsx index 6759b282..8783e17e 100644 --- a/src/app/management/stuff/detail/page.jsx +++ b/src/app/management/stuff/detail/page.jsx @@ -1,18 +1,45 @@ import React from 'react' -import Hero from '@/components/Hero' -import StuffDetail from '@/components/management/StuffDetail' import Link from 'next/link' +import Image from 'next/image' +import '@/styles/contents.scss' +import StuffHeader from '@/components/management/StuffHeader' +import StuffDetail from '@/components/management/StuffDetail' export default function ManagementStuffDetailPage() { return ( <> -
-

물건정보

- -

도면작성

- +
+
+
    +
  • + + 商品情報 + +
  • +
+
    +
  • + + react + +
  • +
  • + 物品及び図面管理 +
  • +
  • + 商品情報 +
  • +
+
-
- +
+
+
+ +
+
+ +
+
) diff --git a/src/app/management/stuff/page.jsx b/src/app/management/stuff/page.jsx index 7590a7cf..9c1abdfb 100644 --- a/src/app/management/stuff/page.jsx +++ b/src/app/management/stuff/page.jsx @@ -1,20 +1,40 @@ import StuffSearchCondition from '@/components/management/StuffSearchCondition' import Stuff from '@/components/management/Stuff' import { initCheck } from '@/util/session-util' -import Hero from '@/components/Hero' +import Image from 'next/image' +import '@/styles/contents.scss' export default async function ManagementStuffPage() { await initCheck() return ( <> - -
-
- +
+
+

物品及び図面管理

+
    +
  • + + react + +
  • +
  • + 物品及び図面管理 +
  • +
  • + 新規物件登録 +
  • +
-
- +
+
+
+ +
+
+ +
+
) diff --git a/src/app/management/stuff/tempdetail/page.jsx b/src/app/management/stuff/tempdetail/page.jsx index 8b84287a..d122d717 100644 --- a/src/app/management/stuff/tempdetail/page.jsx +++ b/src/app/management/stuff/tempdetail/page.jsx @@ -1,14 +1,41 @@ import React from 'react' -import Hero from '@/components/Hero' +import Link from 'next/link' +import Image from 'next/image' +import '@/styles/contents.scss' import StuffDetail from '@/components/management/StuffDetail' export default function ManagementStuffDetailPage() { return ( <> -
-

물건정보

+
+
+
    +
  • + + 商品情報 + +
  • +
+
    +
  • + + react + +
  • +
  • + 物品及び図面管理 +
  • +
  • + 商品情報 +
  • +
+
-
- +
+
+
+ +
+
) diff --git a/src/common/common.js b/src/common/common.js index 84cf60a3..f9ca6fff 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -53,8 +53,9 @@ export const Mode = { export const LINE_TYPE = { WALLLINE: { /** - * 처마 / 캐라바 / 벽 / 팔작지붕 / 반절처 / 한쪽흐름 + * 없음 / 처마 / 캐라바 / 벽 / 팔작지붕 / 반절처 / 한쪽흐름 */ + DEFAULT: 'default', EAVES: 'eaves', GABLE: 'gable', WALL: 'wall', diff --git a/src/components/GridSettingsModal.jsx b/src/components/GridSettingsModal.jsx index 35bb1793..92b2e618 100644 --- a/src/components/GridSettingsModal.jsx +++ b/src/components/GridSettingsModal.jsx @@ -6,6 +6,7 @@ import { guideLineState, horiGuideLinesState, vertGuideLinesState } from '@/stor import { fabric } from 'fabric' import { ColorPicker, useColor } from 'react-color-palette' import 'react-color-palette/css' +import { gridColorState } from '@/store/gridAtom' export default function GridSettingsModal(props) { const { canvasProps } = props @@ -23,7 +24,7 @@ export default function GridSettingsModal(props) { const gridSettingArray = [] - const [guideColor, setGuideColor] = useColor('rgb(200, 15, 15)') + const gridColor = useRecoilValue(gridColorState) const [colorPickerShow, setColorPickerShow] = useState(false) const boxStyle = { @@ -67,7 +68,7 @@ export default function GridSettingsModal(props) { const horizontalLine = new fabric.Line( [0, i * moduleVertLength - moduleVertLength / 2, canvasProps.width, i * moduleVertLength - moduleVertLength / 2], { - stroke: guideColor.hex, + stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, @@ -89,7 +90,7 @@ export default function GridSettingsModal(props) { const verticalLine = new fabric.Line( [i * moduleHoriLength - moduleHoriLength / 2, 0, i * moduleHoriLength - moduleHoriLength / 2, canvasProps.height], { - stroke: guideColor.hex, + stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index bf63b348..fbe61038 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -39,7 +39,7 @@ import QEmptyContextMenu from '@/components/common/context-menu/QEmptyContextMen import InitSettingsModal from './InitSettingsModal' import GridSettingsModal from './GridSettingsModal' import { SurfaceShapeModal } from '@/components/ui/SurfaceShape' -import { changeAllGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils' +import { changeAllHipAndGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils' import ThumbnailList from '@/components/ui/ThumbnailLIst' import ObjectPlacement from '@/components/ui/ObjectPlacement' import { globalLocaleStore } from '@/store/localeAtom' @@ -144,10 +144,10 @@ export default function Roof2(props) { useEffect(() => { get({ url: `/api/canvas-management/canvas-statuses/by-object/test123240822001/${userId}` }).then((res) => { - console.log(res) + // console.log(res) const arrangeData = res.map((item) => { - console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')) + // console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')) const test = item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '') const test2 = test.substring(1, test.length - 1) return { diff --git a/src/components/common/draggable/withDraggable.jsx b/src/components/common/draggable/withDraggable.jsx index 23195fbe..29952981 100644 --- a/src/components/common/draggable/withDraggable.jsx +++ b/src/components/common/draggable/withDraggable.jsx @@ -3,7 +3,7 @@ import { useEffect, useState } from 'react' import Draggable from 'react-draggable' -export default function WithDraggable({ isShow, children, pos }) { +export default function WithDraggable({ isShow, children, pos, handle = '' }) { const [position, setPosition] = useState({ x: 0, y: 0 }) const handleOnDrag = (e, data) => { @@ -17,7 +17,11 @@ export default function WithDraggable({ isShow, children, pos }) { return ( <> {isShow && ( - handleOnDrag(e, data)}> + handleOnDrag(e, data)} + handle={handle === '' ? '.modal-head' : handle} + > {children} )} diff --git a/src/components/fabric/QLine.js b/src/components/fabric/QLine.js index da141287..c73e442f 100644 --- a/src/components/fabric/QLine.js +++ b/src/components/fabric/QLine.js @@ -13,7 +13,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { area: 0, children: [], initialize: function (points, options, canvas) { - this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? false }) + this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true }) if (options.id) { this.id = options.id } else { @@ -75,7 +75,7 @@ export const QLine = fabric.util.createClass(fabric.Line, { const y2 = this.top + this.height * scaleY const dx = x2 - x1 const dy = y2 - y1 - this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) + this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 }, addLengthText() { diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 84e57974..fec9e227 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -27,6 +27,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { point.x = Math.round(point.x) point.y = Math.round(point.y) }) + options.selectable = options.selectable ?? true options.sort = options.sort ?? true options.parentId = options.parentId ?? null @@ -51,6 +52,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { } this.callSuper('initialize', points, options) + if (options.id) { this.id = options.id } else { @@ -164,6 +166,9 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { stroke: this.stroke, strokeWidth: this.strokeWidth, fontSize: this.fontSize, + attributes: { + offset: 0, + }, direction: getDirectionByPoint(point, nextPoint), idx: i, }) @@ -193,14 +198,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const end = points[(i + 1) % points.length] const dx = end.x - start.x const dy = end.y - start.y - const length = Math.sqrt(dx * dx + dy * dy) + const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10 const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2) const degree = (Math.atan2(dy, dx) * 180) / Math.PI // Create new text object if it doesn't exist - const text = new fabric.IText(length.toFixed(0), { + const text = new fabric.Text(length.toFixed(0), { left: midPoint.x, top: midPoint.y, fontSize: this.fontSize, diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx index 5d3817a2..3abebd85 100644 --- a/src/components/floor-plan/CanvasLayout.jsx +++ b/src/components/floor-plan/CanvasLayout.jsx @@ -3,9 +3,12 @@ import { useEffect, useState } from 'react' import { useRecoilState, useRecoilValue } from 'recoil' import CanvasFrame from './CanvasFrame' +import { useMessage } from '@/hooks/useMessage' +import { useSwal } from '@/hooks/useSwal' import { usePlan } from '@/hooks/usePlan' import { globalLocaleStore } from '@/store/localeAtom' import { currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom' +import { sessionStore } from '@/store/commonAtom' export default function CanvasLayout() { const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요 @@ -14,8 +17,11 @@ export default function CanvasLayout() { const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) const globalLocaleState = useRecoilValue(globalLocaleStore) + const sessionState = useRecoilValue(sessionStore) - const { getCanvasByObjectNo } = usePlan() + const { getMessage } = useMessage() + const { swalFire } = useSwal() + const { getCanvasByObjectNo, delCanvasById } = usePlan() const handleCurrentPlan = (newCurrentId) => { if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) { @@ -38,21 +44,29 @@ export default function CanvasLayout() { const handleDeletePlan = (e, id) => { e.stopPropagation() // 이벤트 버블링 방지 - // 삭제할 아이디와 다른 아이템만 남김 - const filterInitPlans = initCanvasPlans.filter((plan) => plan.id !== id) - setInitCanvasPlans(filterInitPlans) - const filterAddPlans = addCanvasPlans.filter((plan) => plan.id !== id) - setAddCanvasPlans(filterAddPlans) - - const combinedPlans = [...filterInitPlans, ...filterAddPlans] - if (combinedPlans.length === 0) { - // 모든 데이터가 삭제된 경우 - setPlanNum(0) + if (initCanvasPlans.some((plan) => plan.id === id)) { + delCanvasById(id) + .then((res) => { + swalFire({ text: getMessage('common.message.delete') }) + console.log('[DELETE] canvas-statuses res :::::::: %o', res) + setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id)) + }) + .catch((error) => { + swalFire({ text: error.message, icon: 'error' }) + console.error('[DELETE] canvas-statuses res error :::::::: %o', error) + }) } else { - const lastPlanId = combinedPlans.at(-1).id - if (id !== lastPlanId) { - handleCurrentPlan(lastPlanId) - } + setAddCanvasPlans(addCanvasPlans.filter((plan) => plan.id !== id)) + swalFire({ text: getMessage('common.message.delete') }) + } + + // 삭제 후 last 데이터에 포커싱 + const lastPlan = [...initCanvasPlans, ...addCanvasPlans].filter((plan) => plan.id !== id).at(-1) + if (!lastPlan) { + setPlanNum(0) + setCurrentCanvasPlan(null) + } else if (id !== lastPlan.id) { + handleCurrentPlan(lastPlan.id) } } @@ -63,7 +77,7 @@ export default function CanvasLayout() { } useEffect(() => { - getCanvasByObjectNo(objectNo).then((res) => { + getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => { console.log('canvas 목록 ', res) if (res.length > 0) { setInitCanvasPlans(res) @@ -82,7 +96,18 @@ export default function CanvasLayout() { {[...initCanvasPlans, ...addCanvasPlans].map((plan) => ( ))}
diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 99b3e048..91c5179c 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -9,7 +9,8 @@ import QSelectBox from '@/components/common/select/QSelectBox' import { useMessage } from '@/hooks/useMessage' import { usePlan } from '@/hooks/usePlan' -import { canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' +import { useSwal } from '@/hooks/useSwal' +import { canvasState, canvasZoomState, currentCanvasPlanState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' import { sessionStore } from '@/store/commonAtom' import { outerLinePointsState } from '@/store/outerLineAtom' import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' @@ -30,7 +31,7 @@ const canvasMenus = [ ] export default function CanvasMenu(props) { - const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props + const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, setShowPlaceShapeModal, setShowRoofShapeSettingModal } = props const [menuNumber, setMenuNumber] = useState(null) const [type, setType] = useState('') @@ -40,6 +41,7 @@ export default function CanvasMenu(props) { const setCurrentMenu = useSetRecoilState(currentMenuState) const setPoints = useSetRecoilState(outerLinePointsState) const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) + const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const globalLocale = useRecoilValue(globalLocaleStore) const canvas = useRecoilValue(canvasState) @@ -47,6 +49,7 @@ export default function CanvasMenu(props) { const { getMessage } = useMessage() const { saveCanvas } = usePlan() + const { swalFire } = useSwal() const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const onClickNav = (menu) => { @@ -54,6 +57,10 @@ export default function CanvasMenu(props) { setCurrentMenu(menu.title) switch (menu.index) { + case 1: + setType('placementShape') + onClickPlacementInitialMenu() + break case 2: setType('outline') break @@ -67,18 +74,35 @@ export default function CanvasMenu(props) { } const menuProps = { setShowOutlineModal, + setShowPlaceShapeModal, + setShowRoofShapeSettingModal, type, } const settingsModalOptions = useRecoilState(settingModalFirstOptionsState) useEffect(() => { + if (menuNumber === 1) { + onClickPlacementInitialMenu() + } if (menuNumber !== 2 && showOutlineModal) setShowOutlineModal(false) }, [menuNumber, type]) // 저장버튼(btn08) 클릭 시 호출되는 함수 const handleSaveCanvas = () => { - saveCanvas(sessionState.userId) + swalFire({ + html: getMessage('common.message.confirm.save') + `
${currentCanvasPlan.name}`, + type: 'confirm', + confirmFn: () => { + saveCanvas(sessionState.userId) + }, + }) + } + + const onClickPlacementInitialMenu = () => { + setShowOutlineModal(false) + setShowCanvasSettingModal(false) + setShowPlaceShapeModal(true) } const handleClear = () => { @@ -126,7 +150,7 @@ export default function CanvasMenu(props) {
}
- +
diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx index 699c96c5..38d31f9d 100644 --- a/src/components/floor-plan/FloorPlan.jsx +++ b/src/components/floor-plan/FloorPlan.jsx @@ -11,10 +11,17 @@ import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal import CanvasLayout from '@/components/floor-plan/CanvasLayout' import DotLineGrid from '@/components/floor-plan/modal/grid/DotLineGrid' import WallLineSetting from '@/components/floor-plan/modal/outerlinesetting/WallLineSetting' +import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting' +import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' +import GridColorSetting from './modal/grid/GridColorSetting' +import RoofShapeSetting from '@/components/floor-plan/modal/roofShape/RoofShapeSetting' export default function FloorPlan() { const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false) const [showOutlineModal, setShowOutlineModal] = useState(false) + const [showPlaceShapeModal, setShowPlaceShapeModal] = useState(false) + const [showPropertiesSettingModal, setShowPropertiesSettingModal] = useState(false) + const [showRoofShapeSettingModal, setShowRoofShapeSettingModal] = useState(false) const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) @@ -25,19 +32,24 @@ export default function FloorPlan() { const [showDotLineGridModal, setShowDotLineGridModal] = useState(false) const [showGridCopyModal, setShowGridCopyModal] = useState(false) const [showGridMoveModal, setShowGridMoveModal] = useState(false) + const [showColorPickerModal, setShowColorPickerModal] = useState(false) const canvasSettingProps = { setShowCanvasSettingModal, setShowDotLineGridModal, + setShowColorPickerModal, } const outlineProps = { setShowOutlineModal, + setShowPropertiesSettingModal, } const modalProps = { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, + setShowPlaceShapeModal, + setShowRoofShapeSettingModal, } useEffect(() => { @@ -75,6 +87,14 @@ export default function FloorPlan() { setShowDotLineGridModal, } + const gridColorProps = { + setShowColorPickerModal, + } + + const propertiesSettingProps = { + setShowPropertiesSettingModal, + } + useEffect(() => {}, [showOutlineModal]) return ( @@ -84,9 +104,12 @@ export default function FloorPlan() {
{showCanvasSettingModal && } - {/*{showOutlineModal && }*/} {showOutlineModal && } {showDotLineGridModal && } + {showColorPickerModal && } + {showPropertiesSettingModal && } + {showPlaceShapeModal && } + {showRoofShapeSettingModal && }
diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index a6b12877..43a059f0 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -7,7 +7,7 @@ import { currentMenuState } from '@/store/canvasAtom' import { useSetRecoilState } from 'recoil' export default function MenuDepth01(props) { - const { setShowOutlineModal, type } = props + const { setShowOutlineModal, type, setShowPlaceShapeModal, setShowRoofShapeSettingModal } = props const { getMessage } = useMessage() const [activeMenu, setActiveMenu] = useState() const setCurrentMenu = useSetRecoilState(currentMenuState) @@ -16,7 +16,9 @@ export default function MenuDepth01(props) { setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) setCurrentMenu(menu) if (type === 'outline') { + setShowPlaceShapeModal(false) setShowOutlineModal(id === 0) + setShowRoofShapeSettingModal(id === 1) } } diff --git a/src/components/floor-plan/modal/grid/DotLineGrid.jsx b/src/components/floor-plan/modal/grid/DotLineGrid.jsx index e9d32a3a..54780ab5 100644 --- a/src/components/floor-plan/modal/grid/DotLineGrid.jsx +++ b/src/components/floor-plan/modal/grid/DotLineGrid.jsx @@ -1,13 +1,11 @@ -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import QSelectBox from '@/components/common/select/QSelectBox' import { useEffect, useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom' -import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil' import { onlyNumberInputChange } from '@/util/input-utils' import { fabric } from 'fabric' -import { useAxios } from '@/hooks/useAxios' -import { useSwal } from '@/hooks/useSwal' import { gridColorState } from '@/store/gridAtom' import { settingModalGridOptionsState } from '@/store/settingAtom' @@ -18,9 +16,9 @@ const TYPE = { export default function DotLineGrid(props) { // const [modalOption, setModalOption] = useRecoilState(modalState); //modal 열림닫힘 state - const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 const [close, setClose] = useState(false) const { setShowDotLineGridModal } = props + const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState) const gridColor = useRecoilValue(gridColorState) const canvas = useRecoilValue(canvasState) @@ -29,10 +27,16 @@ export default function DotLineGrid(props) { const interval = useRecoilValue(dotLineIntervalSelector) const { getMessage } = useMessage() - const { get, post } = useAxios() - const { swalFire } = useSwal() - const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState) + useEffect(() => { + return () => { + setSettingModalGridOptions((prev) => { + const newSettingOptions = [...prev] + newSettingOptions[1].selected = false + return [...newSettingOptions] + }) + } + }, []) const SelectOption = [ { id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 }, @@ -43,23 +47,13 @@ export default function DotLineGrid(props) { const [selectOption, setSelectOption] = useState(SelectOption[0]) const HandleClickClose = () => { - setShowDotLineGridModal(false) // 모달 닫기 처리 - - const newGridOptions = [...gridOptions] // 모달 닫으면서 점선그리드 버튼 비활성화 - newGridOptions.map((item) => { - if (item.id === 2) { - item.selected = false - } - }) - setGridOptions(newGridOptions) + // setClose(true) + // setTimeout(() => { + // setModalOption({ ...modalOption, gridoption: false }) + // setClose(false) + // }, 180) } - // 데이터를 최초 한 번만 조회 - useEffect(() => { - console.log('DotLineGrid useEffect 실행') - fetchGridSettings() - }, [objectNo]) - const handleCheckBoxChange = (e) => { const { value, checked } = e.target setDotLineGridSettingState((prev) => { @@ -70,174 +64,118 @@ export default function DotLineGrid(props) { }) } - // Canvas Grid Setting 조회 및 초기화 - const fetchGridSettings = async () => { - try { - const res = await get({ url: `/api/canvas-management/canvas-grid-settings/by-object/${objectNo}` }) + const handleSave = () => { + // 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거 + canvas + ?.getObjects() + .filter((obj) => obj.name === 'lineGrid') + .forEach((obj) => canvas?.remove(obj)) + canvas + ?.getObjects() + .filter((obj) => obj.name === 'dotGrid') + .forEach((obj) => canvas?.remove(obj)) - const patternData = { - INTERVAL: { - type: res.gridType, - horizontalInterval: res.gridHorizon, - verticalInterval: res.gridVertical, - ratioInterval: res.gridRatio, + const horizontalInterval = interval.horizontalInterval + const verticalInterval = interval.verticalInterval + + if (dotLineGridSetting.DOT) { + const circle = new fabric.Circle({ + radius: 2, + fill: 'red', + strokeWidth: 0.7, + originX: 'center', + originY: 'center', + selectable: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + }) + + const patternSourceCanvas = new fabric.StaticCanvas(null, { + width: horizontalInterval, + height: verticalInterval, + }) + + patternSourceCanvas.add(circle) + + circle.set({ + left: patternSourceCanvas.width / 2, + top: patternSourceCanvas.height / 2, + }) + + patternSourceCanvas.renderAll() + + const pattern = new fabric.Pattern({ + source: patternSourceCanvas.getElement(), + repeat: 'repeat', + }) + + const backgroundPolygon = new fabric.Polygon( + [ + { x: 0, y: 0 }, + { x: canvas.width, y: 0 }, + { x: canvas.width, y: canvas.height }, + { x: 0, y: canvas.height }, + ], + { + fill: pattern, + selectable: false, + name: 'dotGrid', }, - dimension: res.gridDimen, - DOT: res.dotGridDisplay, - LINE: res.lineGridDisplay, - } + ) - const matchedOption = SelectOption.find((option) => option.value == res.gridDimen) - - // dimension 값에 맞는 옵션을 선택 - setSelectOption(matchedOption) - - // 서버에서 받은 데이터로 상태 업데이트 - setDotLineGridSettingState(patternData) - } catch (error) { - console.error('Data fetching error:', error) + canvas.add(backgroundPolygon) + backgroundPolygon.sendToBack() + canvas.renderAll() } - } - // 저장 버튼 클릭 시 - const handleSave = async () => { - try { - const patternData = { - objectNo, - dotGridDisplay: dotLineGridSetting.DOT, - lineGridDisplay: dotLineGridSetting.LINE, - gridType: dotLineGridSetting.INTERVAL.type, - gridHorizon: dotLineGridSetting.INTERVAL.horizontalInterval, - gridVertical: dotLineGridSetting.INTERVAL.verticalInterval, - gridRatio: dotLineGridSetting.INTERVAL.ratioInterval, - gridDimen: dotLineGridSetting.INTERVAL.dimension, - } - - // HTTP POST 요청 보내기 - await post({ url: `/api/canvas-management/canvas-grid-settings`, data: patternData }).then((res) => { - swalFire({ text: getMessage(res.returnMessage) }) - - // 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거 - canvas - ?.getObjects() - .filter((obj) => obj.name === 'lineGrid') - .forEach((obj) => canvas?.remove(obj)) - canvas - ?.getObjects() - .filter((obj) => obj.name === 'dotGrid') - .forEach((obj) => canvas?.remove(obj)) - - if (patternData.dotGridDisplay) { - const circle = new fabric.Circle({ - radius: 2, - fill: 'red', - strokeWidth: 0.7, - originX: 'center', - originY: 'center', - selectable: false, + if (dotLineGridSetting.LINE) { + for (let i = 0; i < canvas.height / verticalInterval + 1; i++) { + const horizontalLine = new fabric.Line( + [0, i * verticalInterval - verticalInterval / 2, canvas.width, i * verticalInterval - verticalInterval / 2], + { + stroke: gridColor, + strokeWidth: 1, + selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, - }) + name: 'lineGrid', + strokeDashArray: [5, 2], + opacity: 0.3, + direction: 'horizontal', + }, + ) + canvas.add(horizontalLine) + } - const patternSourceCanvas = new fabric.StaticCanvas(null, { - width: patternData.gridHorizon, - height: patternData.gridVertical, - }) - - patternSourceCanvas.add(circle) - - circle.set({ - left: patternSourceCanvas.width / 2, - top: patternSourceCanvas.height / 2, - }) - - patternSourceCanvas.renderAll() - - const pattern = new fabric.Pattern({ - source: patternSourceCanvas.getElement(), - repeat: 'repeat', - }) - - const backgroundPolygon = new fabric.Polygon( - [ - { x: 0, y: 0 }, - { x: canvas.width, y: 0 }, - { x: canvas.width, y: canvas.height }, - { x: 0, y: canvas.height }, - ], - { - fill: pattern, - selectable: false, - name: 'dotGrid', - }, - ) - - canvas.add(backgroundPolygon) - backgroundPolygon.sendToBack() - canvas.renderAll() - } - - if (patternData.lineGridDisplay) { - for (let i = 0; i < canvas.height / patternData.gridVertical + 1; i++) { - const horizontalLine = new fabric.Line( - [ - 0, - i * patternData.gridVertical - patternData.gridVertical / 2, - canvas.width, - i * patternData.gridVertical - patternData.gridVertical / 2, - ], - { - stroke: gridColor, - strokeWidth: 1, - selectable: true, - lockMovementX: true, - lockMovementY: true, - lockRotation: true, - lockScalingX: true, - lockScalingY: true, - name: 'lineGrid', - strokeDashArray: [5, 2], - opacity: 0.3, - direction: 'horizontal', - }, - ) - canvas.add(horizontalLine) - } - - for (let i = 0; i < canvas.width / patternData.gridHorizon + 1; i++) { - const verticalLine = new fabric.Line( - [ - i * patternData.gridHorizon - patternData.gridHorizon / 2, - 0, - i * patternData.gridHorizon - patternData.gridHorizon / 2, - canvas.height, - ], - { - stroke: 'black', - strokeWidth: 1, - selectable: true, - lockMovementX: true, - lockMovementY: true, - lockRotation: true, - lockScalingX: true, - lockScalingY: true, - name: 'lineGrid', - strokeDashArray: [5, 2], - opacity: 0.3, - direction: 'vertical', - }, - ) - canvas.add(verticalLine) - } - } - canvas.renderAll() - }) - } catch (error) { - swalFire({ text: getMessage(res.returnMessage), icon: 'error' }) + for (let i = 0; i < canvas.width / horizontalInterval + 1; i++) { + const verticalLine = new fabric.Line( + [i * horizontalInterval - horizontalInterval / 2, 0, i * horizontalInterval - horizontalInterval / 2, canvas.height], + { + stroke: gridColor, + strokeWidth: 1, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: 'lineGrid', + strokeDashArray: [5, 2], + opacity: 0.3, + direction: 'vertical', + }, + ) + canvas.add(verticalLine) + } } + + canvas.renderAll() } const handleRadioChange = (e) => { @@ -295,11 +233,11 @@ export default function DotLineGrid(props) { } return ( - +

{getMessage('modal.canvas.setting.grid.dot.line.setting')}

-
diff --git a/src/components/floor-plan/modal/grid/GridColorSetting.jsx b/src/components/floor-plan/modal/grid/GridColorSetting.jsx new file mode 100644 index 00000000..5b7b2ff6 --- /dev/null +++ b/src/components/floor-plan/modal/grid/GridColorSetting.jsx @@ -0,0 +1,39 @@ +import WithDraggable from '@/components/common/draggable/WithDraggable' +import ColorPicker from '@/components/common/color-picker/ColorPicker' +import { useRecoilState, useSetRecoilState } from 'recoil' +import { gridColorState } from '@/store/gridAtom' +import { useMessage } from '@/hooks/useMessage' +import { useEffect } from 'react' +import { settingModalGridOptionsState } from '@/store/settingAtom' + +export default function GridColorSetting(props) { + const { setShowColorPickerModal } = props + const [color, setColor] = useRecoilState(gridColorState) + const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState) + const { getMessage } = useMessage() + + useEffect(() => { + return () => { + setSettingModalGridOptions((prev) => { + const newSettingOptions = [...prev] + newSettingOptions[3].selected = false + return [...newSettingOptions] + }) + } + }, []) + return ( + +
+
+

{getMessage('modal.canvas.setting.grid.color.setting')}

+ +
+
+ +
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/grid/GridCopy.jsx b/src/components/floor-plan/modal/grid/GridCopy.jsx index 92609d80..a6709d09 100644 --- a/src/components/floor-plan/modal/grid/GridCopy.jsx +++ b/src/components/floor-plan/modal/grid/GridCopy.jsx @@ -1,4 +1,4 @@ -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' export default function GridCopy(props) { diff --git a/src/components/floor-plan/modal/grid/GridMove.jsx b/src/components/floor-plan/modal/grid/GridMove.jsx index 1da8b6fa..db226872 100644 --- a/src/components/floor-plan/modal/grid/GridMove.jsx +++ b/src/components/floor-plan/modal/grid/GridMove.jsx @@ -1,4 +1,4 @@ -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' export default function GridMove(props) { diff --git a/src/components/floor-plan/modal/outerlinesetting/Angle.jsx b/src/components/floor-plan/modal/outerlinesetting/Angle.jsx index ca3ea288..83985016 100644 --- a/src/components/floor-plan/modal/outerlinesetting/Angle.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/Angle.jsx @@ -1,8 +1,11 @@ import Image from 'next/image' import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils' -export default function Angle() { +export default function Angle({ props }) { const { getMessage } = useMessage() + const { angle1, setAngle1, angle1Ref, length1, setLength1, length1Ref } = props + return ( <>
@@ -11,16 +14,40 @@ export default function Angle() {
{getMessage('modal.cover.outline.angle')}
- + onlyNumberWithDotInputChange(e, setAngle1)} + placeholder="45" + />
- +
- {getMessage('modal.cover.outline.arrow')} + {getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
diff --git a/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx b/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx index 9ae9f02e..84c25329 100644 --- a/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/Diagonal.jsx @@ -1,7 +1,24 @@ import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange } from '@/util/input-utils' -export default function Diagonal() { +export default function Diagonal({ props }) { const { getMessage } = useMessage() + + const { + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, + arrow1, + setArrow1, + arrow2, + setArrow2, + } = props return ( <>
@@ -13,26 +30,77 @@ export default function Diagonal() { {getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setOuterLineDiagonalLength)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
@@ -40,16 +108,48 @@ export default function Diagonal() {
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength2)} + readOnly={true} + placeholder="3000" + />
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx b/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx index ea386d4b..5bfa5950 100644 --- a/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/DoublePitch.jsx @@ -1,7 +1,48 @@ import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils' +import { getDegreeByChon } from '@/util/canvas-util' -export default function DoublePitch() { +export default function DoublePitch({ props }) { const { getMessage } = useMessage() + const { + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + arrow1Ref, + arrow2Ref, + } = props + + const getLength2 = () => { + const angle1Value = angle1Ref.current.value + const angle2Value = angle2Ref.current.value + const length1Value = length1Ref.current.value + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '') { + const radian1 = (getDegreeByChon(angle1Value) * Math.PI) / 180 + + const radian2 = (getDegreeByChon(angle2Value) * Math.PI) / 180 + return Math.floor((Math.tan(radian1) * length1Value) / Math.tan(radian2)) + } + + return 0 + } + return ( <>
@@ -9,26 +50,70 @@ export default function DoublePitch() {
{getMessage('modal.cover.outline.angle')}
- + onlyNumberWithDotInputChange(e, setAngle1)} + placeholder="45" + />
- +
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
@@ -38,26 +123,80 @@ export default function DoublePitch() {
{getMessage('modal.cover.outline.angle')}
- + { + onlyNumberWithDotInputChange(e, setAngle2) + console.log(getLength2()) + setLength2(getLength2()) + }} + placeholder="45" + />
- +
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength2)} + readOnly={true} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index 9dfa85c8..ffa5e0e7 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -1,12 +1,13 @@ 'use client' import { useMessage } from '@/hooks/useMessage' -import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall' import { onlyNumberInputChange } from '@/util/input-utils' +import GridMove from '@/components/floor-plan/modal/grid/GridMove' -export default function OuterLineWall(props) { +export default function OuterLineWall({ props }) { const { getMessage } = useMessage() - const { length1, setLength1, length1Ref, arrow1, setArrow1 } = useOuterLineWall() + + const { length1, setLength1, length1Ref, arrow1, setArrow1 } = props return (
@@ -22,15 +23,39 @@ export default function OuterLineWall(props) { placeholder="3000" />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx new file mode 100644 index 00000000..248567e3 --- /dev/null +++ b/src/components/floor-plan/modal/outerlinesetting/PropertiesSetting.jsx @@ -0,0 +1,56 @@ +import WithDraggable from '@/components/common/draggable/WithDraggable' +import { useMessage } from '@/hooks/useMessage' +import { usePropertiesSetting } from '@/hooks/roofcover/usePropertiesSetting' + +export default function PropertiesSetting(props) { + const { getMessage } = useMessage() + const { setShowPropertiesSettingModal } = props + + const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting() + + return ( + +
+
+

{getMessage('modal.canvas.setting.wallline.properties.setting')}

+ +
+
+
{getMessage('modal.canvas.setting.wallline.properties.setting.info')}
+
+
{getMessage('setting')}
+
+ + +
+
+
+ + +
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx b/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx index 815ab087..306a09f0 100644 --- a/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/RightAngle.jsx @@ -1,24 +1,63 @@ import { useMessage } from '@/hooks/useMessage' +import { onlyNumberInputChange } from '@/util/input-utils' -export default function RightAngle() { +export default function RightAngle({ props }) { const { getMessage } = useMessage() + const { length1, setLength1, length1Ref, length2, setLength2, length2Ref, arrow1, setArrow1, arrow2, setArrow2 } = props return (
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength1)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
@@ -26,17 +65,53 @@ export default function RightAngle() {
{getMessage('modal.cover.outline.length')}
- + onlyNumberInputChange(e, setLength2)} + placeholder="3000" + />
- +
{getMessage('modal.cover.outline.arrow')}
- - - - + + + +
diff --git a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx index 7e90bfb9..07925a94 100644 --- a/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/WallLineSetting.jsx @@ -1,6 +1,6 @@ 'use client' -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import { useMessage } from '@/hooks/useMessage' import { OUTER_LINE_TYPE } from '@/store/outerLineAtom' import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall' @@ -11,12 +11,105 @@ import DoublePitch from '@/components/floor-plan/modal/outerlinesetting/DoublePi import Diagonal from '@/components/floor-plan/modal/outerlinesetting/Diagonal' export default function WallLineSetting(props) { - const { setShowOutlineModal } = props + const { setShowOutlineModal, setShowPropertiesSettingModal } = props const { getMessage } = useMessage() - const { type, setType, handleFix, handleRollback } = useOuterLineWall() + const { + length1, + setLength1, + length2, + setLength2, + length1Ref, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + type, + setType, + arrow1Ref, + arrow2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, + handleRollback, + handleFix, + } = useOuterLineWall() + + const outerLineProps = { + length1, + setLength1, + length1Ref, + arrow1, + setArrow1, + } + + const rightAngleProps = { + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + } + + const doublePitchProps = { + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + arrow1, + setArrow1, + arrow2, + setArrow2, + arrow1Ref, + arrow2Ref, + } + + const angleProps = { + angle1, + setAngle1, + angle1Ref, + length1, + setLength1, + length1Ref, + } + + const diagonalLineProps = { + length1, + setLength1, + length1Ref, + length2, + setLength2, + length2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, + arrow1, + setArrow1, + arrow2, + setArrow2, + } return ( - +

{getMessage('modal.cover.outline.drawing')}

@@ -55,24 +148,33 @@ export default function WallLineSetting(props) { {getMessage('modal.cover.outline.diagonal')}
- {type === OUTER_LINE_TYPE.OUTER_LINE ? ( - - ) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? ( - - ) : type === OUTER_LINE_TYPE.DOUBLE_PITCH ? ( - - ) : type === OUTER_LINE_TYPE.ANGLE ? ( - - ) : type === OUTER_LINE_TYPE.DIAGONAL_LINE ? ( - - ) : ( - <> - )} +
+
{getMessage('modal.cover.outline.setting')}
+ {type === OUTER_LINE_TYPE.OUTER_LINE ? ( + + ) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? ( + + ) : type === OUTER_LINE_TYPE.DOUBLE_PITCH ? ( + + ) : type === OUTER_LINE_TYPE.ANGLE ? ( + + ) : type === OUTER_LINE_TYPE.DIAGONAL_LINE ? ( + + ) : ( + <> + )} +
-
diff --git a/src/components/floor-plan/modal/placementShape/MaterialGuide.jsx b/src/components/floor-plan/modal/placementShape/MaterialGuide.jsx new file mode 100644 index 00000000..eff09305 --- /dev/null +++ b/src/components/floor-plan/modal/placementShape/MaterialGuide.jsx @@ -0,0 +1,17 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function MaterialGuide({ setShowMaterialGuidModal }) { + const { getMessage } = useMessage() + return ( +
+
+ +
+
+
{getMessage('modal.placement.initial.setting.roof.material.info')}
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx new file mode 100644 index 00000000..606f932d --- /dev/null +++ b/src/components/floor-plan/modal/placementShape/PlacementShapeSetting.jsx @@ -0,0 +1,120 @@ +import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide' +import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide' +import WithDraggable from '@/components/common/draggable/WithDraggable' +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' + +export default function PlacementShapeSetting({ setShowPlaceShapeModal }) { + const [showSizeGuideModal, setShowSizeGuidModal] = useState(false) + const [showMaterialGuideModal, setShowMaterialGuidModal] = useState(false) + const { getMessage } = useMessage() + return ( + +
+
+

{getMessage('plan.menu.placement.surface.initial.setting')}

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + +
{getMessage('modal.placement.initial.setting.plan.drawing')}{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}
+ {getMessage('modal.placement.initial.setting.size')} + + +
+
+ + +
+
+ + +
+
+ + +
+
+
{getMessage('modal.placement.initial.setting.roof.angle.setting')} +
+
+ + +
+
+ + +
+
+
+ {getMessage('modal.placement.initial.setting.roof.material')} + + +
+
+ +
+
+ W +
+ +
+
+
+ L +
+ +
+
+
+ {getMessage('modal.placement.initial.setting.rafter')} +
+ +
+
+
+
+
+
+ +
+
+ {showSizeGuideModal && } + {showMaterialGuideModal && } +
+
+ ) +} diff --git a/src/components/floor-plan/modal/placementShape/SizeGuide.jsx b/src/components/floor-plan/modal/placementShape/SizeGuide.jsx new file mode 100644 index 00000000..1f9d2770 --- /dev/null +++ b/src/components/floor-plan/modal/placementShape/SizeGuide.jsx @@ -0,0 +1,39 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function SizeGuide({ setShowSizeGuidModal }) { + const { getMessage } = useMessage() + return ( +
+
+ +
+
+
{getMessage('modal.placement.initial.setting.size.info')}
+
+ + + + + + + + + + + + + + + + + + + +
{getMessage('modal.placement.initial.setting.size.roof')}{getMessage('modal.placement.initial.setting.size.roof.info')}
{getMessage('modal.placement.initial.setting.size.actual')}{getMessage('modal.placement.initial.setting.size.actual.info')}
{getMessage('modal.placement.initial.setting.size.none.pitch')}{getMessage('modal.placement.initial.setting.size.none.pitch.info')}
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx new file mode 100644 index 00000000..517a7ba3 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/RoofShapeSetting.jsx @@ -0,0 +1,58 @@ +import { useMessage } from '@/hooks/useMessage' +import WithDraggable from '@/components/common/draggable/WithDraggable' +import Ridge from '@/components/floor-plan/modal/roofShape/type/Ridge' +import Pattern from '@/components/floor-plan/modal/roofShape/type/Pattern' +import Side from '@/components/floor-plan/modal/roofShape/type/Side' +import { useState } from 'react' +import Image from 'next/image' +import Direction from '@/components/floor-plan/modal/roofShape/type/Direction' + +export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) { + const { getMessage } = useMessage() + const [shapeNum, setShapeNum] = useState(1) + const shapeMenu = [ + { id: 1, name: getMessage('modal.roof.shape.setting.ridge') }, // 용마루 + { id: 2, name: getMessage('modal.roof.shape.setting.patten.a') }, // 패턴A + { id: 3, name: getMessage('modal.roof.shape.setting.patten.b') }, // 패턴B + { id: 4, name: getMessage('modal.roof.shape.setting.side') }, // 변별로 설정 + { id: 5, name: getMessage('commons.west') }, // 서 + { id: 6, name: getMessage('commons.east') }, // 서 + { id: 7, name: getMessage('commons.south') }, // 서 + { id: 8, name: getMessage('commons.north') }, // 북 + ] + + return ( + +
+
+

{getMessage('modal.roof.shape.setting')}

+ +
+
+
+ {shapeMenu.map((item) => ( + + ))} +
+
+
{getMessage('setting')}
+ {shapeNum === 1 && } + {(shapeNum === 2 || shapeNum === 3) && } + {shapeNum === 4 && } + {(shapeNum === 5 || shapeNum === 6 || shapeNum === 7 || shapeNum === 8) && } +
+
+ +
+
+
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Direction.jsx b/src/components/floor-plan/modal/roofShape/type/Direction.jsx new file mode 100644 index 00000000..a02258f9 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Direction.jsx @@ -0,0 +1,45 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Direction() { + const { getMessage } = useMessage() + return ( +
+
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+
+ + {getMessage('windage.width')} + +
+ +
+ mm +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Pattern.jsx b/src/components/floor-plan/modal/roofShape/type/Pattern.jsx new file mode 100644 index 00000000..606c154d --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Pattern.jsx @@ -0,0 +1,36 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Pattern() { + const { getMessage } = useMessage() + return ( +
+
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Ridge.jsx b/src/components/floor-plan/modal/roofShape/type/Ridge.jsx new file mode 100644 index 00000000..7897046c --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Ridge.jsx @@ -0,0 +1,27 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Ridge() { + const { getMessage } = useMessage() + return ( +
+
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/Side.jsx b/src/components/floor-plan/modal/roofShape/type/Side.jsx new file mode 100644 index 00000000..3fc1737c --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/Side.jsx @@ -0,0 +1,46 @@ +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' +import Eaves from '@/components/floor-plan/modal/roofShape/type/option/Eaves' +import Gable from '@/components/floor-plan/modal/roofShape/type/option/Gable' +import HipAndGable from '@/components/floor-plan/modal/roofShape/type/option/HipAndGable' +import Wall from '@/components/floor-plan/modal/roofShape/type/option/Wall' +import Jerkinhead from '@/components/floor-plan/modal/roofShape/type/option/Jerkinhead' +import Shed from '@/components/floor-plan/modal/roofShape/type/option/Shed' + +export default function Side() { + const [buttonAct, setButtonAct] = useState(1) + const { getMessage } = useMessage() + const buttonMenu = [ + { id: 1, name: getMessage('eaves') }, + { id: 2, name: getMessage('gable') }, + { id: 3, name: getMessage('wall') }, + { id: 4, name: getMessage('hipandgable') }, + { id: 5, name: getMessage('jerkinhead') }, + { id: 6, name: getMessage('shed') }, + ] + return ( +
+
+
+ {buttonMenu.map((item) => ( + + ))} +
+
+
+ {buttonAct === 1 && } + {buttonAct === 2 && } + {buttonAct === 3 && } + {buttonAct === 4 && } + {buttonAct === 5 && } + {buttonAct === 6 && } +
+
+ + +
+
+ ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx b/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx new file mode 100644 index 00000000..303dad55 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Eaves.jsx @@ -0,0 +1,27 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Eaves() { + const { getMessage } = useMessage() + return ( + <> +
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Gable.jsx b/src/components/floor-plan/modal/roofShape/type/option/Gable.jsx new file mode 100644 index 00000000..63092550 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Gable.jsx @@ -0,0 +1,16 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Gable() { + const { getMessage } = useMessage() + return ( + <> +
+ {getMessage('gable.offset')} +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx b/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx new file mode 100644 index 00000000..ed2e1b60 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/HipAndGable.jsx @@ -0,0 +1,36 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function HipAndGable() { + const { getMessage } = useMessage() + return ( + <> +
+ + {getMessage('slope')} + +
+ +
+ {getMessage('size')} +
+
+ + {getMessage('eaves.offset')} + +
+ +
+ mm +
+
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx b/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx new file mode 100644 index 00000000..d5737470 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Jerkinhead.jsx @@ -0,0 +1,36 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Jerkinhead() { + const { getMessage } = useMessage() + return ( + <> +
+ + {getMessage('gable.offset')} + +
+ +
+ mm +
+
+ + {getMessage('jerkinhead.width')} + +
+ +
+ mm +
+
+ + {getMessage('jerkinhead.slope')} + +
+ +
+ {getMessage('size')} +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Shed.jsx b/src/components/floor-plan/modal/roofShape/type/option/Shed.jsx new file mode 100644 index 00000000..c4fd4333 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Shed.jsx @@ -0,0 +1,16 @@ +import { useMessage } from '@/hooks/useMessage' + +export default function Shed() { + const { getMessage } = useMessage() + return ( + <> +
+ {getMessage('shed.width')} +
+ +
+ mm +
+ + ) +} diff --git a/src/components/floor-plan/modal/roofShape/type/option/Wall.jsx b/src/components/floor-plan/modal/roofShape/type/option/Wall.jsx new file mode 100644 index 00000000..6b716892 --- /dev/null +++ b/src/components/floor-plan/modal/roofShape/type/option/Wall.jsx @@ -0,0 +1,38 @@ +import { useState } from 'react' +import { useMessage } from '@/hooks/useMessage' + +export default function Wall() { + const [hasSleeve, setHasSleeve] = useState('0') + const { getMessage } = useMessage() + return ( + <> + {hasSleeve} +
+
+
+
+ setHasSleeve(e.target.value)} /> + +
+
+
+
+
+
+ setHasSleeve(e.target.value)} /> + +
+
+
+
+
+ +
+ mm +
+
+
+
+ + ) +} diff --git a/src/components/floor-plan/modal/setting01/GridOption.jsx b/src/components/floor-plan/modal/setting01/GridOption.jsx index c5502d6f..d9301afc 100644 --- a/src/components/floor-plan/modal/setting01/GridOption.jsx +++ b/src/components/floor-plan/modal/setting01/GridOption.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react' +import React, { useEffect } from 'react' import { useRecoilState } from 'recoil' import { settingModalGridOptionsState } from '@/store/settingAtom' import { useMessage } from '@/hooks/useMessage' @@ -8,7 +8,7 @@ import { gridColorState } from '@/store/gridAtom' import { useColor } from 'react-color-palette' export default function GridOption(props) { - const { setShowDotLineGridModal } = props + const { setShowDotLineGridModal, setShowColorPickerModal } = props const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState) const [gridColor, setGridColor] = useRecoilState(gridColorState) const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState) @@ -16,11 +16,8 @@ export default function GridOption(props) { const { tempGridMode, setTempGridMode } = useTempGrid() const [color, setColor] = useColor(gridColor) - const [colorPickerShow, setColorPickerShow] = useState(false) useEffect(() => { - console.log('GridOption useEffect 실행') - //console.log(color) setGridColor(color.hex) }, [color]) @@ -57,7 +54,9 @@ export default function GridOption(props) { if (option.id === 4) { // 그리드 색 설정 if (option.selected) { - setColorPickerShow(true) + setShowColorPickerModal(true) + } else { + setShowColorPickerModal(false) } } diff --git a/src/components/floor-plan/modal/setting01/SettingModal01.jsx b/src/components/floor-plan/modal/setting01/SettingModal01.jsx index 293d29df..78511548 100644 --- a/src/components/floor-plan/modal/setting01/SettingModal01.jsx +++ b/src/components/floor-plan/modal/setting01/SettingModal01.jsx @@ -2,18 +2,25 @@ import { useState } from 'react' import FirstOption from './FirstOption' -import WithDraggable from '@/components/common/draggable/withDraggable' +import WithDraggable from '@/components/common/draggable/WithDraggable' import SecondOption from '@/components/floor-plan/modal/setting01/SecondOption' import { useMessage } from '@/hooks/useMessage' import GridOption from '@/components/floor-plan/modal/setting01/GridOption' +import { canGridOptionSeletor } from '@/store/canvasAtom' +import { useRecoilValue } from 'recoil' export default function SettingModal01(props) { - const { setShowCanvasSettingModal, setShowDotLineGridModal } = props + const { setShowCanvasSettingModal, setShowDotLineGridModal, setShowColorPickerModal } = props const [buttonAct, setButtonAct] = useState(1) const { getMessage } = useMessage() + const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor) + + const handleBtnClick = (num) => { + setButtonAct(num) + } return ( - +

{getMessage('modal.canvas.setting')}

@@ -23,20 +30,22 @@ export default function SettingModal01(props) {
- - - + {canGridOptionSeletorValue && ( + + )}
{buttonAct === 1 && } {buttonAct === 2 && } - {buttonAct === 3 && } + {buttonAct === 3 && }
diff --git a/src/components/management/Stuff.jsx b/src/components/management/Stuff.jsx index 6f615c3c..638126ee 100644 --- a/src/components/management/Stuff.jsx +++ b/src/components/management/Stuff.jsx @@ -12,7 +12,7 @@ import { queryStringFormatter, isEmptyArray } from '@/util/common-utils' import dayjs from 'dayjs' import isLeapYear from 'dayjs/plugin/isLeapYear' // 윤년 판단 플러그인 dayjs.extend(isLeapYear) - +import { globalLocaleStore } from '@/store/localeAtom' export default function Stuff() { const stuffSearchParams = useRecoilValue(stuffSearchState) const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState) @@ -20,7 +20,9 @@ export default function Stuff() { const [curPage, setCurPage] = useState(1) //현재 페이지 번호 const [defaultSize, setDefaultSize] = useState(100) //페이지 당 게시물 수 const [defaultSortType, setDefaultSortType] = useState('R') - const { get } = useAxios() + + const globalLocaleState = useRecoilValue(globalLocaleStore) + const { get } = useAxios(globalLocaleState) const gridRef = useRef() const [gridCount, setGridCount] = useState(0) @@ -364,50 +366,46 @@ export default function Stuff() { } return ( <> -
- 물건목록 - - 전체 : {gridCount} // 선택 : {selectedRowDataCount} - - - -
- {/* */} - {/* - */} + {/* 퍼블시작 */} +
+
+
+

물건목록

+
    +
  • + 전체 + {gridCount} +
  • +
  • + 선택 + {selectedRowDataCount} +
  • +
+
+
+
+ +
+
+ +
+
-
- +
+
+ +
페이징 컴포넌트예정
+
+ {/* 퍼블종료 */} ) } diff --git a/src/components/management/StuffDetail.jsx b/src/components/management/StuffDetail.jsx index 66baf8e3..898b4176 100644 --- a/src/components/management/StuffDetail.jsx +++ b/src/components/management/StuffDetail.jsx @@ -4,16 +4,20 @@ import React, { useState, useEffect } from 'react' import { useRouter, useSearchParams } from 'next/navigation' import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem, Select, SelectItem, Checkbox, Textarea, button } from '@nextui-org/react' import Link from 'next/link' -import { del, get, post } from '@/lib/Axios' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' import { queryStringFormatter, isEmptyArray } from '@/util/common-utils' import dayjs from 'dayjs' import { useMessage } from '@/hooks/useMessage' import { useForm } from 'react-hook-form' +import { useRecoilState, useRecoilValue } from 'recoil' export default function StuffDetail() { const router = useRouter() const searchParams = useSearchParams() const { getMessage } = useMessage() + const globalLocaleState = useRecoilValue(globalLocaleStore) + const { get, post, del } = useAxios(globalLocaleState) //form const formInitValue = { // 물건번호 T...(임시) R...(진짜) @@ -63,7 +67,7 @@ export default function StuffDetail() { useEffect(() => { if (objectNo) { - console.log('수정화면') + //console.log('수정화면') setEditMode('EDIT') if (objectNo.substring(0, 1) === 'R') { @@ -90,7 +94,7 @@ export default function StuffDetail() { //1차점 : X167 get({ url: `/api/object/saleStore/X167/list` }).then((res) => { if (!isEmptyArray(res)) { - console.log('판매점 결과:::::', res) + // console.log('판매점 결과:::::', res) setSaleStoreList(res) //1차 판매점 자동완성 값 셋팅 form.setValue('saleStoreId', res[0].saleStoreId) @@ -99,9 +103,6 @@ export default function StuffDetail() { setOtherSaleStoreList([]) } }) - } else { - alert('삭제된 물건입니다') - router.push('/management/stuff') } }) } else { @@ -385,6 +386,312 @@ export default function StuffDetail() { return ( <> {(editMode === 'NEW' && ( +
+
+
+ * 필수 입력항목 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 담당자 * + +
+ +
+
+ 물건구분/물건명 * + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+
+
물건명 후리가나 +
+ +
+
+
+
+ 1차 판매점명 / ID + * +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+ 2차 판매점명 / ID * +
+
+
+
+
+
+ +
+
+
+ +
+
+ 우편번호 * + +
+
+ +
+ +
*우편번호 7자리를 입력한 후, 주소검색 버튼을 클릭해 주십시오
+
+
+ 도도부현 / 주소 * + +
+
+ {/* {prefCodeList?.length > 0 && ( + + )} */} +
+
+ +
+
+
+ 발전량시뮬레이션지역 * + +
+
+ 기준풍속 * + +
+
+ +
+ m/s이하 +
+
+ 수직적설량 * + +
+
+ cm +
+ + +
+
+
+ 면조도구분 * + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ 설치높이 * + +
+
+ +
+ m +
+
계약조건 +
+
+ + +
+
+ + +
+
+
메모 +
+ +
+
+
+
+ {!isFormValid ? ( + + ) : ( + + )} + + + +
+
+
+ )) || ( + <> + {objectNo.substring(0, 1) === 'R' ? ( + <> + + + + + + + ) : ( + <> + + + + + + )} + + )} + {/* {(editMode === 'NEW' && (
(*필수 입력항목)
@@ -610,7 +917,7 @@ export default function StuffDetail() { )} - )} + )} */} ) } diff --git a/src/components/management/StuffHeader.jsx b/src/components/management/StuffHeader.jsx new file mode 100644 index 00000000..fb4d831c --- /dev/null +++ b/src/components/management/StuffHeader.jsx @@ -0,0 +1,51 @@ +'use client' + +import React, { useState, useEffect } from 'react' +import { useAxios } from '@/hooks/useAxios' +import { useRouter, useSearchParams } from 'next/navigation' +import { globalLocaleStore } from '@/store/localeAtom' +import { useRecoilValue } from 'recoil' +export default function StuffHeader() { + const router = useRouter() + const searchParams = useSearchParams() + const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set + const globalLocaleState = useRecoilValue(globalLocaleStore) + const { get } = useAxios(globalLocaleState) + const [headerData, setHeaderData] = useState({}) + + useEffect(() => { + get({ url: `/api/object/${objectNo}/detail` }).then((res) => { + //console.log('res::', res) + if (res != null && res != '') { + console.log('헤더상세::::::::::', res) + setHeaderData(res) + } else { + alert('삭제된 물건입니다') + router.push('/management/stuff') + } + }) + }, [objectNo]) + + return ( +
+
+
물건번호
+
+ {headerData.objectNo} +
+
+
+
사양확정일
+
{headerData.specificationConfirmDate}
+
+
+
갱신일시
+
{headerData.lastEditDatetime}
+
+
+
등록일
+
{headerData.createDatetime}
+
+
+ ) +} diff --git a/src/components/management/StuffSearchCondition.jsx b/src/components/management/StuffSearchCondition.jsx index 58aa4ce9..420b2145 100644 --- a/src/components/management/StuffSearchCondition.jsx +++ b/src/components/management/StuffSearchCondition.jsx @@ -2,25 +2,36 @@ import React, { useEffect } from 'react' import { useState } from 'react' -import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem } from '@nextui-org/react' +import { Autocomplete, AutocompleteItem } from '@nextui-org/react' +import { useAxios } from '@/hooks/useAxios' +import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil' +import { globalLocaleStore } from '@/store/localeAtom' + import RangeDatePicker from '@/components/common/datepicker/RangeDatePicker' -import { useRecoilState, useResetRecoilState } from 'recoil' import { stuffSearchState } from '@/store/stuffAtom' import { isEmptyArray } from '@/util/common-utils' -import { get } from '@/lib/Axios' import dayjs from 'dayjs' import isLeapYear from 'dayjs/plugin/isLeapYear' // 윤년 판단 플러그인 dayjs.extend(isLeapYear) import Link from 'next/link' +import SingleDatePicker from '../common/datepicker/SingleDatePicker' export default function StuffSearchCondition() { - //달력 props 관련 날짜 셋팅 - const [dateRange, setDateRange] = useState([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]) - const [startRangeDate, endRangeDate] = dateRange + const globalLocaleState = useRecoilValue(globalLocaleStore) - const rangeDatePickerProps = { - startRangeDate, //시작일 - endRangeDate, //종료일 - setDateRange, + const { get } = useAxios(globalLocaleState) + + //달력 props 관련 날짜 셋팅 + const [startDate, setStartDate] = useState(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) + const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD')) + + const rangeDatePickerProps1 = { + startDate, //시작일 + setStartDate, + } + + const rangeDatePickerProps2 = { + startDate: endDate, //종료일 + setStartDate: setEndDate, } //여기서 선택한 검색조건들을 recoil로 관리 @@ -40,7 +51,7 @@ export default function StuffSearchCondition() { const [schSelSaleStoreList, setSchSelSaleStoreList] = useState([]) //판매대리점 자동완성 SELECT // 조회 const onSubmit = () => { - let diff = dayjs(endRangeDate).diff(startRangeDate, 'day') + let diff = dayjs(endDate).diff(startDate, 'day') if (diff > 366) { return alert('최대1년 조회 가능합니다.') } @@ -55,8 +66,8 @@ export default function StuffSearchCondition() { schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser, schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName, schDateType: stuffSearch?.schDateType ? stuffSearch.schDateType : dateType, - schFromDt: dayjs(startRangeDate).format('YYYY-MM-DD'), - schToDt: dayjs(endRangeDate).format('YYYY-MM-DD'), + schFromDt: dayjs(startDate).format('YYYY-MM-DD'), + schToDt: dayjs(endDate).format('YYYY-MM-DD'), code: 'E', schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId, startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1, @@ -76,7 +87,8 @@ export default function StuffSearchCondition() { setReceiveUser('') setDispCompanyName('') setDateType('U') - setDateRange([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]) + setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD')) + setEndDate(dayjs(new Date()).format('YYYY-MM-DD')) setSchSelSaleStoreId('') resetStuffRecoil() } @@ -102,23 +114,207 @@ export default function StuffSearchCondition() { } } - //x로 날짜 비웠을때 기본값으로 셋팅 useEffect(() => { - if (!startRangeDate && !endRangeDate) { - setDateRange([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')]) - } - }, [startRangeDate, endRangeDate]) - - useEffect(() => { - setDateRange([ - stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), - stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'), - ]) + 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')) }, [stuffSearch]) return ( <> -
+ {/* 퍼블적용시작 */} +
+
+
+

물건현황

+
+
+ + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
물건번호 +
+ { + setObjectNo(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schObjectNo: e.target.value }) + }} + /> +
+
판매대리점명 +
+ { + setSaleStoreId(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreId: e.target.value }) + }} + /> +
+
물건주소 +
+ { + setAddress(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schAddress: e.target.value }) + }} + /> +
+
물건명 +
+ { + setobjectName(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schObjectName: e.target.value }) + }} + /> +
+
견적처 +
+ { + setDispCompanyName(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schDispCompanyName: e.target.value }) + }} + /> +
+
판매대리점 선택 + {/*
+ +
*/} + {schSelSaleStoreList?.length > 0 && ( + + {(option) => {option.saleStoreName}} + + )} +
담당자 +
+ { + setReceiveUser(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schReceiveUser: e.target.value }) + }} + /> +
+
기간검색 +
+
+
+ { + setDateType(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) + }} + /> + +
+
+ { + setDateType(e.target.value) + setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value }) + }} + /> + +
+
+
+
+ +
+ ~ +
+ +
+
+
+
+
+
+ {/* 퍼블적용끝 */} + {/*
@@ -300,7 +496,7 @@ export default function StuffSearchCondition() { ) } })} -
+
*/} ) } diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index 355eca16..130593a0 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -1,6 +1,6 @@ import { useEffect, useRef } from 'react' -import { distanceBetweenPoints } from '@/util/canvas-util' -import { useRecoilState, useRecoilValue } from 'recoil' +import { distanceBetweenPoints, getDegreeByChon } from '@/util/canvas-util' +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { adsorptionPointAddModeState, adsorptionPointModeState, @@ -16,14 +16,18 @@ import { useTempGrid } from '@/hooks/useTempGrid' import { usePolygon } from '@/hooks/usePolygon' import { outerLineAngle1State, + outerLineAngle2State, outerLineArrow1State, outerLineArrow2State, + outerLineDiagonalState, + outerLineFixState, outerLineLength1State, outerLineLength2State, outerLinePointsState, outerLineTypeState, } from '@/store/outerLineAtom' import { calculateAngle } from '@/util/qpolygon-utils' +import { fabric } from 'fabric' export function useOuterLineWall() { const canvas = useRecoilValue(canvasState) @@ -33,6 +37,7 @@ export function useOuterLineWall() { const { addLine, removeLine } = useLine() const { tempGridMode } = useTempGrid() const { addPolygonByLines } = usePolygon() + const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) const adsorptionPointMode = useRecoilValue(adsorptionPointModeState) @@ -42,28 +47,42 @@ export function useOuterLineWall() { const length1Ref = useRef(null) const length2Ref = useRef(null) const angle1Ref = useRef(null) + const angle2Ref = useRef(null) const [length1, setLength1] = useRecoilState(outerLineLength1State) const [length2, setLength2] = useRecoilState(outerLineLength2State) const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State) const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State) const [points, setPoints] = useRecoilState(outerLinePointsState) const [type, setType] = useRecoilState(outerLineTypeState) + const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) + const [angle2, setAngle2] = useRecoilState(outerLineAngle2State) + const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(outerLineDiagonalState) const arrow1Ref = useRef(arrow1) const arrow2Ref = useRef(arrow2) + const setOuterLineFix = useSetRecoilState(outerLineFixState) + + const outerLineDiagonalLengthRef = useRef(null) + const isFix = useRef(false) - const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) + const closeModalFn = useRef(null) + useEffect(() => { if (adsorptionPointAddMode || tempGridMode) { return } - removeMouseEvent('mouse:down', mouseDown) + + if (points.length === 0) { + // 만약 포인트가 없다면 모든 라인과 텍스트를 삭제 후 outerLines에서 point를 뽑아 points에 넣어준다. + const lengthTxts = canvas?.getObjects().filter((obj) => obj.name === 'lengthTxt') + lengthTxts.forEach((txt) => { + canvas?.remove(txt) + }) + } + addCanvasMouseEventListener('mouse:down', mouseDown) clear() - return () => { - removeAllMouseEventListeners() - } }, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode]) useEffect(() => { @@ -75,9 +94,8 @@ export function useOuterLineWall() { }, [arrow2]) useEffect(() => { - removeAllDocumentEventListeners() - addDocumentEventListener('keydown', document, keydown[type]) clear() + addDocumentEventListener('keydown', document, keydown[type]) }, [type]) const clear = () => { @@ -88,6 +106,9 @@ export function useOuterLineWall() { setArrow2('') setAngle1(0) + setAngle2(0) + + setOuterLineDiagonalLength(0) } const mouseDown = (e) => { @@ -143,14 +164,14 @@ export function useOuterLineWall() { canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint')) - // point가 변경 될때마다 이벤트 리스너를 제거하고 다시 등록 - removeAllDocumentEventListeners() - addDocumentEventListener('keydown', document, keydown[type]) - if (points.length === 0) { + setOuterLineFix(true) + removeAllDocumentEventListeners() return } + addDocumentEventListener('keydown', document, keydown[type]) + if (points.length === 1) { const point = new fabric.Circle({ radius: 5, @@ -164,6 +185,7 @@ export function useOuterLineWall() { canvas?.add(point) } else { + setOuterLineFix(false) points.forEach((point, idx) => { if (idx === 0) { return @@ -174,6 +196,13 @@ export function useOuterLineWall() { const lastPoint = points[points.length - 1] const firstPoint = points[0] + if (isFix.current) { + removeAllMouseEventListeners() + removeAllDocumentEventListeners() + canvas?.renderAll() + closeModalFn.current(false) + } + if (points.length < 3) { return } @@ -241,7 +270,7 @@ export function useOuterLineWall() { stroke: 'black', strokeWidth: 3, idx: idx, - selectable: false, + selectable: true, name: 'outerLine', }) } @@ -261,7 +290,26 @@ export function useOuterLineWall() { } // 직각 완료될 경우 확인 - const checkRightAngle = () => { + const checkRightAngle = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current || activeElem === angle1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + length2Ref.current.focus() + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + const length1Num = Number(length1Ref.current.value) / 10 const length2Num = Number(length2Ref.current.value) / 10 @@ -332,6 +380,191 @@ export function useOuterLineWall() { } } + //이구배 완료될 경우 확인 ↓, ↑, ←, → + const checkDoublePitch = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current || activeElem === angle1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + angle2Ref.current.focus() + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + + const angle1Value = angle1Ref.current.value + const angle2Value = angle2Ref.current.value + const length1Value = length1Ref.current.value + const length2Value = length2Ref.current.value + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') { + if (arrow1Value === '↓' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '↓' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '←' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '←' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } + + angle1Ref.current.focus() + } + } + + //대각선 완료될 경우 확인 + const checkDiagonal = (direction) => { + const activeElem = document.activeElement + + const canDirection = + direction === '↓' || direction === '↑' + ? arrow1Ref.current === '←' || arrow1Ref.current === '→' + : arrow1Ref.current === '↓' || arrow1Ref.current === '↑' + + if (activeElem === length1Ref.current) { + setArrow1(direction) + arrow1Ref.current = direction + } else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) { + if (!canDirection) { + return + } + setArrow2(direction) + arrow2Ref.current = direction + } + + const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이 + + const length1Value = length1Ref.current.value + + const arrow1Value = arrow1Ref.current + const arrow2Value = arrow2Ref.current + + const getLength2 = () => { + return Math.floor(Math.sqrt(diagonalLength ** 2 - length1Value ** 2)) + } + + const length2Value = getLength2() + + console.log(length2Value) + + if (diagonalLength !== 0 && length1Value !== 0 && arrow1Value !== '') { + setLength2(getLength2()) + length2Ref.current.focus() + } + + if (length1Value !== 0 && length2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') { + if (arrow1Value === '↓' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '↓' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '→') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '↑' && arrow2Value === '←') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↓') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }] + }) + } else if (arrow1Value === '→' && arrow2Value === '↑') { + setPoints((prev) => { + if (prev.length === 0) { + return [] + } + return [ + ...prev, + { + x: prev[prev.length - 1].x + length1Value / 10, + y: prev[prev.length - 1].y - length2Value / 10, + }, + ] + }) + } + } + } + const keydown = { outerLine: (e) => { if (points.length === 0) { @@ -411,75 +644,52 @@ export function useOuterLineWall() { switch (key) { case 'Down': // IE/Edge에서 사용되는 값 case 'ArrowDown': { - if (activeElem === length1Ref.current) { - setArrow1('↓') - arrow1Ref.current = '↓' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { - break - } - setArrow2('↓') - arrow2Ref.current = '↓' - checkRightAngle() - } - + checkRightAngle('↓') break } case 'Up': // IE/Edge에서 사용되는 값 case 'ArrowUp': - if (activeElem === length1Ref.current) { - setArrow1('↑') - arrow1Ref.current = '↑' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') { - break - } - setArrow2('↑') - arrow2Ref.current = '↑' - checkRightAngle() - } - + checkRightAngle('↑') break case 'Left': // IE/Edge에서 사용되는 값 case 'ArrowLeft': - if (activeElem === length1Ref.current) { - setArrow1('←') - arrow1Ref.current = '←' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { - break - } - setArrow2('←') - arrow2Ref.current = '←' - checkRightAngle() - } - + checkRightAngle('←') break case 'Right': // IE/Edge에서 사용되는 값 case 'ArrowRight': - if (activeElem === length1Ref.current) { - setArrow1('→') - arrow1Ref.current = '→' - length2Ref.current.focus() - } else if (activeElem === length2Ref.current) { - if (arrow1Ref.current === '←' || arrow1Ref.current === '→') { - break - } - setArrow2('→') - arrow2Ref.current = '→' - checkRightAngle() - } - + checkRightAngle('→') break } }, - leeGubae: (e) => { - console.log('leegubae') + doublePitch: (e) => { + if (points.length === 0) { + return + } + const key = e.key + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + checkDoublePitch('↓') + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + checkDoublePitch('↑') + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + checkDoublePitch('←') + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + checkDoublePitch('→') + break + } }, angle: (e) => { + if (points.length === 0) { + return + } const key = e.key switch (key) { case 'Enter': { @@ -501,7 +711,30 @@ export function useOuterLineWall() { } }, diagonalLine: (e) => { - console.log('diagonalLine') + if (points.length === 0) { + return + } + + const key = e.key + switch (key) { + case 'Down': // IE/Edge에서 사용되는 값 + case 'ArrowDown': { + checkDiagonal('↓') + break + } + case 'Up': // IE/Edge에서 사용되는 값 + case 'ArrowUp': + checkDiagonal('↑') + break + case 'Left': // IE/Edge에서 사용되는 값 + case 'ArrowLeft': + checkDiagonal('←') + break + case 'Right': // IE/Edge에서 사용되는 값 + case 'ArrowRight': + checkDiagonal('→') + break + } }, } @@ -513,7 +746,7 @@ export function useOuterLineWall() { setPoints((prev) => prev.slice(0, prev.length - 1)) } - const handleFix = () => { + const handleFix = (fn) => { if (points.length < 3) { return } @@ -541,6 +774,9 @@ export function useOuterLineWall() { setPoints((prev) => { return [...prev, { x: prev[0].x, y: prev[0].y }] }) + + isFix.current = true + closeModalFn.current = fn } return { @@ -558,6 +794,15 @@ export function useOuterLineWall() { setArrow2, arrow1Ref, arrow2Ref, + angle1, + setAngle1, + angle1Ref, + angle2, + setAngle2, + angle2Ref, + outerLineDiagonalLength, + setOuterLineDiagonalLength, + outerLineDiagonalLengthRef, type, setType, handleFix, diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js new file mode 100644 index 00000000..d478a1bc --- /dev/null +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -0,0 +1,160 @@ +import { useEffect, useRef } from 'react' +import { LINE_TYPE } from '@/common/common' +import { useRecoilValue } from 'recoil' +import { canvasState, currentObjectState } from '@/store/canvasAtom' +import { useMode } from '@/hooks/useMode' +import { usePolygon } from '@/hooks/usePolygon' +import { useLine } from '@/hooks/useLine' + +export function usePropertiesSetting() { + const canvas = useRecoilValue(canvasState) + + const currentObject = useRecoilValue(currentObjectState) + + const { drawRoofPolygon } = useMode() + + const { addPolygonByLines } = usePolygon() + const { removeLine } = useLine() + + useEffect(() => { + if (!currentObject) { + return + } + if (currentObject.name !== 'outerLine') { + return + } + + const type = currentObject.attributes?.type + + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + + lines.forEach((line) => { + const lineType = line.attributes?.type + if (!lineType) { + line.set({ + stroke: '#000000', + strokeWidth: 4, + }) + } + }) + + if (!type) { + currentObject.set({ + stroke: '#EA10AC', + strokeWidth: 4, + }) + } + }, [currentObject]) + + const history = useRef([]) + + const handleSetEaves = () => { + const selectedLine = canvas?.getActiveObject() + if (!selectedLine) { + return + } + selectedLine.set({ + stroke: '#45CD7D', + strokeWidth: 4, + attributes: { + offset: 50, + type: LINE_TYPE.WALLLINE.EAVES, + pitch: 4, + }, + }) + history.current.push(selectedLine) + nextLineFocus(selectedLine) + canvas.renderAll() + } + + const handleSetGable = () => { + const selectedLine = canvas?.getActiveObject() + if (!selectedLine) { + return + } + selectedLine.set({ + stroke: '#3FBAE6', + strokeWidth: 4, + attributes: { + offset: 30, + type: LINE_TYPE.WALLLINE.GABLE, + }, + }) + history.current.push(selectedLine) + nextLineFocus(selectedLine) + canvas.renderAll() + } + + const nextLineFocus = (selectedLine) => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const index = lines.findIndex((line) => line === selectedLine) + + const nextLine = lines[index + 1] || lines[0] + if (!nextLine.attributes?.type) { + canvas.setActiveObject(nextLine) + } else { + //activeObject 해제 + canvas.discardActiveObject() + } + } + + const handleRollback = () => { + if (history.current.length === 0) { + return + } + const lastLine = history.current.pop() + + lastLine.set({ + stroke: '#000000', + strokeWidth: 4, + }) + + canvas.setActiveObject(lastLine) + canvas.renderAll() + } + + const handleFix = () => { + if (!confirm('외벽선 속성 설정을 완료하시겠습니까?')) { + return + } + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + + lines.forEach((line) => { + line.set({ + attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL }, + stroke: '#000000', + strokeWidth: 4, + }) + removeLine(line) + }) + + const wall = addPolygonByLines(lines, { name: 'WallLine', fill: 'transparent', stroke: 'black' }) + + wall.lines = [...lines] + + drawRoofPolygon(wall) + + canvas.renderAll() + } + + const closeModal = (fn) => { + if (!confirm('외벽선 속성 설정을 종료 하시겠습니까?')) { + return + } + + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + + lines.forEach((line) => { + line.set({ + attributes: { offset: 0, type: LINE_TYPE.WALLLINE.WALL }, + stroke: '#000000', + strokeWidth: 4, + }) + }) + + canvas.renderAll() + fn(false) + } + + return { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } +} diff --git a/src/hooks/useAdsorptionPoint.js b/src/hooks/useAdsorptionPoint.js index 44e2495b..49d49714 100644 --- a/src/hooks/useAdsorptionPoint.js +++ b/src/hooks/useAdsorptionPoint.js @@ -26,7 +26,7 @@ export function useAdsorptionPoint() { top: pointer.y - 3, x: pointer.x, y: pointer.y, - selectable: false, + selectable: true, name: 'adsorptionPoint', }) diff --git a/src/hooks/useAxios.js b/src/hooks/useAxios.js index a1ecda2a..1a7cd03e 100644 --- a/src/hooks/useAxios.js +++ b/src/hooks/useAxios.js @@ -79,5 +79,9 @@ export function useAxios(lang = '') { .catch(console.error) } - return { get, promiseGet, post, promisePost, put, promisePut, patch, del } + const promiseDel = async ({ url }) => { + return await getInstances(url).delete(url) + } + + return { get, promiseGet, post, promisePost, put, promisePut, patch, del, promiseDel } } diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index 37ffdc5f..0f506fd3 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -36,6 +36,7 @@ export function useCanvas(id) { setCanvas(c) setCanvasForEvent(c) + attachDefaultEventOnCanvas() return () => { // c.dispose() @@ -45,7 +46,6 @@ export function useCanvas(id) { useEffect(() => { // canvas 사이즈가 변경되면 다시 - attachDefaultEventOnCanvas() }, [canvasSize]) useEffect(() => { @@ -91,6 +91,13 @@ export function useCanvas(id) { // settings for all canvas in the app fabric.Object.prototype.transparentCorners = false fabric.Object.prototype.id = uuidv4() + fabric.Object.prototype.selectable = true + fabric.Object.prototype.lockMovementX = true + fabric.Object.prototype.lockMovementY = true + fabric.Object.prototype.lockRotation = true + fabric.Object.prototype.lockScalingX = true + fabric.Object.prototype.lockScalingY = true + fabric.Object.prototype.cornerColor = '#2BEBC8' fabric.Object.prototype.cornerStyle = 'rect' fabric.Object.prototype.cornerStrokeColor = '#2BEBC8' diff --git a/src/hooks/useDotLineGrid.js b/src/hooks/useDotLineGrid.js index 34d82fa7..0c21c030 100644 --- a/src/hooks/useDotLineGrid.js +++ b/src/hooks/useDotLineGrid.js @@ -6,6 +6,7 @@ import { gridColorState } from '@/store/gridAtom' export function useDotLineGrid() { const canvas = useRecoilValue(canvasState) + const gridColor = useRecoilValue(gridColorState) const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState) const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격 diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index 31a2cd8a..f9203dc1 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -1,38 +1,31 @@ import { useEffect, useRef } from 'react' -import { useRecoilState, useRecoilValue } from 'recoil' -import { - adsorptionPointAddModeState, - adsorptionPointModeState, - adsorptionRangeState, - canvasState, - canvasZoomState, - currentMenuState, -} from '@/store/canvasAtom' +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom' import { fabric } from 'fabric' -import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' +import { calculateDistance, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' -import { useMouse } from '@/hooks/useMouse' import { useDotLineGrid } from '@/hooks/useDotLineGrid' import { useTempGrid } from '@/hooks/useTempGrid' export function useEvent() { const canvas = useRecoilValue(canvasState) const currentMenu = useRecoilValue(currentMenuState) - const keyboardEventListeners = useRef([]) + const documentEventListeners = useRef([]) const mouseEventListeners = useRef([]) - const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) + const setCanvasZoom = useSetRecoilState(canvasZoomState) const { adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, getAdsorptionPoints, adsorptionPointAddModeStateEvent } = useAdsorptionPoint() const { dotLineGridSetting, interval, getClosestLineGrid } = useDotLineGrid() const { tempGridModeStateLeftClickEvent, tempGridMode, tempGridRightClickEvent } = useTempGrid() + const textMode = useRecoilValue(textModeState) + useEffect(() => { if (!canvas) { return } - removeAllMouseEventListeners() removeAllDocumentEventListeners() - + removeAllMouseEventListeners() /** * wheelEvent */ @@ -47,6 +40,7 @@ export function useEvent() { addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) addDocumentEventListener('keydown', document, defaultKeyboardEvent) + addDocumentEventListener('contextmenu', document, defaultContextMenuEvent) if (adsorptionPointAddMode) { addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent) } @@ -193,13 +187,14 @@ export function useEvent() { } const addCanvasMouseEventListener = (eventType, handler) => { + canvas.off(eventType) canvas.on(eventType, handler) mouseEventListeners.current.push({ eventType, handler }) } const removeAllMouseEventListeners = () => { mouseEventListeners.current.forEach(({ eventType, handler }) => { - canvas.off(eventType, handler) + canvas.off(eventType) }) mouseEventListeners.current.length = 0 // 배열 초기화 } @@ -211,24 +206,36 @@ export function useEvent() { * @param handler */ const addDocumentEventListener = (eventType, element, handler) => { + removeDocumentEvent(eventType) element.addEventListener(eventType, handler) - keyboardEventListeners.current.push({ eventType, element, handler }) + documentEventListeners.current.push({ eventType, element, handler }) } /** * document에 등록되는 event 제거 */ const removeAllDocumentEventListeners = () => { - keyboardEventListeners.current.forEach(({ eventType, element, handler }) => { + documentEventListeners.current.forEach(({ eventType, element, handler }) => { element.removeEventListener(eventType, handler) }) - keyboardEventListeners.current.length = 0 // 배열 초기화 + documentEventListeners.current.length = 0 // 배열 초기화 } - const removeMouseEvent = (type, handler) => { + const removeMouseEvent = (type) => { mouseEventListeners.current = mouseEventListeners.current.filter((event) => { - if (event.type === type && event.handler === handler) { - canvas.off(type, handler) + if (event.eventType === type) { + canvas.off(type, event.handler) + return false + } + return true + }) + } + + const removeDocumentEvent = (type) => { + documentEventListeners.current = documentEventListeners.current.filter((event) => { + if (event.eventType === type) { + console.log(type) + event.element.removeEventListener(type, event.handler) return false } return true diff --git a/src/hooks/useLine.js b/src/hooks/useLine.js index e701a2ff..b6c8a532 100644 --- a/src/hooks/useLine.js +++ b/src/hooks/useLine.js @@ -1,5 +1,6 @@ import { useRecoilValue } from 'recoil' import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom' +import { QLine } from '@/components/fabric/QLine' export const useLine = () => { const canvas = useRecoilValue(canvasState) @@ -7,13 +8,13 @@ export const useLine = () => { const fontFamily = useRecoilValue(fontFamilyState) const addLine = (points = [], options) => { - const line = new fabric.Line(points, { + const line = new QLine(points, { ...options, - selectable: options.selectable ?? false, + fontSize: fontSize, + fontFamily: fontFamily, }) canvas?.add(line) - addLineText(line) return line } @@ -38,6 +39,7 @@ export const useLine = () => { const removeLine = (line) => { removeLineText(line) canvas?.remove(line) + canvas?.renderAll() } const removeLineText = (line) => { diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index c2b29121..ec0842b8 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -39,6 +39,7 @@ import * as turf from '@turf/turf' import { INPUT_TYPE, Mode } from '@/common/common' import { m } from 'framer-motion' import { set } from 'react-hook-form' +import { FaWineGlassEmpty } from 'react-icons/fa6' export function useMode() { const [mode, setMode] = useRecoilState(modeState) @@ -1266,7 +1267,8 @@ export function useMode() { historyPoints.current = [] const wall = makePolygon(null, sort) - wall.set({ name: 'wall' }) + wall.name = 'wall' + // wall.set({ name: 'wall' }) return wall } @@ -1507,344 +1509,253 @@ export function useMode() { *벽 지붕 외곽선 생성 polygon을 입력받아 만들기 */ const handleOuterlinesTest2 = (polygon, offset = 50) => { - const offsetPoints = offsetPolygon(polygon.points, offset) + const roof = drawRoofPolygon(polygon) //지붕 그리기 + roof.drawHelpLine() + // roof.divideLine() + } + + function inwardEdgeNormal(vertex1, vertex2) { + // Assuming that polygon vertices are in clockwise order + const dx = vertex2.x - vertex1.x + const dy = vertex2.y - vertex1.y + const edgeLength = Math.sqrt(dx * dx + dy * dy) + + return { + x: -dy / edgeLength, + y: dx / edgeLength, + } + } + + function outwardEdgeNormal(vertex1, vertex2) { + const n = inwardEdgeNormal(vertex1, vertex2) + + return { + x: -n.x, + y: -n.y, + } + } + + function createRoofPolygon(vertices) { + const edges = [] + let minX = vertices.length > 0 ? vertices[0].x : undefined + let minY = vertices.length > 0 ? vertices[0].y : undefined + let maxX = minX + let maxY = minY + + for (let i = 0; i < vertices.length; i++) { + const vertex1 = vertices[i] + const vertex2 = vertices[(i + 1) % vertices.length] + + const outwardNormal = outwardEdgeNormal(vertex1, vertex2) + + const inwardNormal = inwardEdgeNormal(vertex1, vertex2) + + const edge = { + vertex1, + vertex2, + index: i, + outwardNormal, + inwardNormal, + } + + edges.push(edge) + + const x = vertices[i].x + const y = vertices[i].y + minX = Math.min(x, minX) + minY = Math.min(y, minY) + maxX = Math.max(x, maxX) + maxY = Math.max(y, maxY) + } + + return { + vertices, + edges, + minX, + minY, + maxX, + maxY, + } + } + + function createOffsetEdge(edge, dx, dy) { + return { + vertex1: { + x: edge.vertex1.x + dx, + y: edge.vertex1.y + dy, + }, + vertex2: { + x: edge.vertex2.x + dx, + y: edge.vertex2.y + dy, + }, + } + } + + function edgesIntersection(edgeA, edgeB) { + const den = + (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) - + (edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y) + + if (den === 0) { + return null // lines are parallel or coincident + } + + const ua = + ((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - + (edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / + den + + const ub = + ((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) - + (edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) / + den + + // Edges are not intersecting but the lines defined by them are + const isIntersectionOutside = ua < 0 || ub < 0 || ua > 1 || ub > 1 + + return { + x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x), + y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y), + isIntersectionOutside, + } + } + + function createMarginPolygon(polygon, lines, arcSegments = 0) { + const offsetEdges = [] + + polygon.edges.forEach((edge, i) => { + const offset = lines[i % lines.length].attributes.offset + const dx = edge.outwardNormal.x * offset + const dy = edge.outwardNormal.y * offset + offsetEdges.push(createOffsetEdge(edge, dx, dy)) + }) + + const vertices = [] + + offsetEdges.forEach((thisEdge, i) => { + const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length] + const vertex = edgesIntersection(prevEdge, thisEdge) + if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) { + vertices.push({ + x: vertex.x, + y: vertex.y, + }) + } + }) + + const marginPolygon = createRoofPolygon(vertices) + marginPolygon.offsetEdges = offsetEdges + return marginPolygon + } + + function createPaddingPolygon(polygon, lines, arcSegments = 0) { + const offsetEdges = [] + + polygon.edges.forEach((edge, i) => { + const offset = lines[i % lines.length].attributes.offset + const dx = edge.inwardNormal.x * offset + const dy = edge.inwardNormal.y * offset + offsetEdges.push(createOffsetEdge(edge, dx, dy)) + }) + + const vertices = [] + + offsetEdges.forEach((thisEdge, i) => { + const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length] + const vertex = edgesIntersection(prevEdge, thisEdge) + if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) { + vertices.push({ + x: vertex.x, + y: vertex.y, + }) + } + }) + + const paddingPolygon = createRoofPolygon(vertices) + paddingPolygon.offsetEdges = offsetEdges + return paddingPolygon + } + + const drawRoofPolygon = (wall) => { + // TODO [ljyoung] : offset 입력 처리 후 제거 해야함. + /*wall.lines.forEach((line, index) => { + if (index === wall.lines.length - 1 || index === 3) { + line.attributes = { + type: 'gable', + offset: 30, + } + } else { + line.attributes = { + type: 'hip', + offset: 50, + } + } + })*/ + + const polygon = createRoofPolygon(wall.points) + const originPolygon = new QPolygon(wall.points, { fontSize: 0 }) + let offsetPolygon + + let result = createMarginPolygon(polygon, wall.lines).vertices + const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point)) + + if (allPointsOutside) { + offsetPolygon = createMarginPolygon(polygon, wall.lines).vertices + } else { + offsetPolygon = createPaddingPolygon(polygon, wall.lines).vertices + } const roof = makePolygon( - offsetPoints.map((point) => { + offsetPolygon.map((point) => { return { x1: point.x, y1: point.y } }), ) roof.name = 'roofBase' - roof.setWall(polygon) + roof.setWall(wall) + + roof.lines.forEach((line, index) => { + line.attributes = { type: wall.lines[index].attributes.type } + }) + setRoof(roof) - setWall(polygon) + setWall(wall) - roof.drawHelpLine() - roof.divideLine() - } - - const drawRoofPolygon = (wall, offset = 50) => { - console.log(wall) - let points = wall.points, - expandedPoints = [] - - let minX = points[0].x, - minY = points[0].y, - maxX = points[0].x, - maxY = points[0].y - - points.forEach((point) => { - if (point.x < minX) minX = point.x - if (point.y < minY) minY = point.y - if (point.x > maxX) maxX = point.x - if (point.y > maxY) maxY = point.y - }) - - console.log(points) - points.forEach((point, index) => { - const prevIndex = index === 0 ? points.length - 1 : index - 1 - const nextIndex = index === points.length - 1 ? 0 : index + 1 - point.direction = getDirectionByPoint(point, points[nextIndex]) - point.length = Math.abs(point.x - points[nextIndex].x) === 0 ? Math.abs(point.y - points[nextIndex].y) : Math.abs(point.x - points[nextIndex].x) - // point.degree = Math.round(getDegreeBetweenTwoLines(points[prevIndex], point, points[nextIndex])) - }) - console.log('points : ', points) - - points.forEach((currentWall, index) => { - let prevWall = points[index === 0 ? points.length - 1 : index - 1] - let nextWall = points[index === points.length - 1 ? 0 : index + 1] - let isStartPointIn = minX < currentWall.x && currentWall.x < maxX && minY < currentWall.y && currentWall.y < maxY - // let isEndPointIn = minX < currentWall.x2 && currentWall.x2 < maxX && minY < currentWall.y2 && currentWall.y2 < maxY - - if (prevWall.direction !== nextWall.direction) { - if (currentWall.direction === 'top') { - console.log('prevWall degree : ', 45) - if (prevWall.direction === 'right') { - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - expandedPoints.push({ - x: currentWall.x + addLength, - y: currentWall.y + addLength, - }) - } - if (prevWall.direction === 'left') { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } - } - if (currentWall.direction === 'bottom') { - if (prevWall.direction === 'right') { - console.log('prevWall degree : ', 45) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log(currentWall.x, '-', offset, '+', addLength) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x + addLength, - y: currentWall.y - addLength, - }) - } - if (prevWall.direction === 'left') { - console.log('prevWall degree : ', 315) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log(currentWall.x, '-', offset, '+', addLength) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } - if (currentWall.direction === 'right') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 45) - - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 225) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } - } - } - if (currentWall.direction === 'left') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - console.log('addLength : ', addLength) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - - let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset) - - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } - } - } - } else { - console.log('else :::: ') - if (currentWall.direction === 'top') { - if (prevWall.direction === 'right') { - if (isStartPointIn) { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } - } - if (prevWall.direction === 'left') { - if (isStartPointIn) { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } else { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } - } - } - if (currentWall.direction === 'bottom') { - if (prevWall.direction === 'right') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - } - if (currentWall.direction === 'right') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - } - if (currentWall.direction === 'left') { - if (prevWall.direction === 'top') { - if (isStartPointIn) { - console.log('prevWall degree : ', 225) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 45) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y - offset, - }) - } - } - if (prevWall.direction === 'bottom') { - if (isStartPointIn) { - console.log('prevWall degree : ', 135) - expandedPoints.push({ - x: currentWall.x + offset, - y: currentWall.y + offset, - }) - } else { - console.log('prevWall degree : ', 315) - expandedPoints.push({ - x: currentWall.x - offset, - y: currentWall.y - offset, - }) - } - } - } - } - }) - - console.log('expandedPoints : ', expandedPoints) - - /*const roof = new fabric.Polygon(expandedPoints, { - fill: 'transparent', - stroke: 'red', - strokeWidth: 1, - selectable: true, - fontSize: fontSize, - name: 'QPolygon1', - }) - - roof.wall = wall - canvas?.add(roof)*/ - - // roof.drawHelpLine() + return roof } /** - * 구하려는 라인의 x1,y1좌표가 기준 - * @param line1 이전 라인 - * @param line2 현재 라인 - * @param line3 다음 라인 - * @param offset - * @returns {number} + * 라인 사이가 지붕골 인지 확인. + * @param polygon + * @param line1 + * @param line2 + * @returns {boolean} */ - const getLineOffsetPoint = (line1, line2, line3, offset) => { - //밑변 - let a = Math.abs(line1.x - line2.x) - //빗변 - let c = Math.sqrt(Math.abs(line1.x - line2.x) ** 2 + Math.abs(line1.y - line2.y) ** 2) + const checkValley = (polygon, line1, line2) => { + let points = [ + { x: line1.x1, y: line1.y1 }, + { x: line1.x2, y: line1.y2 }, + { x: line2.x1, y: line2.y1 }, + { x: line2.x2, y: line2.y2 }, + ] + points = Array.from(new Set(points.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point)) + const centroidX = points.reduce((acc, point) => acc + point.x, 0) / points.length + const centroidY = points.reduce((acc, point) => acc + point.y, 0) / points.length - console.log(a, c) - //밑변과 빗변사이의 각도 - let alphaDegree = getDegreeBetweenTwoLines(line1, line2, line3) - alphaDegree = alphaDegree <= 90 ? alphaDegree : 180 - alphaDegree - alphaDegree = 90 - alphaDegree - console.log('alphaDegree : ', alphaDegree) + let isValley = true + const pPoints = polygon.points + pPoints.forEach((point, index) => { + if (index < pPoints.length - 1) { + let j = index + 1 + let xi = pPoints[index].x + polygon.left, + yi = pPoints[index].y + polygon.top + let xj = pPoints[j].x + polygon.left, + yj = pPoints[j].y + polygon.top - // console.log('Math.tan(alphaDegree * (Math.PI / 180)) : ', Math.tan(alphaDegree * (Math.PI / 180))) - console.log(Math.round(offset * Math.tan(alphaDegree * (Math.PI / 180)))) - - const angle = getDegreeBetweenTwoLines(line1, line2, line3) - const side1 = line1.length - const side2 = line2.length - const side3 = Math.sqrt(side1 ** 2 + side2 ** 2 - 2 * side1 * side2 * Math.cos(angle * (Math.PI / 180))) - const beta = Math.round(Math.asin((side2 * Math.sin(angle * (Math.PI / 180))) / side3) * (180 / Math.PI) * 10) / 10 - const alpha = 180 - angle - beta - - console.log('angle : ', angle, 'alpha : ', alpha, 'beta : ', beta) - - const h = side2 * Math.sin(alpha * (Math.PI / 180)) - const h_new = h + offset - console.log('빗변까지 길이 : ', h, 'offset 적용된 빗변까지 길이 : ', h_new) - const newAdjacent = h_new / Math.sin(angle * (Math.PI / 180)) - console.log('offset 적용된 밑변 : ', newAdjacent) - - // offset = Math.sqrt(diffAdjacent ** 2 + diffAdjacent ** 2) / 2 - console.log('offset : ', offset) - return offset + let intersect = yi > centroidY !== yj > centroidY && centroidX < ((xj - xi) * (centroidY - yi)) / (yj - yi) + xi + if (intersect) isValley = !isValley + } + }) + return isValley } /** @@ -4941,6 +4852,12 @@ export function useMode() { ) } + const coordToTurfPolygon = (points) => { + const coordinates = points.map((point) => [point.x, point.y]) + coordinates.push(coordinates[0]) + return turf.polygon([coordinates]) + } + /** * trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인 * 확인 후 셀을 이동시킴 @@ -4948,59 +4865,191 @@ export function useMode() { const drawCellManualInTrestle = () => { const trestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle') //가대를 가져옴 + const dormerTrestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'dormerTrestle') //도머 객체 + if (trestlePolygons.length !== 0) { - let lastPointPosition = { x: 0, y: 0 } let fabricPolygon = null let inside = false let turfPolygon - let manualDrawCells = drewRoofCells // + let manualDrawCells = drewRoofCells // 앞에서 자동으로 했을때 추가됨 + let direction canvas.on('mouse:move', (e) => { //마우스 이벤트 삭제 후 재추가 const mousePoint = canvas.getPointer(e.e) - const turfPoint = turf.point([mousePoint.x, mousePoint.y]) for (let i = 0; i < trestlePolygons.length; i++) { turfPolygon = polygonToTurfPolygon(trestlePolygons[i]) - if (turf.booleanPointInPolygon(turfPoint, turfPolygon)) { - //turf에 보면 폴리곤안에 포인트가 있는지 함수가 있다 - const direction = trestlePolygons[i].direction //도형의 방향 - let width = direction === 'south' || direction === 'north' ? 172.2 : 113.4 - let height = direction === 'south' || direction === 'north' ? 113.4 : 172.2 - if (Math.abs(mousePoint.x - lastPointPosition.x) >= 5 || Math.abs(mousePoint.y - lastPointPosition.y) >= 5) { - let isDrawing = false + direction = trestlePolygons[i].direction //도형의 방향 + let width = direction === 'south' || direction === 'north' ? 172 : 113 + let height = direction === 'south' || direction === 'north' ? 113 : 172 - if (isDrawing) return - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tmpCell')) //움직일때 일단 지워가면서 움직임 + const points = [ + { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 }, + { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 }, + { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 }, + { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 }, + ] - const points = [ - { x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 }, - { x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 }, - { x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 }, - { x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 }, - ] + const turfPoints = coordToTurfPolygon(points) - fabricPolygon = new QPolygon(points, { - fill: '#BFFD9F', - stroke: 'black', - selectable: false, // 선택 가능하게 설정 - lockMovementX: true, // X 축 이동 잠금 - lockMovementY: true, // Y 축 이동 잠금 - lockRotation: true, // 회전 잠금 - lockScalingX: true, // X 축 크기 조정 잠금 - lockScalingY: true, // Y 축 크기 조정 잠금 - opacity: 0.8, - parentId: trestlePolygons[i].parentId, - name: 'tmpCell', + if (turf.booleanWithin(turfPoints, turfPolygon)) { + let isDrawing = false + + if (isDrawing) return + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tmpCell')) //움직일때 일단 지워가면서 움직임 + + fabricPolygon = new fabric.Rect({ + fill: 'white', + stroke: 'black', + strokeWidth: 1, + width: width, + height: height, + left: mousePoint.x - width / 2, + top: mousePoint.y - height / 2, + selectable: false, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + opacity: 0.8, + name: 'tmpCell', + parentId: trestlePolygons[i].parentId, + }) + + canvas?.add(fabricPolygon) //움직여가면서 추가됨 + + /** + * 스냅기능 + */ + let snapDistance = 10 + let cellSnapDistance = 20 + + const trestleLeft = trestlePolygons[i].left + const trestleTop = trestlePolygons[i].top + const trestleRight = trestleLeft + trestlePolygons[i].width * trestlePolygons[i].scaleX + const trestleBottom = trestleTop + trestlePolygons[i].height * trestlePolygons[i].scaleY + const bigCenterY = (trestleTop + trestleTop + trestlePolygons[i].height) / 2 + + // 작은 폴리곤의 경계 좌표 계산 + const smallLeft = fabricPolygon.left + const smallTop = fabricPolygon.top + const smallRight = smallLeft + fabricPolygon.width * fabricPolygon.scaleX + const smallBottom = smallTop + fabricPolygon.height * fabricPolygon.scaleY + const smallCenterX = smallLeft + (fabricPolygon.width * fabricPolygon.scaleX) / 2 + const smallCenterY = smallTop + (fabricPolygon.height * fabricPolygon.scaleX) / 2 + + /** + * 미리 깔아놓은 셀이 있을때 셀에 흡착됨 + */ + if (manualDrawCells) { + manualDrawCells.forEach((cell) => { + const holdCellLeft = cell.left + const holdCellTop = cell.top + const holdCellRight = holdCellLeft + cell.width * cell.scaleX + const holdCellBottom = holdCellTop + cell.height * cell.scaleY + const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2 + const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2 + + //설치된 셀에 좌측에 스냅 + if (Math.abs(smallRight - holdCellLeft) < snapDistance) { + fabricPolygon.left = holdCellLeft - width - 0.5 + } + + //설치된 셀에 우측에 스냅 + if (Math.abs(smallLeft - holdCellRight) < snapDistance) { + fabricPolygon.left = holdCellRight + 0.5 + } + + //설치된 셀에 위쪽에 스냅 + if (Math.abs(smallBottom - holdCellTop) < snapDistance) { + fabricPolygon.top = holdCellTop - height - 0.5 + } + + //설치된 셀에 밑쪽에 스냅 + if (Math.abs(smallTop - holdCellBottom) < snapDistance) { + fabricPolygon.top = holdCellBottom + 0.5 + } + //가운데 -> 가운데 + if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) { + fabricPolygon.left = holdCellCenterX - width / 2 + } + //왼쪽 -> 가운데 + if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) { + fabricPolygon.left = holdCellCenterX + } + // 오른쪽 -> 가운데 + if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) { + fabricPolygon.left = holdCellCenterX - width + } + //세로 가운데 -> 가운데 + if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) { + fabricPolygon.top = holdCellCenterY - height / 2 + } + //위쪽 -> 가운데 + if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) { + fabricPolygon.top = holdCellCenterY + } + //아랫쪽 -> 가운데 + if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) { + fabricPolygon.top = holdCellCenterY - height + } }) - - canvas?.add(fabricPolygon) //움직여가면서 추가됨 - lastPointPosition = { x: mousePoint.x, y: mousePoint.y } } + // 위쪽 변에 스냅 + if (Math.abs(smallTop - trestleTop) < snapDistance) { + fabricPolygon.top = trestleTop + } + + // 아래쪽 변에 스냅 + if (Math.abs(smallTop + fabricPolygon.height * fabricPolygon.scaleY - (trestleTop + trestlePolygons[i].height)) < snapDistance) { + fabricPolygon.top = trestleTop + trestlePolygons[i].height - fabricPolygon.height * fabricPolygon.scaleY + } + + // 왼쪽변에 스냅 + if (Math.abs(smallLeft - trestleLeft) < snapDistance) { + fabricPolygon.left = trestleLeft + } + //오른쪽 변에 스냅 + if (Math.abs(smallRight - trestleRight) < snapDistance) { + fabricPolygon.left = trestleRight - fabricPolygon.width * fabricPolygon.scaleX + } + + if (direction === 'south' || direction === 'north') { + // 모듈왼쪽이 세로중앙선에 붙게 스냅 + if (Math.abs(smallLeft - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2 + } + + // 모듈이 가운데가 세로중앙선에 붙게 스냅 + if (Math.abs(smallCenterX - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2 - (fabricPolygon.width * fabricPolygon.scaleX) / 2 + } + + // 모듈오른쪽이 세로중앙선에 붙게 스냅 + if (Math.abs(smallRight - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) { + fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2 - fabricPolygon.width * fabricPolygon.scaleX + } + } else { + // 모듈이 가로중앙선에 스냅 + if (Math.abs(smallTop + fabricPolygon.height / 2 - bigCenterY) < snapDistance) { + fabricPolygon.top = bigCenterY - fabricPolygon.height / 2 + } + + if (Math.abs(smallTop - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) { + fabricPolygon.top = trestleTop + trestlePolygons[i].height / 2 + } + // 모듈 밑면이 가로중앙선에 스냅 + if (Math.abs(smallBottom - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) { + fabricPolygon.top = trestleTop + trestlePolygons[i].height / 2 - fabricPolygon.height * fabricPolygon.scaleY + } + } + + fabricPolygon.setCoords() canvas?.renderAll() inside = true - break } else { inside = false } @@ -5013,17 +5062,47 @@ export function useMode() { }) canvas?.on('mouse:up', (e) => { + let isIntersection = true if (!inside) return if (fabricPolygon) { - const turfCellPolygon = polygonToTurfPolygon(fabricPolygon) + const rectPoints = [ + { x: fabricPolygon.left + 0.5, y: fabricPolygon.top + 0.5 }, + { x: fabricPolygon.left + 0.5 + fabricPolygon.width * fabricPolygon.scaleX, y: fabricPolygon.top + 0.5 }, + { + x: fabricPolygon.left + fabricPolygon.width * fabricPolygon.scaleX + 0.5, + y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5, + }, + { x: fabricPolygon.left + 0.5, y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5 }, + ] - if (turf.booleanWithin(turfCellPolygon, turfPolygon)) { + fabricPolygon.set({ points: rectPoints }) + const tempTurfModule = polygonToTurfPolygon(fabricPolygon) + + if (dormerTrestlePolygons) { + dormerTrestlePolygons.forEach((dormerTrestle) => { + const dormerTurfPolygon = polygonToTurfPolygon(dormerTrestle) + + const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) + + if (intersection) { + alert('도머위에 모듈을 올릴 수 없습니다.') + isIntersection = false + } + }) + } + + if (!isIntersection) return + + fabricPolygon.setCoords() //좌표 재정렬 + + if (turf.booleanWithin(tempTurfModule, turfPolygon)) { //마우스 클릭시 set으로 해당 위치에 셀을 넣음 - const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(turfCellPolygon, polygonToTurfPolygon(cell))) + + const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(cell))) if (!isOverlap) { //안겹치면 넣는다 - fabricPolygon.set({ name: 'cell' }) fabricPolygon.setCoords() + fabricPolygon.set({ name: 'cell', fill: '#BFFD9F' }) manualDrawCells.push(fabricPolygon) } else { alert('셀끼리 겹치면 안되죠?') @@ -5104,29 +5183,6 @@ export function useMode() { const cols = Math.floor((bbox[2] - bbox[0]) / width) const rows = Math.floor((bbox[3] - bbox[1]) / height) - // console.log('bbox', bbox) - - const boxes = [] - const installedCellsArray = [] - - for (let x = bbox[0]; x < bbox[2]; x += width) { - for (let y = bbox[1]; y < bbox[3]; y += height) { - const box = turf.polygon([ - [ - [x, y], - [x + width, y], - [x + width, y + height], - [x, y + height], - [x, y], - ], - ]) - - if (turf.booleanWithin(box, turfTrestlePolygon)) { - boxes.push(box) - } - } - } - for (let col = 0; col <= cols; col++) { for (let row = 0; row <= rows; row++) { let x = 0, @@ -5176,20 +5232,6 @@ export function useMode() { const squarePolygon = turf.polygon([square]) - // console.log('turfTrestlePolygon', turfTrestlePolygon) - // console.log('squarePolygon', squarePolygon) - - const areaSize = turf.area(turfTrestlePolygon) - - // console.log('areaSize', areaSize) - const objSize = turf.area(squarePolygon) - - // console.log('objSize', objSize) - - const maxObject = Math.floor(areaSize / objSize) - - // console.log('maxObjectSize', maxObject) - const disjointFromTrestle = turf.booleanContains(turfTrestlePolygon, squarePolygon) || turf.booleanWithin(squarePolygon, turfTrestlePolygon) if (disjointFromTrestle) { @@ -5230,6 +5272,8 @@ export function useMode() { lockScalingY: true, // Y 축 크기 조정 잠금 opacity: 0.8, parentId: trestle.parentId, + lineCol: col, + lineRow: row, }) canvas?.add(fabricPolygon) drawCellsArray.push(fabricPolygon) diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js index 55854cb7..fedbb3be 100644 --- a/src/hooks/usePlan.js +++ b/src/hooks/usePlan.js @@ -2,18 +2,15 @@ import { useRecoilState } from 'recoil' import { canvasState, currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom' import { useAxios } from '@/hooks/useAxios' import { useMessage } from '@/hooks/useMessage' -import { toastUp } from '@/hooks/useToast' -import { sessionStore } from '@/store/commonAtom' -import { useState } from 'react' +import { useSwal } from '@/hooks/useSwal' export function usePlan() { const [canvas, setCanvas] = useRecoilState(canvasState) const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState) const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState) + const { swalFire } = useSwal() const { getMessage } = useMessage() - const { get, promisePost, promisePut } = useAxios() - const [sessionState, setSessionState] = useRecoilState(sessionStore) - const [userId, setUserId] = useState(sessionState.userId) + const { get, promisePost, promisePut, promiseDel } = useAxios() /** * 마우스 포인터의 가이드라인을 제거합니다. @@ -103,14 +100,14 @@ export function usePlan() { await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData }) .then((res) => { - toastUp({ message: getMessage('common.message.save'), type: 'success' }) // 성공 시 메세지 없음 + swalFire({ text: getMessage('common.message.save') }) console.log('[PUT] canvas-statuses res :::::::: %o', res) setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)), ) }) .catch((error) => { - toastUp({ message: error.message, type: 'error' }) + swalFire({ text: error.message, icon: 'error' }) console.error('[PUT] canvas-statuses error :::::::: %o', error) }) } else { @@ -124,11 +121,11 @@ export function usePlan() { await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData }) .then((res) => { - toastUp({ message: getMessage('common.message.save'), type: 'success' }) // 성공 시 메세지 없음 + swalFire({ text: getMessage('common.message.save') }) console.log('[POST] canvas-statuses response :::::::: %o', res) }) .catch((error) => { - toastUp({ message: error.message, type: 'error' }) + swalFire({ text: error.message, icon: 'error' }) console.error('[POST] canvas-statuses res error :::::::: %o', error) }) } @@ -137,7 +134,7 @@ export function usePlan() { /** * objectNo에 해당하는 canvas 목록을 조회하는 함수 */ - const getCanvasByObjectNo = async (objectNo) => { + const getCanvasByObjectNo = async (userId, objectNo) => { return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) => res.map((item) => ({ id: item.id, @@ -149,11 +146,26 @@ export function usePlan() { ) } + /** + * id에 해당하는 canvas 데이터를 삭제하는 함수 + */ + const delCanvasById = (id) => { + return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` }) + } + + /** + * objectNo에 해당하는 canvas 데이터들을 삭제하는 함수 + */ + const delCanvasByObjectNo = (objectNo) => { + return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` }) + } + return { canvas, removeMouseLines, saveCanvas, addCanvas, getCanvasByObjectNo, + delCanvasById, } } diff --git a/src/hooks/usePolygon.js b/src/hooks/usePolygon.js index b364bf86..13b12fe1 100644 --- a/src/hooks/usePolygon.js +++ b/src/hooks/usePolygon.js @@ -2,6 +2,7 @@ import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { fabric } from 'fabric' import { getDirectionByPoint } from '@/util/canvas-util' +import { QPolygon } from '@/components/fabric/QPolygon' export const usePolygon = () => { const canvas = useRecoilValue(canvasState) @@ -9,19 +10,22 @@ export const usePolygon = () => { const fontFamily = useRecoilValue(fontFamilyState) const addPolygon = (points, options) => { - const polygon = new fabric.Polygon(points, { + const polygon = new QPolygon(points, { ...options, - selectable: options.selectable ?? false, + fontSize: fontSize, + fontFamily: fontFamily, + selectable: true, }) canvas?.add(polygon) - addLengthText(polygon) + + return polygon } const addPolygonByLines = (lines, options) => { const points = createPolygonPointsFromOuterLines(lines) - addPolygon(points, { + return addPolygon(points, { ...options, }) } @@ -39,10 +43,11 @@ export const usePolygon = () => { const degree = (Math.atan2(dy, dx) * 180) / Math.PI // Create new text object if it doesn't exist - const text = new fabric.IText(length.toString(), { + const text = new fabric.Text(length.toString(), { left: midPoint.x, top: midPoint.y, fontSize: fontSize, + fontFamily: fontFamily, parentId: polygon.id, minX: Math.min(start.x, end.x), maxX: Math.max(start.x, end.x), diff --git a/src/locales/ja.json b/src/locales/ja.json index 8e62d630..71c6fb8d 100644 --- a/src/locales/ja.json +++ b/src/locales/ja.json @@ -15,6 +15,27 @@ "header.stem": "ステム", "plan.menu.plan.drawing": "도면작성", "plan.menu.placement.surface.initial.setting": "配置面 初期設定", + "modal.placement.initial.setting.plan.drawing.size.stuff": "寸法入力による物品作成", + "modal.placement.initial.setting.plan.": "図面の作成方法", + "modal.placement.initial.setting.size": "寸法入力方法", + "modal.placement.initial.setting.size.info": "寸法入力方法案内", + "modal.placement.initial.setting.size.roof": "複視図入力", + "modal.placement.initial.setting.size.roof.info": "平面の外壁線と立面の屋根勾配に基づいて作画する場合に選択", + "modal.placement.initial.setting.size.actual": "実測値入力", + "modal.placement.initial.setting.size.actual.info": "現地屋根の外周寸法を入力して作画する場合選択", + "modal.placement.initial.setting.size.none.pitch": "陸上屋根", + "modal.placement.initial.setting.size.none.pitch.info": "傾斜のない平面形状の屋根にパネルを配置する場合に選択", + "modal.placement.initial.setting.roof.angle.setting": "屋根角度設定", + "modal.placement.initial.setting.roof.pitch": "傾斜", + "modal.placement.initial.setting.roof.angle": "角度", + "modal.placement.initial.setting.roof.material": "屋根材の選択(単位:mm)", + "modal.placement.initial.setting.roof.material.info": "対応可能な屋根材や足場は限定されますので、必ず事前マニュアルをご確認ください。", + "modal.placement.initial.setting.rafter": "垂木の間隔", + "modal.roof.shape.setting": "屋根形状の設定", + "modal.roof.shape.setting.ridge": "龍丸屋根", + "modal.roof.shape.setting.patten.a": "Aパターン", + "modal.roof.shape.setting.patten.b": "Bパターン", + "modal.roof.shape.setting.side": "別に設定", "plan.menu.roof.cover": "지붕덮개", "plan.menu.roof.cover.outline.drawing": "外壁線を描", "plan.menu.roof.cover.roof.shape.setting": "屋根形状設定", @@ -32,6 +53,8 @@ "modal.cover.outline.arrow": "方向 (矢印)", "modal.cover.outline.fix": "外壁線確定", "modal.cover.outline.rollback": "一変戦に戻る", + "modal.cover.outline.finish": "設定完了", + "common.setting.finish": "設定完了", "modal.cover.outline.remove": "外壁の削除", "modal.cover.outline.select.move": "外壁の選択、移動", "plan.menu.roof.cover.roof.setting": "屋根形状設定", @@ -83,6 +106,7 @@ "modal.grid.copy.info": "間隔を設定し、コピー方向を選択します", "modal.grid.copy.length": "長さ", "modal.grid.copy.save": "保存", + "modal.common.save": "保存", "modal.canvas.setting.font.plan.edit": "フォントとサイズの変更", "modal.canvas.setting.font.plan.edit.word": "文字フォントの変更", "modal.canvas.setting.font.plan.edit.flow": "フロー方向フォントの変更", @@ -115,6 +139,11 @@ "modal.canvas.setting.first.option.border": "ボーダーのみ", "modal.canvas.setting.first.option.line": "ラインハッチ", "modal.canvas.setting.first.option.all": "All painted", + "modal.canvas.setting.wallline.properties.setting": "外壁のプロパティの設定", + "modal.canvas.setting.wallline.properties.setting.info": "※属性を変更する外壁線を選択し、軒で設定またはケラバで設定 ボタンをクリックして設定値を適用します。", + "modal.canvas.setting.wallline.properties.setting.eaves": "軒で設定", + "modal.canvas.setting.wallline.properties.setting.edge": "ケラバに設定", + "setting": "設定", "common.message.no.data": "No data", "common.message.no.dataDown": "ダウンロードするデータがありません", "common.message.noData": "表示するデータがありません", @@ -203,6 +232,10 @@ "common.message.password.init.success": "パスワード [{0}] に初期化されました。", "common.message.no.edit.save": "この文書は変更できません。", "common.require": "필수", + "commons.west": "立つ", + "commons.east": "ドン", + "commons.south": "M", + "commons.north": "北", "site.name": "Q.CAST III", "site.sub_name": "태양광 발전 시스템 도면관리 사이트", "login": "로그인", @@ -261,5 +294,22 @@ "stuff.gridHeader.dispCompanyName": "견적처", "stuff.gridHeader.receiveUser": "담당자", "stuff.gridHeader.specDate": "사양확인", - "stuff.gridHeader.createDatetime": "등록일" + "stuff.gridHeader.createDatetime": "등록일", + "slope": "傾斜", + "eaves.offset": "軒の", + "gable.offset": "ケラバ出幅", + "size": "寸", + "eaves": "軒", + "gable": "ケラバ", + "wall": "壁", + "hipandgable": "八作屋根", + "jerkinhead": "半折", + "shed": "片側の流れ", + "apply": "適用", + "has.sleeve": "袖あり", + "has.not.sleeve": "袖なし", + "jerkinhead.width": "半折先幅", + "jerkinhead.slope": "半折先傾斜", + "shed.width": "片流幅", + "windage.width": "漂流の出幅" } diff --git a/src/locales/ko.json b/src/locales/ko.json index 9bf624d6..572c1eef 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -15,6 +15,27 @@ "header.stem": "Stem", "plan.menu.plan.drawing": "도면작성", "plan.menu.placement.surface.initial.setting": "배치면 초기 설정", + "modal.placement.initial.setting.plan.drawing": "도면 작성방법", + "modal.placement.initial.setting.plan.drawing.size.stuff": "치수 입력에 의한 물건 작성", + "modal.placement.initial.setting.size": "치수 입력방법", + "modal.placement.initial.setting.size.info": "치수 입력방법 안내", + "modal.placement.initial.setting.size.roof": "복시도 입력", + "modal.placement.initial.setting.size.roof.info": "평면의 외벽선과 입면의 지붕 구배를 바탕으로 작화할 경우 선택", + "modal.placement.initial.setting.size.actual": "실측값 입력", + "modal.placement.initial.setting.size.actual.info": "현지 지붕의 외주 치수를 입력하여 작화하는 경우 선택", + "modal.placement.initial.setting.size.none.pitch": "육지붕", + "modal.placement.initial.setting.size.none.pitch.info": "경사가 없는 평면 형태의 지붕에 패널을 배치할 경우 선택", + "modal.placement.initial.setting.roof.angle.setting": "지붕각도 설정", + "modal.placement.initial.setting.roof.pitch": "경사", + "modal.placement.initial.setting.roof.angle": "각도", + "modal.placement.initial.setting.roof.material": "지붕재 선택(단위: mm)", + "modal.placement.initial.setting.roof.material.info": "대응 가능한 지붕재 및 발판은 한정되므로 반드시 사전 매뉴얼을 확인하십시오.", + "modal.placement.initial.setting.rafter": "서까래", + "modal.roof.shape.setting": "지붕형상 설정", + "modal.roof.shape.setting.ridge": "용마루", + "modal.roof.shape.setting.patten.a": "A 패턴", + "modal.roof.shape.setting.patten.b": "A 패턴", + "modal.roof.shape.setting.side": "변별로 설정", "plan.menu.roof.cover": "지붕덮개", "plan.menu.roof.cover.outline.drawing": "외벽선 그리기", "plan.menu.roof.cover.roof.shape.setting": "지붕형상 설정", @@ -36,6 +57,9 @@ "modal.cover.outline.arrow": "방향(화살표)", "modal.cover.outline.fix": "외벽선 확정", "modal.cover.outline.rollback": "일변전으로 돌아가기", + "modal.cover.outline.finish": "설정완료", + "common.setting.finish": "설정완료", + "common.setting.rollback": "일변전으로 돌아가기", "modal.cover.outline.remove": "외벽 제거", "modal.cover.outline.select.move": "외벽 선택, 이동", "plan.menu.placement.surface": "배치면", @@ -84,6 +108,7 @@ "modal.grid.copy.info": "간격을 설정하고 복사 방향을 선택하십시오", "modal.grid.copy.length": "길이", "modal.grid.copy.save": "저장", + "modal.common.save": "저장", "modal.canvas.setting.font.plan.edit": "글꼴 및 크기 변경", "modal.canvas.setting.font.plan.edit.word": "문자 글꼴 변경", "modal.canvas.setting.font.plan.edit.flow": "흐름 방향 글꼴 변경", @@ -116,6 +141,11 @@ "modal.canvas.setting.first.option.border": "테두리만", "modal.canvas.setting.first.option.line": "라인해치", "modal.canvas.setting.first.option.all": "All painted", + "modal.canvas.setting.wallline.properties.setting": "외벽선 속성 설정", + "modal.canvas.setting.wallline.properties.setting.info": "※ 속성을 변경할 외벽선을 선택하고, 처마로 설정 또는 케라바로 설정\n 버튼을 클릭하여 설정값을 적용하십시오.\n", + "modal.canvas.setting.wallline.properties.setting.eaves": "처마로 설정", + "modal.canvas.setting.wallline.properties.setting.edge": "케라바로 설정", + "setting": "설정", "common.message.no.data": "No data", "common.message.no.dataDown": "No data to download", "common.message.noData": "No data to display", @@ -204,6 +234,10 @@ "common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.", "common.message.no.edit.save": "This document cannot be changed.", "common.require": "필수", + "commons.west": "서", + "commons.east": "동", + "commons.south": "남", + "commons.north": "북", "site.name": "Q.CAST III", "site.sub_name": "태양광 발전 시스템 도면관리 사이트", "login": "로그인", @@ -262,5 +296,22 @@ "stuff.gridHeader.dispCompanyName": "견적처", "stuff.gridHeader.receiveUser": "담당자", "stuff.gridHeader.specDate": "사양확인", - "stuff.gridHeader.createDatetime": "등록일" + "stuff.gridHeader.createDatetime": "등록일", + "slope": "경사", + "eaves.offset": "처마 출폭", + "gable.offset": "케라바 출폭", + "size": "치수", + "eaves": "처마", + "gable": "케라바", + "wall": "벽", + "hipandgable": "팔작지붕", + "jerkinhead": "반절처", + "shed": "한쪽흐름", + "apply": "적용", + "has.sleeve": "소매 있음", + "has.not.sleeve": "소매 없음", + "jerkinhead.width": "반절처 폭", + "jerkinhead.slope": "반절처 경사", + "shed.width": "한쪽흐름 폭", + "windage.width": "편류의 출폭" } diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index aa400a46..9fb440c6 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -1,5 +1,6 @@ import { atom, selector } from 'recoil' import { MENU } from '@/common/common' +import { outerLineFixState, outerLinePointsState } from '@/store/outerLineAtom' export const canvasState = atom({ key: 'canvasState', @@ -267,3 +268,18 @@ export const tempGridModeState = atom({ key: 'tempGridModeState', default: false, }) + +export const textModeState = atom({ + key: 'textModeState', + default: false, +}) + +export const canGridOptionSeletor = selector({ + key: 'canGridOptionSeletor', + get: ({ get }) => { + const points = get(outerLinePointsState) + const currentMenu = get(currentMenuState) + const outerLineFix = get(outerLineFixState) + return points.length === 0 || outerLineFix + }, +}) diff --git a/src/store/gridAtom.js b/src/store/gridAtom.js index 9b21a9f5..a4b4b15b 100644 --- a/src/store/gridAtom.js +++ b/src/store/gridAtom.js @@ -2,5 +2,5 @@ import { atom } from 'recoil' export const gridColorState = atom({ key: 'gridColorState', - default: '#000000', + default: '#FF0000', }) diff --git a/src/store/outerLineAtom.js b/src/store/outerLineAtom.js index 2fcc5f9d..361bc526 100644 --- a/src/store/outerLineAtom.js +++ b/src/store/outerLineAtom.js @@ -63,3 +63,8 @@ export const outerLinePointsState = atom({ key: 'outerLinePointsState', default: [], }) + +export const outerLineFixState = atom({ + key: 'outerLineFixState', + default: false, +}) diff --git a/src/store/settingAtom.js b/src/store/settingAtom.js index 7c637c2f..80fa9c4c 100644 --- a/src/store/settingAtom.js +++ b/src/store/settingAtom.js @@ -1,4 +1,4 @@ -import { atom } from 'recoil' +import { atom, selector } from 'recoil' export const settingModalFirstOptionsState = atom({ key: 'settingModalFirstOptions', diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss index 6309f50e..30e19173 100644 --- a/src/styles/_modal.scss +++ b/src/styles/_modal.scss @@ -4,466 +4,941 @@ $pop-bold-weight: 500; $pop-normal-size: 12px; $alert-color: #101010; -@keyframes mountpop{ - from{opacity: 0; scale: 0.95;} - to{opacity: 1; scale: 1;} -} -@keyframes unmountpop{ - from{opacity: 1; scale: 1;} - to{opacity: 0; scale: 0.95;} +@keyframes mountpop { + from { + opacity: 0; + scale: 0.95; + } + to { + opacity: 1; + scale: 1; + } } -.modal-pop-wrap{ - position: fixed; - top: 200px; - right: 100px; - width: 100%; - min-width: 300px; - height: -webkit-fit-content; - height: -moz-fit-content; - height: fit-content; - border: 1px solid #000; - border-radius: 4px; - background-color: #272727; - z-index: 9999999; - overflow: hidden; - &.r{ - width: 400px; - } - &.sm{ - width: 580px; - } - &.ssm{ - width: 380px; - } - &.xm{ - width: 300px; - } - &.l{ - width: 800px; - } - &.mount{ - animation: mountpop .17s ease-in-out forwards; - } - &.unmount{ - animation: unmountpop .17s ease-in-out forwards; - } - &.alert{ - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: transparent; - border: none; - .modal-head{ - background-color: transparent; - padding: 0 0 8px; - .modal-close{ - width: 20px; - height: 20px; - background: url(../../public/static/images/canvas/alert_close.svg)no-repeat center; - } - } - .modal-body{ - background-color: #fff; - padding: 22px; - border-radius: 4px; - border: 1px solid #101010; - color: $alert-color; - .alert-title{ - font-size: 13px; - font-weight: 700; - color: $alert-color; - margin-bottom: 15px; - } - } - } +@keyframes unmountpop { + from { + opacity: 1; + scale: 1; + } + to { + opacity: 0; + scale: 0.95; + } } -.modal-head{ - display: flex; - align-items: center; - padding: 10px 24px; - background-color: #000; - h1.title{ + +.modal-pop-wrap { + position: fixed; + width: 100%; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + border: 1px solid #000; + border-radius: 4px; + background-color: #272727; + z-index: 9999999; + + &.xxxm { + width: 230px; + } + + &.xxm { + width: 270px; + } + + &.xm { + width: 300px; + } + + &.ssm { + width: 380px; + } + + &.sm { + width: 580px; + } + + &.r { + width: 400px; + } + + &.lr { + width: 440px; + } + + &.ml { + width: 530px; + } + + &.l-2 { + width: 640px; + } + + &.l { + width: 800px; + } + + &.mount { + animation: mountpop .17s ease-in-out forwards; + } + + &.unmount { + animation: unmountpop .17s ease-in-out forwards; + } + + &.alert { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background-color: transparent; + border: none; + + .modal-head { + background-color: transparent; + padding: 0 0 8px; + + .modal-close { + width: 20px; + height: 20px; + background: url(../../public/static/images/canvas/alert_close.svg) no-repeat center; + } + } + + .modal-body { + background-color: #fff; + padding: 22px; + border-radius: 4px; + border: 1px solid #101010; + color: $alert-color; + + .alert-title { font-size: 13px; - color: $pop-color; font-weight: 700; - } - .modal-close{ - margin-left: auto; - color: $pop-color; - text-indent: -999999999px; - width: 10px; - height: 10px; - background: url(../../public/static/images/canvas/modal_close.svg)no-repeat center; - } -} -.modal-body{ - padding: 24px; - .modal-btn-wrap{ - display: flex; - align-items: center; - gap: 5px; - button{ - flex: 1; - } - } - .modal-check-btn-wrap{ - margin-top: 15px; - .check-wrap-title{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: 600; - &.light{ - font-weight: $pop-normal-weight; - } - } - .flex-check-box{ - display: flex; - flex-wrap: wrap; - gap: 10px; - margin-top: 15px; - &.for2{ - justify-content: flex-end; - button{ - width: calc(50% - 5px); - } - &.btn{ - gap: 5px; - button{ - width: calc(50% - 2.5px); - } - } - } - &.for-line{ - button{ - flex: 1; - } - } - } - } - .outer-line-wrap{ - border-top: 1px solid #3C3C3C; - margin-top: 10px; - padding-top: 15px; - margin-bottom: 15px; - > div{ - margin-bottom: 15px; - &:last-child{ - margin-bottom: 0; - } - } - } - .modal-guide{ - display: block; - font-size: $pop-normal-size; color: $alert-color; - font-weight: $pop-normal-weight; + margin-bottom: 15px; + } } + } } -.adsorption-point{ +.modal-head { + display: flex; + align-items: center; + padding: 10px 24px; + background-color: #000; + // overflow: hidden; + h1.title { + font-size: 13px; + color: $pop-color; + font-weight: 700; + } + + .modal-close { + margin-left: auto; + color: transparent; + font-size: 0; + width: 10px; + height: 10px; + background: url(../../public/static/images/canvas/modal_close.svg) no-repeat center; + } +} + +.modal-body { + padding: 24px; + + .modal-btn-wrap { display: flex; align-items: center; - background-color: #3A3A3A; - border-radius: 3px; - padding-left: 11px; - overflow: hidden; - transition: all 0.17s ease-in-out; - span{ - font-size: $pop-normal-size; - color: #898989; + gap: 5px; + + button { + flex: 1; } - i{ - display: flex; - align-items: center; - padding: 0 7px; - margin-left: auto; - height: 100%; - font-size: 13px; - color: #898989; + + &.sub { + button { + flex: 1 1 auto; + padding: 0; + } + + margin-bottom: 14px; } - &.act{ - i{ - color: $pop-color; - background-color: #1083E3; + } + + .modal-check-btn-wrap { + margin-top: 15px; + + .check-wrap-title { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: 600; + + &.light { + font-weight: $pop-normal-weight; + } + } + + .flex-check-box { + display: flex; + flex-wrap: wrap; + gap: 10px; + margin-top: 15px; + + &.for2 { + justify-content: flex-end; + + button { + width: calc(50% - 5px); } + + &.btn { + gap: 5px; + + button { + width: calc(50% - 2.5px); + } + } + } + + &.for-line { + button { + flex: 1; + } + } } + } + + .outer-line-wrap { + border-top: 1px solid #3C3C3C; + margin-top: 10px; + padding-top: 15px; + margin-bottom: 15px; + + > div { + margin-bottom: 15px; + + &:last-child { + margin-bottom: 0; + } + } + } + + .modal-guide { + display: block; + font-size: $pop-normal-size; + color: $alert-color; + font-weight: $pop-normal-weight; + } +} + +.adsorption-point { + display: flex; + align-items: center; + background-color: #3A3A3A; + border-radius: 3px; + padding-left: 11px; + overflow: hidden; + transition: all 0.17s ease-in-out; + + span { + font-size: $pop-normal-size; + color: #898989; + } + + i { + display: flex; + align-items: center; + padding: 0 7px; + margin-left: auto; + height: 100%; + font-size: 13px; + color: #898989; + } + + &.act { + i { + color: $pop-color; + background-color: #1083E3; + } + } } // grid-option -.grid-check-form{ +.grid-check-form { + display: flex; + align-items: center; + gap: 15px; + padding-bottom: 15px; +} + +.grid-option-wrap { + .grid-option-box { display: flex; align-items: center; - gap: 15px; - padding-bottom: 15px; - border-bottom: 1px solid #3C3C3C; -} -.grid-option-wrap{ - padding: 15px 0; - border-bottom: 1px solid #3C3C3C; - .grid-option-box{ - display: flex; - align-items: center; - background-color: #3D3D3D; - border-radius: 2px; - padding: 10px; - gap: 20px; - margin-bottom: 5px; - .grid-input-form{ - display: flex; - align-items: center; - span{ - flex: none; - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - } - .input-grid{ - width: 54px; - input{ - width: 100%; - } - } - } - &:last-child{ - margin-bottom: 0; + background-color: transparent; + border: 1px solid #3D3D3D; + border-radius: 2px; + padding: 15px 10px; + gap: 20px; + margin-bottom: 10px; + + .grid-input-form { + display: flex; + align-items: center; + + span { + flex: none; + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + } + + .input-grid { + width: 54px; + + input { + width: 100%; } + } } + + &:last-child { + margin-bottom: 0; + } + } } -.grid-select{ - flex: 1; - .sort-select{ - width: 100%; - background-color: #313131; + +.grid-select { + flex: 1; + + .sort-select { + width: 100%; + background-color: #313131; + min-width: auto; + font-size: 12px; + border: none; + + p { + font-size: 12px; } + + > ul { + border: none; + } + } + + &.right { + p { + text-align: right; + } + + ul { + li { + justify-content: flex-end; + } + } + } } -.grid-btn-wrap{ - padding-top: 15px; - text-align: right; - button{ - padding: 0 20px; - } + +.grid-btn-wrap { + padding-top: 15px; + text-align: right; + + button { + padding: 0 10px; + } } // grid copy -.grid-option-tit{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-normal-weight; - padding-bottom: 15px; - border-bottom: 1px solid #3C3C3C; +.grid-option-tit { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + padding-bottom: 15px; + } -.grid-direction{ - display: flex; - align-items: center; - gap: 5px; - flex: 1; + +.grid-direction { + display: flex; + align-items: center; + gap: 5px; + flex: 1; } -.direction{ - width: 22px; - height: 22px; - background-color: #757575; - background-image: url(../../public/static/images/canvas/grid_option_arr.svg); - background-repeat: no-repeat; - background-position: center; - background-size: 16px 15px; - border-radius: 50%; - transition: all .15s ease-in-out; - opacity: 0.6; - &.down{transform: rotate(180deg);} - &.left{transform: rotate(-90deg);} - &.right{transform: rotate(90deg);} - &:hover, - &.act{ - opacity: 1; - } + +.direction { + width: 22px; + height: 22px; + background-color: #757575; + background-image: url(../../public/static/images/canvas/grid_option_arr.svg); + background-repeat: no-repeat; + background-position: center; + background-size: 16px 15px; + border-radius: 50%; + transition: all .15s ease-in-out; + opacity: 0.6; + + &.down { + transform: rotate(180deg); + } + + &.left { + transform: rotate(-90deg); + } + + &.right { + transform: rotate(90deg); + } + + &:hover, + &.act { + opacity: 1; + } } // grid-move -.move-form{ - p{ - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - } +.move-form { + p { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + } } -.input-move-wrap{ - display: flex; - align-items: center; - gap: 5px; - span{ - color: $pop-color; - font-size: $pop-normal-size; - } - .input-move{ - width: 130px; - input{ - width: 100%; - } + +.input-move-wrap { + display: flex; + align-items: center; + gap: 5px; + + span { + color: $pop-color; + font-size: $pop-normal-size; + } + + .input-move { + width: 130px; + + input { + width: 100%; } + } } -.direction-move-wrap{ - flex: none; - display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; + +.direction-move-wrap { + flex: none; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 10px; } // 배치면 초기 설정 -.placement-table{ - table{ - table-layout: fixed; - tr{ - th{ - display: flex; - align-items: center; - font-size: $pop-normal-size; - color: $pop-color; - font-weight: $pop-bold-weight; - padding: 18px 0; - border-bottom: 1px solid #424242; - } - td{ - font-size: $pop-normal-size; - color: $pop-color; - border-bottom: 1px solid #424242; - padding-left: 20px; - } - &:first-child{ - td, - th{ - padding-top: 0; - } - } - } - } - .tooltip{ - position: relative; - display: block; - width: 15px; - height: 15px; - margin-left: 5px; - background: url(../../public/static/images/canvas/pop_tip.svg)no-repeat center; - background-size: cover; - } - &.light{ - padding: 0; - th,td{ - color: $alert-color; - border-bottom: none; - border-top: 1px solid #EFEFEF; - } - th{ - padding: 14px 0; - } - tr{ - &:first-child{ - td, - th{ - padding-top: 14px; - } - } - &:last-child{ - td, - th{ - padding-bottom: 0px; - } - } - } - } -} +.placement-table { + table { + table-layout: fixed; -.pop-form-radio{ - display: flex; - align-items: center; - gap: 10px; -} -.placement-option{ - display: flex; - align-items: center; - gap: 20px; -} -.select-wrap{ - div{ - width: 100%; - } -} -.flex-ment{ - display: flex; - align-items: center; - gap: 5px; - span{ + tr { + th { + display: flex; + align-items: center; font-size: $pop-normal-size; color: $pop-color; - font-weight: $pop-normal-weight; + font-weight: $pop-bold-weight; + padding: 18px 0; + border-bottom: 1px solid #424242; + } + + td { + font-size: $pop-normal-size; + color: $pop-color; + border-bottom: 1px solid #424242; + padding-left: 20px; + } + + &:first-child { + td, + th { + padding-top: 0; + } + } } + } + + .tooltip { + position: relative; + display: block; + width: 15px; + height: 15px; + margin-left: 5px; + background: url(../../public/static/images/canvas/pop_tip.svg) no-repeat center; + background-size: cover; + } + + &.light { + padding: 0; + + th, td { + color: $alert-color; + border-bottom: none; + border-top: 1px solid #EFEFEF; + } + + th { + padding: 14px 0; + } + + tr { + &:first-child { + td, + th { + padding-top: 14px; + } + } + + &:last-child { + td, + th { + padding-bottom: 0px; + } + } + } + } +} + +.pop-form-radio { + display: flex; + align-items: center; + gap: 10px; +} + +.placement-option { + display: flex; + align-items: center; + gap: 20px; +} + +.select-wrap { + div { + width: 100%; + } +} + +.flex-ment { + display: flex; + align-items: center; + gap: 5px; + + span { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + } } // 외벽선 그리기 -.outline-wrap{ - padding: 24px 0; - border-bottom: 1px solid #424242; - .outline-inner{ - display: flex; - align-items: center; - margin-bottom: 14px; - &:last-child{ - margin-bottom: 0; - } - } -} -.outline-form{ - width: 50%; +.outline-wrap { + padding: 24px 0; + border-top: 1px solid #424242; + + .outline-inner { display: flex; align-items: center; - margin-right: 15px; - span{ - width: 60px; - flex: none; - font-size: $pop-normal-size; - font-weight: $pop-bold-weight; - color: $pop-color; - margin-right: 10px; + margin-bottom: 14px; + + &:last-child { + margin-bottom: 0; } - .reset-btn{ - flex: none; - width: 30px; - height: 30px; - background: transparent; - border: 1px solid #484848; - border-radius: 2px; - margin-left: 5px; - background-image: url(../../public/static/images/canvas/reset_ico.svg); - background-repeat: no-repeat; - background-size: 12px 12px; - background-position: center; - } - &:last-child{ - margin-right: 0; + + .outline-form { + // width: 50%; + margin-right: 15px; } + } + + &:last-child { + border-bottom: 1px solid #424242; + } } -.cul-wrap{ +.outline-form { + display: flex; + align-items: center; + + span { + width: 60px; + flex: none; + font-size: $pop-normal-size; + font-weight: $pop-bold-weight; + color: $pop-color; + margin-right: 10px; + + &.thin { + width: auto; + font-weight: $pop-normal-weight; + margin-right: 0; + } + } + + .reset-btn { + flex: none; + width: 30px; + height: 30px; + background: transparent; + border: 1px solid #484848; + border-radius: 2px; + margin-left: 5px; + background-image: url(../../public/static/images/canvas/reset_ico.svg); + background-repeat: no-repeat; + background-size: 12px 12px; + background-position: center; + } + + &:last-child { + margin-right: 0; + } +} + +.cul-wrap { + display: flex; + + .outline-box { + width: 50%; + margin-right: 15px; + + .outline-form { + width: 100%; + margin-bottom: 14px; + margin-right: 0; + + &:last-child { + margin-bottom: 0; + } + } + } + + .cul-box { display: flex; - .outline-box{ - width: 50%; - margin-right: 15px; - .outline-form{ - width: 100%; - margin-bottom: 14px; - margin-right: 0; - &:last-child{ - margin-bottom: 0; - } + align-items: center; + justify-content: center; + width: 50%; + background-color: #3D3D3D; + border-radius: 2px; + } +} + +// 외벽선 속성 설정 +.properties-guide { + font-size: $pop-normal-size; + color: #AAA; + font-weight: $pop-normal-weight; + margin-bottom: 14px; +} + +.setting-tit { + font-size: 13px; + color: $pop-color; + font-weight: $pop-bold-weight; + margin-bottom: 10px; +} + +.properties-setting-wrap { + &.outer { + margin-top: 24px; + } + + .setting-btn-wrap { + display: flex; + align-items: center; + padding: 14px 0; + border-top: 1px solid #424242; + border-bottom: 1px solid #424242; + + .setting-btn { + display: block; + width: 100%; + height: 40px; + font-size: 13px; + color: #fff; + font-weight: 700; + border-radius: 2px; + transition: all .15s ease-in-out; + + &.green { + background-color: #305941; + border: 1px solid #45CD7D; + + &:hover { + background-color: #3a6b4e; } + } + + &.blue { + background-color: #2E5360; + border: 1px solid #3FBAE6; + + &:hover { + background-color: #365f6e; + } + } } - .cul-box{ - display: flex; - align-items: center; - justify-content: center; - width: 50%; - background-color: #3D3D3D; - border-radius: 2px ; + } +} + +// 지붕형상 설정 +.roof-shape-menu { + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr; + gap: 24px 10px; + margin-bottom: 24px; + + .shape-box { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + padding: 13px; + background-color: #3D3D3D; + transition: background .15s ease-in-out; + + img { + max-width: 100%; } + } + + .shape-title { + font-size: $pop-normal-size; + font-weight: $pop-bold-weight; + color: $pop-color; + margin-top: 10px; + text-align: center; + transition: color .15s ease-in-out; + } + + .shape-menu-box { + &.act, + &:hover { + .shape-box { + background-color: #008BFF; + } + + .shape-title { + color: #008BFF; + } + } + } +} + +.setting-box { + padding: 14px 0; + border-top: 1px solid #424242; + border-bottom: 1px solid #424242; +} + +.padding-form { + padding-left: 23px; +} + +.discrimination-box { + padding: 16px 12px; + border: 1px solid #3D3D3D; + border-radius: 2px; +} + +.modal-bottom-border-bx { + margin-top: 24px; + padding-bottom: 14px; + border-bottom: 1px solid #424242; +} + +// 처마∙케라바 변경 +.eaves-keraba-table { + display: table; + border-collapse: collapse; + + .eaves-keraba-item { + display: table-row; + + .eaves-keraba-th, + .eaves-keraba-td { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + display: table-cell; + vertical-align: middle; + padding-bottom: 14px; + } + + .eaves-keraba-td { + padding-left: 15px; + } + + .eaves-keraba-ico { + display: flex; + align-items: center; + justify-content: center; + padding: 5px; + background-color: #3D3D3D; + border: 1px solid #3D3D3D; + border-radius: 2px; + + &.act { + border: 1px solid #ED0004; + } + } + + &:last-child { + .eaves-keraba-th, + .eaves-keraba-td { + padding-bottom: 0; + } + } + } +} + +.guide { + font-size: $pop-normal-size; + font-weight: $pop-normal-weight; + color: $pop-color; + margin-bottom: 24px; + + &.sm { + margin-bottom: 15px; + } +} + +// 지붕면 할당 +.allocation-select-wrap { + display: flex; + align-items: center; + padding-bottom: 14px; + border-bottom: 1px solid #424242; + margin-bottom: 14px; + + span { + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-bold-weight; + margin-right: 10px; + } + + .allocation-edit { + display: flex; + align-items: center; + height: 30px; + padding: 0 10px; + margin-left: 5px; + font-size: $pop-normal-size; + color: $pop-color; + font-weight: $pop-normal-weight; + border: 1px solid #484848; + background-color: #323234; + + i { + display: block; + width: 12px; + height: 12px; + margin-right: 5px; + background: url(../../public/static/images/canvas/allocation_edit.svg) no-repeat center; + background-size: cover; + } + } +} + +.block-box { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 10px; + + .flex-ment { + gap: 10px; + + .dec { + text-decoration: underline; + } + + .delete { + display: block; + width: 15px; + height: 15px; + background: url(../../public/static/images/canvas/allocation_delete.svg) no-repeat center; + background-size: cover; + } + } + + &:last-child { + margin-bottom: 0; + } +} + +.icon-btn-wrap { + flex: 1; + display: flex; + align-items: center; + gap: 5px; + + button { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 30px; + font-size: $pop-normal-size; + font-weight: $pop-normal-weight; + color: $pop-color; + border: 1px solid #646464; + border-radius: 2px; + padding: 0 10px; + transition: all .15s ease-in-out; + + i { + height: 15px; + display: block; + margin-left: 10px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + transition: all .15s ease-in-out; + + &.allocation01 { + background-image: url(../../public/static/images/canvas/allocation_icon01_white.svg); + width: 15px; + } + + &.allocation02 { + background-image: url(../../public/static/images/canvas/allocation_icon02_white.svg); + width: 18px; + } + } + + &.act, + &:hover { + color: #101010; + border: 1px solid #101010; + background-color: #fff; + + i { + &.allocation01 { + background-image: url(../../public/static/images/canvas/allocation_icon01_black.svg); + } + + &.allocation02 { + background-image: url(../../public/static/images/canvas/allocation_icon02_black.svg); + } + } + } + } +} + +// 경사설정 +.slope-wrap { + padding-bottom: 24px; + border-bottom: 1px solid #424242; +} + +// 면형상 배치 +.roof-shape-menu { + &.plane { + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr 1fr; + } } \ No newline at end of file diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss index 2c5b1e22..6d899458 100644 --- a/src/styles/_reset.scss +++ b/src/styles/_reset.scss @@ -133,12 +133,16 @@ button{ // margin .mt5{margin-top: 5px !important;} .mt10{margin-top: 10px !important;} +.mt15{margin-top: 15px !important;} .mb5{margin-bottom: 5px !important;} .mb10{margin-bottom: 10px !important;} +.mb15{margin-bottom: 15px !important;} .mr5{margin-right: 5px !important;} .mr10{margin-right: 10px !important;} +.mr15{margin-right: 15px !important;} .ml5{margin-left: 5px !important;} .ml10{margin-left: 10px !important;} +.ml15{margin-left: 15px !important;} // button .btn-frame{ @@ -189,6 +193,22 @@ button{ font-weight: 500; } } + &.sub-tab{ + height: 30px; + padding: 0 10px; + line-height: 28px; + font-family: 'Noto Sans JP', sans-serif; + background-color: #2D2D2D; + border: 1px solid #393939; + color: #aaa; + &.act, + &:hover{ + background-color: #414E6C; + border: 1px solid #414E6C; + color: #fff; + font-weight: 500; + } + } &:hover, &.act{ background-color: #1083E3; @@ -244,7 +264,7 @@ button{ min-width: 100px; height: 30px; line-height: 30px; - padding: 0 10px; + padding: 0 25px 0 10px; background-color: #373737; border: 1px solid #3F3F3F; border-radius: 2px; @@ -263,11 +283,14 @@ button{ clip-path:inset(0 0 100% 0); width: calc(100% + 2px); padding: 8px 0; + max-height: 100px; + overflow-y: auto; background-color: #373737; border: 1px solid #3F3F3F; border-radius: 2px; transition: all 0.17s ease-in-out; visibility: hidden; + z-index: 999; .select-item{ display: flex; align-items: center; @@ -283,6 +306,18 @@ button{ background-color: #2C2C2C; } } + &::-webkit-scrollbar { + width: 2px; + background-color: transparent; + + } + &::-webkit-scrollbar-thumb { + background-color: #5a5a5a; + border-radius: 10px; + } + &::-webkit-scrollbar-track { + background-color: transparent; + } } &::after{ content: ''; @@ -372,6 +407,9 @@ input[type=text]{ &.block{ width: 100%; } + &:read-only{ + color: #AAA; + } } &.input-light{ display: block; @@ -586,6 +624,7 @@ input[type=text]{ } &.pop{ label{ + font-size: 12px; &:before{ width: 16px; height: 16px; diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 641016d5..a7bcb0e2 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -1,13 +1,6 @@ import { fabric } from 'fabric' import { QLine } from '@/components/fabric/QLine' -import { - calculateIntersection, - distanceBetweenPoints, - findClosestPoint, - getAdjacent, - getDirectionByPoint, - getRoofHypotenuse, -} from '@/util/canvas-util' +import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDirectionByPoint } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import * as turf from '@turf/turf' @@ -1194,314 +1187,294 @@ function calculateAngleBetweenLines(line1, line2) { // Calculate the angle in radians and then convert to degrees const angleInRadians = Math.acos(cosTheta) - const angleInDegrees = (angleInRadians * 180) / Math.PI - return angleInDegrees + return (angleInRadians * 180) / Math.PI } export const drawHippedRoof = (polygon, chon) => { - drawRoofRidge(polygon, chon) + const hasNonParallelLines = polygon.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2) + if (hasNonParallelLines.length > 0) { + alert('대각선이 존재합니다.') + return + } + drawRidgeRoof(polygon, chon) drawHips(polygon) connectLinePoint(polygon) } -const drawRoofRidge = (polygon, chon) => { - const points = [] - polygon.wall.points.forEach((point) => points.push({ x: point.x, y: point.y })) - console.log('points : ', points) - +/** + * + * @param polygon + * @param chon + */ +const drawRidgeRoof = (polygon, chon) => { const walls = polygon.wall.lines // 외벽의 라인 const roofs = polygon.lines // 지붕의 라인 - let ridgeWall = [] - walls.forEach((wall, index) => { - let currentRoof, prevWall, currentWall, nextWall + let ridgeRoof = [] - if (index === 0) { - prevWall = walls[walls.length - 1] - } else { - prevWall = walls[index - 1] - } - currentRoof = roofs[index] - currentWall = walls[index] + roofs.forEach((currentRoof, index) => { + let prevRoof, + nextRoof, + currentWall = walls[index] - if (index === walls.length - 1) { - nextWall = walls[0] - } else if (index === walls.length) { - nextWall = walls[1] - } else { - nextWall = walls[index + 1] - } + prevRoof = index === 0 ? walls[walls.length - 1] : walls[index - 1] + nextRoof = index === walls.length - 1 ? walls[0] : index === walls.length ? walls[1] : walls[index + 1] - if (prevWall.direction !== nextWall.direction && currentWall.length < currentRoof.length) { - ridgeWall.push({ index: index, wall: currentWall, length: currentWall.length }) + if (prevRoof.direction !== nextRoof.direction && currentWall.length <= currentRoof.length) { + ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.length }) } }) // 지붕의 길이가 짧은 순으로 정렬 - ridgeWall.sort((a, b) => a.length - b.length) + ridgeRoof.sort((a, b) => a.length - b.length) - ridgeWall.forEach((item) => { - if (getMaxRidge(walls.length) > polygon.ridges.length) { + ridgeRoof.forEach((item) => { + if (getMaxRidge(roofs.length) > polygon.ridges.length) { let index = item.index, - // currentRoof = roofs[index], - beforePrevWall, - prevWall, - currentWall = item.wall, - nextWall, - afterNextWall + beforePrevRoof, + prevRoof, + currentRoof = item.roof, + nextRoof, + afterNextRoof let startXPoint, startYPoint, endXPoint, endYPoint - if (index === 0) { - prevWall = walls[walls.length - 1] - } else { - prevWall = walls[index - 1] - } + prevRoof = index === 0 ? roofs[walls.length - 1] : roofs[index - 1] + nextRoof = index === roofs.length - 1 ? roofs[0] : index === roofs.length ? roofs[1] : roofs[index + 1] - if (index === 0) { - beforePrevWall = walls[roofs.length - 2] - } else if (index === 1) { - beforePrevWall = walls[roofs.length - 1] - } else { - beforePrevWall = walls[index - 2] - } + beforePrevRoof = index <= 1 ? roofs[roofs.length - 2 + index] : roofs[index - 2] + afterNextRoof = index >= roofs.length - 2 ? roofs[(index + 2) % roofs.length] : roofs[index + 2] - if (index === walls.length - 1) { - nextWall = walls[0] - } else if (index === walls.length) { - nextWall = walls[1] - } else { - nextWall = walls[index + 1] - } + const anotherRoof = roofs.filter((roof) => roof !== currentRoof && roof !== prevRoof && roof !== nextRoof) - if (index === walls.length - 2) { - afterNextWall = walls[0] - } else if (index === walls.length - 1) { - afterNextWall = walls[1] - } else if (index === walls.length) { - afterNextWall = walls[2] - } else { - afterNextWall = walls[index + 2] - } + let xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)), //x가 같은 내부선 + yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)) //y가 같은 내부선 - const anotherRoof = walls.filter((wall) => wall !== currentWall && wall !== prevWall && wall !== nextWall) - let acrossRoof, xEqualInnerLines, yEqualInnerLines - xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevWall, currentWall, nextWall, roof)) //x가 같은 내부선 - yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevWall, currentWall, nextWall, roof)) //y가 같은 내부선 + let ridgeBaseLength = currentRoof.length / 2, // 지붕의 기반 길이 + ridgeMaxLength = Math.min(prevRoof.length, nextRoof.length), // 지붕의 최대 길이. 이전, 다음 벽 중 짧은 길이 + ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - currentRoof.length // 맞은편 벽까지의 길이 - 지붕의 기반 길이 - let ridgeBaseLength = currentWall.length / 2, // 지붕의 기반 길이 - ridgeMaxLength = Math.min(prevWall.length, nextWall.length), // 지붕의 최대 길이. 이전, 다음 벽 중 짧은 길이 - ridgeAcrossLength = Math.max(prevWall.length, nextWall.length) - currentWall.length // 맞은편 벽까지의 길이 - 지붕의 기반 길이 - - acrossRoof = anotherRoof + let acrossRoof = anotherRoof .filter((roof) => { - if (currentWall.direction === 'top' && roof.direction === 'bottom') { - if (nextWall.direction === 'right' && roof.x1 > currentWall.x1) { - return roof - } - if (nextWall.direction === 'left' && roof.x1 < currentWall.x1) { + if (roof.x1 === roof.x2) { + if ((nextRoof.direction === 'right' && roof.x1 > currentRoof.x1) || (nextRoof.direction === 'left' && roof.x1 < currentRoof.x1)) { return roof } } - if (currentWall.direction === 'right' && roof.direction === 'left') { - if (nextWall.direction === 'top' && roof.y1 < currentWall.y1) { - return roof - } - if (nextWall.direction === 'bottom' && roof.y1 > currentWall.y1) { + if (roof.y1 === roof.y2) { + if ((nextRoof.direction === 'top' && roof.y1 < currentRoof.y1) || (nextRoof.direction === 'bottom' && roof.y1 > currentRoof.y1)) { return roof } } }) .reduce((prev, current) => { - let hasBetweenWall = false - if (current.direction === 'top' || current.direction === 'bottom') { - hasBetweenWall = walls - .filter((wall) => wall !== current && wall !== currentWall) + let hasBetweenRoof = false + if (current.x1 === current.x2) { + hasBetweenRoof = roofs + .filter((roof) => roof !== current && roof !== currentRoof) .some((line) => { - let currentY2 = currentWall.y2 + let currentY2 = currentRoof.y2 if (yEqualInnerLines.length > 0) { yEqualInnerLines.forEach((line) => { - currentY2 = Math.abs(currentWall.y1 - currentY2) < Math.abs(currentWall.y1 - line.y1) ? currentY2 : line.y1 + currentY2 = Math.abs(currentRoof.y1 - currentY2) < Math.abs(currentRoof.y1 - line.y1) ? currentY2 : line.y1 }) } - const isY1Between = (line.y1 > currentWall.y1 && line.y1 < currentY2) || (line.y1 > currentY2 && line.y1 < currentWall.y1) - const isY2Between = (line.y2 > currentWall.y1 && line.y2 < currentY2) || (line.y2 > currentY2 && line.y2 < currentWall.y1) - const isX1Between = (line.x1 > currentWall.x1 && line.x1 < current.x1) || (line.x1 > currentWall.x1 && line.x1 < current.x1) - const isX2Between = (line.x2 > currentWall.x1 && line.x2 < current.x1) || (line.x2 > currentWall.x1 && line.x2 < current.x1) + const isY1Between = (line.y1 > currentRoof.y1 && line.y1 < currentY2) || (line.y1 > currentY2 && line.y1 < currentRoof.y1) + const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < currentY2) || (line.y2 > currentY2 && line.y2 < currentRoof.y1) + const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < current.x1) || (line.x1 > currentRoof.x1 && line.x1 < current.x1) + const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < current.x1) || (line.x2 > currentRoof.x1 && line.x2 < current.x1) return isY1Between && isY2Between && isX1Between && isX2Between }) } - if (current.direction === 'right' || current.direction === 'left') { - hasBetweenWall = walls - .filter((wall) => wall !== current && wall !== currentWall) + if (current.y1 === current.y2) { + hasBetweenRoof = walls + .filter((roof) => roof !== current && roof !== currentRoof) .some((line) => { - let currentX2 = currentWall.x2 + let currentX2 = currentRoof.x2 if (xEqualInnerLines.length > 0) { xEqualInnerLines.forEach((line) => { - currentX2 = Math.abs(currentWall.x1 - currentX2) < Math.abs(currentWall.x1 - line.x1) ? currentX2 : line.x1 + currentX2 = Math.abs(currentRoof.x1 - currentX2) < Math.abs(currentRoof.x1 - line.x1) ? currentX2 : line.x1 }) } - const isX1Between = (line.x1 > currentWall.x1 && line.x1 < currentX2) || (line.x1 > currentX2 && line.x1 < currentWall.x1) - const isX2Between = (line.x2 > currentWall.x1 && line.x2 < currentX2) || (line.x2 > currentX2 && line.x2 < currentWall.x1) - const isY1Between = (line.y1 > currentWall.y1 && line.y1 < current.y1) || (line.y1 > currentWall.y1 && line.y1 < current.y1) - const isY2Between = (line.y2 > currentWall.y1 && line.y2 < current.y1) || (line.y2 > currentWall.y1 && line.y2 < current.y1) + const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < currentX2) || (line.x1 > currentX2 && line.x1 < currentRoof.x1) + const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < currentX2) || (line.x2 > currentX2 && line.x2 < currentRoof.x1) + const isY1Between = (line.y1 > currentRoof.y1 && line.y1 < current.y1) || (line.y1 > currentRoof.y1 && line.y1 < current.y1) + const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < current.y1) || (line.y2 > currentRoof.y1 && line.y2 < current.y1) return isX1Between && isX2Between && isY1Between && isY2Between }) } + if (prev !== undefined) { - if (currentWall.direction === 'top' || currentWall.direction === 'bottom') { - return Math.abs(currentWall.y1 - prev.y1) > Math.abs(currentWall.y1 - current.y1) ? prev : current + if (currentRoof.x1 === currentRoof.x2) { + return Math.abs(currentRoof.y1 - prev.y1) > Math.abs(currentRoof.y1 - current.y1) ? prev : current } - if (currentWall.direction === 'right' || currentWall.direction === 'left') { - return Math.abs(currentWall.x1 - prev.x1) > Math.abs(currentWall.x1 - current.x1) ? prev : current + if (currentRoof.y1 === currentRoof.y2) { + return Math.abs(currentRoof.x1 - prev.x1) > Math.abs(currentRoof.x1 - current.x1) ? prev : current } } else { - if (!hasBetweenWall) { - return current + if (!hasBetweenRoof) { + if (currentRoof.x1 === currentRoof.x2) { + return Math.sign(currentRoof.y1 - currentRoof.y2) !== Math.sign(current.y1 - current.y2) ? current : undefined + } + if (currentRoof.y1 === currentRoof.y2) { + return Math.sign(currentRoof.x1 - currentRoof.x2) !== Math.sign(current.x1 - current.x2) ? current : undefined + } + return undefined } else { return undefined } } }, undefined) + if (acrossRoof !== undefined) { - if (currentWall.direction === 'top' || currentWall.direction === 'bottom') { - if (ridgeAcrossLength < Math.abs(currentWall.x1 - acrossRoof.x1)) { - ridgeAcrossLength = Math.abs(currentWall.x1 - acrossRoof.x1) - currentWall.length + if (currentRoof.x1 === currentRoof.x2) { + if (ridgeAcrossLength < Math.abs(currentRoof.x1 - acrossRoof.x1)) { + ridgeAcrossLength = Math.abs(currentRoof.x1 - acrossRoof.x1) - currentRoof.length } } - if (currentWall.direction === 'right' || currentWall.direction === 'left') { - if (ridgeAcrossLength < Math.abs(currentWall.y1 - acrossRoof.y1)) { - ridgeAcrossLength = Math.abs(currentWall.y1 - acrossRoof.y1) - currentWall.length + if (currentRoof.y1 === currentRoof.y2) { + if (ridgeAcrossLength < Math.abs(currentRoof.y1 - acrossRoof.y1)) { + ridgeAcrossLength = Math.abs(currentRoof.y1 - acrossRoof.y1) - currentRoof.length } } } if (ridgeBaseLength > 0 && ridgeMaxLength > 0 && ridgeAcrossLength > 0) { let ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength) - if (currentWall.direction === 'top' || currentWall.direction === 'bottom') { - startXPoint = currentWall.x1 + (nextWall.direction === 'right' ? 1 : -1) * ridgeBaseLength - startYPoint = currentWall.y1 + (currentWall.direction === 'top' ? -1 : 1) * ridgeBaseLength - endXPoint = startXPoint + (nextWall.direction === 'right' ? 1 : -1) * ridgeLength + if (currentRoof.x1 === currentRoof.x2) { + startXPoint = currentRoof.x1 + (nextRoof.direction === 'right' ? 1 : -1) * ridgeBaseLength + startYPoint = currentRoof.y1 + (currentRoof.direction === 'top' ? -1 : 1) * ridgeBaseLength + endXPoint = startXPoint + (nextRoof.direction === 'right' ? 1 : -1) * ridgeLength endYPoint = startYPoint let adjustY - if (currentWall.direction === 'top') { - if (afterNextWall.direction === 'bottom' && beforePrevWall.direction === 'bottom') { + if (currentRoof.direction === 'top') { + if (afterNextRoof.direction === 'bottom' && beforePrevRoof.direction === 'bottom') { adjustY = - Math.abs(currentWall.x1 - afterNextWall.x1) < Math.abs(currentWall.x1 - beforePrevWall.x1) ? afterNextWall.y2 : beforePrevWall.y1 - } else if (afterNextWall.direction === 'bottom' && afterNextWall.y2 > currentWall.y2 && afterNextWall.y2 < currentWall.y1) { - adjustY = afterNextWall.y2 - } else if (beforePrevWall.direction === 'bottom' && beforePrevWall.y1 > currentWall.y2 && beforePrevWall.y1 < currentWall.y1) { - adjustY = beforePrevWall.y1 + Math.abs(currentRoof.x1 - afterNextRoof.x1) < Math.abs(currentRoof.x1 - beforePrevRoof.x1) ? afterNextRoof.y2 : beforePrevRoof.y1 + } else if (afterNextRoof.direction === 'bottom' && afterNextRoof.y2 > currentRoof.y2 && afterNextRoof.y2 < currentRoof.y1) { + adjustY = afterNextRoof.y2 + } else if (beforePrevRoof.direction === 'bottom' && beforePrevRoof.y1 > currentRoof.y2 && beforePrevRoof.y1 < currentRoof.y1) { + adjustY = beforePrevRoof.y1 } if (adjustY) { - startYPoint = currentWall.y1 - Math.abs(currentWall.y1 - adjustY) / 2 + startYPoint = currentRoof.y1 - Math.abs(currentRoof.y1 - adjustY) / 2 endYPoint = startYPoint } } - if (currentWall.direction === 'bottom') { - if (afterNextWall.direction === 'top' && beforePrevWall.direction === 'top') { + if (currentRoof.direction === 'bottom') { + if (afterNextRoof.direction === 'top' && beforePrevRoof.direction === 'top') { adjustY = - Math.abs(currentWall.x1 - afterNextWall.x1) < Math.abs(currentWall.x1 - beforePrevWall.x1) ? afterNextWall.y2 : beforePrevWall.y1 - } else if (afterNextWall.direction === 'top' && afterNextWall.y2 < currentWall.y2 && afterNextWall.y2 > currentWall.y1) { - adjustY = afterNextWall.y2 - } else if (beforePrevWall.direction === 'top' && beforePrevWall.y1 < currentWall.y2 && beforePrevWall.y1 > currentWall.y1) { - adjustY = beforePrevWall.y1 + Math.abs(currentRoof.x1 - afterNextRoof.x1) < Math.abs(currentRoof.x1 - beforePrevRoof.x1) ? afterNextRoof.y2 : beforePrevRoof.y1 + } else if (afterNextRoof.direction === 'top' && afterNextRoof.y2 < currentRoof.y2 && afterNextRoof.y2 > currentRoof.y1) { + adjustY = afterNextRoof.y2 + } else if (beforePrevRoof.direction === 'top' && beforePrevRoof.y1 < currentRoof.y2 && beforePrevRoof.y1 > currentRoof.y1) { + adjustY = beforePrevRoof.y1 } if (adjustY) { - startYPoint = currentWall.y1 + Math.abs(currentWall.y1 - adjustY) / 2 + startYPoint = currentRoof.y1 + Math.abs(currentRoof.y1 - adjustY) / 2 endYPoint = startYPoint } } if (yEqualInnerLines.length > 0) { yEqualInnerLines.reduce((prev, current) => { if (prev !== undefined) { - return Math.abs(currentWall.y1 - prev.y1) < Math.abs(currentWall.y1 - current.y1) ? prev : current + return Math.abs(currentRoof.y1 - prev.y1) < Math.abs(currentRoof.y1 - current.y1) ? prev : current } else { return current } }, undefined) startYPoint = - Math.abs(currentWall.y1 - startYPoint) * 2 <= Math.abs(currentWall.y1 - yEqualInnerLines[0].y1) + Math.abs(currentRoof.y1 - startYPoint) * 2 <= Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1) ? startYPoint - : Math.abs(currentWall.y1 - yEqualInnerLines[0].y1) + : Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1) endYPoint = startYPoint - ridgeAcrossLength = Math.max(prevWall.length, nextWall.length) - Math.abs(currentWall.y1 - startYPoint) * 2 + ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - Math.abs(currentRoof.y1 - startYPoint) * 2 if ( //yEqualInnerLines 이 다음 벽보다 안쪽에 있을때 - Math.abs(currentWall.y1 - yEqualInnerLines[0].y1) <= Math.abs(currentWall.y1 - nextWall.y1) && - Math.abs(currentWall.x1 - yEqualInnerLines[0].x2) >= Math.abs(currentWall.x1 - nextWall.x2) + Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1) <= Math.abs(currentRoof.y1 - nextRoof.y1) && + Math.abs(currentRoof.x1 - yEqualInnerLines[0].x2) >= Math.abs(currentRoof.x1 - nextRoof.x2) ) { - ridgeMaxLength = Math.abs(currentWall.x1 - yEqualInnerLines[0].x2) + ridgeMaxLength = Math.abs(currentRoof.x1 - yEqualInnerLines[0].x2) } ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength) - startXPoint = currentWall.x1 + (nextWall.direction === 'right' ? 1 : -1) * Math.abs(currentWall.y1 - startYPoint) - endXPoint = startXPoint + (nextWall.direction === 'right' ? 1 : -1) * ridgeLength + startXPoint = currentRoof.x1 + (nextRoof.direction === 'right' ? 1 : -1) * Math.abs(currentRoof.y1 - startYPoint) + endXPoint = startXPoint + (nextRoof.direction === 'right' ? 1 : -1) * ridgeLength } } - if (currentWall.direction === 'left' || currentWall.direction === 'right') { - startXPoint = currentWall.x1 + (currentWall.direction === 'left' ? -1 : 1) * ridgeBaseLength - startYPoint = currentWall.y1 + (nextWall.direction === 'bottom' ? 1 : -1) * ridgeBaseLength + if (currentRoof.y1 === currentRoof.y2) { + startXPoint = currentRoof.x1 + (currentRoof.direction === 'left' ? -1 : 1) * ridgeBaseLength + startYPoint = currentRoof.y1 + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeBaseLength endXPoint = startXPoint - endYPoint = startYPoint + (nextWall.direction === 'bottom' ? 1 : -1) * ridgeLength + endYPoint = startYPoint + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeLength + let adjustX - if (currentWall.direction === 'right') { - if (afterNextWall.direction === 'left' && beforePrevWall.direction === 'left') { + if (currentRoof.direction === 'right') { + if (afterNextRoof.direction === 'left' && beforePrevRoof.direction === 'left') { adjustX = - Math.abs(currentWall.y1 - afterNextWall.y1) < Math.abs(currentWall.y1 - beforePrevWall.y1) ? afterNextWall.x2 : beforePrevWall.x1 - } else if (afterNextWall.direction === 'left' && afterNextWall.x2 < currentWall.x2 && afterNextWall.x2 > currentWall.x1) { - adjustX = afterNextWall.x2 - } else if (beforePrevWall.direction === 'left' && beforePrevWall.x1 < currentWall.x2 && beforePrevWall.x1 > currentWall.x1) { - adjustX = beforePrevWall.x1 + Math.abs(currentRoof.y1 - afterNextRoof.y1) < Math.abs(currentRoof.y1 - beforePrevRoof.y1) ? afterNextRoof.x2 : beforePrevRoof.x1 + } else if (afterNextRoof.direction === 'left' && afterNextRoof.x2 < currentRoof.x2 && afterNextRoof.x2 > currentRoof.x1) { + adjustX = afterNextRoof.x2 + } else if (beforePrevRoof.direction === 'left' && beforePrevRoof.x1 < currentRoof.x2 && beforePrevRoof.x1 > currentRoof.x1) { + adjustX = beforePrevRoof.x1 } if (adjustX) { - startXPoint = currentWall.x1 + Math.abs(currentWall.x1 - adjustX) / 2 + startXPoint = currentRoof.x1 + Math.abs(currentRoof.x1 - adjustX) / 2 endXPoint = startXPoint } } - if (currentWall.direction === 'left') { - if (afterNextWall.direction === 'right' && beforePrevWall.direction === 'right') { + if (currentRoof.direction === 'left') { + if (afterNextRoof.direction === 'right' && beforePrevRoof.direction === 'right') { adjustX = - Math.abs(currentWall.y1 - afterNextWall.y1) < Math.abs(currentWall.y1 - beforePrevWall.y1) ? afterNextWall.x2 : beforePrevWall.x1 - } else if (afterNextWall.direction === 'right' && afterNextWall.x2 > currentWall.x2 && afterNextWall.x2 < currentWall.x1) { - adjustX = afterNextWall.x2 - } else if (beforePrevWall.direction === 'right' && beforePrevWall.x1 > currentWall.x2 && beforePrevWall.x1 < currentWall.x1) { - adjustX = beforePrevWall.x1 + Math.abs(currentRoof.y1 - afterNextRoof.y1) < Math.abs(currentRoof.y1 - beforePrevRoof.y1) ? afterNextRoof.x2 : beforePrevRoof.x1 + } else if (afterNextRoof.direction === 'right' && afterNextRoof.x2 > currentRoof.x2 && afterNextRoof.x2 < currentRoof.x1) { + adjustX = afterNextRoof.x2 + } else if (beforePrevRoof.direction === 'right' && beforePrevRoof.x1 > currentRoof.x2 && beforePrevRoof.x1 < currentRoof.x1) { + adjustX = beforePrevRoof.x1 } if (adjustX) { - startXPoint = currentWall.x1 - Math.abs(currentWall.x1 - adjustX) / 2 + startXPoint = currentRoof.x1 - Math.abs(currentRoof.x1 - adjustX) / 2 endXPoint = startXPoint } } if (xEqualInnerLines.length > 0) { xEqualInnerLines.reduce((prev, current) => { if (prev !== undefined) { - return Math.abs(currentWall.x1 - prev.x1) < Math.abs(currentWall.x1 - current.x1) ? prev : current + return Math.abs(currentRoof.x1 - prev.x1) < Math.abs(currentRoof.x1 - current.x1) ? prev : current } else { return current } }, undefined) startXPoint = - Math.abs(currentWall.x1 - startXPoint) * 2 <= Math.abs(currentWall.x1 - xEqualInnerLines[0].x1) + Math.abs(currentRoof.x1 - startXPoint) * 2 <= Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1) ? startXPoint - : Math.abs(currentWall.x1 - xEqualInnerLines[0].x1) + : Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1) endXPoint = startXPoint - ridgeAcrossLength = Math.max(prevWall.length, nextWall.length) - Math.abs(currentWall.x1 - startXPoint) * 2 + ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - Math.abs(currentRoof.x1 - startXPoint) * 2 if ( //xEqualInnerLines 이 다음 벽보다 안쪽에 있을때 - Math.abs(currentWall.x1 - xEqualInnerLines[0].x1) <= Math.abs(currentWall.x1 - nextWall.x1) && - Math.abs(currentWall.y1 - xEqualInnerLines[0].y2) >= Math.abs(currentWall.y1 - nextWall.y2) + Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1) <= Math.abs(currentRoof.x1 - nextRoof.x1) && + Math.abs(currentRoof.y1 - xEqualInnerLines[0].y2) >= Math.abs(currentRoof.y1 - nextRoof.y2) ) { - ridgeMaxLength = Math.abs(currentWall.y1 - xEqualInnerLines[0].y2) + ridgeMaxLength = Math.abs(currentRoof.y1 - xEqualInnerLines[0].y2) } ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength) - startYPoint = currentWall.y1 + (nextWall.direction === 'bottom' ? 1 : -1) * Math.abs(currentWall.x1 - startXPoint) - endYPoint = startYPoint + (nextWall.direction === 'bottom' ? 1 : -1) * ridgeLength + startYPoint = currentRoof.y1 + (nextRoof.direction === 'bottom' ? 1 : -1) * Math.abs(currentRoof.x1 - startXPoint) + endYPoint = startYPoint + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeLength } } } + const currentWall = walls[index] + if (currentWall.attributes.type === 'gable') { + if (currentRoof.x1 === currentRoof.x2) { + startXPoint = currentRoof.x1 + } + if (currentRoof.y1 === currentRoof.y2) { + startYPoint = currentRoof.y1 + } + } // 마루 그리기 - if (!(startXPoint === undefined && startYPoint === undefined && endXPoint === undefined && endYPoint === undefined)) { + if (startXPoint !== undefined && startYPoint !== undefined && endXPoint !== undefined && endYPoint !== undefined) { const ridge = new QLine( [Math.min(startXPoint, endXPoint), Math.min(startYPoint, endYPoint), Math.max(startXPoint, endXPoint), Math.max(startYPoint, endYPoint)], { @@ -1762,7 +1735,6 @@ const drawHips = (polygon) => { } } if (ridge.x1 === ridge.x2) { - console.log('세로방향 마루') //위쪽 좌표 기준 45, 315도 방향 라인확인 leftTop = polygon.lines .filter((line) => line.x1 < ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1)) @@ -2229,8 +2201,9 @@ const drawHips = (polygon) => { }) // 마루와 연결되지 않은 hip을 그린다. - polygon.lines.forEach((line, index) => { + /*polygon.lines.forEach((line, index) => { if (!isAlreadyHip(polygon, line)) { + console.log(' 확인 : ', line) let prevLine, currentLine, nextLine if (index === 0) { prevLine = polygon.lines[polygon.lines.length - 1] @@ -2264,6 +2237,7 @@ const drawHips = (polygon) => { let acrossLine = getAcrossLine(polygon, currentLine, dVector) let hypotenuse, adjacent + console.log(acrossLine) if (getLineDirection(prevLine) === getLineDirection(nextLine)) { hypotenuse = Math.round(getRoofHypotenuse(Math.abs(currentLine.x1 - acrossLine.x1) / 2)) @@ -2304,8 +2278,7 @@ const drawHips = (polygon) => { polygon.hips.push(hip) polygon.innerLines.push(hip) } - }) - console.log('polygon.hips : ', polygon.hips) + })*/ } const getPointInPolygon = (polygon, point, isInclude = false) => { @@ -2332,7 +2305,7 @@ const getPointInPolygon = (polygon, point, isInclude = false) => { */ const getAcrossLine = (polygon, currentLine, dVector) => { let acrossLine - + console.log('dVector : ', dVector) switch (dVector) { case 45: acrossLine = polygon.lines @@ -2404,6 +2377,24 @@ const connectLinePoint = (polygon) => { let missedPoints = [] //마루 polygon.ridges.forEach((ridge) => { + if (ridge.x1 === ridge.x2) { + if ( + polygon.lines + .filter((roof) => roof.y1 === roof.y2) + .filter((roof) => roof.y1 === ridge.y1 || roof.y1 === ridge.y2 || roof.y2 === ridge.y1 || roof.y2 === ridge.y2).length > 0 + ) { + return + } + } + if (ridge.y1 === ridge.y2) { + if ( + polygon.lines + .filter((roof) => roof.x1 === roof.x2) + .filter((roof) => roof.x1 === ridge.x1 || roof.x1 === ridge.x2 || roof.x2 === ridge.x1 || roof.x2 === ridge.x2).length > 0 + ) { + return + } + } if (polygon.hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1).length < 2) { missedPoints.push({ x: ridge.x1, y: ridge.y1 }) } @@ -2412,10 +2403,6 @@ const connectLinePoint = (polygon) => { } }) - console.log('polygon.ridges : ', polygon.ridges) - - console.log('missedPoints : ', missedPoints) - //추녀마루 polygon.hips.forEach((hip) => { let count = 0 @@ -2428,7 +2415,6 @@ const connectLinePoint = (polygon) => { }) let missedLine = [] - console.log('missedPoints : ', missedPoints) //중복포인트제거 missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line)) @@ -2490,14 +2476,10 @@ const connectLinePoint = (polygon) => { missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line)) - console.log(missedPoints) - missedPoints.forEach((p1) => { let p2 = missedPoints .filter((p) => !(p.x === p1.x && p.y === p1.y)) .reduce((prev, current) => { - console.log('current : ', current) - console.log('prev : ', prev) if (prev !== undefined) { return Math.abs(current.x - p1.x) + Math.abs(current.y - p1.y) < Math.abs(prev.x - p1.x) + Math.abs(prev.y - p1.y) ? current : prev } else { @@ -2506,7 +2488,6 @@ const connectLinePoint = (polygon) => { }, undefined) if (p2 !== undefined) { - console.log(p1.x, p2.x, p1.y, p2.y) if (p1.x === p2.x && p1.y < p2.y) { missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y }) } @@ -2584,7 +2565,6 @@ const connectLinePoint = (polygon) => { strokeWidth: 1, name: 'ridgeLine', }) - console.log('newRidge : ', newRidge) if (polygon.ridges.filter((r) => newRidge.x1 === r.x1 && newRidge.y1 === r.y1 && newRidge.x2 === r.x2 && newRidge.y2 === r.y2).length === 0) { polygon.canvas.remove(ridge) polygon.canvas.remove(ridge2) @@ -2679,12 +2659,14 @@ const getLineDirection = (line) => { } } -export const changeAllGableRoof = (polygon, offset, canvas) => { +export const changeAllHipAndGableRoof = (polygon, offset, canvas) => { const roof = polygon.filter((p) => p.name === 'roofBase')[0] // 지붕 const roofLines = roof.lines // 지붕의 라인 const ridges = roof.ridges // 마루의 라인 const hips = roof.hips // 추녀마루의 라인 + console.log('roofLines : ', roofLines) + ridges.forEach((ridge) => { let ridgeHip1 = hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1) let ridgeHip2 = hips.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2) @@ -2699,7 +2681,7 @@ export const changeAllGableRoof = (polygon, offset, canvas) => { (roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) || (roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1) ) { - gableLines.push(setGableRoof(polygon, ridge, ridgeHip1[0], ridgeHip1[1], offset, canvas)) + gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip1[0], ridgeHip1[1], offset, canvas)) } }) } @@ -2713,7 +2695,7 @@ export const changeAllGableRoof = (polygon, offset, canvas) => { (roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) || (roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1) ) { - gableLines.push(setGableRoof(polygon, ridge, ridgeHip2[0], ridgeHip2[1], offset, canvas)) + gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip2[0], ridgeHip2[1], offset, canvas)) } }) } @@ -2725,7 +2707,17 @@ export const changeAllGableRoof = (polygon, offset, canvas) => { // splitPolygonWithLines(roof) } -const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { +/** + * 모임지붕 -> 팔작지붕 변경 + * @param roof + * @param ridge + * @param hip1 + * @param hip2 + * @param offset + * @param canvas + * @returns {*} + */ +const setHipAndGableRoof = (roof, ridge, hip1, hip2, offset, canvas) => { let x1 = hip1.x1, y1 = hip1.y1 let gableLine, diffOffset @@ -2741,7 +2733,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { }) gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine', @@ -2772,7 +2764,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { y2: ridge.y2, }) gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine', @@ -2805,7 +2797,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { y2: ridge.y2, }) gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine', @@ -2837,7 +2829,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { }) gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine', @@ -2870,7 +2862,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { y2: ridge.y2, }) gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine', @@ -2901,7 +2893,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { y2: ridge.y2, }) gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine', @@ -2934,7 +2926,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { y2: ridge.y2, }) gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine', @@ -2965,7 +2957,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => { y2: ridge.y2, }) gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], { - fontSize: polygon.fontSize, + fontSize: roof.fontSize, stroke: 'blue', strokeWidth: 1, name: 'gableLine',