273 lines
9.2 KiB
JavaScript
273 lines
9.2 KiB
JavaScript
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, horiGuideLinesState, vertGuideLinesState } from '@/store/canvasAtom'
|
|
import { fabric } from 'fabric'
|
|
import { ColorPicker, useColor } from 'react-color-palette'
|
|
import 'react-color-palette/css'
|
|
|
|
export default function GridSettingsModal(props) {
|
|
const { canvasProps } = props
|
|
const [isCustomGridSetting, setIsCustomGridSetting] = useState(true)
|
|
const [gridCheckedValue, setGridCheckValue] = useState([])
|
|
const [ratioValue, setRatioValue] = useState('1')
|
|
const moduleLength = useRef(null) //모듈 mm 길이 입력
|
|
const customModuleHoriLength = useRef(null)
|
|
const customModuleVertLength = useRef(null)
|
|
|
|
const [open, setOpen] = useRecoilState(modalState)
|
|
const [guideLine, setGuideLine] = useRecoilState(guideLineState)
|
|
const [horiGuideLines, setHoriGuideLines] = useRecoilState(horiGuideLinesState)
|
|
const [vertGuideLines, setVertGuideLines] = useRecoilState(vertGuideLinesState)
|
|
|
|
const gridSettingArray = []
|
|
|
|
const [guideColor, setGuideColor] = useColor('rgb(200, 15, 15)')
|
|
const [colorPickerShow, setColorPickerShow] = useState(false)
|
|
|
|
const boxStyle = {
|
|
width: '50px',
|
|
height: '30px',
|
|
border: '1px solid black',
|
|
backgroundColor: guideColor.hex,
|
|
}
|
|
useEffect(() => {
|
|
moduleLength.current.value = 90
|
|
customModuleHoriLength.current.value = 90
|
|
customModuleVertLength.current.value = 90
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
setIsCustomGridSetting(ratioValue !== 'custom')
|
|
}, [ratioValue])
|
|
|
|
const drawGridSettings = () => {
|
|
//기존에 선택된 데이터가 있으면 그 데이터를 포함한다
|
|
if (!(Object.keys(guideLine).length === 0 && guideLine.constructor === Object)) {
|
|
gridSettingArray.push(...guideLine)
|
|
}
|
|
|
|
let moduleHoriLength = moduleLength.current.value //가로 간격
|
|
let moduleVertLength = moduleLength.current.value //새로 간격
|
|
|
|
if (ratioValue === 'custom') {
|
|
moduleHoriLength = customModuleHoriLength.current.value
|
|
moduleVertLength = customModuleVertLength.current.value
|
|
} else {
|
|
moduleHoriLength = moduleHoriLength / ratioValue
|
|
moduleVertLength = moduleVertLength / ratioValue
|
|
}
|
|
|
|
if (gridCheckedValue.includes('line')) {
|
|
const horizontalLineArray = []
|
|
const verticalLineArray = []
|
|
|
|
for (let i = 0; i < canvasProps.height / moduleVertLength + 1; i++) {
|
|
const horizontalLine = new fabric.Line(
|
|
[0, i * moduleVertLength - moduleVertLength / 2, canvasProps.width, i * moduleVertLength - moduleVertLength / 2],
|
|
{
|
|
stroke: guideColor.hex,
|
|
strokeWidth: 1,
|
|
selectable: true,
|
|
lockMovementX: true,
|
|
lockMovementY: true,
|
|
lockRotation: true,
|
|
lockScalingX: true,
|
|
lockScalingY: true,
|
|
name: 'guideLine',
|
|
strokeDashArray: [5, 2],
|
|
opacity: 0.3,
|
|
direction: 'horizontal',
|
|
},
|
|
)
|
|
canvasProps.add(horizontalLine)
|
|
horizontalLineArray.push(horizontalLine)
|
|
}
|
|
|
|
for (let i = 0; i < canvasProps.width / moduleHoriLength + 1; i++) {
|
|
const verticalLine = new fabric.Line(
|
|
[i * moduleHoriLength - moduleHoriLength / 2, 0, i * moduleHoriLength - moduleHoriLength / 2, canvasProps.height],
|
|
{
|
|
stroke: guideColor.hex,
|
|
strokeWidth: 1,
|
|
selectable: true,
|
|
lockMovementX: true,
|
|
lockMovementY: true,
|
|
lockRotation: true,
|
|
lockScalingX: true,
|
|
lockScalingY: true,
|
|
name: 'guideLine',
|
|
strokeDashArray: [5, 2],
|
|
opacity: 0.3,
|
|
direction: 'vertical',
|
|
},
|
|
)
|
|
canvasProps.add(verticalLine)
|
|
verticalLineArray.push(verticalLine)
|
|
}
|
|
canvasProps.renderAll()
|
|
|
|
const snapDistance = 10
|
|
|
|
const recoilObj = {
|
|
guideMode: 'guideLine',
|
|
horizontalLineArray,
|
|
verticalLineArray,
|
|
moduleVertLength: moduleVertLength,
|
|
moduleHoriLength: moduleHoriLength,
|
|
}
|
|
gridSettingArray.push(recoilObj)
|
|
const newHoriGuideLines = [...horiGuideLines]
|
|
horizontalLineArray.forEach((line) => {
|
|
newHoriGuideLines.push(line)
|
|
})
|
|
const newVertGuideLines = [...vertGuideLines]
|
|
verticalLineArray.forEach((line) => {
|
|
newVertGuideLines.push(line)
|
|
})
|
|
setHoriGuideLines(newHoriGuideLines)
|
|
setVertGuideLines(newVertGuideLines)
|
|
}
|
|
|
|
if (gridCheckedValue.includes('dot')) {
|
|
const circle = new fabric.Circle({
|
|
radius: 2,
|
|
fill: 'white',
|
|
stroke: guideColor.hex,
|
|
strokeWidth: 0.7,
|
|
originX: 'center',
|
|
originY: 'center',
|
|
selectable: false,
|
|
lockMovementX: true,
|
|
lockMovementY: true,
|
|
lockRotation: true,
|
|
lockScalingX: true,
|
|
lockScalingY: true,
|
|
})
|
|
|
|
const patternSourceCanvas = new fabric.StaticCanvas(null, {
|
|
width: moduleHoriLength,
|
|
height: moduleVertLength,
|
|
})
|
|
|
|
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,
|
|
name: 'guideDot',
|
|
},
|
|
)
|
|
|
|
canvasProps.add(backgroundPolygon)
|
|
backgroundPolygon.sendToBack()
|
|
canvasProps.renderAll()
|
|
|
|
const recoilObj = {
|
|
guideMode: 'guideDot',
|
|
moduleVertLength: moduleVertLength,
|
|
moduleHoriLength: moduleHoriLength,
|
|
}
|
|
|
|
gridSettingArray.push(recoilObj)
|
|
}
|
|
canvasProps.renderAll()
|
|
setGuideLine(gridSettingArray)
|
|
}
|
|
|
|
const removeGuideLines = () => {
|
|
if (!(Object.keys(guideLine).length === 0 && guideLine.constructor === Object)) {
|
|
const guideLines = canvasProps._objects.filter((obj) => obj.name === 'guideLine' || obj.name === 'guideDot')
|
|
guideLines?.forEach((item) => canvasProps.remove(item))
|
|
canvasProps.renderAll()
|
|
setGuideLine([])
|
|
setHoriGuideLines([])
|
|
setVertGuideLines([])
|
|
} else {
|
|
alert('그리드가 없습니다.')
|
|
return
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div>
|
|
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
|
<label style={{ display: 'block', marginBottom: '5px' }}>
|
|
<CheckboxGroup label="그리드 설정" value={gridCheckedValue} defaultChecked={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} />
|
|
mm
|
|
</div>
|
|
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
|
<RadioGroup label="비율 설정" value={ratioValue} defaultValue={ratioValue} onValueChange={setRatioValue}>
|
|
<Radio value="1">원치수</Radio>
|
|
<Radio value="2">1/2</Radio>
|
|
<Radio value="4">1/4</Radio>
|
|
<Radio value="10">1/10</Radio>
|
|
<Radio value="custom">임의간격</Radio>
|
|
</RadioGroup>
|
|
</div>
|
|
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
|
가이드컬러 <div style={boxStyle} onClick={() => setColorPickerShow(!colorPickerShow)}></div>
|
|
</div>
|
|
{colorPickerShow && (
|
|
<ColorPicker color={guideColor} onChange={setGuideColor} hideInput={['hsv', 'rgb', 'hex']} height={100} hideAlpha={true} />
|
|
)}
|
|
|
|
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
|
<Checkbox value="linked" isDisabled={isCustomGridSetting}>
|
|
종횡연동
|
|
</Checkbox>
|
|
</div>
|
|
</div>
|
|
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
|
<Input type="number" label="가로간격" ref={customModuleHoriLength} min={0} isDisabled={isCustomGridSetting} />
|
|
mm
|
|
</div>
|
|
<div className="flex w-full flex-wrap md:flex-nowrap gap-4">
|
|
<Input type="number" label="세로간격" ref={customModuleVertLength} 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>
|
|
<Button size="sm" onClick={() => removeGuideLines()}>
|
|
그리드 삭제
|
|
</Button>
|
|
</div>
|
|
</>
|
|
)
|
|
}
|