삼각형 도머 크기 변경

This commit is contained in:
yjnoh 2024-11-01 15:37:05 +09:00
parent 088b0c5e77
commit e0cf50beec
7 changed files with 203 additions and 48 deletions

View File

@ -49,9 +49,8 @@ export default function DimensionLineSetting(props) {
resultText = calculateLength(basicLength, slopeInput1.angleValue).toFixed(0) resultText = calculateLength(basicLength, slopeInput1.angleValue).toFixed(0)
if (slopeInput2) { if (slopeInput2) {
const angle = slopeInput1 + slopeInput2 const length = calculateLength(basicLength, slopeInput1.angleValue, slopeInput2.angleValue)
const length = calculateLength(basicLength, angle) resultText = length.toFixed(0)
resultText = length.toFixed(2)
} }
} }
} }
@ -71,11 +70,18 @@ export default function DimensionLineSetting(props) {
} }
function calculateLength(originalLength, angle) { function calculateLength(originalLength, angle) {
const angleInRadians = angle * (Math.PI / 180) // const angleInRadians = angle * (Math.PI / 180)
const result = Math.sqrt(Math.pow(originalLength * Math.tan(angleInRadians), 2) + Math.pow(originalLength, 2)) const result = Math.sqrt(Math.pow(originalLength * Math.tan(angleInRadians), 2) + Math.pow(originalLength, 2))
return result return result
} }
function calculateLength(originalLength, angle1, angle2) {
const numerator = Math.sqrt(Math.pow(angle1, 2) + 100 + Math.pow((10 * angle1) / angle2, 2)) * originalLength
const denominator = Math.sqrt(Math.pow((10 * angle1) / angle2, 2) + 100)
const result = numerator / denominator
return result
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>

View File

@ -5,7 +5,9 @@ import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { useState } from 'react' import { useRef, useState, useEffect } from 'react'
import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useEvent } from '@/hooks/useEvent'
export default function SizeSetting(props) { export default function SizeSetting(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -13,6 +15,23 @@ export default function SizeSetting(props) {
const { id, pos = contextPopupPosition, target } = props const { id, pos = contextPopupPosition, target } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { reSizeObject } = useObjectBatch()
const widthRef = useRef(null)
const heightRef = useRef(null)
const { initEvent } = useEvent()
useEffect(() => {
initEvent()
}, [])
const handleReSizeObject = () => {
const width = widthRef.current.value
const height = heightRef.current.value
reSizeObject(settingTarget, target, width, height)
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
@ -28,11 +47,11 @@ export default function SizeSetting(props) {
<div className="size-option-top"> <div className="size-option-top">
<div className="size-option-wrap"> <div className="size-option-wrap">
<div className="size-option mb5"> <div className="size-option mb5">
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly value={target?.width * 10 * 2} /> <input type="text" className="input-origin mr5" value={target?.width.toFixed(0) * 10} readOnly={true} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
<div className="size-option"> <div className="size-option">
<input type="text" className="input-origin mr5" defaultValue={1000} value={target?.width * 10 * 2} /> <input type="text" className="input-origin mr5" defaultValue={target?.width.toFixed(0) * 10} ref={widthRef} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
</div> </div>
@ -41,11 +60,11 @@ export default function SizeSetting(props) {
<div className="size-option-side"> <div className="size-option-side">
<div className="size-option-wrap"> <div className="size-option-wrap">
<div className="size-option mb5"> <div className="size-option mb5">
<input type="text" className="input-origin mr5" defaultValue={1000} readOnly value={target?.height * 10} /> <input type="text" className="input-origin mr5" value={target?.height.toFixed(0) * 10} readOnly={true} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
<div className="size-option"> <div className="size-option">
<input type="text" className="input-origin mr5" defaultValue={1000} value={target?.height * 10} /> <input type="text" className="input-origin mr5" defaultValue={target?.height.toFixed(0) * 10} ref={heightRef} />
<span className="normal-font">mm</span> <span className="normal-font">mm</span>
</div> </div>
</div> </div>
@ -60,7 +79,9 @@ export default function SizeSetting(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('write')}</button> <button className="btn-frame modal act" onClick={() => handleReSizeObject()}>
{getMessage('write')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -38,7 +38,7 @@ const TriangleDormer = forwardRef((props, refs) => {
<div className="eaves-keraba-td"> <div className="eaves-keraba-td">
<div className="outline-form"> <div className="outline-form">
<div className="input-grid mr5" style={{ width: '60px' }}> <div className="input-grid mr5" style={{ width: '60px' }}>
<input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} /> <input type="text" className="input-origin block" placeholder={0} ref={refs.offsetRef} defaultValue={400} />
</div> </div>
<span className="thin">mm</span> <span className="thin">mm</span>
</div> </div>

View File

@ -547,7 +547,7 @@ export function useCommonUtils() {
initEvent() initEvent()
obj.setCoords() obj.setCoords()
updateGroupObjectCoords(obj, originLeft, originTop) updateGroupObjectCoords(obj, originLeft, originTop)
// canvas?.renderAll() canvas?.renderAll()
}) })
} }
} }
@ -585,6 +585,10 @@ export function useCommonUtils() {
editable: false, editable: false,
id: uuidv4(), //복사된 객체라 새로 따준다 id: uuidv4(), //복사된 객체라 새로 따준다
}) })
//객체가 그룹일 경우에는 그룹 아이디를 따로 넣어준다
if (clonedObj.type === 'group') clonedObj.set({ groupId: uuidv4() })
initEvent() initEvent()
}) })
} }
@ -748,7 +752,6 @@ export function useCommonUtils() {
} else { } else {
// 다른 객체의 경우 left, top 절대 좌표 설정 // 다른 객체의 경우 left, top 절대 좌표 설정
obj.set({ obj.set({
...obj,
left: obj.left, left: obj.left,
top: obj.top, top: obj.top,
}) })

View File

@ -43,8 +43,12 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
// 클릭한 위치에 있는 객체 찾기 // 클릭한 위치에 있는 객체 찾기
const clickedObject = objects.find((obj) => { const clickedObject = objects.find((obj) => {
console.log(obj)
if (obj.type === 'QPolygon') { if (obj.type === 'QPolygon') {
return obj.inPolygon({ x: pointer.x, y: pointer.y }) const polygon = pointsToTurfPolygon(obj.getCurrentPoints())
const turfPointer = turf.point([pointer.x, pointer.y])
return turf.booleanPointInPolygon(turfPointer, polygon)
} else { } else {
return obj.containsPoint(pointer) return obj.containsPoint(pointer)
} }
@ -403,7 +407,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
}) //오프셋이 있을땐 같이 도머로 만든다 }) //오프셋이 있을땐 같이 도머로 만든다
const leftTriangle = new QPolygon(splitedTriangle[0], { const leftTriangle = new QPolygon(splitedTriangle[0], {
fill: 'transparent', fill: 'white',
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: true, selectable: true,
@ -422,7 +426,7 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
}) })
const rightTriangle = new QPolygon(splitedTriangle[1], { const rightTriangle = new QPolygon(splitedTriangle[1], {
fill: 'transparent', fill: 'white',
stroke: 'black', stroke: 'black',
strokeWidth: 1, strokeWidth: 1,
selectable: true, selectable: true,
@ -450,7 +454,33 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
drawDirectionArrow(leftTriangle) drawDirectionArrow(leftTriangle)
drawDirectionArrow(rightTriangle) drawDirectionArrow(rightTriangle)
const objectGroup = new fabric.Group([leftTriangle, rightTriangle], { let offsetPolygon
if (offsetRef > 0) {
canvas?.remove(dormer)
offsetPolygon = new QPolygon(triangleToPolygon(dormer), {
selectable: true,
lockMovementX: true, // X 축 이동 잠금
lockMovementY: true, // Y 축 이동 잠금
lockRotation: true, // 회전 잠금
viewLengthText: true,
name: 'triangleDormerOffset',
id: id,
fill: 'rgba(255, 255, 255, 0.6)',
stroke: 'black',
strokeWidth: 1,
originX: 'center',
originY: 'center',
fontSize: lengthTextFont.fontSize.value,
fontStyle: lengthTextFont.fontWeight.value.toLowerCase().includes('italic') ? 'italic' : 'normal',
fontWeight: lengthTextFont.fontWeight.value,
})
}
const groupPolygon = offsetPolygon ? [leftTriangle, rightTriangle, offsetPolygon] : [leftTriangle, rightTriangle]
const objectGroup = new fabric.Group(groupPolygon, {
subTargetCheck: true, subTargetCheck: true,
name: dormerName, name: dormerName,
id: id, id: id,
@ -796,10 +826,102 @@ export function useObjectBatch({ isHidden, setIsHidden }) {
return [leftPoints, rightPoints] return [leftPoints, rightPoints]
} }
const reSizeObject = (side, target, width, height) => {
console.log('reSizeTarget', target)
target.getObjects().forEach((obj) => {
console.log('obj', obj.type)
})
const objectWidth = target.width
const objectHeight = target.height
const changeWidth = (width / 10 / objectWidth).toFixed(2)
const changeHeight = (height / 10 / objectHeight).toFixed(2)
let sideX = 'left'
let sideY = 'top'
//그룹 중심점 변경
if (side === 2) {
sideX = 'right'
sideY = 'top'
} else if (side === 3) {
sideX = 'left'
sideY = 'bottom'
} else if (side === 4) {
sideX = 'right'
sideY = 'bottom'
}
//변경 전 좌표
const newCoords = target.getPointByOrigin(sideX, sideY)
target.set({
...target,
originX: sideX,
originY: sideY,
left: newCoords.x,
top: newCoords.y,
})
target.setCoords()
canvas?.renderAll() //변경 좌표를 한번 적용
target.scaleX = changeWidth === 0 ? 1 : changeWidth
target.scaleY = changeHeight === 0 ? 1 : changeHeight
//크기 변경후 좌표를 재 적용
const changedCoords = target.getPointByOrigin('center', 'center')
target.set({
...target,
originX: 'center',
originY: 'center',
left: changedCoords.x,
top: changedCoords.y,
})
if (target.name === 'roof') {
//얘는 일단 도머에 적용함
target._objects.forEach((obj) => {
setSurfaceShapePattern(obj)
})
}
// target.setCoords()
canvas.renderAll()
// reGroupObject(target)
}
const reGroupObject = (groupObj) => {
groupObj._restoreObjectsState() //이건 실행만 되도 그룹이 변경됨
const reGroupObjects = []
groupObj._objects.forEach((obj) => {
const newObj = new QPolygon(obj.getCurrentPoints(), {
...obj,
points: obj.getCurrentPoints(),
scaleX: 1,
scaleY: 1,
})
reGroupObjects.push(newObj)
canvas.remove(obj)
})
const reGroup = new fabric.Group(reGroupObjects, {
subTargetCheck: true,
name: groupObj.name,
id: groupObj.id,
groupYn: true,
})
canvas?.add(reGroup)
canvas?.remove(groupObj)
}
return { return {
applyOpeningAndShadow, applyOpeningAndShadow,
applyDormers, applyDormers,
splitDormerTriangle, splitDormerTriangle,
splitDormerPentagon, splitDormerPentagon,
reSizeObject,
} }
} }

View File

@ -273,16 +273,19 @@ export function useContextMenu() {
id: 'dormerRemove', id: 'dormerRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.remove')}(D)`, name: `${getMessage('contextmenu.remove')}(D)`,
fn: () => deleteObject(),
}, },
{ {
id: 'dormerMove', id: 'dormerMove',
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
name: `${getMessage('contextmenu.move')}(M)`, name: `${getMessage('contextmenu.move')}(M)`,
fn: () => moveObject(),
}, },
{ {
id: 'dormerCopy', id: 'dormerCopy',
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
name: `${getMessage('contextmenu.copy')}(C)`, name: `${getMessage('contextmenu.copy')}(C)`,
fn: () => copyObject(),
}, },
{ {
id: 'roofMaterialEdit', id: 'roofMaterialEdit',
@ -451,7 +454,7 @@ export function useContextMenu() {
], ],
]) ])
break break
case 'dimensionLine': case 'dimensionGroup':
setContextMenu([ setContextMenu([
[ [
{ {

View File

@ -55,13 +55,12 @@ export function usePlan() {
/** /**
* 현재 캔버스에 그려진 데이터를 추출 * 현재 캔버스에 그려진 데이터를 추출
*/ */
const currentCanvasData = () => { const currentCanvasData = (mode = '') => {
removeMouseLines() removeMouseLines()
if (mode === 'save') {
const groups = canvas.getObjects().filter((obj) => obj.type === 'group') const groups = canvas.getObjects().filter((obj) => obj.type === 'group')
console.log('groups', groups)
if (groups.length > 0) { if (groups.length > 0) {
groups.forEach((group) => { groups.forEach((group) => {
canvas?.remove(group) canvas?.remove(group)
@ -90,6 +89,7 @@ export function usePlan() {
}) })
}) })
} }
}
return addCanvas() return addCanvas()
} }
@ -172,7 +172,7 @@ export function usePlan() {
* 페이지 캔버스를 저장 * 페이지 캔버스를 저장
*/ */
const saveCanvas = async (userId) => { const saveCanvas = async (userId) => {
const canvasStatus = currentCanvasData() const canvasStatus = currentCanvasData('save')
initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id) initCanvasPlans.some((plan) => plan.id === currentCanvasPlan.id)
? await putCanvasStatus(canvasStatus) ? await putCanvasStatus(canvasStatus)
: await postCanvasStatus(userId, canvasStatus) : await postCanvasStatus(userId, canvasStatus)