(*필수 입력항목)
@@ -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 })
+ }}
+ />
+
+ |
+ 기간검색 |
+
+
+ |
+
+
+
+
+
+ {/* 퍼블적용끝 */}
+ {/*
@@ -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',