Merge branch 'dev' of https://git.hanasys.jp/qcast3/qcast-front into feature/skeleton-dev
# Conflicts: # src/hooks/usePolygon.js
This commit is contained in:
commit
05604fb859
@ -6,7 +6,7 @@ import { useRouter } from 'next/navigation'
|
|||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import Cookies from 'js-cookie'
|
import Cookies from 'js-cookie'
|
||||||
|
|
||||||
import { isObjectNotEmpty, inputTelNumberCheck, inputNumberCheck } from '@/util/common-utils'
|
import { isObjectNotEmpty, inputTelNumberCheck, inputNumberCheck, inputUserIdCheck } from '@/util/common-utils'
|
||||||
|
|
||||||
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
|
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
|
||||||
|
|
||||||
@ -98,6 +98,10 @@ export default function Join() {
|
|||||||
alert(getMessage('common.message.required.data', [getMessage('join.sub1.fax')]))
|
alert(getMessage('common.message.required.data', [getMessage('join.sub1.fax')]))
|
||||||
faxRef.current.focus()
|
faxRef.current.focus()
|
||||||
return false
|
return false
|
||||||
|
}else if (!telRegex.test(fax)) {
|
||||||
|
alert(getMessage('join.validation.check1', [getMessage('join.sub1.fax')]))
|
||||||
|
faxRef.current.focus()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const bizNo = formData.get('bizNo')
|
const bizNo = formData.get('bizNo')
|
||||||
@ -129,6 +133,13 @@ export default function Join() {
|
|||||||
alert(getMessage('common.message.required.data', [getMessage('join.sub2.userId')]))
|
alert(getMessage('common.message.required.data', [getMessage('join.sub2.userId')]))
|
||||||
userIdRef.current.focus()
|
userIdRef.current.focus()
|
||||||
return false
|
return false
|
||||||
|
} else {
|
||||||
|
const userIdRegex = /^[A-Za-z0-9!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?`~]+$/
|
||||||
|
if (!userIdRegex.test(userId)) {
|
||||||
|
alert(getMessage('join.validation.check1', [getMessage('join.sub2.userId')]))
|
||||||
|
userIdRef.current.focus()
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 담당자 정보 - 이메일 주소
|
// 담당자 정보 - 이메일 주소
|
||||||
@ -174,6 +185,10 @@ export default function Join() {
|
|||||||
alert(getMessage('common.message.required.data', [getMessage('join.sub2.fax')]))
|
alert(getMessage('common.message.required.data', [getMessage('join.sub2.fax')]))
|
||||||
userFaxRef.current.focus()
|
userFaxRef.current.focus()
|
||||||
return false
|
return false
|
||||||
|
} else if (!telRegex.test(userFax)) {
|
||||||
|
alert(getMessage('join.validation.check1', [getMessage('join.sub2.fax')]))
|
||||||
|
userFaxRef.current.focus()
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -349,7 +364,15 @@ export default function Join() {
|
|||||||
<th>{getMessage('join.sub1.fax')}<span className="important">*</span></th>
|
<th>{getMessage('join.sub1.fax')}<span className="important">*</span></th>
|
||||||
<td>
|
<td>
|
||||||
<div className="input-wrap" style={{ width: '200px' }}>
|
<div className="input-wrap" style={{ width: '200px' }}>
|
||||||
<input type="text" id="fax" name="fax" className="input-light" maxLength={15} onChange={inputNumberCheck} ref={faxRef} />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="fax"
|
||||||
|
name="fax"
|
||||||
|
className="input-light"
|
||||||
|
maxLength={15}
|
||||||
|
placeholder={getMessage('join.sub1.telNo_placeholder')}
|
||||||
|
onChange={inputTelNumberCheck}
|
||||||
|
ref={faxRef} />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -420,7 +443,15 @@ export default function Join() {
|
|||||||
</th>
|
</th>
|
||||||
<td>
|
<td>
|
||||||
<div className="input-wrap" style={{ width: '200px' }}>
|
<div className="input-wrap" style={{ width: '200px' }}>
|
||||||
<input type="text" id="userId" name="userId" className="input-light" maxLength={20} ref={userIdRef} />
|
<input
|
||||||
|
type="text"
|
||||||
|
id="userId"
|
||||||
|
name="userId"
|
||||||
|
className="input-light"
|
||||||
|
maxLength={20}
|
||||||
|
onChange={inputUserIdCheck}
|
||||||
|
ref={userIdRef}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -466,7 +497,8 @@ export default function Join() {
|
|||||||
name="userFax"
|
name="userFax"
|
||||||
className="input-light"
|
className="input-light"
|
||||||
maxLength={15}
|
maxLength={15}
|
||||||
onChange={inputNumberCheck}
|
placeholder={getMessage('join.sub1.telNo_placeholder')}
|
||||||
|
onChange={inputTelNumberCheck}
|
||||||
ref={userFaxRef}
|
ref={userFaxRef}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -46,9 +46,9 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
setType,
|
setType,
|
||||||
arrow1Ref,
|
arrow1Ref,
|
||||||
arrow2Ref,
|
arrow2Ref,
|
||||||
outerLineDiagonalLength,
|
auxiliaryLineDiagonalLength,
|
||||||
setOuterLineDiagonalLength,
|
setAuxiliaryLineDiagonalLength,
|
||||||
outerLineDiagonalLengthRef,
|
auxiliaryLineDiagonalLengthRef,
|
||||||
handleRollback,
|
handleRollback,
|
||||||
handleFix,
|
handleFix,
|
||||||
buttonAct,
|
buttonAct,
|
||||||
@ -123,9 +123,9 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
length2,
|
length2,
|
||||||
setLength2,
|
setLength2,
|
||||||
length2Ref,
|
length2Ref,
|
||||||
outerLineDiagonalLength,
|
diagonalLength: auxiliaryLineDiagonalLength,
|
||||||
setOuterLineDiagonalLength,
|
setDiagonalLength: setAuxiliaryLineDiagonalLength,
|
||||||
outerLineDiagonalLengthRef,
|
diagonalLengthRef: auxiliaryLineDiagonalLengthRef,
|
||||||
arrow1,
|
arrow1,
|
||||||
setArrow1,
|
setArrow1,
|
||||||
arrow2,
|
arrow2,
|
||||||
|
|||||||
@ -212,7 +212,7 @@ export default function ModuleTabContents({ tabIndex, addRoof, setAddedRoofs, ro
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
id={`ch01_${tabIndex}`}
|
id={`ch01_${tabIndex}`}
|
||||||
disabled={cvrYn === 'N' ? true : false}
|
disabled={cvrYn === 'N' ? true : false}
|
||||||
checked={cvrChecked || false}
|
checked={cvrYn === 'N' ? false : cvrChecked ?? true}
|
||||||
onChange={handleCvrChecked}
|
onChange={handleCvrChecked}
|
||||||
/>
|
/>
|
||||||
<label htmlFor={`ch01_${tabIndex}`}>{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
|
<label htmlFor={`ch01_${tabIndex}`}>{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
|
||||||
|
|||||||
@ -413,7 +413,7 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
|
|
||||||
setCvrYn(constructionList[index].cvrYn)
|
setCvrYn(constructionList[index].cvrYn)
|
||||||
setSnowGdPossYn(constructionList[index].snowGdPossYn)
|
setSnowGdPossYn(constructionList[index].snowGdPossYn)
|
||||||
setCvrChecked(false)
|
setCvrChecked(true)
|
||||||
setSnowGdChecked(false)
|
setSnowGdChecked(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -859,7 +859,7 @@ const Trestle = forwardRef((props, ref) => {
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
id={`ch01`}
|
id={`ch01`}
|
||||||
disabled={!cvrYn || cvrYn === 'N'}
|
disabled={!cvrYn || cvrYn === 'N'}
|
||||||
checked={cvrChecked || false}
|
checked={!cvrYn || cvrYn === 'N' ? false : cvrChecked ?? true}
|
||||||
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })}
|
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })}
|
||||||
onChange={() => setCvrChecked(!cvrChecked)}
|
onChange={() => setCvrChecked(!cvrChecked)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -124,6 +124,9 @@ export default function CircuitTrestleSetting({ id }) {
|
|||||||
*/
|
*/
|
||||||
const validateModuleSizeForRack = (surface) => {
|
const validateModuleSizeForRack = (surface) => {
|
||||||
const { modules, direction, trestleDetail } = surface
|
const { modules, direction, trestleDetail } = surface
|
||||||
|
if (!trestleDetail) {
|
||||||
|
return true // 상세 정보 없음
|
||||||
|
}
|
||||||
const { rackYn, moduleIntvlHor, moduleIntvlVer } = trestleDetail
|
const { rackYn, moduleIntvlHor, moduleIntvlVer } = trestleDetail
|
||||||
|
|
||||||
if (rackYn === 'N' || !modules || modules.length < 2) {
|
if (rackYn === 'N' || !modules || modules.length < 2) {
|
||||||
|
|||||||
@ -125,7 +125,8 @@ export default function StepUp(props) {
|
|||||||
setSeletedMainOption(optionList[0])
|
setSeletedMainOption(optionList[0])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const selectedSerQty = pcsItem.serQtyList.find((serQty) => serQty.selected)
|
const serQtyList = pcsItem.serQtyList ?? []
|
||||||
|
const selectedSerQty = serQtyList.find((serQty) => serQty.selected)
|
||||||
if (selectedSerQty) {
|
if (selectedSerQty) {
|
||||||
selectedSerQty.roofSurfaceList.forEach((roofSurface) => {
|
selectedSerQty.roofSurfaceList.forEach((roofSurface) => {
|
||||||
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
|
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
|
||||||
|
|||||||
@ -21,6 +21,7 @@ export default function FlowDirectionSetting(props) {
|
|||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const { setSurfaceShapePattern } = useRoofFn()
|
const { setSurfaceShapePattern } = useRoofFn()
|
||||||
|
const { setPolygonLinesActualSize } = usePolygon()
|
||||||
|
|
||||||
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
|
||||||
|
|
||||||
@ -85,6 +86,8 @@ export default function FlowDirectionSetting(props) {
|
|||||||
drawDirectionArrow(roof)
|
drawDirectionArrow(roof)
|
||||||
canvas?.renderAll()
|
canvas?.renderAll()
|
||||||
changeSurfaceLineType(roof)
|
changeSurfaceLineType(roof)
|
||||||
|
setPolygonLinesActualSize(roof, true)
|
||||||
|
canvas.renderAll()
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,9 @@ export default function Diagonal({ props }) {
|
|||||||
length2,
|
length2,
|
||||||
setLength2,
|
setLength2,
|
||||||
length2Ref,
|
length2Ref,
|
||||||
outerLineDiagonalLength,
|
diagonalLength,
|
||||||
setOuterLineDiagonalLength,
|
setDiagonalLength,
|
||||||
outerLineDiagonalLengthRef,
|
diagonalLengthRef,
|
||||||
arrow1,
|
arrow1,
|
||||||
setArrow1,
|
setArrow1,
|
||||||
arrow2,
|
arrow2,
|
||||||
@ -45,11 +45,11 @@ export default function Diagonal({ props }) {
|
|||||||
name=""
|
name=""
|
||||||
label=""
|
label=""
|
||||||
className="input-origin block"
|
className="input-origin block"
|
||||||
value={outerLineDiagonalLength}
|
value={diagonalLength}
|
||||||
ref={outerLineDiagonalLengthRef}
|
ref={diagonalLengthRef}
|
||||||
onChange={(value) => setOuterLineDiagonalLength(value)}
|
onChange={(value) => setDiagonalLength(value)}
|
||||||
placeholder="3000"
|
placeholder="3000"
|
||||||
onFocus={() => (outerLineDiagonalLengthRef.current.value = '')}
|
onFocus={() => (diagonalLengthRef.current.value = '')}
|
||||||
options={{
|
options={{
|
||||||
allowNegative: false,
|
allowNegative: false,
|
||||||
allowDecimal: false
|
allowDecimal: false
|
||||||
@ -59,7 +59,7 @@ export default function Diagonal({ props }) {
|
|||||||
<button
|
<button
|
||||||
className="reset-btn"
|
className="reset-btn"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setOuterLineDiagonalLength(0)
|
setDiagonalLength(0)
|
||||||
}}
|
}}
|
||||||
></button>
|
></button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -104,9 +104,9 @@ export default function WallLineSetting(props) {
|
|||||||
length2,
|
length2,
|
||||||
setLength2,
|
setLength2,
|
||||||
length2Ref,
|
length2Ref,
|
||||||
outerLineDiagonalLength,
|
diagonalLength: outerLineDiagonalLength,
|
||||||
setOuterLineDiagonalLength,
|
setDiagonalLength: setOuterLineDiagonalLength,
|
||||||
outerLineDiagonalLengthRef,
|
diagonalLengthRef: outerLineDiagonalLengthRef,
|
||||||
arrow1,
|
arrow1,
|
||||||
setArrow1,
|
setArrow1,
|
||||||
arrow2,
|
arrow2,
|
||||||
|
|||||||
@ -107,9 +107,9 @@ export default function PlacementShapeDrawing({ id, pos = { x: 50, y: 230 } }) {
|
|||||||
length2,
|
length2,
|
||||||
setLength2,
|
setLength2,
|
||||||
length2Ref,
|
length2Ref,
|
||||||
outerLineDiagonalLength,
|
diagonalLength: outerLineDiagonalLength,
|
||||||
setOuterLineDiagonalLength,
|
setDiagonalLength: setOuterLineDiagonalLength,
|
||||||
outerLineDiagonalLengthRef,
|
diagonalLengthRef: outerLineDiagonalLengthRef,
|
||||||
arrow1,
|
arrow1,
|
||||||
setArrow1,
|
setArrow1,
|
||||||
arrow2,
|
arrow2,
|
||||||
|
|||||||
@ -63,7 +63,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
|
|||||||
const roofSizeSetArray = [
|
const roofSizeSetArray = [
|
||||||
{ id: 'ra01', name: 'roofSizeSet', value: '1', message: 'modal.placement.initial.setting.size.roof' },
|
{ id: 'ra01', name: 'roofSizeSet', value: '1', message: 'modal.placement.initial.setting.size.roof' },
|
||||||
{ id: 'ra02', name: 'roofSizeSet', value: '2', message: 'modal.placement.initial.setting.size.actual' },
|
{ id: 'ra02', name: 'roofSizeSet', value: '2', message: 'modal.placement.initial.setting.size.actual' },
|
||||||
{ id: 'ra03', name: 'roofSizeSet', value: '3', message: 'modal.placement.initial.setting.size.none.pitch' },
|
// { id: 'ra03', name: 'roofSizeSet', value: '3', message: 'modal.placement.initial.setting.size.none.pitch' },
|
||||||
]
|
]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -152,12 +152,16 @@ export default function GridOption(props) {
|
|||||||
<div className="modal-check-btn-wrap">
|
<div className="modal-check-btn-wrap">
|
||||||
<h3 className="check-wrap-title light">{getMessage('modal.canvas.setting.grid')}</h3>
|
<h3 className="check-wrap-title light">{getMessage('modal.canvas.setting.grid')}</h3>
|
||||||
<div className="flex-check-box for2">
|
<div className="flex-check-box for2">
|
||||||
{gridOptions?.map((option) => (
|
{gridOptions?.map((option) =>
|
||||||
|
option.id === 2 ? (
|
||||||
|
<></>
|
||||||
|
) : (
|
||||||
<button key={option.id} className={`check-btn ${option.selected ? 'act' : ''}`} onClick={(e) => onClickOption(option)}>
|
<button key={option.id} className={`check-btn ${option.selected ? 'act' : ''}`} onClick={(e) => onClickOption(option)}>
|
||||||
<span className="check-area"></span>
|
<span className="check-area"></span>
|
||||||
<span className="title-area">{getMessage(option.name)}</span>
|
<span className="title-area">{getMessage(option.name)}</span>
|
||||||
</button>
|
</button>
|
||||||
))}
|
),
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/*<ColorPickerModal {...colorPickerProps} />*/}
|
{/*<ColorPickerModal {...colorPickerProps} />*/}
|
||||||
|
|||||||
@ -100,11 +100,11 @@ export default function SettingModal01(props) {
|
|||||||
<button className={`btn-frame modal ${buttonAct === 2 ? 'act' : ''}`} onClick={() => handleBtnClick(2)}>
|
<button className={`btn-frame modal ${buttonAct === 2 ? 'act' : ''}`} onClick={() => handleBtnClick(2)}>
|
||||||
{getMessage('modal.canvas.setting.font.plan')}
|
{getMessage('modal.canvas.setting.font.plan')}
|
||||||
</button>
|
</button>
|
||||||
{/*{canGridOptionSeletorValue && (
|
{canGridOptionSeletorValue && (
|
||||||
<button className={`btn-frame modal ${buttonAct === 3 ? 'act' : ''}`} onClick={() => handleBtnClick(3)}>
|
<button className={`btn-frame modal ${buttonAct === 3 ? 'act' : ''}`} onClick={() => handleBtnClick(3)}>
|
||||||
{getMessage('modal.canvas.setting.grid')}
|
{getMessage('modal.canvas.setting.grid')}
|
||||||
</button>
|
</button>
|
||||||
)}*/}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{buttonAct === 1 && <FirstOption {...firstProps} />}
|
{buttonAct === 1 && <FirstOption {...firstProps} />}
|
||||||
{buttonAct === 2 && <SecondOption {...secondProps} />}
|
{buttonAct === 2 && <SecondOption {...secondProps} />}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { usePolygon } from '@/hooks/usePolygon'
|
|||||||
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
|
||||||
import { BATCH_TYPE } from '@/common/common'
|
import { BATCH_TYPE } from '@/common/common'
|
||||||
import { useMouse } from '@/hooks/useMouse'
|
import { useMouse } from '@/hooks/useMouse'
|
||||||
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
|
|
||||||
export function useCommonUtils() {
|
export function useCommonUtils() {
|
||||||
const canvas = useRecoilValue(canvasState)
|
const canvas = useRecoilValue(canvasState)
|
||||||
@ -617,6 +618,168 @@ export function useCommonUtils() {
|
|||||||
|
|
||||||
const buttonAct = dormerName == BATCH_TYPE.TRIANGLE_DORMER ? 3 : 4
|
const buttonAct = dormerName == BATCH_TYPE.TRIANGLE_DORMER ? 3 : 4
|
||||||
applyDormers(dormerParams, buttonAct)
|
applyDormers(dormerParams, buttonAct)
|
||||||
|
} else if (obj.name === 'roof' && obj.type === 'QPolygon') {
|
||||||
|
// roof(QPolygon) 객체는 순환 참조(lines[].parent -> polygon)로 인해
|
||||||
|
// fabric.clone() 사용 시 Maximum call stack size exceeded 에러 발생
|
||||||
|
// getCurrentPoints()를 사용하여 새 QPolygon을 직접 생성
|
||||||
|
|
||||||
|
// 원본 객체의 line attributes 복사 (순환 참조 제거)
|
||||||
|
const lineAttributes = obj.lines.map((line) => ({
|
||||||
|
type: line.attributes?.type,
|
||||||
|
offset: line.attributes?.offset,
|
||||||
|
actualSize: line.attributes?.actualSize,
|
||||||
|
planeSize: line.attributes?.planeSize,
|
||||||
|
}))
|
||||||
|
|
||||||
|
// 원본 roof의 자식 오브젝트들 찾기 (개구, 그림자, 도머 등)
|
||||||
|
const childObjectTypes = [BATCH_TYPE.OPENING, BATCH_TYPE.SHADOW, BATCH_TYPE.TRIANGLE_DORMER, BATCH_TYPE.PENTAGON_DORMER]
|
||||||
|
const childObjects = canvas.getObjects().filter(
|
||||||
|
(o) => o.parentId === obj.id && childObjectTypes.includes(o.name)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 원본 roof 중심점 계산
|
||||||
|
const originalPoints = obj.getCurrentPoints()
|
||||||
|
const originalCenterX = originalPoints.reduce((sum, p) => sum + p.x, 0) / originalPoints.length
|
||||||
|
const originalCenterY = originalPoints.reduce((sum, p) => sum + p.y, 0) / originalPoints.length
|
||||||
|
|
||||||
|
let clonedObj = null
|
||||||
|
let clonedChildren = []
|
||||||
|
|
||||||
|
addCanvasMouseEventListener('mouse:move', (e) => {
|
||||||
|
const pointer = canvas?.getPointer(e.e)
|
||||||
|
|
||||||
|
// 이전 임시 객체들 제거
|
||||||
|
canvas
|
||||||
|
.getObjects()
|
||||||
|
.filter((o) => o.name === 'clonedObj' || o.name === 'clonedChildTemp')
|
||||||
|
.forEach((o) => canvas?.remove(o))
|
||||||
|
|
||||||
|
// 새 QPolygon 생성 (매 move마다 생성하여 위치 업데이트)
|
||||||
|
const currentPoints = obj.getCurrentPoints()
|
||||||
|
const centerX = currentPoints.reduce((sum, p) => sum + p.x, 0) / currentPoints.length
|
||||||
|
const centerY = currentPoints.reduce((sum, p) => sum + p.y, 0) / currentPoints.length
|
||||||
|
|
||||||
|
// 이동 오프셋 계산
|
||||||
|
const offsetX = pointer.x - centerX
|
||||||
|
const offsetY = pointer.y - centerY
|
||||||
|
|
||||||
|
// 포인터 위치로 이동된 새 points 계산
|
||||||
|
const newPoints = currentPoints.map((p) => ({
|
||||||
|
x: p.x + offsetX,
|
||||||
|
y: p.y + offsetY,
|
||||||
|
}))
|
||||||
|
|
||||||
|
clonedObj = new QPolygon(newPoints, {
|
||||||
|
fill: obj.fill || 'transparent',
|
||||||
|
stroke: obj.stroke || 'black',
|
||||||
|
strokeWidth: obj.strokeWidth || 1,
|
||||||
|
fontSize: 0, // 이동 중에는 lengthText 생성하지 않음 (fontSize=0이면 addLengthText가 스킵됨)
|
||||||
|
selectable: true,
|
||||||
|
lockMovementX: true,
|
||||||
|
lockMovementY: true,
|
||||||
|
lockRotation: true,
|
||||||
|
lockScalingX: true,
|
||||||
|
lockScalingY: true,
|
||||||
|
name: 'clonedObj',
|
||||||
|
originX: 'center',
|
||||||
|
originY: 'center',
|
||||||
|
pitch: obj.pitch,
|
||||||
|
surfaceId: obj.surfaceId,
|
||||||
|
sort: false,
|
||||||
|
}, canvas)
|
||||||
|
|
||||||
|
canvas.add(clonedObj)
|
||||||
|
|
||||||
|
// 자식 오브젝트들도 이동해서 미리보기 표시
|
||||||
|
clonedChildren = []
|
||||||
|
childObjects.forEach((child) => {
|
||||||
|
child.clone((clonedChild) => {
|
||||||
|
clonedChild.set({
|
||||||
|
left: child.left + offsetX,
|
||||||
|
top: child.top + offsetY,
|
||||||
|
name: 'clonedChildTemp',
|
||||||
|
selectable: false,
|
||||||
|
evented: false,
|
||||||
|
})
|
||||||
|
clonedChildren.push({ original: child, cloned: clonedChild })
|
||||||
|
canvas.add(clonedChild)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
canvas.renderAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
addCanvasMouseEventListener('mouse:down', (e) => {
|
||||||
|
if (!clonedObj) return
|
||||||
|
|
||||||
|
const newRoofId = uuidv4()
|
||||||
|
|
||||||
|
clonedObj.set({
|
||||||
|
lockMovementX: true,
|
||||||
|
lockMovementY: true,
|
||||||
|
name: 'roof',
|
||||||
|
editable: false,
|
||||||
|
selectable: true,
|
||||||
|
id: newRoofId,
|
||||||
|
direction: obj.direction,
|
||||||
|
directionText: obj.directionText,
|
||||||
|
roofMaterial: obj.roofMaterial,
|
||||||
|
stroke: 'black',
|
||||||
|
evented: true,
|
||||||
|
isFixed: false,
|
||||||
|
fontSize: lengthTextFont.fontSize.value, // 최종 확정 시 fontSize 설정
|
||||||
|
})
|
||||||
|
|
||||||
|
// line attributes 복원
|
||||||
|
lineAttributes.forEach((attr, index) => {
|
||||||
|
if (clonedObj.lines[index]) {
|
||||||
|
clonedObj.lines[index].set({ attributes: attr })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 임시 자식 오브젝트들 제거
|
||||||
|
canvas
|
||||||
|
.getObjects()
|
||||||
|
.filter((o) => o.name === 'clonedChildTemp')
|
||||||
|
.forEach((o) => canvas?.remove(o))
|
||||||
|
|
||||||
|
// 자식 오브젝트들 최종 복사 (새 roof의 id를 parentId로 설정)
|
||||||
|
const pointer = canvas?.getPointer(e.e)
|
||||||
|
const currentPoints = obj.getCurrentPoints()
|
||||||
|
const centerX = currentPoints.reduce((sum, p) => sum + p.x, 0) / currentPoints.length
|
||||||
|
const centerY = currentPoints.reduce((sum, p) => sum + p.y, 0) / currentPoints.length
|
||||||
|
const offsetX = pointer.x - centerX
|
||||||
|
const offsetY = pointer.y - centerY
|
||||||
|
|
||||||
|
childObjects.forEach((child) => {
|
||||||
|
child.clone((clonedChild) => {
|
||||||
|
clonedChild.set({
|
||||||
|
left: child.left + offsetX,
|
||||||
|
top: child.top + offsetY,
|
||||||
|
id: uuidv4(),
|
||||||
|
parentId: newRoofId, // 새 roof의 id를 부모로 설정
|
||||||
|
name: child.name,
|
||||||
|
selectable: true,
|
||||||
|
evented: true,
|
||||||
|
})
|
||||||
|
// 그룹 객체인 경우 groupId도 새로 설정
|
||||||
|
if (clonedChild.type === 'group') {
|
||||||
|
clonedChild.set({ groupId: uuidv4() })
|
||||||
|
}
|
||||||
|
canvas.add(clonedChild)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
clonedObj.fire('polygonMoved')
|
||||||
|
clonedObj.fire('modified')
|
||||||
|
clonedObj.setCoords()
|
||||||
|
canvas.setActiveObject(clonedObj)
|
||||||
|
canvas.renderAll()
|
||||||
|
addLengthText(clonedObj) // fontSize가 설정된 후 lengthText 추가
|
||||||
|
drawDirectionArrow(clonedObj)
|
||||||
|
|
||||||
|
initEvent()
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let clonedObj = null
|
let clonedObj = null
|
||||||
|
|
||||||
@ -655,32 +818,6 @@ export function useCommonUtils() {
|
|||||||
//객체가 그룹일 경우에는 그룹 아이디를 따로 넣어준다
|
//객체가 그룹일 경우에는 그룹 아이디를 따로 넣어준다
|
||||||
if (clonedObj.type === 'group') clonedObj.set({ groupId: uuidv4() })
|
if (clonedObj.type === 'group') clonedObj.set({ groupId: uuidv4() })
|
||||||
|
|
||||||
//배치면일 경우
|
|
||||||
if (obj.name === 'roof') {
|
|
||||||
clonedObj.canvas = canvas // canvas 참조 설정
|
|
||||||
clonedObj.set({
|
|
||||||
direction: obj.direction,
|
|
||||||
directionText: obj.directionText,
|
|
||||||
roofMaterial: obj.roofMaterial,
|
|
||||||
stroke: 'black', // 복사된 객체는 선택 해제 상태의 색상으로 설정
|
|
||||||
selectable: true, // 선택 가능하도록 설정
|
|
||||||
evented: true, // 마우스 이벤트를 받을 수 있도록 설정
|
|
||||||
isFixed: false, // containsPoint에서 특별 처리 방지
|
|
||||||
})
|
|
||||||
|
|
||||||
obj.lines.forEach((line, index) => {
|
|
||||||
clonedObj.lines[index].set({ attributes: line.attributes })
|
|
||||||
})
|
|
||||||
|
|
||||||
clonedObj.fire('polygonMoved') // 내부 좌표 재계산 (points, pathOffset)
|
|
||||||
clonedObj.fire('modified')
|
|
||||||
clonedObj.setCoords() // 모든 속성 설정 후 좌표 업데이트
|
|
||||||
canvas.setActiveObject(clonedObj)
|
|
||||||
canvas.renderAll()
|
|
||||||
addLengthText(clonedObj) //수치 추가
|
|
||||||
drawDirectionArrow(clonedObj) //방향 화살표 추가
|
|
||||||
}
|
|
||||||
|
|
||||||
initEvent()
|
initEvent()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ export const useTurf = () => {
|
|||||||
* @param spare
|
* @param spare
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const checkModuleDisjointSurface = (module, surface, spare = 1) => {
|
const checkModuleDisjointSurface = (module, surface, spare = 0) => {
|
||||||
// 표면 영역을 spare만큼 수동 확장
|
// 표면 영역을 spare만큼 수동 확장
|
||||||
const expandedSurface = {
|
const expandedSurface = {
|
||||||
type: 'Polygon',
|
type: 'Polygon',
|
||||||
|
|||||||
@ -58,7 +58,12 @@ export function useImgLoader() {
|
|||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
|
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
const dataUrl = canvas.toDataURL('image/png')
|
// 고해상도 캡처를 위해 multiplier 옵션 추가 (2배 해상도)
|
||||||
|
const multiplier = 2
|
||||||
|
const dataUrl = canvas.toDataURL({
|
||||||
|
format: 'png',
|
||||||
|
multiplier: multiplier,
|
||||||
|
})
|
||||||
const blobBin = atob(dataUrl.split(',')[1])
|
const blobBin = atob(dataUrl.split(',')[1])
|
||||||
const array = []
|
const array = []
|
||||||
for (let i = 0; i < blobBin.length; i++) {
|
for (let i = 0; i < blobBin.length; i++) {
|
||||||
@ -69,13 +74,13 @@ export function useImgLoader() {
|
|||||||
formData.append('objectNo', currentCanvasPlan.objectNo)
|
formData.append('objectNo', currentCanvasPlan.objectNo)
|
||||||
formData.append('planNo', currentCanvasPlan.planNo)
|
formData.append('planNo', currentCanvasPlan.planNo)
|
||||||
formData.append('type', type)
|
formData.append('type', type)
|
||||||
/** 이미지 크롭 좌표 계산 */
|
/** 이미지 크롭 좌표 계산 (multiplier 배율 적용) */
|
||||||
const positionObj = getImageCoordinates()
|
const positionObj = getImageCoordinates()
|
||||||
console.log('🚀 ~ handleCanvasToPng ~ positionObj:', positionObj)
|
console.log('🚀 ~ handleCanvasToPng ~ positionObj:', positionObj)
|
||||||
formData.append('width', Math.round(positionObj[1].x - positionObj[0].x + 100))
|
formData.append('width', Math.round((positionObj[1].x - positionObj[0].x + 100) * multiplier))
|
||||||
formData.append('height', Math.round(positionObj[1].y - positionObj[0].y + 100))
|
formData.append('height', Math.round((positionObj[1].y - positionObj[0].y + 100) * multiplier))
|
||||||
formData.append('left', Math.round(positionObj[0].x))
|
formData.append('left', Math.round(positionObj[0].x * multiplier))
|
||||||
formData.append('top', Math.round(positionObj[0].y))
|
formData.append('top', Math.round(positionObj[0].y * multiplier))
|
||||||
console.log('🚀 ~ handleCanvasToPng ~ formData:', formData)
|
console.log('🚀 ~ handleCanvasToPng ~ formData:', formData)
|
||||||
|
|
||||||
/** 이미지 크롭 요청 */
|
/** 이미지 크롭 요청 */
|
||||||
|
|||||||
@ -749,7 +749,7 @@ export function useModule() {
|
|||||||
const copyModules = []
|
const copyModules = []
|
||||||
const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)
|
const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)
|
||||||
let isWarning = false
|
let isWarning = false
|
||||||
const { moduleIntvlHor, moduleIntvlVer } = moduleSetupSurface.trestleDetail
|
const { moduleIntvlHor = 0, moduleIntvlVer = 0 } = moduleSetupSurface.trestleDetail || {}
|
||||||
canvas.discardActiveObject()
|
canvas.discardActiveObject()
|
||||||
targetModules.forEach((module) => {
|
targetModules.forEach((module) => {
|
||||||
const { top, left } = getPosotion(module, type, moduleIntvlHor, true)
|
const { top, left } = getPosotion(module, type, moduleIntvlHor, true)
|
||||||
@ -859,7 +859,7 @@ export function useModule() {
|
|||||||
const copyModules = []
|
const copyModules = []
|
||||||
const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)
|
const moduleSetupSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.id === activeModule.surfaceId)
|
||||||
let isWarning = false
|
let isWarning = false
|
||||||
const { moduleIntvlHor, moduleIntvlVer } = moduleSetupSurface.trestleDetail
|
const { moduleIntvlHor = 0, moduleIntvlVer = 0 } = moduleSetupSurface.trestleDetail || {}
|
||||||
canvas.discardActiveObject()
|
canvas.discardActiveObject()
|
||||||
targetModules.forEach((module) => {
|
targetModules.forEach((module) => {
|
||||||
const { top, left } = getPosotion(module, type, moduleIntvlVer, true)
|
const { top, left } = getPosotion(module, type, moduleIntvlVer, true)
|
||||||
|
|||||||
@ -17,7 +17,7 @@ import {
|
|||||||
import { calculateVisibleModuleHeight, getDegreeByChon, polygonToTurfPolygon, rectToPolygon, toFixedWithoutRounding } from '@/util/canvas-util'
|
import { calculateVisibleModuleHeight, getDegreeByChon, polygonToTurfPolygon, rectToPolygon, toFixedWithoutRounding } from '@/util/canvas-util'
|
||||||
import '@/util/fabric-extensions' // fabric 객체들에 getCurrentPoints 메서드 추가
|
import '@/util/fabric-extensions' // fabric 객체들에 getCurrentPoints 메서드 추가
|
||||||
import { basicSettingState, roofDisplaySelector } from '@/store/settingAtom'
|
import { basicSettingState, roofDisplaySelector } from '@/store/settingAtom'
|
||||||
import offsetPolygon, { calculateAngle, createLinesFromPolygon } from '@/util/qpolygon-utils'
|
import offsetPolygon, { calculateAngle, cleanSelfIntersectingPolygon, createLinesFromPolygon } from '@/util/qpolygon-utils'
|
||||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
import { useEvent } from '@/hooks/useEvent'
|
import { useEvent } from '@/hooks/useEvent'
|
||||||
import { BATCH_TYPE, LINE_TYPE, MODULE_SETUP_TYPE, POLYGON_TYPE } from '@/common/common'
|
import { BATCH_TYPE, LINE_TYPE, MODULE_SETUP_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||||
@ -338,10 +338,27 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
let isNorth = false
|
let isNorth = false
|
||||||
|
const defaultTrestleDetail = {
|
||||||
|
rackYn: 'N',
|
||||||
|
moduleIntvlHor: +roofSizeSet === 3 ? 300 : 0,
|
||||||
|
moduleIntvlVer: +roofSizeSet === 3 ? 100 : 0,
|
||||||
|
rack: null,
|
||||||
|
rackQty: 0,
|
||||||
|
rackIntvlPct: 0,
|
||||||
|
cvrPlvrYn: 'N',
|
||||||
|
lessSupFitIntvlPct: 0,
|
||||||
|
lessSupFitQty: 0,
|
||||||
|
}
|
||||||
|
|
||||||
const isExistSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.parentId === roof.id)
|
const isExistSurface = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE && obj.parentId === roof.id)
|
||||||
|
const normalizedTrestleDetail = trestleDetail
|
||||||
|
? { ...defaultTrestleDetail, ...trestleDetail }
|
||||||
|
: isExistSurface?.trestleDetail
|
||||||
|
? { ...defaultTrestleDetail, ...isExistSurface.trestleDetail }
|
||||||
|
: defaultTrestleDetail
|
||||||
|
|
||||||
if (isExistSurface) {
|
if (isExistSurface) {
|
||||||
|
isExistSurface.set({ trestleDetail: normalizedTrestleDetail })
|
||||||
if (canvasSetting.roofSizeSet != '3') {
|
if (canvasSetting.roofSizeSet != '3') {
|
||||||
//북면이 있지만
|
//북면이 있지만
|
||||||
if (roof.directionText && roof.directionText.indexOf('北') > -1) {
|
if (roof.directionText && roof.directionText.indexOf('北') > -1) {
|
||||||
@ -384,6 +401,8 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
} else {
|
} else {
|
||||||
offsetPoints = createPaddingPolygon(polygon, roof.lines).vertices
|
offsetPoints = createPaddingPolygon(polygon, roof.lines).vertices
|
||||||
}
|
}
|
||||||
|
// 자기교차(꼬임) 제거
|
||||||
|
offsetPoints = cleanSelfIntersectingPolygon(offsetPoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
//모듈설치영역?? 생성
|
//모듈설치영역?? 생성
|
||||||
@ -422,7 +441,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
originY: 'center',
|
originY: 'center',
|
||||||
modules: [],
|
modules: [],
|
||||||
roofMaterial: roof.roofMaterial,
|
roofMaterial: roof.roofMaterial,
|
||||||
trestleDetail: trestleDetail,
|
trestleDetail: normalizedTrestleDetail,
|
||||||
isNorth: isNorth,
|
isNorth: isNorth,
|
||||||
perPixelTargetFind: true,
|
perPixelTargetFind: true,
|
||||||
isSaleStoreNorthFlg: moduleSelectionData.common.saleStoreNorthFlg == '1' ? true : false, //북면설치가능점 여부
|
isSaleStoreNorthFlg: moduleSelectionData.common.saleStoreNorthFlg == '1' ? true : false, //북면설치가능점 여부
|
||||||
@ -2103,7 +2122,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//흐름 방향이 남쪽(아래)
|
//흐름 방향이 남쪽(아래)
|
||||||
const downFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
|
const downFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => {
|
||||||
let setupModule = []
|
let setupModule = []
|
||||||
|
|
||||||
const trestleDetailData = moduleSetupSurface.trestleDetail
|
const trestleDetailData = moduleSetupSurface.trestleDetail
|
||||||
@ -2182,6 +2201,11 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
calcAreaHeight = isNaN(calcAreaHeight) ? moduleSetupSurface.height : calcAreaHeight
|
calcAreaHeight = isNaN(calcAreaHeight) ? moduleSetupSurface.height : calcAreaHeight
|
||||||
let calcModuleHeightCount = calcAreaHeight / (height + intvVer)
|
let calcModuleHeightCount = calcAreaHeight / (height + intvVer)
|
||||||
|
|
||||||
|
// 대칭 지붕을 위해 south의 calcAreaWidth 저장 (north에서 참조)
|
||||||
|
if (symmetricWidthRef && moduleIndex === 0) {
|
||||||
|
symmetricWidthRef.south = calcAreaWidth
|
||||||
|
}
|
||||||
|
|
||||||
if (type === MODULE_SETUP_TYPE.LAYOUT) {
|
if (type === MODULE_SETUP_TYPE.LAYOUT) {
|
||||||
calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
|
calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
|
||||||
calcModuleHeightCount = layoutRow
|
calcModuleHeightCount = layoutRow
|
||||||
@ -2205,7 +2229,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
//첫번재 모듈 설치 후 두번째 모듈을 몇개까지 설치 할 수 있는지 계산
|
//첫번재 모듈 설치 후 두번째 모듈을 몇개까지 설치 할 수 있는지 계산
|
||||||
if (installedModuleHeightCount > 0) {
|
if (installedModuleHeightCount > 0) {
|
||||||
// moduleMaxRows = totalModuleMaxRows - installedModuleHeightCount //두번째 모듈일때
|
// moduleMaxRows = totalModuleMaxRows - installedModuleHeightCount //두번째 모듈일때
|
||||||
isChidoriLine = installedModuleHeightCount % 2 != 0 ? true : false //첫번째에서 짝수에서 끝났으면 홀수는 치도리가 아님 짝수는 치도리
|
isChidoriLine = installedModuleHeightCount % 2 !== 0 //첫번째에서 짝수에서 끝났으면 홀수는 치도리가 아님 짝수는 치도리
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < calcModuleHeightCount; i++) {
|
for (let i = 0; i < calcModuleHeightCount; i++) {
|
||||||
@ -2225,7 +2249,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
widthMargin = j === 0 ? 0 : intvHor * j // 가로 마진값
|
widthMargin = j === 0 ? 0 : intvHor * j // 가로 마진값
|
||||||
chidoriLength = 0 //치도리가 아니여도 기본값을 5정도 준다
|
chidoriLength = 0 //치도리가 아니여도 기본값을 5정도 준다
|
||||||
if (isChidori) {
|
if (isChidori) {
|
||||||
chidoriLength = installedModuleHeightCount % 2 == 0 ? 0 : width / 2 - intvHor
|
chidoriLength = installedModuleHeightCount % 2 === 0 ? 0 : width / 2 - intvHor
|
||||||
}
|
}
|
||||||
|
|
||||||
//치도리 일때 는 짝수(1 기준) 일때만 치도리 라인으로 본다
|
//치도리 일때 는 짝수(1 기준) 일때만 치도리 라인으로 본다
|
||||||
@ -2285,7 +2309,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const topFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
|
const topFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => {
|
||||||
let setupModule = []
|
let setupModule = []
|
||||||
|
|
||||||
const trestleDetailData = moduleSetupSurface.trestleDetail
|
const trestleDetailData = moduleSetupSurface.trestleDetail
|
||||||
@ -2364,9 +2388,22 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
||||||
const moduleArray = []
|
const moduleArray = []
|
||||||
|
|
||||||
let calcAreaWidth = flowLines.right.x1 - flowLines.left.x1 //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직
|
// 북쪽: 남쪽과 동일한 방식으로 계산 (대칭을 위해)
|
||||||
|
let calcAreaWidth = Math.abs(flowLines.right.x1 - flowLines.left.x1) //오른쪽 x에서 왼쪽 x를 뺀 가운데를 찾는 로직
|
||||||
|
|
||||||
|
// 대칭 지붕: south의 calcAreaWidth가 있고 north의 값이 south보다 10% 이상 작으면 south 값 사용
|
||||||
|
if (symmetricWidthRef?.south && calcAreaWidth < symmetricWidthRef.south * 0.9) {
|
||||||
|
// flowLines 좌표도 보정 (중심점 유지하면서 너비 확장)
|
||||||
|
const center = (flowLines.right.x1 + flowLines.left.x1) / 2
|
||||||
|
const halfWidth = symmetricWidthRef.south / 2
|
||||||
|
flowLines.left.x1 = center - halfWidth
|
||||||
|
flowLines.right.x1 = center + halfWidth
|
||||||
|
|
||||||
|
calcAreaWidth = symmetricWidthRef.south
|
||||||
|
}
|
||||||
|
|
||||||
let calcModuleWidthCount = calcAreaWidth / (width + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
let calcModuleWidthCount = calcAreaWidth / (width + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
||||||
let calcAreaHeight = flowLines.bottom.y1 - flowLines.top.y1
|
let calcAreaHeight = Math.abs(flowLines.bottom.y1 - flowLines.top.y1)
|
||||||
let calcModuleHeightCount = calcAreaHeight / (height + intvVer)
|
let calcModuleHeightCount = calcAreaHeight / (height + intvVer)
|
||||||
|
|
||||||
//단수지정 자동이면
|
//단수지정 자동이면
|
||||||
@ -2467,7 +2504,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
|
|
||||||
//남, 북과 같은 로직으로 적용하려면 좌우는 열 -> 행 으로 그려야함
|
//남, 북과 같은 로직으로 적용하려면 좌우는 열 -> 행 으로 그려야함
|
||||||
//변수명은 bottom 기준으로 작성하여 동일한 방향으로 진행한다
|
//변수명은 bottom 기준으로 작성하여 동일한 방향으로 진행한다
|
||||||
const leftFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
|
const leftFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => {
|
||||||
let setupModule = []
|
let setupModule = []
|
||||||
|
|
||||||
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
|
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
|
||||||
@ -2555,6 +2592,11 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
let calcAreaHeight = Math.abs(flowLines.right.x1 - flowLines.left.x1)
|
let calcAreaHeight = Math.abs(flowLines.right.x1 - flowLines.left.x1)
|
||||||
let calcModuleHeightCount = calcAreaHeight / (width + intvVer)
|
let calcModuleHeightCount = calcAreaHeight / (width + intvVer)
|
||||||
|
|
||||||
|
// 대칭 지붕을 위해 west의 calcAreaWidth 저장 (east에서 참조)
|
||||||
|
if (symmetricWidthRef && moduleIndex === 0) {
|
||||||
|
symmetricWidthRef.west = calcAreaWidth
|
||||||
|
}
|
||||||
|
|
||||||
//단수지정 자동이면
|
//단수지정 자동이면
|
||||||
if (type === MODULE_SETUP_TYPE.LAYOUT) {
|
if (type === MODULE_SETUP_TYPE.LAYOUT) {
|
||||||
calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
|
calcModuleWidthCount = layoutCol > calcModuleWidthCount ? calcModuleWidthCount : layoutCol
|
||||||
@ -2653,7 +2695,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rightFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer) => {
|
const rightFlowSetupModule = (maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef) => {
|
||||||
let setupModule = []
|
let setupModule = []
|
||||||
|
|
||||||
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
|
const trestleDetailData = moduleSetupSurface.trestleDetail //가대 상세 데이터
|
||||||
@ -2734,9 +2776,22 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
//육지붕이 아닐때만 넣는다 육지붕일땐 클릭 이벤트에 별도로 넣어놓음
|
||||||
const moduleArray = []
|
const moduleArray = []
|
||||||
|
|
||||||
let calcAreaWidth = flowLines.bottom.y1 - flowLines.top.y1 //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직
|
// 동쪽: 서쪽과 동일한 방식으로 계산 (대칭을 위해)
|
||||||
|
let calcAreaWidth = Math.abs(flowLines.bottom.y1 - flowLines.top.y1) //아래에서 y에서 위를 y를 뺀 가운데를 찾는 로직
|
||||||
|
|
||||||
|
// 대칭 지붕: west의 calcAreaWidth가 있고 east의 값이 west보다 10% 이상 작으면 west 값 사용
|
||||||
|
if (symmetricWidthRef?.west && calcAreaWidth < symmetricWidthRef.west * 0.9) {
|
||||||
|
// flowLines 좌표도 보정 (중심점 유지하면서 높이 확장)
|
||||||
|
const center = (flowLines.bottom.y1 + flowLines.top.y1) / 2
|
||||||
|
const halfHeight = symmetricWidthRef.west / 2
|
||||||
|
flowLines.top.y1 = center - halfHeight
|
||||||
|
flowLines.bottom.y1 = center + halfHeight
|
||||||
|
|
||||||
|
calcAreaWidth = symmetricWidthRef.west
|
||||||
|
}
|
||||||
|
|
||||||
let calcModuleWidthCount = calcAreaWidth / (height + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
let calcModuleWidthCount = calcAreaWidth / (height + intvHor) //뺀 공간에서 모듈을 몇개를 넣을수 있는지 확인하는 로직
|
||||||
let calcAreaHeight = flowLines.right.x1 - flowLines.left.x1
|
let calcAreaHeight = Math.abs(flowLines.right.x1 - flowLines.left.x1)
|
||||||
let calcModuleHeightCount = calcAreaHeight / (width + intvVer)
|
let calcModuleHeightCount = calcAreaHeight / (width + intvVer)
|
||||||
|
|
||||||
//단수지정 자동이면
|
//단수지정 자동이면
|
||||||
@ -2746,15 +2801,14 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
let calcMaxModuleWidthCount = calcModuleWidthCount > moduleMaxCols ? moduleMaxCols : calcModuleWidthCount //최대 모듈 단수가 있기 때문에 최대 단수보다 카운트가 크면 최대 단수로 씀씀
|
||||||
// let totalModuleWidthCount = isChidori ? Math.abs(calcMaxModuleWidthCount) : Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount)
|
||||||
let totalModuleWidthCount = Math.floor(calcMaxModuleWidthCount) //치조배치일경우는 한개 더 넣는다
|
|
||||||
|
|
||||||
let calcStartPoint = flowLines.top.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * height) / 2 : 0 //반씩 나눠서 중앙에 맞춤 left 높이 기준으로 양변이 직선일때만 가운데 정렬
|
let calcStartPoint = flowLines.top.type === 'flat' ? (calcAreaWidth - totalModuleWidthCount * height) / 2 : 0 //반씩 나눠서 중앙에 맞춤 left 높이 기준으로 양변이 직선일때만 가운데 정렬
|
||||||
let startPointX = flowLines.bottom.y2 - calcStartPoint //시작점을 만든다
|
let startPointX = flowLines.bottom.y1 - calcStartPoint //시작점을 만든다
|
||||||
|
|
||||||
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
|
//근데 양변이 곡선이면 중앙에 맞추기 위해 아래와 위의 길이를 재서 모듈의 길이를 나눠서 들어갈수 있는 갯수가 동일하면 가운데로 정렬 시킨다
|
||||||
if (flowLines.top.type === 'curve' && flowLines.bottom.type === 'curve') {
|
if (flowLines.top.type === 'curve' && flowLines.bottom.type === 'curve') {
|
||||||
startPointX = flowLines.bottom.y2 - (calcAreaWidth - totalModuleWidthCount * height) / 2
|
startPointX = flowLines.bottom.y1 - (calcAreaWidth - totalModuleWidthCount * height) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
let heightMargin = 0
|
let heightMargin = 0
|
||||||
@ -2841,7 +2895,18 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
moduleSetupSurfaces.forEach((moduleSetupSurface, index) => {
|
// 대칭 지붕을 위한 calcAreaWidth 공유 객체 (south→north, west→east)
|
||||||
|
const symmetricWidthRef = { south: null, west: null }
|
||||||
|
|
||||||
|
// 대칭 보정을 위해 south/west가 north/east보다 먼저 처리되도록 정렬
|
||||||
|
const directionOrder = { south: 0, west: 1, north: 2, east: 3 }
|
||||||
|
const sortedModuleSetupSurfaces = [...moduleSetupSurfaces].sort((a, b) => {
|
||||||
|
const orderA = directionOrder[a.direction] ?? 4
|
||||||
|
const orderB = directionOrder[b.direction] ?? 4
|
||||||
|
return orderA - orderB
|
||||||
|
})
|
||||||
|
|
||||||
|
sortedModuleSetupSurfaces.forEach((moduleSetupSurface, index) => {
|
||||||
moduleSetupSurface.fire('mousedown')
|
moduleSetupSurface.fire('mousedown')
|
||||||
const moduleSetupArray = []
|
const moduleSetupArray = []
|
||||||
|
|
||||||
@ -2867,30 +2932,30 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
if (setupLocation === 'eaves') {
|
if (setupLocation === 'eaves') {
|
||||||
// 흐름방향이 남쪽일때
|
// 흐름방향이 남쪽일때
|
||||||
if (moduleSetupSurface.direction === 'south') {
|
if (moduleSetupSurface.direction === 'south') {
|
||||||
downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
if (moduleSetupSurface.direction === 'west') {
|
if (moduleSetupSurface.direction === 'west') {
|
||||||
leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
if (moduleSetupSurface.direction === 'east') {
|
if (moduleSetupSurface.direction === 'east') {
|
||||||
rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
if (moduleSetupSurface.direction === 'north') {
|
if (moduleSetupSurface.direction === 'north') {
|
||||||
topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
} else if (setupLocation === 'ridge') {
|
} else if (setupLocation === 'ridge') {
|
||||||
//용마루
|
//용마루
|
||||||
if (moduleSetupSurface.direction === 'south') {
|
if (moduleSetupSurface.direction === 'south') {
|
||||||
topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
topFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
if (moduleSetupSurface.direction === 'west') {
|
if (moduleSetupSurface.direction === 'west') {
|
||||||
rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
rightFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
if (moduleSetupSurface.direction === 'east') {
|
if (moduleSetupSurface.direction === 'east') {
|
||||||
leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
leftFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
if (moduleSetupSurface.direction === 'north') {
|
if (moduleSetupSurface.direction === 'north') {
|
||||||
downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer)
|
downFlowSetupModule(maxLengthLine, moduleSetupArray, moduleSetupSurface, containsBatchObjects, intvHor, intvVer, symmetricWidthRef)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3069,6 +3134,18 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
y2: standardLine.y2,
|
y2: standardLine.y2,
|
||||||
type: 'flat',
|
type: 'flat',
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// NaN 체크: offset이 너무 커서 꼭짓점을 넘어가면 NaN 발생
|
||||||
|
if (isNaN(pointX1) || isNaN(pointX2) || isNaN(pointY1) || isNaN(pointY2)) {
|
||||||
|
// NaN이면 꼭짓점 좌표 사용 (모듈 설치 영역 없음)
|
||||||
|
rtnObj = {
|
||||||
|
target: index === 0 ? 'bottom' : 'top',
|
||||||
|
x1: center.x1,
|
||||||
|
y1: center.y1,
|
||||||
|
x2: center.x1,
|
||||||
|
y2: center.y1,
|
||||||
|
type: 'curve',
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rtnObj = {
|
rtnObj = {
|
||||||
target: index === 0 ? 'bottom' : 'top',
|
target: index === 0 ? 'bottom' : 'top',
|
||||||
@ -3079,6 +3156,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
type: 'curve',
|
type: 'curve',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rtnObjArray.push(rtnObj)
|
rtnObjArray.push(rtnObj)
|
||||||
})
|
})
|
||||||
@ -3201,6 +3279,18 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
y2: standardLine.y2,
|
y2: standardLine.y2,
|
||||||
type: 'flat',
|
type: 'flat',
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// NaN 체크: offset이 너무 커서 꼭짓점을 넘어가면 NaN 발생
|
||||||
|
if (isNaN(pointX1) || isNaN(pointX2) || isNaN(pointY1) || isNaN(pointY2)) {
|
||||||
|
// NaN이면 꼭짓점 좌표 사용 (모듈 설치 영역 없음)
|
||||||
|
rtnObj = {
|
||||||
|
target: index === 0 ? 'left' : 'right',
|
||||||
|
x1: center.x1,
|
||||||
|
y1: center.y1,
|
||||||
|
x2: center.x1,
|
||||||
|
y2: center.y1,
|
||||||
|
type: 'curve',
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
rtnObj = {
|
rtnObj = {
|
||||||
target: index === 0 ? 'left' : 'right',
|
target: index === 0 ? 'left' : 'right',
|
||||||
@ -3211,6 +3301,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
type: 'curve',
|
type: 'curve',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
rtnObjArray.push(rtnObj)
|
rtnObjArray.push(rtnObj)
|
||||||
})
|
})
|
||||||
return rtnObjArray
|
return rtnObjArray
|
||||||
@ -4099,6 +4190,7 @@ export function useModuleBasicSetting(tabNum) {
|
|||||||
left: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'left'),
|
left: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'left'),
|
||||||
right: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'right'),
|
right: leftRightFlowLine(moduleSetupSurface, length).find((obj) => obj.target === 'right'),
|
||||||
}
|
}
|
||||||
|
|
||||||
return flowLines
|
return flowLines
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -61,7 +61,7 @@ export function useModuleTrestle(props) {
|
|||||||
const [lengthBase, setLengthBase] = useState(0)
|
const [lengthBase, setLengthBase] = useState(0)
|
||||||
const [hajebichi, setHajebichi] = useState(0)
|
const [hajebichi, setHajebichi] = useState(0)
|
||||||
const [cvrYn, setCvrYn] = useState('N')
|
const [cvrYn, setCvrYn] = useState('N')
|
||||||
const [cvrChecked, setCvrChecked] = useState(false)
|
const [cvrChecked, setCvrChecked] = useState(true)
|
||||||
const [snowGdPossYn, setSnowGdPossYn] = useState('N')
|
const [snowGdPossYn, setSnowGdPossYn] = useState('N')
|
||||||
const [snowGdChecked, setSnowGdChecked] = useState(false)
|
const [snowGdChecked, setSnowGdChecked] = useState(false)
|
||||||
const [eavesMargin, setEavesMargin] = useState(0)
|
const [eavesMargin, setEavesMargin] = useState(0)
|
||||||
@ -88,7 +88,7 @@ export function useModuleTrestle(props) {
|
|||||||
setKerabaMargin(selectedRoof?.kerabaMargin ?? 0)
|
setKerabaMargin(selectedRoof?.kerabaMargin ?? 0)
|
||||||
setLengthBase(Math.round(selectedRoof?.length ?? 0))
|
setLengthBase(Math.round(selectedRoof?.length ?? 0))
|
||||||
setCvrYn(selectedRoof?.construction?.cvrYn ?? 'N')
|
setCvrYn(selectedRoof?.construction?.cvrYn ?? 'N')
|
||||||
setCvrChecked(selectedRoof?.construction?.cvrChecked ?? false)
|
setCvrChecked(selectedRoof?.construction?.cvrChecked ?? true)
|
||||||
setSnowGdPossYn(selectedRoof?.construction?.snowGdPossYn ?? 'N')
|
setSnowGdPossYn(selectedRoof?.construction?.snowGdPossYn ?? 'N')
|
||||||
setSnowGdChecked(selectedRoof?.construction?.snowGdChecked ?? false)
|
setSnowGdChecked(selectedRoof?.construction?.snowGdChecked ?? false)
|
||||||
setTrestleDetail(selectedRoof?.trestleDetail)
|
setTrestleDetail(selectedRoof?.trestleDetail)
|
||||||
|
|||||||
@ -65,6 +65,10 @@ export const useTrestle = () => {
|
|||||||
if (+roofSizeSet === 3) {
|
if (+roofSizeSet === 3) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const trestleDetail = surface.trestleDetail
|
||||||
|
if (!trestleDetail) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction
|
const construction = moduleSelectionData?.roofConstructions?.find((construction) => construction.roofIndex === roofMaterialIndex).construction
|
||||||
if (!construction) {
|
if (!construction) {
|
||||||
return
|
return
|
||||||
@ -76,8 +80,8 @@ export const useTrestle = () => {
|
|||||||
let isSnowGuard = construction.setupSnowCover
|
let isSnowGuard = construction.setupSnowCover
|
||||||
let cvrLmtRow = construction.cvrLmtRow
|
let cvrLmtRow = construction.cvrLmtRow
|
||||||
const direction = parent.direction
|
const direction = parent.direction
|
||||||
const rack = surface.trestleDetail.rack
|
const rack = trestleDetail.rack
|
||||||
let { rackQty, rackIntvlPct, rackYn, cvrPlvrYn, lessSupFitIntvlPct, lessSupFitQty } = surface.trestleDetail
|
let { rackQty, rackIntvlPct, rackYn, cvrPlvrYn, lessSupFitIntvlPct, lessSupFitQty } = trestleDetail
|
||||||
|
|
||||||
if (!rack && lessSupFitIntvlPct === 0 && lessSupFitQty === 0) {
|
if (!rack && lessSupFitIntvlPct === 0 && lessSupFitQty === 0) {
|
||||||
//25/02/06 가대없음의 경우 랙정보가 없음
|
//25/02/06 가대없음의 경우 랙정보가 없음
|
||||||
|
|||||||
@ -152,11 +152,17 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
|||||||
|
|
||||||
rect.set({ width: Math.abs(width), height: Math.abs(height) })
|
rect.set({ width: Math.abs(width), height: Math.abs(height) })
|
||||||
|
|
||||||
|
// 마우스를 왼쪽으로 드래그한 경우 left를 현재 포인터 위치로 설정
|
||||||
if (width < 0) {
|
if (width < 0) {
|
||||||
rect.set({ left: Math.abs(pointer.x) })
|
rect.set({ left: pointer.x })
|
||||||
|
} else {
|
||||||
|
rect.set({ left: origX })
|
||||||
}
|
}
|
||||||
|
// 마우스를 위쪽으로 드래그한 경우 top을 현재 포인터 위치로 설정
|
||||||
if (height < 0) {
|
if (height < 0) {
|
||||||
rect.set({ top: Math.abs(pointer.y) })
|
rect.set({ top: pointer.y })
|
||||||
|
} else {
|
||||||
|
rect.set({ top: origY })
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas?.renderAll()
|
canvas?.renderAll()
|
||||||
@ -179,7 +185,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isCrossChecked) {
|
// 그림자(SHADOW)는 중복 설치 허용, 개구(OPENING)만 중복 체크
|
||||||
|
if (!isCrossChecked && buttonAct === 1) {
|
||||||
const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW)
|
const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW)
|
||||||
const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj))
|
const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj))
|
||||||
const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon))
|
const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon))
|
||||||
@ -266,7 +273,8 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isCrossChecked) {
|
// 그림자(SHADOW)는 중복 설치 허용, 개구(OPENING)만 중복 체크
|
||||||
|
if (!isCrossChecked && buttonAct === 1) {
|
||||||
const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW)
|
const preObjects = canvas?.getObjects().filter((obj) => obj.name === BATCH_TYPE.OPENING || obj.name === BATCH_TYPE.SHADOW)
|
||||||
const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj))
|
const preObjectsArray = preObjects.map((obj) => rectToPolygon(obj))
|
||||||
const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon))
|
const isCross = preObjectsArray.some((object) => turf.booleanOverlap(pointsToTurfPolygon(object), rectPolygon))
|
||||||
|
|||||||
@ -179,7 +179,7 @@ export function useCanvasSetting(executeEffect = true) {
|
|||||||
layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL,
|
layout: ['ROOF_ID_SLATE', 'ROOF_ID_SINGLE'].includes(item.roofMatlCd) ? ROOF_MATERIAL_LAYOUT.STAIRS : ROOF_MATERIAL_LAYOUT.PARALLEL,
|
||||||
hajebichi: item.roofPchBase && parseInt(item.roofPchBase),
|
hajebichi: item.roofPchBase && parseInt(item.roofPchBase),
|
||||||
pitch: item.inclBase ? parseInt(item.inclBase) : 4,
|
pitch: item.inclBase ? parseInt(item.inclBase) : 4,
|
||||||
angle: getDegreeByChon(item.inclBase ? parseInt(item.inclBase): 4) //item.angle ? parseInt(item.angle) : 21.8,
|
angle: getDegreeByChon(item.inclBase ? parseInt(item.inclBase) : 4), //item.angle ? parseInt(item.angle) : 21.8,
|
||||||
}))
|
}))
|
||||||
setRoofMaterials(roofLists)
|
setRoofMaterials(roofLists)
|
||||||
return roofLists
|
return roofLists
|
||||||
@ -231,8 +231,9 @@ export function useCanvasSetting(executeEffect = true) {
|
|||||||
setPolygonLinesActualSize(roof)
|
setPolygonLinesActualSize(roof)
|
||||||
})
|
})
|
||||||
changeCorridorDimensionText()
|
changeCorridorDimensionText()
|
||||||
|
canvas.renderAll()
|
||||||
}
|
}
|
||||||
}, [corridorDimension])
|
}, [corridorDimension, canvasSetting])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!executeEffect) {
|
if (!executeEffect) {
|
||||||
@ -801,25 +802,25 @@ export function useCanvasSetting(executeEffect = true) {
|
|||||||
/** 문자 글꼴 설정 */
|
/** 문자 글꼴 설정 */
|
||||||
wordFont: globalFont.commonText.fontFamily?.value ?? 'MS PGothic',
|
wordFont: globalFont.commonText.fontFamily?.value ?? 'MS PGothic',
|
||||||
wordFontStyle: globalFont.commonText.fontWeight?.value ?? 'normal',
|
wordFontStyle: globalFont.commonText.fontWeight?.value ?? 'normal',
|
||||||
wordFontSize: globalFont.commonText.fontSize?.value ?? 16,
|
wordFontSize: globalFont.commonText.fontSize?.value ?? 28,
|
||||||
wordFontColor: globalFont.commonText.fontColor?.value ?? 'black',
|
wordFontColor: globalFont.commonText.fontColor?.value ?? 'black',
|
||||||
|
|
||||||
/** 흐름방향 글꼴 설정 */
|
/** 흐름방향 글꼴 설정 */
|
||||||
flowFont: globalFont.flowText.fontFamily?.value ?? 'MS PGothic',
|
flowFont: globalFont.flowText.fontFamily?.value ?? 'MS PGothic',
|
||||||
flowFontStyle: globalFont.flowText.fontWeight?.value ?? 'normal',
|
flowFontStyle: globalFont.flowText.fontWeight?.value ?? 'normal',
|
||||||
flowFontSize: globalFont.flowText.fontSize?.value ?? 16,
|
flowFontSize: globalFont.flowText.fontSize?.value ?? 28,
|
||||||
flowFontColor: globalFont.flowText.fontColor?.value ?? 'black',
|
flowFontColor: globalFont.flowText.fontColor?.value ?? 'black',
|
||||||
|
|
||||||
/** 치수 글꼴 설정 */
|
/** 치수 글꼴 설정 */
|
||||||
dimensioFont: globalFont.dimensionLineText.fontFamily?.value ?? 'MS PGothic',
|
dimensioFont: globalFont.dimensionLineText.fontFamily?.value ?? 'MS PGothic',
|
||||||
dimensioFontStyle: globalFont.dimensionLineText.fontWeight?.value ?? 'normal',
|
dimensioFontStyle: globalFont.dimensionLineText.fontWeight?.value ?? 'normal',
|
||||||
dimensioFontSize: globalFont.dimensionLineText.fontSize?.value ?? 16,
|
dimensioFontSize: globalFont.dimensionLineText.fontSize?.value ?? 28,
|
||||||
dimensioFontColor: globalFont.dimensionLineText.fontColor?.value ?? 'black',
|
dimensioFontColor: globalFont.dimensionLineText.fontColor?.value ?? 'black',
|
||||||
|
|
||||||
/** 회로번호 글꼴 설정 */
|
/** 회로번호 글꼴 설정 */
|
||||||
circuitNumFont: globalFont.circuitNumberText.fontFamily?.value ?? 'MS PGothic',
|
circuitNumFont: globalFont.circuitNumberText.fontFamily?.value ?? 'MS PGothic',
|
||||||
circuitNumFontStyle: globalFont.circuitNumberText.fontWeight?.value ?? 'normal',
|
circuitNumFontStyle: globalFont.circuitNumberText.fontWeight?.value ?? 'normal',
|
||||||
circuitNumFontSize: globalFont.circuitNumberText.fontSize?.value ?? 16,
|
circuitNumFontSize: globalFont.circuitNumberText.fontSize?.value ?? 36,
|
||||||
circuitNumFontColor: globalFont.circuitNumberText.fontColor?.value ?? 'black',
|
circuitNumFontColor: globalFont.circuitNumberText.fontColor?.value ?? 'black',
|
||||||
|
|
||||||
/** 치수선 글꼴 설정 */
|
/** 치수선 글꼴 설정 */
|
||||||
|
|||||||
@ -75,13 +75,14 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
|
|||||||
}, [arrow2])
|
}, [arrow2])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
typeRef.current = type
|
|
||||||
clear()
|
|
||||||
if (type === null) {
|
if (type === null) {
|
||||||
initEvent()
|
initEvent()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
initEvent()
|
initEvent()
|
||||||
|
typeRef.current = type
|
||||||
|
clear()
|
||||||
addCanvasMouseEventListener('mouse:move', mouseMove)
|
addCanvasMouseEventListener('mouse:move', mouseMove)
|
||||||
addCanvasMouseEventListener('mouse:down', mouseDown)
|
addCanvasMouseEventListener('mouse:down', mouseDown)
|
||||||
addDocumentEventListener('keydown', document, keydown[type])
|
addDocumentEventListener('keydown', document, keydown[type])
|
||||||
@ -96,6 +97,10 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 지붕의 각 꼭지점을 흡착점으로 설정
|
// 지붕의 각 꼭지점을 흡착점으로 설정
|
||||||
const roofsPoints = roofs.map((roof) => roof.points).flat()
|
const roofsPoints = roofs.map((roof) => roof.points).flat()
|
||||||
roofAdsorptionPoints.current = [...roofsPoints]
|
roofAdsorptionPoints.current = [...roofsPoints]
|
||||||
@ -113,6 +118,9 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (type === null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
const roofs = canvas?.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
if (roofs.length === 0) {
|
if (roofs.length === 0) {
|
||||||
return
|
return
|
||||||
@ -488,6 +496,10 @@ export function useAuxiliaryDrawing(id, isUseEffect = true) {
|
|||||||
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y + length2Value / 10 })
|
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y + length2Value / 10 })
|
||||||
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
||||||
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y - length2Value / 10 })
|
mousePointerArr.current.push({ x: lastPoint.x + length1Value / 10, y: lastPoint.y - length2Value / 10 })
|
||||||
|
} else if (arrow1Value === '←' && arrow2Value === '↓') {
|
||||||
|
mousePointerArr.current.push({ x: lastPoint.x - length1Value / 10, y: lastPoint.y + length2Value / 10 })
|
||||||
|
} else if (arrow1Value === '←' && arrow2Value === '↑') {
|
||||||
|
mousePointerArr.current.push({ x: lastPoint.x - length1Value / 10, y: lastPoint.y - length2Value / 10 })
|
||||||
}
|
}
|
||||||
drawLine()
|
drawLine()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -678,6 +678,32 @@ export function useOuterLineWall(id, propertiesId) {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
} 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,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,9 +48,9 @@ export function useRoofShapePassivitySetting(id) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||||
if (!canvas.outerLineFix || outerLines.length === 0) {
|
if (!canvas.outerLineFix || outerLines.length === 0) {
|
||||||
swalFire({ text: getMessage('wall.line.not.found') })
|
swalFire({ text: getMessage('wall.line.not.found'),icon: 'warning' })
|
||||||
closePopup(id)
|
closePopup(id)
|
||||||
return
|
//return
|
||||||
}
|
}
|
||||||
setIsLoading(true)
|
setIsLoading(true)
|
||||||
}, [])
|
}, [])
|
||||||
@ -142,8 +142,9 @@ export function useRoofShapePassivitySetting(id) {
|
|||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
if (!currentLineRef.current) {
|
if (!currentLineRef.current) {
|
||||||
alert('선택된 외곽선이 없습니다.')
|
//alert('선택된 외곽선이 없습니다.')
|
||||||
return
|
swalFire({ text: getMessage('wall.line.not.selected'), icon: 'warning' })
|
||||||
|
//return
|
||||||
}
|
}
|
||||||
let attributes
|
let attributes
|
||||||
const offset = Number(offsetRef.current.value) / 10
|
const offset = Number(offsetRef.current.value) / 10
|
||||||
@ -210,8 +211,8 @@ export function useRoofShapePassivitySetting(id) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!checkedAllSetting) {
|
if (!checkedAllSetting) {
|
||||||
swalFire({ text: '설정이 완료되지 않은 외벽선이 있습니다.', icon: 'warning' })
|
swalFire({ text: getMessage('modal.canvas.setting.roofline.properties.setting.not.setting'), icon: 'warning' })
|
||||||
return
|
//return
|
||||||
}
|
}
|
||||||
|
|
||||||
exceptObjs.forEach((obj) => {
|
exceptObjs.forEach((obj) => {
|
||||||
|
|||||||
@ -241,6 +241,7 @@ export function usePlacementShapeDrawing(id) {
|
|||||||
originY: 'center',
|
originY: 'center',
|
||||||
direction: 'south',
|
direction: 'south',
|
||||||
pitch: globalPitch,
|
pitch: globalPitch,
|
||||||
|
from: 'surface',
|
||||||
})
|
})
|
||||||
|
|
||||||
setSurfaceShapePattern(roof, roofDisplay.column)
|
setSurfaceShapePattern(roof, roofDisplay.column)
|
||||||
@ -680,6 +681,32 @@ export function usePlacementShapeDrawing(id) {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
} 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,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1045,6 +1045,7 @@ export function useSurfaceShapeBatch({ isHidden, setIsHidden }) {
|
|||||||
newPolygon.set({
|
newPolygon.set({
|
||||||
originWidth: width,
|
originWidth: width,
|
||||||
originHeight: height,
|
originHeight: height,
|
||||||
|
from: 'surface',
|
||||||
})
|
})
|
||||||
|
|
||||||
// 캔버스에 추가
|
// 캔버스에 추가
|
||||||
|
|||||||
@ -117,7 +117,6 @@ export function useContextMenu() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
currentMenuSetting()
|
currentMenuSetting()
|
||||||
}, [gridColor, currentMenu])
|
}, [gridColor, currentMenu])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentContextMenu?.component) addPopup(popupId, 1, currentContextMenu?.component)
|
if (currentContextMenu?.component) addPopup(popupId, 1, currentContextMenu?.component)
|
||||||
}, [currentContextMenu])
|
}, [currentContextMenu])
|
||||||
@ -158,11 +157,40 @@ export function useContextMenu() {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case 'outerLine': {
|
||||||
|
setContextMenu([
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: 'roofMaterialPlacement',
|
||||||
|
name: getMessage('contextmenu.roof.material.placement'),
|
||||||
|
component: <RoofAllocationSetting id={popupId} />,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 'roofMaterialRemoveAll',
|
||||||
|
name: getMessage('contextmenu.roof.material.remove.all'),
|
||||||
|
fn: () => removeAllRoofMaterial(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'selectMove',
|
||||||
|
name: getMessage('contextmenu.select.move'),
|
||||||
|
fn: (currentMousePos) => {
|
||||||
|
moveRoofMaterial(currentMousePos)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'wallLineRemove',
|
||||||
|
name: getMessage('contextmenu.wallline.remove'),
|
||||||
|
fn: (currentMousePos) => {
|
||||||
|
removeOuterLines(currentMousePos)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
break
|
||||||
|
}
|
||||||
case 'roof':
|
case 'roof':
|
||||||
case 'auxiliaryLine':
|
|
||||||
case 'hip':
|
|
||||||
case 'ridge':
|
|
||||||
case 'eaveHelpLine':
|
|
||||||
if (selectedMenu === 'surface') {
|
if (selectedMenu === 'surface') {
|
||||||
setContextMenu([
|
setContextMenu([
|
||||||
[
|
[
|
||||||
@ -249,6 +277,73 @@ export function useContextMenu() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
case 'auxiliaryLine':
|
||||||
|
case 'hip':
|
||||||
|
case 'ridge':
|
||||||
|
case 'eaveHelpLine':
|
||||||
|
if (selectedMenu === 'surface') {
|
||||||
|
setContextMenu([
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: 'sizeEdit',
|
||||||
|
name: getMessage('contextmenu.size.edit'),
|
||||||
|
component: <SizeSetting id={popupId} target={currentObject} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'rotate',
|
||||||
|
name: `${getMessage('contextmenu.rotate')}`,
|
||||||
|
fn: () => rotateSurfaceShapeBatch(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'roofMaterialRemove',
|
||||||
|
shortcut: ['d', 'D'],
|
||||||
|
name: `${getMessage('contextmenu.remove')}(D)`,
|
||||||
|
fn: () => deleteObject(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'roofMaterialMove',
|
||||||
|
shortcut: ['m', 'M'],
|
||||||
|
name: `${getMessage('contextmenu.move')}(M)`,
|
||||||
|
fn: () => moveSurfaceShapeBatch(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'roofMaterialCopy',
|
||||||
|
shortcut: ['c', 'C'],
|
||||||
|
name: `${getMessage('contextmenu.copy')}(C)`,
|
||||||
|
fn: () => copyObject(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: 'roofMaterialEdit',
|
||||||
|
name: getMessage('contextmenu.roof.material.edit'),
|
||||||
|
component: <ContextRoofAllocationSetting id={popupId} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'linePropertyEdit',
|
||||||
|
name: getMessage('contextmenu.line.property.edit'),
|
||||||
|
fn: () => {
|
||||||
|
if (+canvasSetting.roofSizeSet === 3) {
|
||||||
|
swalFire({ text: getMessage('contextmenu.line.property.edit.roof.size.3') })
|
||||||
|
} else {
|
||||||
|
addPopup(popupId, 1, <PlacementSurfaceLineProperty id={popupId} roof={currentObject} />)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// component: <LinePropertySetting id={popupId} target={currentObject} />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'flowDirectionEdit',
|
||||||
|
name: getMessage('contextmenu.flow.direction.edit'),
|
||||||
|
component: <FlowDirectionSetting id={popupId} target={currentObject} />,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
])
|
||||||
|
} else if (selectedMenu === 'outline') {
|
||||||
|
setContextMenu([
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
id: 'sizeEdit',
|
id: 'sizeEdit',
|
||||||
|
|||||||
@ -166,8 +166,12 @@ export const useLine = () => {
|
|||||||
* @param line
|
* @param line
|
||||||
* @param direction polygon의 방향
|
* @param direction polygon의 방향
|
||||||
* @param pitch
|
* @param pitch
|
||||||
|
* @param forceUpdate
|
||||||
*/
|
*/
|
||||||
const setActualSize = (line, direction, pitch = globalPitch) => {
|
const setActualSize = (line, direction, pitch = globalPitch, forceUpdate = false) => {
|
||||||
|
if (line.attributes.isCalculated && !forceUpdate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
const { x1, y1, x2, y2 } = line
|
const { x1, y1, x2, y2 } = line
|
||||||
|
|
||||||
const isHorizontal = y1 === y2
|
const isHorizontal = y1 === y2
|
||||||
@ -209,7 +213,11 @@ export const useLine = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
line.attributes = { ...line.attributes, actualSize: Number(line.attributes.actualSize.toFixed(0)) }
|
line.attributes = {
|
||||||
|
...line.attributes,
|
||||||
|
actualSize: Number(line.attributes.actualSize.toFixed(0)),
|
||||||
|
isCalculated: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -973,7 +973,6 @@ export const usePolygon = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
|
|
||||||
/*polygonLines.forEach((line) => {
|
/*polygonLines.forEach((line) => {
|
||||||
line.set({ strokeWidth: 10 })
|
line.set({ strokeWidth: 10 })
|
||||||
canvas.add(line)
|
canvas.add(line)
|
||||||
@ -1023,7 +1022,7 @@ export const usePolygon = () => {
|
|||||||
for (let i = divideLines.length - 1; i >= 0; i--) {
|
for (let i = divideLines.length - 1; i >= 0; i--) {
|
||||||
const line = divideLines[i]
|
const line = divideLines[i]
|
||||||
const { intersections, startPoint, endPoint } = line
|
const { intersections, startPoint, endPoint } = line
|
||||||
console.log("intersections::::::::::", intersections)
|
|
||||||
if (intersections.length === 1) {
|
if (intersections.length === 1) {
|
||||||
const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y]
|
const newLinePoint1 = [line.x1, line.y1, intersections[0].x, intersections[0].y]
|
||||||
const newLinePoint2 = [intersections[0].x, intersections[0].y, line.x2, line.y2]
|
const newLinePoint2 = [intersections[0].x, intersections[0].y, line.x2, line.y2]
|
||||||
@ -1043,16 +1042,25 @@ export const usePolygon = () => {
|
|||||||
name: 'newLine',
|
name: 'newLine',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 두 라인 중 큰 길이로 통일
|
||||||
|
const length1 = Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10
|
||||||
|
const length2 = Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10
|
||||||
|
const maxLength = Math.max(length1, length2)
|
||||||
|
const unifiedPlaneSize = line.attributes.planeSize ?? maxLength
|
||||||
|
const unifiedActualSize = line.attributes.actualSize ?? maxLength
|
||||||
|
|
||||||
newLine1.attributes = {
|
newLine1.attributes = {
|
||||||
...line.attributes,
|
...line.attributes,
|
||||||
planeSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10,
|
planeSize: unifiedPlaneSize,
|
||||||
actualSize: Math.round(Math.hypot(newLine1.x1 - newLine1.x2, newLine1.y1 - newLine1.y2)) * 10,
|
actualSize: unifiedActualSize,
|
||||||
}
|
}
|
||||||
|
newLine1.length = maxLength
|
||||||
newLine2.attributes = {
|
newLine2.attributes = {
|
||||||
...line.attributes,
|
...line.attributes,
|
||||||
planeSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10,
|
planeSize: unifiedPlaneSize,
|
||||||
actualSize: Math.round(Math.hypot(newLine2.x1 - newLine2.x2, newLine2.y1 - newLine2.y2)) * 10,
|
actualSize: unifiedActualSize,
|
||||||
}
|
}
|
||||||
|
newLine2.length = maxLength
|
||||||
|
|
||||||
newLines.push(newLine1, newLine2)
|
newLines.push(newLine1, newLine2)
|
||||||
divideLines.splice(i, 1) // 기존 line 제거
|
divideLines.splice(i, 1) // 기존 line 제거
|
||||||
@ -1071,11 +1079,13 @@ export const usePolygon = () => {
|
|||||||
name: 'newLine',
|
name: 'newLine',
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const calcLength = Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10
|
||||||
newLine.attributes = {
|
newLine.attributes = {
|
||||||
...line.attributes,
|
...line.attributes,
|
||||||
planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
|
planeSize: line.attributes.planeSize ?? calcLength,
|
||||||
actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
|
actualSize: line.attributes.actualSize ?? calcLength,
|
||||||
}
|
}
|
||||||
|
newLine.length = line.attributes.planeSize ?? calcLength
|
||||||
|
|
||||||
newLines.push(newLine)
|
newLines.push(newLine)
|
||||||
currentPoint = minDistancePoint
|
currentPoint = minDistancePoint
|
||||||
@ -1089,11 +1099,13 @@ export const usePolygon = () => {
|
|||||||
attributes: line.attributes,
|
attributes: line.attributes,
|
||||||
name: 'newLine',
|
name: 'newLine',
|
||||||
})
|
})
|
||||||
|
const lastCalcLength = Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10
|
||||||
newLine.attributes = {
|
newLine.attributes = {
|
||||||
...line.attributes,
|
...line.attributes,
|
||||||
planeSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
|
planeSize: line.attributes.planeSize ?? lastCalcLength,
|
||||||
actualSize: Math.round(Math.hypot(newLine.x1 - newLine.x2, newLine.y1 - newLine.y2)) * 10,
|
actualSize: line.attributes.actualSize ?? lastCalcLength,
|
||||||
}
|
}
|
||||||
|
newLine.length = line.attributes.planeSize ?? lastCalcLength
|
||||||
|
|
||||||
newLines.push(newLine)
|
newLines.push(newLine)
|
||||||
divideLines.splice(i, 1) // 기존 line 제거
|
divideLines.splice(i, 1) // 기존 line 제거
|
||||||
@ -1931,12 +1943,19 @@ export const usePolygon = () => {
|
|||||||
/**
|
/**
|
||||||
* 폴리곤의 라인 속성을 복도치수, 실제치수에 따라 actualSize 설정
|
* 폴리곤의 라인 속성을 복도치수, 실제치수에 따라 actualSize 설정
|
||||||
* @param polygon
|
* @param polygon
|
||||||
|
* @param forceUpdate
|
||||||
*/
|
*/
|
||||||
const setPolygonLinesActualSize = (polygon) => {
|
const setPolygonLinesActualSize = (polygon, forceUpdate = false) => {
|
||||||
if (!polygon.lines || polygon.lines.length === 0 || !polygon.roofMaterial) {
|
if (!polygon.lines || polygon.lines.length === 0 || !polygon.roofMaterial) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 배치면으로 그린 내용은 업데이트를 해준다.
|
||||||
|
if (polygon.from === 'surface') {
|
||||||
|
forceUpdate = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (polygon.from !== 'surface') {
|
||||||
// createdRoofs들의 모든 lines를 확인해서 length값이 1이하인 차이가 있으면 통일 시킨다.
|
// createdRoofs들의 모든 lines를 확인해서 length값이 1이하인 차이가 있으면 통일 시킨다.
|
||||||
const allRoofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
const allRoofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
|
||||||
const allRoofLines = allRoofs.flatMap((roof) => roof.lines)
|
const allRoofLines = allRoofs.flatMap((roof) => roof.lines)
|
||||||
@ -1946,24 +1965,35 @@ export const usePolygon = () => {
|
|||||||
const line2 = allRoofLines[j]
|
const line2 = allRoofLines[j]
|
||||||
const diff = Math.abs(line1.length - line2.length)
|
const diff = Math.abs(line1.length - line2.length)
|
||||||
if (diff > 0 && diff <= 2) {
|
if (diff > 0 && diff <= 2) {
|
||||||
const minLength = Math.min(line1.length, line2.length)
|
const maxLength = Math.max(line1.length, line2.length)
|
||||||
line1.setLengthByValue(minLength * 10)
|
line1.setLengthByValue(maxLength * 10)
|
||||||
line2.setLengthByValue(minLength * 10)
|
line2.setLengthByValue(maxLength * 10)
|
||||||
|
// attributes도 통일
|
||||||
|
const maxPlaneSize = Math.max(line1.attributes.planeSize || 0, line2.attributes.planeSize || 0)
|
||||||
|
const maxActualSize = Math.max(line1.attributes.actualSize || 0, line2.attributes.actualSize || 0)
|
||||||
|
line1.attributes.planeSize = maxPlaneSize
|
||||||
|
line1.attributes.actualSize = maxActualSize
|
||||||
|
line2.attributes.planeSize = maxPlaneSize
|
||||||
|
line2.attributes.actualSize = maxActualSize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
polygon.lines.forEach((line, index) => {
|
polygon.lines.forEach((line, index) => {
|
||||||
|
if (line.attributes.isCalculated && !forceUpdate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
//text 와 planSize 및 actualSize가 안맞는 문제
|
//text 와 planSize 및 actualSize가 안맞는 문제
|
||||||
const nextText = polygon?.texts?.[index]?.text
|
/*const nextText = polygon?.texts?.[index]?.text
|
||||||
const nextPlaneSize = Number(nextText)
|
const nextPlaneSize = Number(nextText)
|
||||||
if (nextText != null && nextText !== '' && Number.isFinite(nextPlaneSize)) {
|
if (nextText != null && nextText !== '' && Number.isFinite(nextPlaneSize)) {
|
||||||
if (line.attributes.actualSize !== nextPlaneSize && line.attributes.planeSize !== nextPlaneSize) {
|
if (line.attributes.actualSize !== nextPlaneSize && line.attributes.planeSize !== nextPlaneSize) {
|
||||||
line.attributes.planeSize = nextPlaneSize
|
line.attributes.planeSize = nextPlaneSize
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
}
|
setActualSize(line, polygon.direction, +polygon.roofMaterial?.pitch, forceUpdate)
|
||||||
setActualSize(line, polygon.direction, +polygon.roofMaterial?.pitch)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
addLengthText(polygon)
|
addLengthText(polygon)
|
||||||
|
|||||||
@ -997,7 +997,9 @@
|
|||||||
"estimate.detail.itemTableHeader.amount": "数量",
|
"estimate.detail.itemTableHeader.amount": "数量",
|
||||||
"estimate.detail.itemTableHeader.unit": "単位",
|
"estimate.detail.itemTableHeader.unit": "単位",
|
||||||
"estimate.detail.itemTableHeader.salePrice": "単価",
|
"estimate.detail.itemTableHeader.salePrice": "単価",
|
||||||
|
"estimate.detail.itemTableHeader.unitPrice": "定価",
|
||||||
"estimate.detail.itemTableHeader.saleTotPrice": "金額(税別)",
|
"estimate.detail.itemTableHeader.saleTotPrice": "金額(税別)",
|
||||||
|
"estimate.detail.itemTableHeader.unitTotprice": "金額(税別)",
|
||||||
"estimate.detail.docPopup.title": "見積書出力オプションの設定",
|
"estimate.detail.docPopup.title": "見積書出力オプションの設定",
|
||||||
"estimate.detail.docPopup.explane": "ダウンロードする文書オプションを選択し、[見積書出力]ボタンをクリックします。",
|
"estimate.detail.docPopup.explane": "ダウンロードする文書オプションを選択し、[見積書出力]ボタンをクリックします。",
|
||||||
"estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル",
|
"estimate.detail.docPopup.schUnitPriceFlg": "ダウンロードファイル",
|
||||||
@ -1111,6 +1113,7 @@
|
|||||||
"module.layout.setup.has.zero.value": "モジュールの列数、段数を入力して下さい。",
|
"module.layout.setup.has.zero.value": "モジュールの列数、段数を入力して下さい。",
|
||||||
"modal.placement.initial.setting.plan.drawing.only.number": "(※数字は[半角]入力のみ可能です。)",
|
"modal.placement.initial.setting.plan.drawing.only.number": "(※数字は[半角]入力のみ可能です。)",
|
||||||
"wall.line.not.found": "外壁がありません",
|
"wall.line.not.found": "外壁がありません",
|
||||||
|
"wall.line.not.selected": "選択された外郭線がありません。",
|
||||||
"roof.line.not.found": "屋根形状がありません",
|
"roof.line.not.found": "屋根形状がありません",
|
||||||
"roof.material.can.not.delete": "割り当てられた配置面があります。",
|
"roof.material.can.not.delete": "割り当てられた配置面があります。",
|
||||||
"chidory.can.not.install": "千鳥配置できない工法です。",
|
"chidory.can.not.install": "千鳥配置できない工法です。",
|
||||||
|
|||||||
@ -997,7 +997,9 @@
|
|||||||
"estimate.detail.itemTableHeader.amount": "수량",
|
"estimate.detail.itemTableHeader.amount": "수량",
|
||||||
"estimate.detail.itemTableHeader.unit": "단위",
|
"estimate.detail.itemTableHeader.unit": "단위",
|
||||||
"estimate.detail.itemTableHeader.salePrice": "단가",
|
"estimate.detail.itemTableHeader.salePrice": "단가",
|
||||||
|
"estimate.detail.itemTableHeader.unitPrice": "정가",
|
||||||
"estimate.detail.itemTableHeader.saleTotPrice": "금액(부가세별도)",
|
"estimate.detail.itemTableHeader.saleTotPrice": "금액(부가세별도)",
|
||||||
|
"estimate.detail.itemTableHeader.unitTotprice": "금액(부가세별도)",
|
||||||
"estimate.detail.docPopup.title": "문서다운로드 옵션설정",
|
"estimate.detail.docPopup.title": "문서다운로드 옵션설정",
|
||||||
"estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.",
|
"estimate.detail.docPopup.explane": "다운로드할 문서 옵션을 선택한 후 문서 다운로드 버튼을 클릭합니다.",
|
||||||
"estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일",
|
"estimate.detail.docPopup.schUnitPriceFlg": "다운로드 파일",
|
||||||
@ -1111,6 +1113,7 @@
|
|||||||
"module.layout.setup.has.zero.value": "모듈의 열수, 단수를 입력해 주세요.",
|
"module.layout.setup.has.zero.value": "모듈의 열수, 단수를 입력해 주세요.",
|
||||||
"modal.placement.initial.setting.plan.drawing.only.number": "(※ 숫자는 [반각]입력만 가능합니다.)",
|
"modal.placement.initial.setting.plan.drawing.only.number": "(※ 숫자는 [반각]입력만 가능합니다.)",
|
||||||
"wall.line.not.found": "외벽선이 없습니다.",
|
"wall.line.not.found": "외벽선이 없습니다.",
|
||||||
|
"wall.line.not.selected": "선택된 외곽선이 없습니다.",
|
||||||
"roof.line.not.found": "지붕형상이 없습니다.",
|
"roof.line.not.found": "지붕형상이 없습니다.",
|
||||||
"roof.material.can.not.delete": "할당된 배치면이 있습니다.",
|
"roof.material.can.not.delete": "할당된 배치면이 있습니다.",
|
||||||
"chidory.can.not.install": "치조 불가 공법입니다.",
|
"chidory.can.not.install": "치조 불가 공법입니다.",
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import { atom, selectorFamily } from 'recoil'
|
import { atom, selectorFamily } from 'recoil'
|
||||||
|
|
||||||
const defaultFont = {
|
const makeDefaultFont = (fontSize) => ({
|
||||||
fontFamily: { id: 1, name: 'MS PGothic', value: 'MS PGothic' },
|
fontFamily: { id: 1, name: 'MS PGothic', value: 'MS PGothic' },
|
||||||
fontWeight: { id: 'normal', name: '보통', value: 'normal' },
|
fontWeight: { id: 'normal', name: '보통', value: 'normal' },
|
||||||
fontSize: { id: 16, name: 16, value: 16 },
|
fontSize: { id: fontSize, name: fontSize, value: fontSize },
|
||||||
fontColor: { id: 'black', name: '검정색', value: 'black' },
|
fontColor: { id: 'black', name: '검정색', value: 'black' },
|
||||||
}
|
})
|
||||||
|
|
||||||
|
const defaultFont = makeDefaultFont(28)
|
||||||
|
const defaultCircuitNumberFont = makeDefaultFont(36)
|
||||||
|
|
||||||
export const globalFontAtom = atom({
|
export const globalFontAtom = atom({
|
||||||
key: 'fontAtom',
|
key: 'fontAtom',
|
||||||
@ -14,7 +17,7 @@ export const globalFontAtom = atom({
|
|||||||
dimensionLineText: defaultFont,
|
dimensionLineText: defaultFont,
|
||||||
flowText: defaultFont,
|
flowText: defaultFont,
|
||||||
lengthText: defaultFont,
|
lengthText: defaultFont,
|
||||||
circuitNumberText: defaultFont,
|
circuitNumberText: defaultCircuitNumberFont,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -94,6 +94,17 @@ export const inputNumberCheck = (e) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 영문, 숫자, 특수문자(ASCII)만 입력 체크
|
||||||
|
export const inputUserIdCheck = (e) => {
|
||||||
|
const input = e.target
|
||||||
|
const allowedRegex = /^[A-Za-z0-9!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?`~]*$/g
|
||||||
|
if (allowedRegex.test(input.value)) {
|
||||||
|
input.value = input.value
|
||||||
|
} else {
|
||||||
|
input.value = input.value.replace(/[^A-Za-z0-9!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?`~]/g, '')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 값이 숫자인지 확인
|
// 값이 숫자인지 확인
|
||||||
export const numberCheck = (value) => {
|
export const numberCheck = (value) => {
|
||||||
return !isNaN(value)
|
return !isNaN(value)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { getAdjacent, getDegreeByChon, isPointOnLine, isPointOnLineNew } from '@
|
|||||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||||
import Big from 'big.js'
|
import Big from 'big.js'
|
||||||
|
import * as turf from '@turf/turf'
|
||||||
|
|
||||||
const TWO_PI = Math.PI * 2
|
const TWO_PI = Math.PI * 2
|
||||||
const EPSILON = 1e-10 //좌표계산 시 최소 차이값
|
const EPSILON = 1e-10 //좌표계산 시 최소 차이값
|
||||||
@ -248,6 +249,47 @@ function createPaddingPolygon(polygon, offset, arcSegments = 0) {
|
|||||||
return paddingPolygon
|
return paddingPolygon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 자기교차(self-intersection)가 있는 폴리곤을 정리하는 함수
|
||||||
|
* turf.js의 unkinkPolygon을 사용하여 꼬인 부분을 제거하고 가장 큰 폴리곤을 반환
|
||||||
|
* @param {Array} vertices - [{x, y}, ...] 형태의 점 배열
|
||||||
|
* @returns {Array} 정리된 점 배열
|
||||||
|
*/
|
||||||
|
export function cleanSelfIntersectingPolygon(vertices) {
|
||||||
|
if (!vertices || vertices.length < 3) return vertices
|
||||||
|
|
||||||
|
try {
|
||||||
|
// vertices를 GeoJSON 폴리곤으로 변환
|
||||||
|
const coords = vertices.map((p) => [p.x, p.y])
|
||||||
|
coords.push([vertices[0].x, vertices[0].y]) // ring 닫기
|
||||||
|
|
||||||
|
const turfPoly = turf.polygon([coords])
|
||||||
|
|
||||||
|
// 자기교차 검사
|
||||||
|
const kinked = turf.kinks(turfPoly)
|
||||||
|
|
||||||
|
if (kinked.features.length === 0) {
|
||||||
|
return vertices // 꼬임 없음
|
||||||
|
}
|
||||||
|
|
||||||
|
// 꼬인 폴리곤을 분리
|
||||||
|
const unkinked = turf.unkinkPolygon(turfPoly)
|
||||||
|
|
||||||
|
if (unkinked.features.length > 0) {
|
||||||
|
// 가장 큰 면적의 폴리곤 선택
|
||||||
|
const largest = unkinked.features.reduce((max, f) => (turf.area(f) > turf.area(max) ? f : max))
|
||||||
|
|
||||||
|
// GeoJSON 좌표를 다시 {x, y} 형태로 변환
|
||||||
|
const cleanedCoords = largest.geometry.coordinates[0]
|
||||||
|
return cleanedCoords.slice(0, -1).map((c) => ({ x: c[0], y: c[1] }))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to clean self-intersecting polygon:', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vertices
|
||||||
|
}
|
||||||
|
|
||||||
export default function offsetPolygon(vertices, offset) {
|
export default function offsetPolygon(vertices, offset) {
|
||||||
const polygon = createPolygon(vertices)
|
const polygon = createPolygon(vertices)
|
||||||
const arcSegments = 0
|
const arcSegments = 0
|
||||||
@ -255,25 +297,29 @@ export default function offsetPolygon(vertices, offset) {
|
|||||||
const originPolygon = new QPolygon(vertices, { fontSize: 0 })
|
const originPolygon = new QPolygon(vertices, { fontSize: 0 })
|
||||||
originPolygon.setViewLengthText(false)
|
originPolygon.setViewLengthText(false)
|
||||||
|
|
||||||
|
let result
|
||||||
if (offset > 0) {
|
if (offset > 0) {
|
||||||
let result = createMarginPolygon(polygon, offset, arcSegments).vertices
|
let marginResult = createMarginPolygon(polygon, offset, arcSegments).vertices
|
||||||
const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
|
const allPointsOutside = marginResult.every((point) => !originPolygon.inPolygon(point))
|
||||||
|
|
||||||
if (allPointsOutside) {
|
if (allPointsOutside) {
|
||||||
return createMarginPolygon(polygon, offset, arcSegments).vertices
|
result = createMarginPolygon(polygon, offset, arcSegments).vertices
|
||||||
} else {
|
} else {
|
||||||
return createPaddingPolygon(polygon, offset, arcSegments).vertices
|
result = createPaddingPolygon(polygon, offset, arcSegments).vertices
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let result = createPaddingPolygon(polygon, offset, arcSegments).vertices
|
let paddingResult = createPaddingPolygon(polygon, offset, arcSegments).vertices
|
||||||
const allPointsInside = result.every((point) => originPolygon.inPolygon(point))
|
const allPointsInside = paddingResult.every((point) => originPolygon.inPolygon(point))
|
||||||
|
|
||||||
if (allPointsInside) {
|
if (allPointsInside) {
|
||||||
return createPaddingPolygon(polygon, offset, arcSegments).vertices
|
result = createPaddingPolygon(polygon, offset, arcSegments).vertices
|
||||||
} else {
|
} else {
|
||||||
return createMarginPolygon(polygon, offset, arcSegments).vertices
|
result = createMarginPolygon(polygon, offset, arcSegments).vertices
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 자기교차(꼬임) 제거
|
||||||
|
return cleanSelfIntersectingPolygon(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePoint(point) {
|
function normalizePoint(point) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user