Merge branch 'dev' of https://git.jetbrains.space/nalpari/q-cast-iii/qcast-front into dev-ck
This commit is contained in:
commit
e248736aaf
@ -3,6 +3,7 @@
|
||||
import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react'
|
||||
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
// import { get } from '@/lib/Axios'
|
||||
|
||||
import QSelect from '@/components/ui/QSelect'
|
||||
@ -12,6 +13,7 @@ import styles from './playground.module.css'
|
||||
export default function Playground() {
|
||||
const { get } = useAxios()
|
||||
const testVar = process.env.NEXT_PUBLIC_TEST
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const handleUsers = async () => {
|
||||
// const users = await get('/api/user/find-all')
|
||||
@ -58,6 +60,7 @@ export default function Playground() {
|
||||
<div className="test">
|
||||
<p className="text-white">Sass 테스트입니다.</p>
|
||||
</div>
|
||||
<div>{getMessage('hi')}</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -4,6 +4,7 @@ import { useCanvas } from '@/hooks/useCanvas'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useMode } from '@/hooks/useMode'
|
||||
import { Mode } from '@/common/common'
|
||||
import { Button } from '@nextui-org/react'
|
||||
import RangeSlider from './ui/RangeSlider'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
@ -22,14 +23,17 @@ import { getCanvasState, insertCanvasState } from '@/lib/canvas'
|
||||
import { calculateIntersection } from '@/util/canvas-util'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import ThumbnailList from './ui/ThumbnailLIst'
|
||||
import CanvasWithContextMenu from '@/util/context-util'
|
||||
import { Mode } from '@/common/common'
|
||||
import { get } from '@/lib/Axios'
|
||||
import QContextMenu from './common/context-menu/QContextMenu'
|
||||
import { modalContent, modalState } from '@/store/modalAtom'
|
||||
import SettingsModal from './SettingsModal'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
|
||||
export default function Roof2(props) {
|
||||
const { name, userId, email, isLoggedIn } = props
|
||||
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas')
|
||||
|
||||
const { get } = useAxios()
|
||||
|
||||
const canvasRef = useRef(null)
|
||||
|
||||
//canvas 기본 사이즈
|
||||
@ -59,6 +63,9 @@ export default function Roof2(props) {
|
||||
|
||||
const wall = useRecoilValue(wallState)
|
||||
|
||||
const [open, setOpen] = useRecoilState(modalState)
|
||||
const [contents, setContent] = useRecoilState(modalContent)
|
||||
|
||||
const [scale, setScale] = useState(1)
|
||||
|
||||
//canvas 썸네일
|
||||
@ -564,6 +571,15 @@ export default function Roof2(props) {
|
||||
{canvas && (
|
||||
<>
|
||||
<div className=" my-8 w-full text:pretty">
|
||||
<Button
|
||||
className="m-1 p-2"
|
||||
onClick={() => {
|
||||
setContent(<SettingsModal canvasProps={canvas} />)
|
||||
setOpen(true)
|
||||
}}
|
||||
>
|
||||
그리드 설정
|
||||
</Button>
|
||||
<Button className="m-1 p-2" color={`${mode === Mode.DEFAULT ? 'primary' : 'default'}`} onClick={fillCellInPolygon}>
|
||||
모드 DEFAULT
|
||||
</Button>
|
||||
@ -742,7 +758,7 @@ export default function Roof2(props) {
|
||||
<ThumbnailList {...thumbnailProps} />
|
||||
<div className="flex justify-start my-8 mx-2 w-full">
|
||||
<canvas ref={canvasRef} id="canvas" style={{ border: '1px solid black' }} />
|
||||
{canvas !== undefined && <CanvasWithContextMenu ref={canvasRef} canvasProps={canvas} />}
|
||||
{canvas !== undefined && <QContextMenu contextRef={canvasRef} canvasProps={canvas} />}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
274
src/components/SettingsModal.jsx
Normal file
274
src/components/SettingsModal.jsx
Normal file
@ -0,0 +1,274 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { modalContent, modalState } from '@/store/modalAtom'
|
||||
import { guideLineState } from '@/store/canvasAtom'
|
||||
import { fabric } from 'fabric'
|
||||
|
||||
export default function SettingsModal(props) {
|
||||
const { canvasProps } = props
|
||||
const [isCustomGridSetting, setIsCustomGridSetting] = useState(true)
|
||||
const [gridCheckedValue, setGridCheckValue] = useState([])
|
||||
const [ratioValue, setRatioValue] = useState('원치수')
|
||||
const moduleLength = useRef(90) //모듈 mm 길이 입력
|
||||
|
||||
const [open, setOpen] = useRecoilState(modalState)
|
||||
const [guideLine, setGuideLine] = useRecoilState(guideLineState)
|
||||
|
||||
useEffect(() => {
|
||||
setIsCustomGridSetting(ratioValue !== 'custom')
|
||||
}, [ratioValue])
|
||||
|
||||
const drawGridSettings = () => {
|
||||
const gridSettingArray = []
|
||||
|
||||
if (gridCheckedValue.includes('line')) {
|
||||
// fabric.GuideRect = fabric.util.createClass(fabric.Rect, {
|
||||
// _render: function (ctx) {
|
||||
// this.callSuper('_render', ctx)
|
||||
// ctx.strokeStyle = this.stroke || 'blue'
|
||||
// ctx.lineWidth = this.strokeWidth || 1
|
||||
|
||||
// ctx.beginPath()
|
||||
// ctx.moveTo(-this.width / 2, -this.height / 2)
|
||||
// ctx.lineTo(this.width / 2, -this.height / 2)
|
||||
// ctx.lineTo(this.width / 2, this.height / 2)
|
||||
// ctx.stroke()
|
||||
// },
|
||||
// })
|
||||
|
||||
// const guideRect = new fabric.GuideRect({
|
||||
// width: canvasProps.width,
|
||||
// height: canvasProps.height,
|
||||
// stroke: 'blue',
|
||||
// strokeWidth: 1,
|
||||
// selectable: false,
|
||||
// fill: 'transparent',
|
||||
// name: 'guideRect',
|
||||
// })
|
||||
|
||||
// const patternSourceCanvas = new fabric.StaticCanvas(null, {
|
||||
// width: moduleLength.current.value,
|
||||
// height: moduleLength.current.value,
|
||||
// backgroundColor: 'white',
|
||||
// })
|
||||
|
||||
// patternSourceCanvas.add(guideRect)
|
||||
// patternSourceCanvas.renderAll()
|
||||
|
||||
// const pattern = new fabric.Pattern({
|
||||
// source: patternSourceCanvas.getElement(),
|
||||
// repeat: 'repeat',
|
||||
// })
|
||||
|
||||
const horizontalLineArray = []
|
||||
const verticalLineArray = []
|
||||
|
||||
for (let i = 0; i < canvasProps.height / moduleLength.current.value + 1; i++) {
|
||||
const horizontalLine = new fabric.Line(
|
||||
[
|
||||
0,
|
||||
i * moduleLength.current.value - moduleLength.current.value / 2,
|
||||
canvasProps.width,
|
||||
i * moduleLength.current.value - moduleLength.current.value / 2,
|
||||
],
|
||||
{
|
||||
stroke: 'gray',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'guideLine',
|
||||
},
|
||||
)
|
||||
canvasProps.add(horizontalLine)
|
||||
horizontalLineArray.push(horizontalLine)
|
||||
}
|
||||
|
||||
for (let i = 0; i < canvasProps.width / moduleLength.current.value + 1; i++) {
|
||||
const verticalLine = new fabric.Line(
|
||||
[
|
||||
i * moduleLength.current.value - moduleLength.current.value / 2,
|
||||
0,
|
||||
i * moduleLength.current.value - moduleLength.current.value / 2,
|
||||
canvasProps.height,
|
||||
],
|
||||
{
|
||||
stroke: 'gray',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'guideLine',
|
||||
},
|
||||
)
|
||||
canvasProps.add(verticalLine)
|
||||
verticalLineArray.push(verticalLine)
|
||||
}
|
||||
canvasProps.renderAll()
|
||||
|
||||
const snapDistance = 10
|
||||
|
||||
canvasProps.on('mouse:move', function (e) {
|
||||
let mouseObj = e
|
||||
const mouseCursorX = e.pointer.x
|
||||
const mouseCursorY = e.pointer.y
|
||||
|
||||
horizontalLineArray.forEach((line) => {
|
||||
if (Math.abs(mouseCursorY - line.y1) < snapDistance) {
|
||||
mouseObj.x = mouseCursorX
|
||||
mouseObj.y = line.y1
|
||||
}
|
||||
// if (Math.abs(mouseCursorX - line.x2) < snapDistance) {
|
||||
// line.set({ x2: mouseCursorX })
|
||||
// }
|
||||
})
|
||||
})
|
||||
|
||||
// canvasProps.on('mouse:move', (e) => {
|
||||
// console.log('event', e.pointer)
|
||||
|
||||
// const obj = e
|
||||
|
||||
// if (!obj) return
|
||||
|
||||
// canvasProps.forEachObject(function (target) {
|
||||
// if (target.name === 'guideLine' || target.name === 'guideDot') {
|
||||
// console.log('line : ', target)
|
||||
|
||||
// const lineStartX = target.x1,
|
||||
// lineStartY = target.y1
|
||||
// const lineEndX = target.x2,
|
||||
// lineEndY = target.y2
|
||||
|
||||
// console.log('target.x1', target.x1)
|
||||
// console.log('target.y1', target.y1)
|
||||
// console.log(`obj.pointer.y : ${obj.pointer.y}, obj.pointer.x : ${obj.pointer.x}`)
|
||||
|
||||
// // 수평 선에 대한 스냅
|
||||
// if (Math.abs(obj.pointer.y - lineStartY) < snapDistance) {
|
||||
// obj.pointer.y = lineStartY
|
||||
// }
|
||||
|
||||
// // 수직 선에 대한 스냅
|
||||
// if (Math.abs(obj.pointer.x - lineStartX) < snapDistance) {
|
||||
// obj.pointer.x = lineStartX
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
|
||||
const recoilObj = {
|
||||
guideMode: 'guideLine',
|
||||
horizontalLineArray,
|
||||
verticalLineArray,
|
||||
moduleLength: moduleLength.current.value,
|
||||
}
|
||||
gridSettingArray.push(recoilObj)
|
||||
}
|
||||
|
||||
if (gridCheckedValue.includes('dot')) {
|
||||
const circle = new fabric.Circle({
|
||||
radius: 2,
|
||||
fill: 'red',
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
selectable: false,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
name: 'guideDot',
|
||||
})
|
||||
|
||||
const patternSourceCanvas = new fabric.StaticCanvas(null, {
|
||||
width: moduleLength.current.value,
|
||||
height: moduleLength.current.value,
|
||||
})
|
||||
|
||||
patternSourceCanvas.add(circle)
|
||||
|
||||
circle.set({
|
||||
left: patternSourceCanvas.width / 2,
|
||||
top: patternSourceCanvas.height / 2,
|
||||
})
|
||||
|
||||
patternSourceCanvas.renderAll()
|
||||
|
||||
const pattern = new fabric.Pattern({
|
||||
source: patternSourceCanvas.getElement(),
|
||||
repeat: 'repeat',
|
||||
})
|
||||
|
||||
const backgroundPolygon = new fabric.Polygon(
|
||||
[
|
||||
{ x: 0, y: 0 },
|
||||
{ x: canvasProps.width, y: 0 },
|
||||
{ x: canvasProps.width, y: canvasProps.height },
|
||||
{ x: 0, y: canvasProps.height },
|
||||
],
|
||||
{
|
||||
fill: pattern,
|
||||
selectable: false,
|
||||
},
|
||||
)
|
||||
canvasProps.add(backgroundPolygon)
|
||||
canvasProps.renderAll()
|
||||
|
||||
const recoilObj = {
|
||||
guideMode: 'guideDot',
|
||||
moduleLength: moduleLength.current.value,
|
||||
}
|
||||
|
||||
gridSettingArray.push(recoilObj)
|
||||
}
|
||||
canvasProps.renderAll()
|
||||
setGuideLine(gridSettingArray)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<label style={{ display: 'block', marginBottom: '5px' }}>
|
||||
<CheckboxGroup label="그리드 설정" value={gridCheckedValue} onValueChange={setGridCheckValue}>
|
||||
<Checkbox value="dot">점 그리드</Checkbox>
|
||||
<Checkbox value="line">점선 그리드</Checkbox>
|
||||
</CheckboxGroup>
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<Input type="number" label="모듈" ref={moduleLength} defaultValue="90" />
|
||||
mm
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<RadioGroup label="비율 설정" onValueChange={setRatioValue}>
|
||||
<Radio value="원치수">원치수</Radio>
|
||||
<Radio value="1/2">1/2</Radio>
|
||||
<Radio value="1/4">1/4</Radio>
|
||||
<Radio value="1/10">1/10</Radio>
|
||||
<Radio value="custom">임의간격</Radio>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4"></div>
|
||||
<Checkbox value="linked" isDisabled={isCustomGridSetting}>
|
||||
종횡연동
|
||||
</Checkbox>
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<Input type="number" label="가로간격" min={0} isDisabled={isCustomGridSetting} />
|
||||
mm
|
||||
</div>
|
||||
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
||||
<Input type="number" label="세로간격" min={0} isDisabled={isCustomGridSetting} />
|
||||
mm
|
||||
</div>
|
||||
<div className="flex gap-4 items-center">
|
||||
<Button size="sm">초기화</Button>
|
||||
<Button size="sm" color="secondary" onClick={drawGridSettings} isDisabled={gridCheckedValue.length === 0}>
|
||||
저장
|
||||
</Button>
|
||||
<Button size="sm" onClick={() => setOpen(!open)}>
|
||||
취소
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,10 +1,15 @@
|
||||
import React, { useState, useEffect, forwardRef, useContext } from 'react'
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
|
||||
const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => {
|
||||
export default function QContextMenu(props) {
|
||||
const { contextRef, canvasProps } = props
|
||||
|
||||
// const children = useRecoilValue(modalContent)
|
||||
const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 })
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return
|
||||
if (!contextRef.current) return
|
||||
|
||||
const handleContextMenu = (e) => {
|
||||
e.preventDefault() //기존 contextmenu 막고
|
||||
@ -34,7 +39,7 @@ const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => {
|
||||
document.removeEventListener('click', handleClick)
|
||||
document.removeEventListener('click', handleOutsideClick)
|
||||
}
|
||||
}, [ref, contextMenu])
|
||||
}, [contextRef, contextMenu])
|
||||
|
||||
const handleMenuClick = (option) => {
|
||||
alert(`option ${option} clicked`)
|
||||
@ -60,6 +65,4 @@ const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => {
|
||||
)}
|
||||
</>
|
||||
)
|
||||
})
|
||||
|
||||
export default CanvasWithContextMenu
|
||||
}
|
||||
38
src/hooks/useMessage.js
Normal file
38
src/hooks/useMessage.js
Normal file
@ -0,0 +1,38 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { globalLocaleState } from '@/store/localeAtom'
|
||||
|
||||
import KO from '@/locales/ko.json'
|
||||
import JA from '@/locales/ja.json'
|
||||
|
||||
const SESSION_STORAGE_MESSAGE_KEY = 'QCAST_MESSAGE_STORAGE'
|
||||
|
||||
export const useMessage = () => {
|
||||
const globalLocale = useRecoilValue(globalLocaleState)
|
||||
|
||||
const getMessage = (key, args = []) => {
|
||||
if (sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY) === null) {
|
||||
if (globalLocale === 'ko') {
|
||||
setSessionMessage(JSON.stringify(KO))
|
||||
} else {
|
||||
setSessionMessage(JSON.stringify(JA))
|
||||
}
|
||||
}
|
||||
|
||||
const sessionMessage = getSessionMessage()
|
||||
const message = sessionMessage[key] || key
|
||||
|
||||
return args.reduce((acc, arg, i) => {
|
||||
return acc.replaceAll(`{${i}}`, arg)
|
||||
}, message)
|
||||
}
|
||||
|
||||
const setSessionMessage = (sessionMessage) => {
|
||||
sessionStorage.setItem(SESSION_STORAGE_MESSAGE_KEY, sessionMessage)
|
||||
}
|
||||
|
||||
const getSessionMessage = () => {
|
||||
return JSON.parse(sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY))
|
||||
}
|
||||
|
||||
return { getMessage }
|
||||
}
|
||||
@ -4,6 +4,8 @@ import {
|
||||
distanceBetweenPoints,
|
||||
findTopTwoIndexesByDistance,
|
||||
getCenterPoint,
|
||||
getClosestHorizontalLine,
|
||||
getClosestVerticalLine,
|
||||
getDirection,
|
||||
getStartIndex,
|
||||
rearrangeArray,
|
||||
@ -23,6 +25,7 @@ import {
|
||||
sortedPolygonArray,
|
||||
templateTypeState,
|
||||
wallState,
|
||||
guideLineState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import { fabric } from 'fabric'
|
||||
@ -41,7 +44,6 @@ export function useMode() {
|
||||
const [canvas, setCanvas] = useState(null)
|
||||
const [zoom, setZoom] = useState(100)
|
||||
const [fontSize] = useRecoilState(fontSizeState)
|
||||
const [shape, setShape] = useState(0)
|
||||
const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray)
|
||||
const [roof, setRoof] = useRecoilState(roofState)
|
||||
const [wall, setWall] = useRecoilState(wallState)
|
||||
@ -64,6 +66,11 @@ export function useMode() {
|
||||
const compass = useRecoilValue(compassState)
|
||||
const [isCellCenter, setIsCellCenter] = useState(false)
|
||||
|
||||
const guideLineInfo = useRecoilValue(guideLineState)
|
||||
|
||||
const [guideLineMode, setGuideLineMode] = useState(false)
|
||||
const [guideDotMode, setGuideDotMode] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
// 이벤트 리스너 추가
|
||||
// if (!canvas) {
|
||||
@ -101,30 +108,127 @@ export function useMode() {
|
||||
canvas?.off('mouse:down')
|
||||
canvas?.on('mouse:down', mouseEvent.editMode)
|
||||
}
|
||||
}, [mode, isGuidePointMode])
|
||||
}, [mode])
|
||||
|
||||
useEffect(() => {
|
||||
setGuideLineMode(false)
|
||||
setGuideDotMode(false)
|
||||
if (isObjectNotEmpty(guideLineInfo)) {
|
||||
const guideLineState = guideLineInfo.filter((item) => item.guideMode === 'guideLine')
|
||||
const guideDotState = guideLineInfo.filter((item) => item.guideMode === 'guideDot')
|
||||
|
||||
setGuideLineMode(guideLineState.length > 0)
|
||||
setGuideDotMode(guideDotState.length > 0)
|
||||
}
|
||||
}, [guideLineInfo])
|
||||
|
||||
const drawMouseLines = (e) => {
|
||||
let isGuideLineMode = false,
|
||||
isGuideDotMode = false
|
||||
let guideDotLength, guideLineLength, horizontalLineArray, verticalLineArray
|
||||
|
||||
if (isObjectNotEmpty(guideLineInfo)) {
|
||||
const guideLineState = guideLineInfo.filter((item) => item.guideMode === 'guideLine')
|
||||
const guideDotState = guideLineInfo.filter((item) => item.guideMode === 'guideDot')
|
||||
setGuideLineMode(guideLineState.length > 0)
|
||||
setGuideDotMode(guideDotState.length > 0)
|
||||
isGuideLineMode = guideLineState.length > 0
|
||||
isGuideDotMode = guideDotState.length > 0
|
||||
|
||||
if (isGuideDotMode) {
|
||||
guideDotLength = Number(guideDotState[0].moduleLength)
|
||||
}
|
||||
|
||||
if (isGuideLineMode) {
|
||||
horizontalLineArray = [...guideLineState[0].horizontalLineArray]
|
||||
verticalLineArray = [...guideLineState[0].verticalLineArray]
|
||||
guideLineLength = Number(guideLineState[0].moduleLength)
|
||||
}
|
||||
}
|
||||
|
||||
// 현재 마우스 포인터의 위치를 가져옵니다.
|
||||
const pointer = canvas?.getPointer(e.e)
|
||||
|
||||
// 기존에 그려진 가이드라인을 제거합니다.
|
||||
removeMouseLines()
|
||||
|
||||
if (canvas?.getActiveObject()) {
|
||||
return
|
||||
}
|
||||
let newX, newY
|
||||
if (isGuidePointMode && mode === Mode.EDIT) {
|
||||
newX = Math.round(pointer.x / 200) * 200
|
||||
newY = Math.round(pointer.y / 200) * 200
|
||||
} else {
|
||||
newX = pointer.x
|
||||
newY = pointer.y
|
||||
let newX = pointer.x
|
||||
let newY = pointer.y
|
||||
|
||||
if (isGuideLineMode && isGuideDotMode && mode === Mode.EDIT) {
|
||||
const closestHorizontalLine = getClosestHorizontalLine(pointer, horizontalLineArray)
|
||||
const closetVerticalLine = getClosestVerticalLine(pointer, verticalLineArray)
|
||||
const xDiff = Math.abs(pointer.x - closetVerticalLine.x1)
|
||||
const yDiff = Math.abs(pointer.y - closestHorizontalLine.y1)
|
||||
|
||||
const x = pointer.x - guideLineLength * Math.floor(pointer.x / guideLineLength)
|
||||
const y = pointer.y - guideLineLength * Math.floor(pointer.y / guideLineLength)
|
||||
|
||||
const xRate = x / guideLineLength
|
||||
const yRate = y / guideLineLength
|
||||
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
||||
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
||||
|
||||
if (isAttachX && isAttachY) {
|
||||
newX = Math.floor(pointer.x / guideLineLength) * guideLineLength + guideLineLength / 2
|
||||
newY = Math.floor(pointer.y / guideLineLength) * guideLineLength + guideLineLength / 2
|
||||
} else {
|
||||
if (Math.min(xDiff, yDiff) <= 20) {
|
||||
if (xDiff < yDiff) {
|
||||
newX = closetVerticalLine.x1
|
||||
newY = pointer.y
|
||||
} else {
|
||||
newX = pointer.x
|
||||
newY = closestHorizontalLine.y1
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isGuideDotMode && mode === Mode.EDIT) {
|
||||
const x = pointer.x - guideDotLength * Math.floor(pointer.x / guideDotLength)
|
||||
const y = pointer.y - guideDotLength * Math.floor(pointer.y / guideDotLength)
|
||||
|
||||
const xRate = x / guideDotLength
|
||||
const yRate = y / guideDotLength
|
||||
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
||||
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
||||
|
||||
if (isAttachX && isAttachY) {
|
||||
newX = Math.floor(pointer.x / guideDotLength) * guideDotLength + guideDotLength / 2
|
||||
newY = Math.floor(pointer.y / guideDotLength) * guideDotLength + guideDotLength / 2
|
||||
}
|
||||
} else if (isGuideLineMode && mode === Mode.EDIT) {
|
||||
const closestHorizontalLine = getClosestHorizontalLine(pointer, horizontalLineArray)
|
||||
const closetVerticalLine = getClosestVerticalLine(pointer, verticalLineArray)
|
||||
const xDiff = Math.abs(pointer.x - closetVerticalLine.x1)
|
||||
const yDiff = Math.abs(pointer.y - closestHorizontalLine.y1)
|
||||
|
||||
const x = pointer.x - guideLineLength * Math.floor(pointer.x / guideLineLength)
|
||||
const y = pointer.y - guideLineLength * Math.floor(pointer.y / guideLineLength)
|
||||
|
||||
const xRate = x / guideLineLength
|
||||
const yRate = y / guideLineLength
|
||||
const isAttachX = xRate >= 0.4 && xRate <= 0.7
|
||||
const isAttachY = yRate >= 0.4 && yRate <= 0.7
|
||||
|
||||
if (isAttachX && isAttachY) {
|
||||
newX = Math.floor(pointer.x / guideLineLength) * guideLineLength + guideLineLength / 2
|
||||
newY = Math.floor(pointer.y / guideLineLength) * guideLineLength + guideLineLength / 2
|
||||
} else {
|
||||
if (Math.min(xDiff, yDiff) <= 20) {
|
||||
if (xDiff < yDiff) {
|
||||
newX = closetVerticalLine.x1
|
||||
newY = pointer.y
|
||||
} else {
|
||||
newX = pointer.x
|
||||
newY = closestHorizontalLine.y1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 가로선을 그립니다.
|
||||
const horizontalLine = new fabric.Line([0, newY, canvasSize.horizontal, newY], {
|
||||
stroke: 'black',
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'mouseLine',
|
||||
@ -132,7 +236,7 @@ export function useMode() {
|
||||
|
||||
// 세로선을 그립니다.
|
||||
const verticalLine = new fabric.Line([newX, 0, newX, canvasSize.vertical], {
|
||||
stroke: 'black',
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'mouseLine',
|
||||
@ -154,7 +258,7 @@ export function useMode() {
|
||||
}, [pointCount.current])
|
||||
|
||||
const removeGuideLines = () => {
|
||||
const guideLines = canvas?._objects.filter((obj) => obj.name === 'guideLine')
|
||||
const guideLines = canvas?._objects.filter((obj) => obj.name === 'helpGuideLine')
|
||||
guideLines?.forEach((item) => canvas?.remove(item))
|
||||
}
|
||||
|
||||
@ -176,7 +280,7 @@ export function useMode() {
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [1, 1, 1],
|
||||
})
|
||||
guideLine.name = 'guideLine'
|
||||
guideLine.name = 'helpGuideLine'
|
||||
canvas?.add(guideLine)
|
||||
} else {
|
||||
const guideLine1 = new QLine([lastX, lastY, lastX, arrivalY], {
|
||||
@ -193,8 +297,8 @@ export function useMode() {
|
||||
strokeDashArray: [1, 1, 1],
|
||||
})
|
||||
|
||||
guideLine1.name = 'guideLine'
|
||||
guideLine2.name = 'guideLine'
|
||||
guideLine1.name = 'helpGuideLine'
|
||||
guideLine2.name = 'helpGuideLine'
|
||||
|
||||
canvas?.add(guideLine1)
|
||||
canvas?.add(guideLine2)
|
||||
@ -587,7 +691,7 @@ export function useMode() {
|
||||
canvas?.add(circle)
|
||||
|
||||
if (points.current.length === 2) {
|
||||
if (isGuidePointMode) {
|
||||
if (guideLineMode || guideDotMode) {
|
||||
const vector = {
|
||||
x: points.current[1].left - points.current[0].left,
|
||||
y: points.current[1].top - points.current[0].top,
|
||||
@ -757,10 +861,8 @@ export function useMode() {
|
||||
points.current = []
|
||||
historyPoints.current = []
|
||||
|
||||
// handleOuterlines()
|
||||
const wall = makePolygon(null, sort)
|
||||
wall.set({ name: 'wall' })
|
||||
console.log('wall', wall)
|
||||
|
||||
return wall
|
||||
}
|
||||
@ -799,33 +901,7 @@ export function useMode() {
|
||||
const makePolygon = (otherLines, sort = true) => {
|
||||
// 캔버스에서 모든 라인 객체를 찾습니다.
|
||||
const lines = otherLines || historyLines.current
|
||||
|
||||
if (!otherLines) {
|
||||
//외각선 기준
|
||||
const topIndex = findTopTwoIndexesByDistance(sortedArray) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨
|
||||
|
||||
//일단 배열 6개 짜리 기준의 선 번호
|
||||
if (topIndex[0] === 4) {
|
||||
if (topIndex[1] === 5) {
|
||||
//1번
|
||||
setShape(1)
|
||||
}
|
||||
} else if (topIndex[0] === 1) {
|
||||
//4번
|
||||
if (topIndex[1] === 2) {
|
||||
setShape(4)
|
||||
}
|
||||
} else if (topIndex[0] === 0) {
|
||||
if (topIndex[1] === 1) {
|
||||
//2번
|
||||
setShape(2)
|
||||
} else if (topIndex[1] === 5) {
|
||||
setShape(3)
|
||||
}
|
||||
}
|
||||
|
||||
historyLines.current = []
|
||||
}
|
||||
historyLines.current = []
|
||||
|
||||
// 각 라인의 시작점과 끝점을 사용하여 다각형의 점 배열을 생성합니다.
|
||||
const points = lines.map((line) => ({ x: line.x1, y: line.y1 }))
|
||||
@ -853,12 +929,10 @@ export function useMode() {
|
||||
canvas.add(polygon)
|
||||
|
||||
// 캔버스를 다시 그립니다.
|
||||
if (!otherLines) {
|
||||
// polygon.fillCell()
|
||||
canvas?.renderAll()
|
||||
// polygon.setViewLengthText(false)
|
||||
setMode(Mode.DEFAULT)
|
||||
}
|
||||
// polygon.fillCell()
|
||||
canvas?.renderAll()
|
||||
// polygon.setViewLengthText(false)
|
||||
setMode(Mode.DEFAULT)
|
||||
|
||||
return polygon
|
||||
}
|
||||
|
||||
3
src/locales/ja.json
Normal file
3
src/locales/ja.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"hi": "こんにちは"
|
||||
}
|
||||
3
src/locales/ko.json
Normal file
3
src/locales/ko.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"hi": "안녕하세요"
|
||||
}
|
||||
@ -88,3 +88,9 @@ export const compassState = atom({
|
||||
default: undefined,
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
export const guideLineState = atom({
|
||||
key: 'guideLine',
|
||||
default: {},
|
||||
dangerouslyAllowMutability: true,
|
||||
})
|
||||
|
||||
6
src/store/localeAtom.js
Normal file
6
src/store/localeAtom.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil'
|
||||
|
||||
export const globalLocaleState = atom({
|
||||
key: 'globalLocaleState',
|
||||
default: 'ko',
|
||||
})
|
||||
@ -38,7 +38,7 @@ export function actionHandler(eventData, transform, x, y) {
|
||||
|
||||
// define a function that can keep the polygon in the same position when we change its width/height/top/left
|
||||
export function anchorWrapper(anchorIndex, fn) {
|
||||
return function(eventData, transform, x, y) {
|
||||
return function (eventData, transform, x, y) {
|
||||
let fabricObject = transform.target
|
||||
let originX = fabricObject?.points[anchorIndex].x - fabricObject.pathOffset.x
|
||||
let originY = fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y
|
||||
@ -675,3 +675,33 @@ export function findClosestPointWithDifferentXY(targetPoint, points) {
|
||||
|
||||
return closestPoint
|
||||
}
|
||||
|
||||
export const getClosestHorizontalLine = (pointer, horizontalLineArray) => {
|
||||
let closestLine = null
|
||||
let minDistance = Infinity
|
||||
|
||||
horizontalLineArray.forEach((line) => {
|
||||
const distance = Math.abs(line.y1 - pointer.y) // Assuming horizontal lines have the same y1 and y2
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance
|
||||
closestLine = line
|
||||
}
|
||||
})
|
||||
|
||||
return closestLine
|
||||
}
|
||||
|
||||
export const getClosestVerticalLine = (pointer, verticalLineArray) => {
|
||||
let closestLine = null
|
||||
let minDistance = Infinity
|
||||
|
||||
verticalLineArray.forEach((line) => {
|
||||
const distance = Math.abs(line.x1 - pointer.x) // Assuming horizontal lines have the same y1 and y2
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance
|
||||
closestLine = line
|
||||
}
|
||||
})
|
||||
|
||||
return closestLine
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user