Merge branch 'dev' of https://git.jetbrains.space/nalpari/q-cast-iii/qcast-front into dev-ck
# Conflicts: # src/components/floor-plan/modal/grid/DotLineGrid.jsx # src/components/floor-plan/modal/setting01/GridOption.jsx
4
public/static/images/canvas/allocation_delete.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="1" width="14" height="14" rx="7" stroke="white"/>
|
||||
<path d="M9.741 11.059L7.509 8.827L5.277 11.059L4.512 10.294L6.744 8.062L4.512 5.83L5.277 5.065L7.509 7.297L9.741 5.065L10.506 5.83L8.274 8.062L10.506 10.294L9.741 11.059Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 358 B |
4
public/static/images/canvas/allocation_edit.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="12" height="12" rx="6" fill="#EBEBEB"/>
|
||||
<path d="M5.41297 9.531V6.723H2.78697V5.631H5.41297V2.81H6.56997V5.631H9.20897V6.723H6.56997V9.531H5.41297Z" fill="black"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 280 B |
6
public/static/images/canvas/allocation_icon01_black.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="7" height="7" stroke="#101010"/>
|
||||
<rect x="7.5" y="0.5" width="7" height="7" stroke="#101010"/>
|
||||
<rect x="0.5" y="7.5" width="7" height="7" stroke="#101010"/>
|
||||
<rect x="7.5" y="7.5" width="7" height="7" stroke="#101010"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 351 B |
6
public/static/images/canvas/allocation_icon01_white.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="7" height="7" stroke="white"/>
|
||||
<rect x="7.5" y="0.5" width="7" height="7" stroke="white"/>
|
||||
<rect x="0.5" y="7.5" width="7" height="7" stroke="white"/>
|
||||
<rect x="7.5" y="7.5" width="7" height="7" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 343 B |
6
public/static/images/canvas/allocation_icon02_black.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="18" height="15" viewBox="0 0 18 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="7" height="7" stroke="#101010"/>
|
||||
<rect x="7.5" y="0.5" width="7" height="7" stroke="#101010"/>
|
||||
<rect x="3.5" y="7.5" width="7" height="7" stroke="#101010"/>
|
||||
<rect x="10.5" y="7.5" width="7" height="7" stroke="#101010"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 352 B |
6
public/static/images/canvas/allocation_icon02_white.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<svg width="18" height="15" viewBox="0 0 18 15" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="7" height="7" stroke="white"/>
|
||||
<rect x="7.5" y="0.5" width="7" height="7" stroke="white"/>
|
||||
<rect x="3.5" y="7.5" width="7" height="7" stroke="white"/>
|
||||
<rect x="10.5" y="7.5" width="7" height="7" stroke="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 344 B |
10
public/static/images/canvas/eaves_icon01.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.75" width="30" height="30" fill="#45CD7D"/>
|
||||
<mask id="mask0_4083_3047" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="31" height="30">
|
||||
<rect x="0.75" width="30" height="30" fill="#45CD7D"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4083_3047)">
|
||||
<path d="M0.5 -0.5L16.5 13.5L32 -1" stroke="black" stroke-width="2"/>
|
||||
<path d="M16.5 13V30" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 499 B |
10
public/static/images/canvas/eaves_icon02.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1" width="30" height="30" fill="#45CD7D"/>
|
||||
<mask id="mask0_4083_3055" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="1" y="0" width="30" height="30">
|
||||
<rect x="1" width="30" height="30" fill="#45CD7D"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4083_3055)">
|
||||
<path d="M16 7V35" stroke="black" stroke-width="2"/>
|
||||
<path d="M1 -1L8 7H26L32 -1" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 483 B |
15
public/static/images/canvas/eaves_icon03.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1.25" width="30" height="30" fill="white"/>
|
||||
<mask id="mask0_4083_3069" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="1" y="0" width="31" height="30">
|
||||
<rect x="1.25" width="30" height="30" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4083_3069)">
|
||||
<path d="M1 4H16V30" stroke="black" stroke-width="2"/>
|
||||
<path d="M1 30.5L16 4.5" stroke="black" stroke-width="2"/>
|
||||
<rect x="18" y="3" width="14" height="1" fill="#ED0004"/>
|
||||
<rect x="6" y="26" width="26" height="1" fill="#ED0004"/>
|
||||
<rect x="23.5" y="7" width="1" height="16" fill="black"/>
|
||||
<path d="M21.5 9L24 6.5L26.5 9" stroke="black"/>
|
||||
<path d="M21.5 20.5L24 23L26.5 20.5" stroke="black"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 760 B |
9
public/static/images/canvas/eaves_icon04.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.75" width="30" height="30" fill="#45CD7D"/>
|
||||
<mask id="mask0_4083_3140" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="31" height="30">
|
||||
<rect x="0.5" width="30" height="30" fill="#45CD7D"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4083_3140)">
|
||||
<path d="M15.5 0V30" stroke="black" stroke-width="2"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 427 B |
10
public/static/images/canvas/eaves_icon05.svg
Normal file
@ -0,0 +1,10 @@
|
||||
<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.75" width="30" height="30" fill="white"/>
|
||||
<mask id="mask0_4083_3151" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="31" height="30">
|
||||
<rect x="0.75" width="30" height="30" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4083_3151)">
|
||||
<path d="M3.5 30V20L17.5 6H31" stroke="black" stroke-width="2"/>
|
||||
<path d="M28 15.5C28 21.503 22.2579 26.5 15 26.5C7.74211 26.5 2 21.503 2 15.5C2 9.49705 7.74211 4.5 15 4.5C22.2579 4.5 28 9.49705 28 15.5Z" stroke="#ED0004"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 593 B |
18
public/static/images/canvas/eaves_icon06.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="30" height="30" fill="#45CD7D"/>
|
||||
<mask id="path-1-inside-1_4083_3261" fill="white">
|
||||
<path d="M0 13H22V30H0V13Z"/>
|
||||
</mask>
|
||||
<path d="M22 13H24V11H22V13ZM0 15H22V11H0V15ZM20 13V30H24V13H20Z" fill="black" mask="url(#path-1-inside-1_4083_3261)"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect width="22" height="13" fill="#AA5234"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 969 B |
23
public/static/images/canvas/eaves_icon07.svg
Normal file
@ -0,0 +1,23 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="30" height="30" fill="#45CD7D"/>
|
||||
<mask id="path-1-inside-1_4083_3372" fill="white">
|
||||
<path d="M0 13H22V30H0V13Z"/>
|
||||
</mask>
|
||||
<path d="M22 13H24V11H22V13ZM0 15H22V11H0V15ZM20 13V30H24V13H20Z" fill="black" mask="url(#path-1-inside-1_4083_3372)"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="2" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<rect x="13" y="15" width="2" height="15" fill="black"/>
|
||||
<mask id="path-5-inside-2_4083_3372" fill="white">
|
||||
<path d="M13 8H22V15H13V8Z"/>
|
||||
</mask>
|
||||
<path d="M13 8H22V15H13V8Z" fill="#45CD7D"/>
|
||||
<path d="M13 8V6H11V8H13ZM22 8H24V6H22V8ZM13 10H22V6H13V10ZM20 8V15H24V8H20ZM15 15V8H11V15H15Z" fill="black" mask="url(#path-5-inside-2_4083_3372)"/>
|
||||
<rect width="13" height="13" fill="#AA5234"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
15
public/static/images/canvas/eaves_icon08.svg
Normal file
@ -0,0 +1,15 @@
|
||||
<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="1.25" width="30" height="30" fill="white"/>
|
||||
<mask id="mask0_4083_3329" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="1" y="0" width="31" height="30">
|
||||
<rect x="1.25" width="30" height="30" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_4083_3329)">
|
||||
<path d="M7 4H22V30" stroke="black" stroke-width="2"/>
|
||||
<path d="M-9 26H6V-1.19209e-07" stroke="black" stroke-width="2"/>
|
||||
<rect x="24" y="3" width="8" height="1" fill="#ED0004"/>
|
||||
<rect x="8" y="26" width="24" height="1" fill="#ED0004"/>
|
||||
<rect x="26.5" y="7" width="1" height="16" fill="black"/>
|
||||
<path d="M24.5 9L27 6.5L29.5 9" stroke="black"/>
|
||||
<path d="M24.5 20.5L27 23L29.5 20.5" stroke="black"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 766 B |
34
public/static/images/canvas/shape_menu01.svg
Normal file
@ -0,0 +1,34 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_783)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="64" height="1" fill="white"/>
|
||||
<rect y="63" width="34" height="1" fill="white"/>
|
||||
<rect x="34" y="37" width="30" height="1" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="5" y="5" width="54" height="1" fill="#818181"/>
|
||||
<rect x="5" y="58" width="24" height="1" fill="#818181"/>
|
||||
<rect x="28" y="32" width="31" height="1" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="18" y="18" width="1" height="25" fill="white"/>
|
||||
<path d="M64 0.5L43 18.5L63.5 37" stroke="white"/>
|
||||
<path d="M33 63.5L18 42.5L1 63.5" stroke="white"/>
|
||||
<rect x="19" y="18" width="24" height="1" fill="white"/>
|
||||
<path d="M1 1L18.5 20" stroke="white"/>
|
||||
<path d="M19 19L34 38" stroke="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_783">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
31
public/static/images/canvas/shape_menu02.svg
Normal file
@ -0,0 +1,31 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_808)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="64" height="1" fill="white"/>
|
||||
<rect y="63" width="34" height="1" fill="white"/>
|
||||
<rect x="34" y="37" width="30" height="1" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="5" y="5" width="54" height="1" fill="#818181"/>
|
||||
<rect x="5" y="58" width="24" height="1" fill="#818181"/>
|
||||
<rect x="28" y="32" width="31" height="1" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="28" y="1" width="1" height="31" fill="white"/>
|
||||
<rect x="17.5" y="32.5" width="16" height="31" stroke="white"/>
|
||||
<rect x="18" y="37" width="15" height="1" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_808">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
31
public/static/images/canvas/shape_menu03.svg
Normal file
@ -0,0 +1,31 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_830)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="64" height="1" fill="white"/>
|
||||
<rect y="63" width="34" height="1" fill="white"/>
|
||||
<rect x="34" y="37" width="30" height="1" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="5" y="5" width="54" height="1" fill="#818181"/>
|
||||
<rect x="5" y="58" width="24" height="1" fill="#818181"/>
|
||||
<rect x="28" y="32" width="31" height="1" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="28.5" y="16.5" width="35" height="21" stroke="white"/>
|
||||
<rect x="1" y="32" width="32" height="1" fill="white"/>
|
||||
<rect x="33" y="17" width="1" height="20" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_830">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
30
public/static/images/canvas/shape_menu04.svg
Normal file
@ -0,0 +1,30 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_852)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="64" height="1" fill="white"/>
|
||||
<rect y="63" width="34" height="1" fill="white"/>
|
||||
<rect x="34" y="37" width="30" height="1" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect x="33" y="37" width="1" height="26" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect y="1" width="1" height="63" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="63" y="1" width="1" height="37" fill="white"/>
|
||||
<rect x="5" y="5" width="54" height="1" fill="#818181"/>
|
||||
<rect x="5" y="58" width="24" height="1" fill="#818181"/>
|
||||
<rect x="28" y="32" width="31" height="1" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="28" y="33" width="1" height="26" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="5" y="5" width="1" height="54" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="58" y="5" width="1" height="28" fill="#818181"/>
|
||||
<rect x="58" y="1" width="5" height="36" fill="white"/>
|
||||
<path d="M51.5303 19.5303C51.8232 19.2374 51.8232 18.7626 51.5303 18.4697L46.7574 13.6967C46.4645 13.4038 45.9896 13.4038 45.6967 13.6967C45.4038 13.9896 45.4038 14.4645 45.6967 14.7574L49.9393 19L45.6967 23.2426C45.4038 23.5355 45.4038 24.0104 45.6967 24.3033C45.9896 24.5962 46.4645 24.5962 46.7574 24.3033L51.5303 19.5303ZM40 19.75H51V18.25H40V19.75Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_852">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
29
public/static/images/canvas/shape_menu05.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_874)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="44" height="1" transform="matrix(-1 0 0 1 64 0)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(-1 0 0 1 64 63)" fill="white"/>
|
||||
<rect width="10" height="1" transform="matrix(-1 0 0 1 30 37)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(-1 0 0 1 31 37)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(-1 0 0 1 31 37)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(-1 0 0 1 64 1)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(-1 0 0 1 64 1)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(-1 0 0 1 21 1)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(-1 0 0 1 21 1)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(-1 0 0 1 59 5)" fill="#818181"/>
|
||||
<rect width="24" height="1" transform="matrix(-1 0 0 1 59 58)" fill="#818181"/>
|
||||
<rect width="11" height="1" transform="matrix(-1 0 0 1 36 32)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(-1 0 0 1 36 33)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(-1 0 0 1 36 33)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(-1 0 0 1 59 5)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(-1 0 0 1 59 5)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(-1 0 0 1 26 5)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(-1 0 0 1 26 5)" fill="#818181"/>
|
||||
<path d="M4.46967 32.5303C4.17678 32.2374 4.17678 31.7626 4.46967 31.4697L9.24264 26.6967C9.53553 26.4038 10.0104 26.4038 10.3033 26.6967C10.5962 26.9896 10.5962 27.4645 10.3033 27.7574L6.06066 32L10.3033 36.2426C10.5962 36.5355 10.5962 37.0104 10.3033 37.3033C10.0104 37.5962 9.53553 37.5962 9.24264 37.3033L4.46967 32.5303ZM16 32.75H5V31.25H16V32.75Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_874">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
29
public/static/images/canvas/shape_menu06.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_894)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="44" height="1" transform="matrix(1 0 0 -1 0 64)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(1 0 0 -1 0 1)" fill="white"/>
|
||||
<rect width="10" height="1" transform="matrix(1 0 0 -1 34 27)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(1 0 0 -1 33 27)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(1 0 0 -1 33 27)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(1 0 0 -1 0 63)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(1 0 0 -1 0 63)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(1 0 0 -1 43 63)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(1 0 0 -1 43 63)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(1 0 0 -1 5 59)" fill="#818181"/>
|
||||
<rect width="24" height="1" transform="matrix(1 0 0 -1 5 6)" fill="#818181"/>
|
||||
<rect width="11" height="1" transform="matrix(1 0 0 -1 28 32)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(1 0 0 -1 28 31)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(1 0 0 -1 28 31)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(1 0 0 -1 5 59)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(1 0 0 -1 5 59)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(1 0 0 -1 38 59)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(1 0 0 -1 38 59)" fill="#818181"/>
|
||||
<path d="M59.5303 32.5303C59.8232 32.2374 59.8232 31.7626 59.5303 31.4697L54.7574 26.6967C54.4645 26.4038 53.9896 26.4038 53.6967 26.6967C53.4038 26.9896 53.4038 27.4645 53.6967 27.7574L57.9393 32L53.6967 36.2426C53.4038 36.5355 53.4038 37.0104 53.6967 37.3033C53.9896 37.5962 54.4645 37.5962 54.7574 37.3033L59.5303 32.5303ZM48 32.75H59V31.25H48V32.75Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_894">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
29
public/static/images/canvas/shape_menu07.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_914)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="44" height="1" transform="matrix(0 1 1 0 0 0)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(0 1 1 0 63 0)" fill="white"/>
|
||||
<rect width="10" height="1" transform="matrix(0 1 1 0 37 34)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(0 1 1 0 37 33)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(0 1 1 0 37 33)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(0 1 1 0 1 0)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(0 1 1 0 1 0)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(0 1 1 0 1 43)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(0 1 1 0 1 43)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(0 1 1 0 5 5)" fill="#818181"/>
|
||||
<rect width="24" height="1" transform="matrix(0 1 1 0 58 5)" fill="#818181"/>
|
||||
<rect width="11" height="1" transform="matrix(0 1 1 0 32 28)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(0 1 1 0 33 28)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(0 1 1 0 33 28)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(0 1 1 0 5 5)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(0 1 1 0 5 5)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(0 1 1 0 5 38)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(0 1 1 0 5 38)" fill="#818181"/>
|
||||
<path d="M33.0303 59.0303C32.7374 59.3232 32.2626 59.3232 31.9697 59.0303L27.1967 54.2574C26.9038 53.9645 26.9038 53.4896 27.1967 53.1967C27.4896 52.9038 27.9645 52.9038 28.2574 53.1967L32.5 57.4393L36.7426 53.1967C37.0355 52.9038 37.5104 52.9038 37.8033 53.1967C38.0962 53.4896 38.0962 53.9645 37.8033 54.2574L33.0303 59.0303ZM33.25 47.5V58.5H31.75V47.5H33.25Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_914">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
29
public/static/images/canvas/shape_menu08.svg
Normal file
@ -0,0 +1,29 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_4049_934)">
|
||||
<rect width="64" height="64" fill="#313131"/>
|
||||
<rect width="44" height="1" transform="matrix(0 -1 -1 0 64 64)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(0 -1 -1 0 1 64)" fill="white"/>
|
||||
<rect width="10" height="1" transform="matrix(0 -1 -1 0 27 30)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(0 -1 -1 0 27 31)" fill="white"/>
|
||||
<rect width="1" height="26" transform="matrix(0 -1 -1 0 27 31)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(0 -1 -1 0 63 64)" fill="white"/>
|
||||
<rect width="1" height="63" transform="matrix(0 -1 -1 0 63 64)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(0 -1 -1 0 63 21)" fill="white"/>
|
||||
<rect width="1" height="37" transform="matrix(0 -1 -1 0 63 21)" fill="white"/>
|
||||
<rect width="34" height="1" transform="matrix(0 -1 -1 0 59 59)" fill="#818181"/>
|
||||
<rect width="24" height="1" transform="matrix(0 -1 -1 0 6 59)" fill="#818181"/>
|
||||
<rect width="11" height="1" transform="matrix(0 -1 -1 0 32 36)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(0 -1 -1 0 31 36)" fill="#818181"/>
|
||||
<rect width="1" height="26" transform="matrix(0 -1 -1 0 31 36)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(0 -1 -1 0 59 59)" fill="#818181"/>
|
||||
<rect width="1" height="54" transform="matrix(0 -1 -1 0 59 59)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(0 -1 -1 0 59 26)" fill="#818181"/>
|
||||
<rect width="1" height="28" transform="matrix(0 -1 -1 0 59 26)" fill="#818181"/>
|
||||
<path d="M33.0303 4.96967C32.7374 4.67678 32.2626 4.67678 31.9697 4.96967L27.1967 9.74264C26.9038 10.0355 26.9038 10.5104 27.1967 10.8033C27.4896 11.0962 27.9645 11.0962 28.2574 10.8033L32.5 6.56066L36.7426 10.8033C37.0355 11.0962 37.5104 11.0962 37.8033 10.8033C38.0962 10.5104 38.0962 10.0355 37.8033 9.74264L33.0303 4.96967ZM33.25 16.5V5.5H31.75V16.5H33.25Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_4049_934">
|
||||
<rect width="64" height="64" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
@ -1,18 +1,45 @@
|
||||
import React from 'react'
|
||||
import Hero from '@/components/Hero'
|
||||
import StuffDetail from '@/components/management/StuffDetail'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import '@/styles/contents.scss'
|
||||
import StuffHeader from '@/components/management/StuffHeader'
|
||||
import StuffDetail from '@/components/management/StuffDetail'
|
||||
export default function ManagementStuffDetailPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="pt-48 flex justify-left">
|
||||
<h1 className="text-4xl archivo-black-regular">물건정보</h1>
|
||||
<Link href="/management/plan">
|
||||
<h1 className="text-4xl archivo-black-regular">도면작성</h1>
|
||||
</Link>
|
||||
<div className="sub-header">
|
||||
<div className="sub-header-inner">
|
||||
<ul className="sub-header-title-wrap">
|
||||
<li className="title-item">
|
||||
<Link className="sub-header-title" href={'#'}>
|
||||
商品情報
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="sub-header-location">
|
||||
<li className="location-item">
|
||||
<span className="home">
|
||||
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
|
||||
</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>物品及び図面管理</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>商品情報</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="m2">
|
||||
<StuffDetail />
|
||||
<div className="sub-content">
|
||||
<div className="sub-content-inner">
|
||||
<div className="sub-content-box">
|
||||
<StuffHeader />
|
||||
</div>
|
||||
<div className="sub-content-box">
|
||||
<StuffDetail />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,20 +1,40 @@
|
||||
import StuffSearchCondition from '@/components/management/StuffSearchCondition'
|
||||
import Stuff from '@/components/management/Stuff'
|
||||
import { initCheck } from '@/util/session-util'
|
||||
import Hero from '@/components/Hero'
|
||||
import Image from 'next/image'
|
||||
import '@/styles/contents.scss'
|
||||
export default async function ManagementStuffPage() {
|
||||
await initCheck()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Hero title="물건현황" />
|
||||
<div>
|
||||
<div className="m2">
|
||||
<StuffSearchCondition />
|
||||
<div className="sub-header">
|
||||
<div className="sub-header-inner">
|
||||
<h1 className="sub-header-title">物品及び図面管理</h1>
|
||||
<ul className="sub-header-location">
|
||||
<li className="location-item">
|
||||
<span className="home">
|
||||
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
|
||||
</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>物品及び図面管理</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>新規物件登録</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col justify-center my-8 pt-20">
|
||||
<Stuff />
|
||||
<div className="sub-content">
|
||||
<div className="sub-content-inner">
|
||||
<div className="sub-content-box">
|
||||
<StuffSearchCondition />
|
||||
</div>
|
||||
<div className="sub-content-box">
|
||||
<Stuff />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,14 +1,41 @@
|
||||
import React from 'react'
|
||||
import Hero from '@/components/Hero'
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import '@/styles/contents.scss'
|
||||
import StuffDetail from '@/components/management/StuffDetail'
|
||||
export default function ManagementStuffDetailPage() {
|
||||
return (
|
||||
<>
|
||||
<div className="pt-48 flex justify-left">
|
||||
<h1 className="text-4xl archivo-black-regular">물건정보</h1>
|
||||
<div className="sub-header">
|
||||
<div className="sub-header-inner">
|
||||
<ul className="sub-header-title-wrap">
|
||||
<li className="title-item">
|
||||
<Link className="sub-header-title" href={'#'}>
|
||||
商品情報
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="sub-header-location">
|
||||
<li className="location-item">
|
||||
<span className="home">
|
||||
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
|
||||
</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>物品及び図面管理</span>
|
||||
</li>
|
||||
<li className="location-item">
|
||||
<span>商品情報</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div className="m2">
|
||||
<StuffDetail />
|
||||
<div className="sub-content">
|
||||
<div className="sub-content-inner">
|
||||
<div className="sub-content-box">
|
||||
<StuffDetail />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -53,8 +53,9 @@ export const Mode = {
|
||||
export const LINE_TYPE = {
|
||||
WALLLINE: {
|
||||
/**
|
||||
* 처마 / 캐라바 / 벽 / 팔작지붕 / 반절처 / 한쪽흐름
|
||||
* 없음 / 처마 / 캐라바 / 벽 / 팔작지붕 / 반절처 / 한쪽흐름
|
||||
*/
|
||||
DEFAULT: 'default',
|
||||
EAVES: 'eaves',
|
||||
GABLE: 'gable',
|
||||
WALL: 'wall',
|
||||
|
||||
@ -6,6 +6,7 @@ import { guideLineState, horiGuideLinesState, vertGuideLinesState } from '@/stor
|
||||
import { fabric } from 'fabric'
|
||||
import { ColorPicker, useColor } from 'react-color-palette'
|
||||
import 'react-color-palette/css'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
|
||||
export default function GridSettingsModal(props) {
|
||||
const { canvasProps } = props
|
||||
@ -23,7 +24,7 @@ export default function GridSettingsModal(props) {
|
||||
|
||||
const gridSettingArray = []
|
||||
|
||||
const [guideColor, setGuideColor] = useColor('rgb(200, 15, 15)')
|
||||
const gridColor = useRecoilValue(gridColorState)
|
||||
const [colorPickerShow, setColorPickerShow] = useState(false)
|
||||
|
||||
const boxStyle = {
|
||||
@ -67,7 +68,7 @@ export default function GridSettingsModal(props) {
|
||||
const horizontalLine = new fabric.Line(
|
||||
[0, i * moduleVertLength - moduleVertLength / 2, canvasProps.width, i * moduleVertLength - moduleVertLength / 2],
|
||||
{
|
||||
stroke: guideColor.hex,
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
@ -89,7 +90,7 @@ export default function GridSettingsModal(props) {
|
||||
const verticalLine = new fabric.Line(
|
||||
[i * moduleHoriLength - moduleHoriLength / 2, 0, i * moduleHoriLength - moduleHoriLength / 2, canvasProps.height],
|
||||
{
|
||||
stroke: guideColor.hex,
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
|
||||
@ -39,7 +39,7 @@ import QEmptyContextMenu from '@/components/common/context-menu/QEmptyContextMen
|
||||
import InitSettingsModal from './InitSettingsModal'
|
||||
import GridSettingsModal from './GridSettingsModal'
|
||||
import { SurfaceShapeModal } from '@/components/ui/SurfaceShape'
|
||||
import { changeAllGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils'
|
||||
import { changeAllHipAndGableRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils'
|
||||
import ThumbnailList from '@/components/ui/ThumbnailLIst'
|
||||
import ObjectPlacement from '@/components/ui/ObjectPlacement'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
@ -144,10 +144,10 @@ export default function Roof2(props) {
|
||||
|
||||
useEffect(() => {
|
||||
get({ url: `/api/canvas-management/canvas-statuses/by-object/test123240822001/${userId}` }).then((res) => {
|
||||
console.log(res)
|
||||
// console.log(res)
|
||||
|
||||
const arrangeData = res.map((item) => {
|
||||
console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, ''))
|
||||
// console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, ''))
|
||||
const test = item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')
|
||||
const test2 = test.substring(1, test.length - 1)
|
||||
return {
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import Draggable from 'react-draggable'
|
||||
|
||||
export default function WithDraggable({ isShow, children, pos }) {
|
||||
export default function WithDraggable({ isShow, children, pos, handle = '' }) {
|
||||
const [position, setPosition] = useState({ x: 0, y: 0 })
|
||||
|
||||
const handleOnDrag = (e, data) => {
|
||||
@ -17,7 +17,11 @@ export default function WithDraggable({ isShow, children, pos }) {
|
||||
return (
|
||||
<>
|
||||
{isShow && (
|
||||
<Draggable position={{ x: position.x, y: position.y }} onDrag={(e, data) => handleOnDrag(e, data)}>
|
||||
<Draggable
|
||||
position={{ x: position.x, y: position.y }}
|
||||
onDrag={(e, data) => handleOnDrag(e, data)}
|
||||
handle={handle === '' ? '.modal-head' : handle}
|
||||
>
|
||||
{children}
|
||||
</Draggable>
|
||||
)}
|
||||
|
||||
@ -13,7 +13,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
area: 0,
|
||||
children: [],
|
||||
initialize: function (points, options, canvas) {
|
||||
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? false })
|
||||
this.callSuper('initialize', points, { ...options, selectable: options.selectable ?? true })
|
||||
if (options.id) {
|
||||
this.id = options.id
|
||||
} else {
|
||||
@ -75,7 +75,7 @@ export const QLine = fabric.util.createClass(fabric.Line, {
|
||||
const y2 = this.top + this.height * scaleY
|
||||
const dx = x2 - x1
|
||||
const dy = y2 - y1
|
||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0))
|
||||
this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10
|
||||
},
|
||||
|
||||
addLengthText() {
|
||||
|
||||
@ -27,6 +27,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
point.x = Math.round(point.x)
|
||||
point.y = Math.round(point.y)
|
||||
})
|
||||
options.selectable = options.selectable ?? true
|
||||
options.sort = options.sort ?? true
|
||||
options.parentId = options.parentId ?? null
|
||||
|
||||
@ -51,6 +52,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
}
|
||||
|
||||
this.callSuper('initialize', points, options)
|
||||
|
||||
if (options.id) {
|
||||
this.id = options.id
|
||||
} else {
|
||||
@ -164,6 +166,9 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
stroke: this.stroke,
|
||||
strokeWidth: this.strokeWidth,
|
||||
fontSize: this.fontSize,
|
||||
attributes: {
|
||||
offset: 0,
|
||||
},
|
||||
direction: getDirectionByPoint(point, nextPoint),
|
||||
idx: i,
|
||||
})
|
||||
@ -193,14 +198,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
|
||||
const end = points[(i + 1) % points.length]
|
||||
const dx = end.x - start.x
|
||||
const dy = end.y - start.y
|
||||
const length = Math.sqrt(dx * dx + dy * dy)
|
||||
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10
|
||||
|
||||
const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
|
||||
|
||||
const degree = (Math.atan2(dy, dx) * 180) / Math.PI
|
||||
|
||||
// Create new text object if it doesn't exist
|
||||
const text = new fabric.IText(length.toFixed(0), {
|
||||
const text = new fabric.Text(length.toFixed(0), {
|
||||
left: midPoint.x,
|
||||
top: midPoint.y,
|
||||
fontSize: this.fontSize,
|
||||
|
||||
@ -3,9 +3,12 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import CanvasFrame from './CanvasFrame'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
|
||||
export default function CanvasLayout() {
|
||||
const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요
|
||||
@ -14,8 +17,11 @@ export default function CanvasLayout() {
|
||||
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const sessionState = useRecoilValue(sessionStore)
|
||||
|
||||
const { getCanvasByObjectNo } = usePlan()
|
||||
const { getMessage } = useMessage()
|
||||
const { swalFire } = useSwal()
|
||||
const { getCanvasByObjectNo, delCanvasById } = usePlan()
|
||||
|
||||
const handleCurrentPlan = (newCurrentId) => {
|
||||
if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) {
|
||||
@ -38,21 +44,29 @@ export default function CanvasLayout() {
|
||||
const handleDeletePlan = (e, id) => {
|
||||
e.stopPropagation() // 이벤트 버블링 방지
|
||||
|
||||
// 삭제할 아이디와 다른 아이템만 남김
|
||||
const filterInitPlans = initCanvasPlans.filter((plan) => plan.id !== id)
|
||||
setInitCanvasPlans(filterInitPlans)
|
||||
const filterAddPlans = addCanvasPlans.filter((plan) => plan.id !== id)
|
||||
setAddCanvasPlans(filterAddPlans)
|
||||
|
||||
const combinedPlans = [...filterInitPlans, ...filterAddPlans]
|
||||
if (combinedPlans.length === 0) {
|
||||
// 모든 데이터가 삭제된 경우
|
||||
setPlanNum(0)
|
||||
if (initCanvasPlans.some((plan) => plan.id === id)) {
|
||||
delCanvasById(id)
|
||||
.then((res) => {
|
||||
swalFire({ text: getMessage('common.message.delete') })
|
||||
console.log('[DELETE] canvas-statuses res :::::::: %o', res)
|
||||
setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id))
|
||||
})
|
||||
.catch((error) => {
|
||||
swalFire({ text: error.message, icon: 'error' })
|
||||
console.error('[DELETE] canvas-statuses res error :::::::: %o', error)
|
||||
})
|
||||
} else {
|
||||
const lastPlanId = combinedPlans.at(-1).id
|
||||
if (id !== lastPlanId) {
|
||||
handleCurrentPlan(lastPlanId)
|
||||
}
|
||||
setAddCanvasPlans(addCanvasPlans.filter((plan) => plan.id !== id))
|
||||
swalFire({ text: getMessage('common.message.delete') })
|
||||
}
|
||||
|
||||
// 삭제 후 last 데이터에 포커싱
|
||||
const lastPlan = [...initCanvasPlans, ...addCanvasPlans].filter((plan) => plan.id !== id).at(-1)
|
||||
if (!lastPlan) {
|
||||
setPlanNum(0)
|
||||
setCurrentCanvasPlan(null)
|
||||
} else if (id !== lastPlan.id) {
|
||||
handleCurrentPlan(lastPlan.id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,7 +77,7 @@ export default function CanvasLayout() {
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getCanvasByObjectNo(objectNo).then((res) => {
|
||||
getCanvasByObjectNo(sessionState.userId, objectNo).then((res) => {
|
||||
console.log('canvas 목록 ', res)
|
||||
if (res.length > 0) {
|
||||
setInitCanvasPlans(res)
|
||||
@ -82,7 +96,18 @@ export default function CanvasLayout() {
|
||||
{[...initCanvasPlans, ...addCanvasPlans].map((plan) => (
|
||||
<button key={plan.id} className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`} onClick={() => handleCurrentPlan(plan.id)}>
|
||||
<span>{plan.name}</span>
|
||||
<i className="close" onClick={(e) => handleDeletePlan(e, plan.id)}></i>
|
||||
<i
|
||||
className="close"
|
||||
onClick={(e) =>
|
||||
swalFire({
|
||||
html: getMessage('common.message.confirm.delete') + `</br>${plan.name}`,
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
handleDeletePlan(e, plan.id)
|
||||
},
|
||||
})
|
||||
}
|
||||
></i>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -9,7 +9,8 @@ import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePlan } from '@/hooks/usePlan'
|
||||
import { canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { canvasState, canvasZoomState, currentCanvasPlanState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { outerLinePointsState } from '@/store/outerLineAtom'
|
||||
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
|
||||
@ -30,7 +31,7 @@ const canvasMenus = [
|
||||
]
|
||||
|
||||
export default function CanvasMenu(props) {
|
||||
const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props
|
||||
const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal, setShowPlaceShapeModal, setShowRoofShapeSettingModal } = props
|
||||
|
||||
const [menuNumber, setMenuNumber] = useState(null)
|
||||
const [type, setType] = useState('')
|
||||
@ -40,6 +41,7 @@ export default function CanvasMenu(props) {
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
const setPoints = useSetRecoilState(outerLinePointsState)
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||
|
||||
const globalLocale = useRecoilValue(globalLocaleStore)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -47,6 +49,7 @@ export default function CanvasMenu(props) {
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { saveCanvas } = usePlan()
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
|
||||
const onClickNav = (menu) => {
|
||||
@ -54,6 +57,10 @@ export default function CanvasMenu(props) {
|
||||
setCurrentMenu(menu.title)
|
||||
|
||||
switch (menu.index) {
|
||||
case 1:
|
||||
setType('placementShape')
|
||||
onClickPlacementInitialMenu()
|
||||
break
|
||||
case 2:
|
||||
setType('outline')
|
||||
break
|
||||
@ -67,18 +74,35 @@ export default function CanvasMenu(props) {
|
||||
}
|
||||
const menuProps = {
|
||||
setShowOutlineModal,
|
||||
setShowPlaceShapeModal,
|
||||
setShowRoofShapeSettingModal,
|
||||
type,
|
||||
}
|
||||
|
||||
const settingsModalOptions = useRecoilState(settingModalFirstOptionsState)
|
||||
|
||||
useEffect(() => {
|
||||
if (menuNumber === 1) {
|
||||
onClickPlacementInitialMenu()
|
||||
}
|
||||
if (menuNumber !== 2 && showOutlineModal) setShowOutlineModal(false)
|
||||
}, [menuNumber, type])
|
||||
|
||||
// 저장버튼(btn08) 클릭 시 호출되는 함수
|
||||
const handleSaveCanvas = () => {
|
||||
saveCanvas(sessionState.userId)
|
||||
swalFire({
|
||||
html: getMessage('common.message.confirm.save') + `</br>${currentCanvasPlan.name}`,
|
||||
type: 'confirm',
|
||||
confirmFn: () => {
|
||||
saveCanvas(sessionState.userId)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const onClickPlacementInitialMenu = () => {
|
||||
setShowOutlineModal(false)
|
||||
setShowCanvasSettingModal(false)
|
||||
setShowPlaceShapeModal(true)
|
||||
}
|
||||
|
||||
const handleClear = () => {
|
||||
@ -126,7 +150,7 @@ export default function CanvasMenu(props) {
|
||||
</div>
|
||||
}
|
||||
<div className="btn-from">
|
||||
<button className="btn01"></button>
|
||||
<button className="btn01" onClick={() => {}}></button>
|
||||
<button className="btn02 active"></button>
|
||||
<button className="btn03 "></button>
|
||||
</div>
|
||||
|
||||
@ -11,10 +11,17 @@ import SettingModal01 from '@/components/floor-plan/modal/setting01/SettingModal
|
||||
import CanvasLayout from '@/components/floor-plan/CanvasLayout'
|
||||
import DotLineGrid from '@/components/floor-plan/modal/grid/DotLineGrid'
|
||||
import WallLineSetting from '@/components/floor-plan/modal/outerlinesetting/WallLineSetting'
|
||||
import PropertiesSetting from '@/components/floor-plan/modal/outerlinesetting/PropertiesSetting'
|
||||
import PlacementShapeSetting from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting'
|
||||
import GridColorSetting from './modal/grid/GridColorSetting'
|
||||
import RoofShapeSetting from '@/components/floor-plan/modal/roofShape/RoofShapeSetting'
|
||||
|
||||
export default function FloorPlan() {
|
||||
const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false)
|
||||
const [showOutlineModal, setShowOutlineModal] = useState(false)
|
||||
const [showPlaceShapeModal, setShowPlaceShapeModal] = useState(false)
|
||||
const [showPropertiesSettingModal, setShowPropertiesSettingModal] = useState(false)
|
||||
const [showRoofShapeSettingModal, setShowRoofShapeSettingModal] = useState(false)
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
|
||||
@ -25,19 +32,24 @@ export default function FloorPlan() {
|
||||
const [showDotLineGridModal, setShowDotLineGridModal] = useState(false)
|
||||
const [showGridCopyModal, setShowGridCopyModal] = useState(false)
|
||||
const [showGridMoveModal, setShowGridMoveModal] = useState(false)
|
||||
const [showColorPickerModal, setShowColorPickerModal] = useState(false)
|
||||
const canvasSettingProps = {
|
||||
setShowCanvasSettingModal,
|
||||
setShowDotLineGridModal,
|
||||
setShowColorPickerModal,
|
||||
}
|
||||
|
||||
const outlineProps = {
|
||||
setShowOutlineModal,
|
||||
setShowPropertiesSettingModal,
|
||||
}
|
||||
|
||||
const modalProps = {
|
||||
setShowCanvasSettingModal,
|
||||
showOutlineModal,
|
||||
setShowOutlineModal,
|
||||
setShowPlaceShapeModal,
|
||||
setShowRoofShapeSettingModal,
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -75,6 +87,14 @@ export default function FloorPlan() {
|
||||
setShowDotLineGridModal,
|
||||
}
|
||||
|
||||
const gridColorProps = {
|
||||
setShowColorPickerModal,
|
||||
}
|
||||
|
||||
const propertiesSettingProps = {
|
||||
setShowPropertiesSettingModal,
|
||||
}
|
||||
|
||||
useEffect(() => {}, [showOutlineModal])
|
||||
|
||||
return (
|
||||
@ -84,9 +104,12 @@ export default function FloorPlan() {
|
||||
<div className="canvas-content">
|
||||
<CanvasLayout />
|
||||
{showCanvasSettingModal && <SettingModal01 {...canvasSettingProps} />}
|
||||
{/*{showOutlineModal && <OuterLineWall {...outlineProps} />}*/}
|
||||
{showOutlineModal && <WallLineSetting {...outlineProps} />}
|
||||
{showDotLineGridModal && <DotLineGrid {...dotLineProps} />}
|
||||
{showColorPickerModal && <GridColorSetting {...gridColorProps} />}
|
||||
{showPropertiesSettingModal && <PropertiesSetting {...propertiesSettingProps} />}
|
||||
{showPlaceShapeModal && <PlacementShapeSetting setShowPlaceShapeModal={setShowPlaceShapeModal} />}
|
||||
{showRoofShapeSettingModal && <RoofShapeSetting setShowRoofShapeSettingModal={setShowRoofShapeSettingModal} />}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -7,7 +7,7 @@ import { currentMenuState } from '@/store/canvasAtom'
|
||||
import { useSetRecoilState } from 'recoil'
|
||||
|
||||
export default function MenuDepth01(props) {
|
||||
const { setShowOutlineModal, type } = props
|
||||
const { setShowOutlineModal, type, setShowPlaceShapeModal, setShowRoofShapeSettingModal } = props
|
||||
const { getMessage } = useMessage()
|
||||
const [activeMenu, setActiveMenu] = useState()
|
||||
const setCurrentMenu = useSetRecoilState(currentMenuState)
|
||||
@ -16,7 +16,9 @@ export default function MenuDepth01(props) {
|
||||
setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE)
|
||||
setCurrentMenu(menu)
|
||||
if (type === 'outline') {
|
||||
setShowPlaceShapeModal(false)
|
||||
setShowOutlineModal(id === 0)
|
||||
setShowRoofShapeSettingModal(id === 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { fabric } from 'fabric'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
|
||||
@ -18,9 +16,9 @@ const TYPE = {
|
||||
|
||||
export default function DotLineGrid(props) {
|
||||
// const [modalOption, setModalOption] = useRecoilState(modalState); //modal 열림닫힘 state
|
||||
const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요
|
||||
const [close, setClose] = useState(false)
|
||||
const { setShowDotLineGridModal } = props
|
||||
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
|
||||
const gridColor = useRecoilValue(gridColorState)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
@ -29,10 +27,16 @@ export default function DotLineGrid(props) {
|
||||
const interval = useRecoilValue(dotLineIntervalSelector)
|
||||
|
||||
const { getMessage } = useMessage()
|
||||
const { get, post } = useAxios()
|
||||
const { swalFire } = useSwal()
|
||||
|
||||
const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState)
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setSettingModalGridOptions((prev) => {
|
||||
const newSettingOptions = [...prev]
|
||||
newSettingOptions[1].selected = false
|
||||
return [...newSettingOptions]
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
const SelectOption = [
|
||||
{ id: 1, name: getMessage('modal.canvas.setting.grid.dot.line.setting.line.origin'), value: 1 },
|
||||
@ -43,23 +47,13 @@ export default function DotLineGrid(props) {
|
||||
const [selectOption, setSelectOption] = useState(SelectOption[0])
|
||||
|
||||
const HandleClickClose = () => {
|
||||
setShowDotLineGridModal(false) // 모달 닫기 처리
|
||||
|
||||
const newGridOptions = [...gridOptions] // 모달 닫으면서 점선그리드 버튼 비활성화
|
||||
newGridOptions.map((item) => {
|
||||
if (item.id === 2) {
|
||||
item.selected = false
|
||||
}
|
||||
})
|
||||
setGridOptions(newGridOptions)
|
||||
// setClose(true)
|
||||
// setTimeout(() => {
|
||||
// setModalOption({ ...modalOption, gridoption: false })
|
||||
// setClose(false)
|
||||
// }, 180)
|
||||
}
|
||||
|
||||
// 데이터를 최초 한 번만 조회
|
||||
useEffect(() => {
|
||||
console.log('DotLineGrid useEffect 실행')
|
||||
fetchGridSettings()
|
||||
}, [objectNo])
|
||||
|
||||
const handleCheckBoxChange = (e) => {
|
||||
const { value, checked } = e.target
|
||||
setDotLineGridSettingState((prev) => {
|
||||
@ -70,174 +64,118 @@ export default function DotLineGrid(props) {
|
||||
})
|
||||
}
|
||||
|
||||
// Canvas Grid Setting 조회 및 초기화
|
||||
const fetchGridSettings = async () => {
|
||||
try {
|
||||
const res = await get({ url: `/api/canvas-management/canvas-grid-settings/by-object/${objectNo}` })
|
||||
const handleSave = () => {
|
||||
// 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거
|
||||
canvas
|
||||
?.getObjects()
|
||||
.filter((obj) => obj.name === 'lineGrid')
|
||||
.forEach((obj) => canvas?.remove(obj))
|
||||
canvas
|
||||
?.getObjects()
|
||||
.filter((obj) => obj.name === 'dotGrid')
|
||||
.forEach((obj) => canvas?.remove(obj))
|
||||
|
||||
const patternData = {
|
||||
INTERVAL: {
|
||||
type: res.gridType,
|
||||
horizontalInterval: res.gridHorizon,
|
||||
verticalInterval: res.gridVertical,
|
||||
ratioInterval: res.gridRatio,
|
||||
const horizontalInterval = interval.horizontalInterval
|
||||
const verticalInterval = interval.verticalInterval
|
||||
|
||||
if (dotLineGridSetting.DOT) {
|
||||
const circle = new fabric.Circle({
|
||||
radius: 2,
|
||||
fill: 'red',
|
||||
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: horizontalInterval,
|
||||
height: verticalInterval,
|
||||
})
|
||||
|
||||
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: canvas.width, y: 0 },
|
||||
{ x: canvas.width, y: canvas.height },
|
||||
{ x: 0, y: canvas.height },
|
||||
],
|
||||
{
|
||||
fill: pattern,
|
||||
selectable: false,
|
||||
name: 'dotGrid',
|
||||
},
|
||||
dimension: res.gridDimen,
|
||||
DOT: res.dotGridDisplay,
|
||||
LINE: res.lineGridDisplay,
|
||||
}
|
||||
)
|
||||
|
||||
const matchedOption = SelectOption.find((option) => option.value == res.gridDimen)
|
||||
|
||||
// dimension 값에 맞는 옵션을 선택
|
||||
setSelectOption(matchedOption)
|
||||
|
||||
// 서버에서 받은 데이터로 상태 업데이트
|
||||
setDotLineGridSettingState(patternData)
|
||||
} catch (error) {
|
||||
console.error('Data fetching error:', error)
|
||||
canvas.add(backgroundPolygon)
|
||||
backgroundPolygon.sendToBack()
|
||||
canvas.renderAll()
|
||||
}
|
||||
}
|
||||
|
||||
// 저장 버튼 클릭 시
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
const patternData = {
|
||||
objectNo,
|
||||
dotGridDisplay: dotLineGridSetting.DOT,
|
||||
lineGridDisplay: dotLineGridSetting.LINE,
|
||||
gridType: dotLineGridSetting.INTERVAL.type,
|
||||
gridHorizon: dotLineGridSetting.INTERVAL.horizontalInterval,
|
||||
gridVertical: dotLineGridSetting.INTERVAL.verticalInterval,
|
||||
gridRatio: dotLineGridSetting.INTERVAL.ratioInterval,
|
||||
gridDimen: dotLineGridSetting.INTERVAL.dimension,
|
||||
}
|
||||
|
||||
// HTTP POST 요청 보내기
|
||||
await post({ url: `/api/canvas-management/canvas-grid-settings`, data: patternData }).then((res) => {
|
||||
swalFire({ text: getMessage(res.returnMessage) })
|
||||
|
||||
// 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거
|
||||
canvas
|
||||
?.getObjects()
|
||||
.filter((obj) => obj.name === 'lineGrid')
|
||||
.forEach((obj) => canvas?.remove(obj))
|
||||
canvas
|
||||
?.getObjects()
|
||||
.filter((obj) => obj.name === 'dotGrid')
|
||||
.forEach((obj) => canvas?.remove(obj))
|
||||
|
||||
if (patternData.dotGridDisplay) {
|
||||
const circle = new fabric.Circle({
|
||||
radius: 2,
|
||||
fill: 'red',
|
||||
strokeWidth: 0.7,
|
||||
originX: 'center',
|
||||
originY: 'center',
|
||||
selectable: false,
|
||||
if (dotLineGridSetting.LINE) {
|
||||
for (let i = 0; i < canvas.height / verticalInterval + 1; i++) {
|
||||
const horizontalLine = new fabric.Line(
|
||||
[0, i * verticalInterval - verticalInterval / 2, canvas.width, i * verticalInterval - verticalInterval / 2],
|
||||
{
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
})
|
||||
name: 'lineGrid',
|
||||
strokeDashArray: [5, 2],
|
||||
opacity: 0.3,
|
||||
direction: 'horizontal',
|
||||
},
|
||||
)
|
||||
canvas.add(horizontalLine)
|
||||
}
|
||||
|
||||
const patternSourceCanvas = new fabric.StaticCanvas(null, {
|
||||
width: patternData.gridHorizon,
|
||||
height: patternData.gridVertical,
|
||||
})
|
||||
|
||||
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: canvas.width, y: 0 },
|
||||
{ x: canvas.width, y: canvas.height },
|
||||
{ x: 0, y: canvas.height },
|
||||
],
|
||||
{
|
||||
fill: pattern,
|
||||
selectable: false,
|
||||
name: 'dotGrid',
|
||||
},
|
||||
)
|
||||
|
||||
canvas.add(backgroundPolygon)
|
||||
backgroundPolygon.sendToBack()
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
if (patternData.lineGridDisplay) {
|
||||
for (let i = 0; i < canvas.height / patternData.gridVertical + 1; i++) {
|
||||
const horizontalLine = new fabric.Line(
|
||||
[
|
||||
0,
|
||||
i * patternData.gridVertical - patternData.gridVertical / 2,
|
||||
canvas.width,
|
||||
i * patternData.gridVertical - patternData.gridVertical / 2,
|
||||
],
|
||||
{
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
name: 'lineGrid',
|
||||
strokeDashArray: [5, 2],
|
||||
opacity: 0.3,
|
||||
direction: 'horizontal',
|
||||
},
|
||||
)
|
||||
canvas.add(horizontalLine)
|
||||
}
|
||||
|
||||
for (let i = 0; i < canvas.width / patternData.gridHorizon + 1; i++) {
|
||||
const verticalLine = new fabric.Line(
|
||||
[
|
||||
i * patternData.gridHorizon - patternData.gridHorizon / 2,
|
||||
0,
|
||||
i * patternData.gridHorizon - patternData.gridHorizon / 2,
|
||||
canvas.height,
|
||||
],
|
||||
{
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
name: 'lineGrid',
|
||||
strokeDashArray: [5, 2],
|
||||
opacity: 0.3,
|
||||
direction: 'vertical',
|
||||
},
|
||||
)
|
||||
canvas.add(verticalLine)
|
||||
}
|
||||
}
|
||||
canvas.renderAll()
|
||||
})
|
||||
} catch (error) {
|
||||
swalFire({ text: getMessage(res.returnMessage), icon: 'error' })
|
||||
for (let i = 0; i < canvas.width / horizontalInterval + 1; i++) {
|
||||
const verticalLine = new fabric.Line(
|
||||
[i * horizontalInterval - horizontalInterval / 2, 0, i * horizontalInterval - horizontalInterval / 2, canvas.height],
|
||||
{
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
name: 'lineGrid',
|
||||
strokeDashArray: [5, 2],
|
||||
opacity: 0.3,
|
||||
direction: 'vertical',
|
||||
},
|
||||
)
|
||||
canvas.add(verticalLine)
|
||||
}
|
||||
}
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const handleRadioChange = (e) => {
|
||||
@ -295,11 +233,11 @@ export default function DotLineGrid(props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: -150, y: 300 }}>
|
||||
<WithDraggable isShow={true} pos={{ x: 1300, y: -660 }}>
|
||||
<div className={`modal-pop-wrap ssm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting.grid.dot.line.setting')}</h1>
|
||||
<button className="modal-close" onClick={HandleClickClose}>
|
||||
<button className="modal-close" onClick={() => setShowDotLineGridModal(false)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
|
||||
39
src/components/floor-plan/modal/grid/GridColorSetting.jsx
Normal file
@ -0,0 +1,39 @@
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import ColorPicker from '@/components/common/color-picker/ColorPicker'
|
||||
import { useRecoilState, useSetRecoilState } from 'recoil'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEffect } from 'react'
|
||||
import { settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
|
||||
export default function GridColorSetting(props) {
|
||||
const { setShowColorPickerModal } = props
|
||||
const [color, setColor] = useRecoilState(gridColorState)
|
||||
const setSettingModalGridOptions = useSetRecoilState(settingModalGridOptionsState)
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
setSettingModalGridOptions((prev) => {
|
||||
const newSettingOptions = [...prev]
|
||||
newSettingOptions[3].selected = false
|
||||
return [...newSettingOptions]
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 1300, y: -660 }}>
|
||||
<div className={`modal-pop-wrap ssm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting.grid.color.setting')}</h1>
|
||||
<button className="modal-close" onClick={() => setShowColorPickerModal(false)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<ColorPicker color={color} setColor={setColor} />
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function GridCopy(props) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function GridMove(props) {
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
import Image from 'next/image'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function Angle() {
|
||||
export default function Angle({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { angle1, setAngle1, angle1Ref, length1, setLength1, length1Ref } = props
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="outline-wrap">
|
||||
@ -11,16 +14,40 @@ export default function Angle() {
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.angle')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={angle1}
|
||||
ref={angle1Ref}
|
||||
onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
|
||||
placeholder="45"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={(e) => {
|
||||
setAngle1(0)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.arrow')}</span>
|
||||
<span className="mr10">{getMessage('modal.cover.outline.length')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={5000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setLength1(0)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="cul-box">
|
||||
|
||||
@ -1,7 +1,24 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function Diagonal() {
|
||||
export default function Diagonal({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
|
||||
const {
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
length2,
|
||||
setLength2,
|
||||
length2Ref,
|
||||
outerLineDiagonalLength,
|
||||
setOuterLineDiagonalLength,
|
||||
outerLineDiagonalLengthRef,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
} = props
|
||||
return (
|
||||
<>
|
||||
<div className="outline-wrap">
|
||||
@ -13,26 +30,77 @@ export default function Diagonal() {
|
||||
{getMessage('modal.cover.outline.length')}
|
||||
</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={1000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={outerLineDiagonalLength}
|
||||
ref={outerLineDiagonalLengthRef}
|
||||
onChange={(e) => onlyNumberInputChange(e, setOuterLineDiagonalLength)}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setOuterLineDiagonalLength(0)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="outline-inner">
|
||||
<div className="outline-form">
|
||||
<span className="mr10"> {getMessage('modal.cover.outline.length')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={5000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setLength1(0)
|
||||
setLength2(0)
|
||||
setArrow1('')
|
||||
setArrow2('')
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span> {getMessage('modal.cover.outline.arrow')}</span>
|
||||
<div className="grid-direction">
|
||||
<button className="direction up"></button>
|
||||
<button className="direction down act"></button>
|
||||
<button className="direction left"></button>
|
||||
<button className="direction right"></button>
|
||||
<button
|
||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction left ${arrow1 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow1 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -40,16 +108,48 @@ export default function Diagonal() {
|
||||
<div className="outline-form">
|
||||
<span className="mr10"> {getMessage('modal.cover.outline.length')}</span>
|
||||
<div className="input-grid" style={{ width: '98px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={8000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={length2}
|
||||
ref={length2Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
||||
readOnly={true}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span> {getMessage('modal.cover.outline.arrow')}</span>
|
||||
<div className="grid-direction">
|
||||
<button className="direction up"></button>
|
||||
<button className="direction down act"></button>
|
||||
<button className="direction left"></button>
|
||||
<button className="direction right"></button>
|
||||
<button
|
||||
className={`direction up ${arrow2 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow2 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,48 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { getDegreeByChon } from '@/util/canvas-util'
|
||||
|
||||
export default function DoublePitch() {
|
||||
export default function DoublePitch({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
const {
|
||||
angle1,
|
||||
setAngle1,
|
||||
angle1Ref,
|
||||
angle2,
|
||||
setAngle2,
|
||||
angle2Ref,
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
length2,
|
||||
setLength2,
|
||||
length2Ref,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
arrow1Ref,
|
||||
arrow2Ref,
|
||||
} = props
|
||||
|
||||
const getLength2 = () => {
|
||||
const angle1Value = angle1Ref.current.value
|
||||
const angle2Value = angle2Ref.current.value
|
||||
const length1Value = length1Ref.current.value
|
||||
|
||||
const arrow1Value = arrow1Ref.current
|
||||
const arrow2Value = arrow2Ref.current
|
||||
|
||||
if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '') {
|
||||
const radian1 = (getDegreeByChon(angle1Value) * Math.PI) / 180
|
||||
|
||||
const radian2 = (getDegreeByChon(angle2Value) * Math.PI) / 180
|
||||
return Math.floor((Math.tan(radian1) * length1Value) / Math.tan(radian2))
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="outline-wrap">
|
||||
@ -9,26 +50,70 @@ export default function DoublePitch() {
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.angle')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={angle1}
|
||||
ref={angle1Ref}
|
||||
onChange={(e) => onlyNumberWithDotInputChange(e, setAngle1)}
|
||||
placeholder="45"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button className="reset-btn" onClick={() => setAngle1(0)}></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="outline-inner">
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.length')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={5000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setLength1(0)
|
||||
setArrow1('')
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span>{getMessage('modal.cover.outline.arrow')}</span>
|
||||
<div className="grid-direction">
|
||||
<button className="direction up"></button>
|
||||
<button className="direction down act"></button>
|
||||
<button className="direction left"></button>
|
||||
<button className="direction right"></button>
|
||||
<button
|
||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction left ${arrow1 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow1 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -38,26 +123,80 @@ export default function DoublePitch() {
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.angle')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={6} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={angle2}
|
||||
ref={angle2Ref}
|
||||
onChange={(e) => {
|
||||
onlyNumberWithDotInputChange(e, setAngle2)
|
||||
console.log(getLength2())
|
||||
setLength2(getLength2())
|
||||
}}
|
||||
placeholder="45"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setAngle2(0)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="outline-inner">
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.length')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={3000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={length2}
|
||||
ref={length2Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
||||
readOnly={true}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setLength2(0)
|
||||
setArrow2('')
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span>{getMessage('modal.cover.outline.arrow')}</span>
|
||||
<div className="grid-direction">
|
||||
<button className="direction up"></button>
|
||||
<button className="direction down act"></button>
|
||||
<button className="direction left"></button>
|
||||
<button className="direction right"></button>
|
||||
<button
|
||||
className={`direction up ${arrow2 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow2 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import GridMove from '@/components/floor-plan/modal/grid/GridMove'
|
||||
|
||||
export default function OuterLineWall(props) {
|
||||
export default function OuterLineWall({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { length1, setLength1, length1Ref, arrow1, setArrow1 } = useOuterLineWall()
|
||||
|
||||
const { length1, setLength1, length1Ref, arrow1, setArrow1 } = props
|
||||
return (
|
||||
<div className="outline-wrap">
|
||||
<div className="outline-inner">
|
||||
@ -22,15 +23,39 @@ export default function OuterLineWall(props) {
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button className="reset-btn" onClick={() => setLength1(0)}></button>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span>{getMessage('modal.cover.outline.arrow')}</span>
|
||||
<div className="grid-direction">
|
||||
<button className={`direction up ${arrow1 === '↑' ? 'act' : ''}`} onClick={() => setArrow1('↑')}></button>
|
||||
<button className={`direction down ${arrow1 === '↓' ? 'act' : ''}`} onClick={() => setArrow1('↓')}></button>
|
||||
<button className={`direction left ${arrow1 === '←' ? 'act' : ''}`} onClick={() => setArrow1('←')}></button>
|
||||
<button className={`direction right ${arrow1 === '→' ? 'act' : ''}`} onClick={() => setArrow1('→')}></button>
|
||||
<button
|
||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction left ${arrow1 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow1 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { usePropertiesSetting } from '@/hooks/roofcover/usePropertiesSetting'
|
||||
|
||||
export default function PropertiesSetting(props) {
|
||||
const { getMessage } = useMessage()
|
||||
const { setShowPropertiesSettingModal } = props
|
||||
|
||||
const { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal } = usePropertiesSetting()
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
<div className={`modal-pop-wrap ssm`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting.wallline.properties.setting')}</h1>
|
||||
<button
|
||||
className="modal-close"
|
||||
onClick={() => {
|
||||
closeModal(setShowPropertiesSettingModal)
|
||||
}}
|
||||
>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="properties-guide">{getMessage('modal.canvas.setting.wallline.properties.setting.info')}</div>
|
||||
<div className="properties-setting-wrap">
|
||||
<div className="setting-tit">{getMessage('setting')}</div>
|
||||
<div className="setting-btn-wrap">
|
||||
<button className="setting-btn green mr5" onClick={handleSetEaves}>
|
||||
{getMessage('modal.canvas.setting.wallline.properties.setting.eaves')}
|
||||
</button>
|
||||
<button className="setting-btn blue" onClick={handleSetGable}>
|
||||
{getMessage('modal.canvas.setting.wallline.properties.setting.edge')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal mr5" onClick={handleRollback}>
|
||||
{getMessage('modal.cover.outline.rollback')}
|
||||
</button>
|
||||
<button
|
||||
className="btn-frame modal act"
|
||||
onClick={() => {
|
||||
handleFix()
|
||||
setShowPropertiesSettingModal(false)
|
||||
}}
|
||||
>
|
||||
{getMessage('modal.cover.outline.finish')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
@ -1,24 +1,63 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
|
||||
export default function RightAngle() {
|
||||
export default function RightAngle({ props }) {
|
||||
const { getMessage } = useMessage()
|
||||
const { length1, setLength1, length1Ref, length2, setLength2, length2Ref, arrow1, setArrow1, arrow2, setArrow2 } = props
|
||||
return (
|
||||
<div className="outline-wrap">
|
||||
<div className="outline-inner">
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.length')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={1000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={length1}
|
||||
ref={length1Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength1)}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setLength1(0)
|
||||
setArrow1('')
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span>{getMessage('modal.cover.outline.arrow')}</span>
|
||||
<div className="grid-direction">
|
||||
<button className="direction up"></button>
|
||||
<button className="direction down act"></button>
|
||||
<button className="direction left"></button>
|
||||
<button className="direction right"></button>
|
||||
<button
|
||||
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction left ${arrow1 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow1 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow1('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -26,17 +65,53 @@ export default function RightAngle() {
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('modal.cover.outline.length')}</span>
|
||||
<div className="input-grid" style={{ width: '63px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={1000} />
|
||||
<input
|
||||
type="text"
|
||||
className="input-origin block"
|
||||
value={length2}
|
||||
ref={length2Ref}
|
||||
onChange={(e) => onlyNumberInputChange(e, setLength2)}
|
||||
placeholder="3000"
|
||||
/>
|
||||
</div>
|
||||
<button className="reset-btn"></button>
|
||||
<button
|
||||
className="reset-btn"
|
||||
onClick={() => {
|
||||
setLength2(0)
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span>{getMessage('modal.cover.outline.arrow')}</span>
|
||||
<div className="grid-direction">
|
||||
<button className="direction up"></button>
|
||||
<button className="direction down act"></button>
|
||||
<button className="direction left"></button>
|
||||
<button className="direction right"></button>
|
||||
<button
|
||||
className={`direction up ${arrow2 === '↑' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('↑')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction down ${arrow2 === '↓' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('↓')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('←')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
|
||||
}}
|
||||
></button>
|
||||
<button
|
||||
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
|
||||
onClick={() => {
|
||||
setArrow2('→')
|
||||
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
|
||||
}}
|
||||
></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
|
||||
import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall'
|
||||
@ -11,12 +11,105 @@ import DoublePitch from '@/components/floor-plan/modal/outerlinesetting/DoublePi
|
||||
import Diagonal from '@/components/floor-plan/modal/outerlinesetting/Diagonal'
|
||||
|
||||
export default function WallLineSetting(props) {
|
||||
const { setShowOutlineModal } = props
|
||||
const { setShowOutlineModal, setShowPropertiesSettingModal } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { type, setType, handleFix, handleRollback } = useOuterLineWall()
|
||||
const {
|
||||
length1,
|
||||
setLength1,
|
||||
length2,
|
||||
setLength2,
|
||||
length1Ref,
|
||||
length2Ref,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
angle1,
|
||||
setAngle1,
|
||||
angle1Ref,
|
||||
angle2,
|
||||
setAngle2,
|
||||
angle2Ref,
|
||||
type,
|
||||
setType,
|
||||
arrow1Ref,
|
||||
arrow2Ref,
|
||||
outerLineDiagonalLength,
|
||||
setOuterLineDiagonalLength,
|
||||
outerLineDiagonalLengthRef,
|
||||
handleRollback,
|
||||
handleFix,
|
||||
} = useOuterLineWall()
|
||||
|
||||
const outerLineProps = {
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
}
|
||||
|
||||
const rightAngleProps = {
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
length2,
|
||||
setLength2,
|
||||
length2Ref,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
}
|
||||
|
||||
const doublePitchProps = {
|
||||
angle1,
|
||||
setAngle1,
|
||||
angle1Ref,
|
||||
angle2,
|
||||
setAngle2,
|
||||
angle2Ref,
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
length2,
|
||||
setLength2,
|
||||
length2Ref,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
arrow1Ref,
|
||||
arrow2Ref,
|
||||
}
|
||||
|
||||
const angleProps = {
|
||||
angle1,
|
||||
setAngle1,
|
||||
angle1Ref,
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
}
|
||||
|
||||
const diagonalLineProps = {
|
||||
length1,
|
||||
setLength1,
|
||||
length1Ref,
|
||||
length2,
|
||||
setLength2,
|
||||
length2Ref,
|
||||
outerLineDiagonalLength,
|
||||
setOuterLineDiagonalLength,
|
||||
outerLineDiagonalLengthRef,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: -1390, y: 30 }}>
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
<div className={`modal-pop-wrap r mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.cover.outline.drawing')}</h1>
|
||||
@ -55,24 +148,33 @@ export default function WallLineSetting(props) {
|
||||
{getMessage('modal.cover.outline.diagonal')}
|
||||
</button>
|
||||
</div>
|
||||
{type === OUTER_LINE_TYPE.OUTER_LINE ? (
|
||||
<OuterLineWall />
|
||||
) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? (
|
||||
<RightAngle />
|
||||
) : type === OUTER_LINE_TYPE.DOUBLE_PITCH ? (
|
||||
<DoublePitch />
|
||||
) : type === OUTER_LINE_TYPE.ANGLE ? (
|
||||
<Angle />
|
||||
) : type === OUTER_LINE_TYPE.DIAGONAL_LINE ? (
|
||||
<Diagonal />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<div className="properties-setting-wrap outer">
|
||||
<div className="setting-tit">{getMessage('modal.cover.outline.setting')}</div>
|
||||
{type === OUTER_LINE_TYPE.OUTER_LINE ? (
|
||||
<OuterLineWall props={outerLineProps} />
|
||||
) : type === OUTER_LINE_TYPE.RIGHT_ANGLE ? (
|
||||
<RightAngle props={rightAngleProps} />
|
||||
) : type === OUTER_LINE_TYPE.DOUBLE_PITCH ? (
|
||||
<DoublePitch props={doublePitchProps} />
|
||||
) : type === OUTER_LINE_TYPE.ANGLE ? (
|
||||
<Angle props={angleProps} />
|
||||
) : type === OUTER_LINE_TYPE.DIAGONAL_LINE ? (
|
||||
<Diagonal props={diagonalLineProps} />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal mr5" onClick={handleRollback}>
|
||||
{getMessage('modal.cover.outline.rollback')}
|
||||
</button>
|
||||
<button className="btn-frame modal act" onClick={handleFix}>
|
||||
<button
|
||||
className="btn-frame modal act"
|
||||
onClick={() => {
|
||||
handleFix(setShowOutlineModal)
|
||||
setShowPropertiesSettingModal(true)
|
||||
}}
|
||||
>
|
||||
{getMessage('modal.cover.outline.fix')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function MaterialGuide({ setShowMaterialGuidModal }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<div className={`modal-pop-wrap ssm alert`}>
|
||||
<div className="modal-head">
|
||||
<button className="modal-close" onClick={() => setShowMaterialGuidModal(false)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-guide">{getMessage('modal.placement.initial.setting.roof.material.info')}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
import SizeGuide from '@/components/floor-plan/modal/placementShape/SizeGuide'
|
||||
import MaterialGuide from '@/components/floor-plan/modal/placementShape/MaterialGuide'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function PlacementShapeSetting({ setShowPlaceShapeModal }) {
|
||||
const [showSizeGuideModal, setShowSizeGuidModal] = useState(false)
|
||||
const [showMaterialGuideModal, setShowMaterialGuidModal] = useState(false)
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
<div className={`modal-pop-wrap l mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('plan.menu.placement.surface.initial.setting')}</h1>
|
||||
<button className="modal-close" onClick={() => setShowPlaceShapeModal(false)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="placement-table">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '151px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{getMessage('modal.placement.initial.setting.plan.drawing')}</th>
|
||||
<td>{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('modal.placement.initial.setting.size')}
|
||||
<button className="tooltip" onClick={() => setShowSizeGuidModal(true)}></button>
|
||||
</th>
|
||||
<td>
|
||||
<div className="pop-form-radio">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra01" />
|
||||
<label htmlFor="ra01">{getMessage('modal.placement.initial.setting.size.roof')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra02" />
|
||||
<label htmlFor="ra02">{getMessage('modal.placement.initial.setting.size.actual')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" id="ra03" />
|
||||
<label htmlFor="ra03">{getMessage('modal.placement.initial.setting.size.none.pitch')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{getMessage('modal.placement.initial.setting.roof.angle.setting')}</th>
|
||||
<td>
|
||||
<div className="pop-form-radio">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio02" id="ra04" />
|
||||
<label htmlFor="ra04">{getMessage('modal.placement.initial.setting.roof.pitch')}</label>
|
||||
</div>
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio02" id="ra05" />
|
||||
<label htmlFor="ra05">{getMessage('modal.placement.initial.setting.roof.angle')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
{getMessage('modal.placement.initial.setting.roof.material')}
|
||||
<button className="tooltip" onClick={() => setShowMaterialGuidModal(true)}></button>
|
||||
</th>
|
||||
<td>
|
||||
<div className="placement-option">
|
||||
<div className="select-wrap" style={{ width: '171px' }}>
|
||||
<select className="select-light dark" name="" id="">
|
||||
<option>瓦53A</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex-ment">
|
||||
<span>W</span>
|
||||
<div className="select-wrap" style={{ width: '84px' }}>
|
||||
<select className="select-light dark" name="" id="">
|
||||
<option>265</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-ment">
|
||||
<span>L</span>
|
||||
<div className="select-wrap" style={{ width: '84px' }}>
|
||||
<select className="select-light dark" name="" id="">
|
||||
<option>235</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-ment">
|
||||
<span>{getMessage('modal.placement.initial.setting.rafter')}</span>
|
||||
<div className="select-wrap" style={{ width: '84px' }}>
|
||||
<select className="select-light dark" name="" id="">
|
||||
<option>455</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
|
||||
</div>
|
||||
</div>
|
||||
{showSizeGuideModal && <SizeGuide setShowSizeGuidModal={setShowSizeGuidModal} />}
|
||||
{showMaterialGuideModal && <MaterialGuide setShowMaterialGuidModal={setShowMaterialGuidModal} />}
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
39
src/components/floor-plan/modal/placementShape/SizeGuide.jsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function SizeGuide({ setShowSizeGuidModal }) {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<div className={`modal-pop-wrap sm alert`}>
|
||||
<div className="modal-head">
|
||||
<button className="modal-close" onClick={() => setShowSizeGuidModal(false)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="alert-title">{getMessage('modal.placement.initial.setting.size.info')}</div>
|
||||
<div className="placement-table light">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '60px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>{getMessage('modal.placement.initial.setting.size.roof')}</th>
|
||||
<td>{getMessage('modal.placement.initial.setting.size.roof.info')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{getMessage('modal.placement.initial.setting.size.actual')}</th>
|
||||
<td>{getMessage('modal.placement.initial.setting.size.actual.info')}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>{getMessage('modal.placement.initial.setting.size.none.pitch')}</th>
|
||||
<td>{getMessage('modal.placement.initial.setting.size.none.pitch.info')}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import Ridge from '@/components/floor-plan/modal/roofShape/type/Ridge'
|
||||
import Pattern from '@/components/floor-plan/modal/roofShape/type/Pattern'
|
||||
import Side from '@/components/floor-plan/modal/roofShape/type/Side'
|
||||
import { useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
import Direction from '@/components/floor-plan/modal/roofShape/type/Direction'
|
||||
|
||||
export default function RoofShapeSetting({ setShowRoofShapeSettingModal }) {
|
||||
const { getMessage } = useMessage()
|
||||
const [shapeNum, setShapeNum] = useState(1)
|
||||
const shapeMenu = [
|
||||
{ id: 1, name: getMessage('modal.roof.shape.setting.ridge') }, // 용마루
|
||||
{ id: 2, name: getMessage('modal.roof.shape.setting.patten.a') }, // 패턴A
|
||||
{ id: 3, name: getMessage('modal.roof.shape.setting.patten.b') }, // 패턴B
|
||||
{ id: 4, name: getMessage('modal.roof.shape.setting.side') }, // 변별로 설정
|
||||
{ id: 5, name: getMessage('commons.west') }, // 서
|
||||
{ id: 6, name: getMessage('commons.east') }, // 서
|
||||
{ id: 7, name: getMessage('commons.south') }, // 서
|
||||
{ id: 8, name: getMessage('commons.north') }, // 북
|
||||
]
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: -950 }}>
|
||||
<div className={`modal-pop-wrap lr`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.roof.shape.setting')}</h1>
|
||||
<button className="modal-close" onClick={() => setShowRoofShapeSettingModal(false)}>
|
||||
닫기
|
||||
</button>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="roof-shape-menu">
|
||||
{shapeMenu.map((item) => (
|
||||
<button key={item.id} className={`shape-menu-box ${shapeNum === item.id ? 'act' : ''}`} onClick={() => setShapeNum(item.id)}>
|
||||
<div className="shape-box">
|
||||
<Image src={`/static/images/canvas/shape_menu0${item.id}.svg`} alt="react" width={64} height={64} />
|
||||
</div>
|
||||
<div className="shape-title">{item.name}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className="properties-setting-wrap">
|
||||
<div className="setting-tit">{getMessage('setting')}</div>
|
||||
{shapeNum === 1 && <Ridge />}
|
||||
{(shapeNum === 2 || shapeNum === 3) && <Pattern />}
|
||||
{shapeNum === 4 && <Side />}
|
||||
{(shapeNum === 5 || shapeNum === 6 || shapeNum === 7 || shapeNum === 8) && <Direction />}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame modal act">{getMessage('common.setting.finish')}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
)
|
||||
}
|
||||
45
src/components/floor-plan/modal/roofShape/type/Direction.jsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Direction() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<div className="setting-box">
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={500} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={300} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('windage.width')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={300} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
36
src/components/floor-plan/modal/roofShape/type/Pattern.jsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Pattern() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<div className="setting-box">
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4} />
|
||||
</div>
|
||||
<span className="thin"> {getMessage('size')}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={500} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={300} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
27
src/components/floor-plan/modal/roofShape/type/Ridge.jsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Ridge() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<div className="setting-box">
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '24px' }}>
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={100} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '24px' }}>
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={100} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
46
src/components/floor-plan/modal/roofShape/type/Side.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import Eaves from '@/components/floor-plan/modal/roofShape/type/option/Eaves'
|
||||
import Gable from '@/components/floor-plan/modal/roofShape/type/option/Gable'
|
||||
import HipAndGable from '@/components/floor-plan/modal/roofShape/type/option/HipAndGable'
|
||||
import Wall from '@/components/floor-plan/modal/roofShape/type/option/Wall'
|
||||
import Jerkinhead from '@/components/floor-plan/modal/roofShape/type/option/Jerkinhead'
|
||||
import Shed from '@/components/floor-plan/modal/roofShape/type/option/Shed'
|
||||
|
||||
export default function Side() {
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
const { getMessage } = useMessage()
|
||||
const buttonMenu = [
|
||||
{ id: 1, name: getMessage('eaves') },
|
||||
{ id: 2, name: getMessage('gable') },
|
||||
{ id: 3, name: getMessage('wall') },
|
||||
{ id: 4, name: getMessage('hipandgable') },
|
||||
{ id: 5, name: getMessage('jerkinhead') },
|
||||
{ id: 6, name: getMessage('shed') },
|
||||
]
|
||||
return (
|
||||
<div className="setting-box">
|
||||
<div className="discrimination-tab">
|
||||
<div className="modal-btn-wrap sub">
|
||||
{buttonMenu.map((item) => (
|
||||
<button className={`btn-frame sub-tab ${buttonAct === item.id ? 'act' : ''}`} onClick={() => setButtonAct(item.id)}>
|
||||
{item.name}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="discrimination-box">
|
||||
{buttonAct === 1 && <Eaves />}
|
||||
{buttonAct === 2 && <Gable />}
|
||||
{buttonAct === 3 && <Wall />}
|
||||
{buttonAct === 4 && <HipAndGable />}
|
||||
{buttonAct === 5 && <Jerkinhead />}
|
||||
{buttonAct === 6 && <Shed />}
|
||||
</div>
|
||||
<div className="grid-btn-wrap">
|
||||
<button className="btn-frame sub-tab mr5">{getMessage('common.setting.rollback')}</button>
|
||||
<button className="btn-frame sub-tab act">{getMessage('apply')}</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Eaves() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '24px' }}>
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '24px' }}>
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={500} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Gable() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('gable.offset')}</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function HipAndGable() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('eaves.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={500} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={300} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Jerkinhead() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('gable.offset')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={300} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
<div className="outline-form mb10">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('jerkinhead.width')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={800} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
<div className="outline-form">
|
||||
<span className="mr10" style={{ width: '60px' }}>
|
||||
{getMessage('jerkinhead.slope')}
|
||||
</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={4.5} />
|
||||
</div>
|
||||
<span className="thin">{getMessage('size')}</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Shed() {
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
<div className="outline-form">
|
||||
<span className="mr10">{getMessage('shed.width')}</span>
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={600} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
import { useState } from 'react'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
|
||||
export default function Wall() {
|
||||
const [hasSleeve, setHasSleeve] = useState('0')
|
||||
const { getMessage } = useMessage()
|
||||
return (
|
||||
<>
|
||||
{hasSleeve}
|
||||
<div className="eaves-keraba-table">
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" checked={hasSleeve === '0'} id="ra01" value={'0'} onChange={(e) => setHasSleeve(e.target.value)} />
|
||||
<label htmlFor="ra01">{getMessage('has.not.sleeve')}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-item">
|
||||
<div className="eaves-keraba-th">
|
||||
<div className="d-check-radio pop">
|
||||
<input type="radio" name="radio01" checked={hasSleeve !== '0'} id="ra02" value={'1'} onChange={(e) => setHasSleeve(e.target.value)} />
|
||||
<label htmlFor="ra02">{getMessage('has.sleeve')}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="eaves-keraba-td">
|
||||
<div className="outline-form">
|
||||
<div className="input-grid mr5" style={{ width: '100px' }}>
|
||||
<input type="text" className="input-origin block" defaultValue={300} readOnly={hasSleeve === '0'} />
|
||||
</div>
|
||||
<span className="thin">mm</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
@ -8,7 +8,7 @@ import { gridColorState } from '@/store/gridAtom'
|
||||
import { useColor } from 'react-color-palette'
|
||||
|
||||
export default function GridOption(props) {
|
||||
const { setShowDotLineGridModal } = props
|
||||
const { setShowDotLineGridModal, setShowColorPickerModal } = props
|
||||
const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState)
|
||||
const [gridColor, setGridColor] = useRecoilState(gridColorState)
|
||||
const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState)
|
||||
@ -16,11 +16,8 @@ export default function GridOption(props) {
|
||||
const { tempGridMode, setTempGridMode } = useTempGrid()
|
||||
|
||||
const [color, setColor] = useColor(gridColor)
|
||||
const [colorPickerShow, setColorPickerShow] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
console.log('GridOption useEffect 실행')
|
||||
//console.log(color)
|
||||
setGridColor(color.hex)
|
||||
}, [color])
|
||||
|
||||
@ -57,7 +54,9 @@ export default function GridOption(props) {
|
||||
if (option.id === 4) {
|
||||
// 그리드 색 설정
|
||||
if (option.selected) {
|
||||
setColorPickerShow(true)
|
||||
setShowColorPickerModal(true)
|
||||
} else {
|
||||
setShowColorPickerModal(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,18 +2,25 @@
|
||||
|
||||
import { useState } from 'react'
|
||||
import FirstOption from './FirstOption'
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import WithDraggable from '@/components/common/draggable/WithDraggable'
|
||||
import SecondOption from '@/components/floor-plan/modal/setting01/SecondOption'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import GridOption from '@/components/floor-plan/modal/setting01/GridOption'
|
||||
import { canGridOptionSeletor } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
|
||||
export default function SettingModal01(props) {
|
||||
const { setShowCanvasSettingModal, setShowDotLineGridModal } = props
|
||||
const { setShowCanvasSettingModal, setShowDotLineGridModal, setShowColorPickerModal } = props
|
||||
const [buttonAct, setButtonAct] = useState(1)
|
||||
const { getMessage } = useMessage()
|
||||
const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor)
|
||||
|
||||
const handleBtnClick = (num) => {
|
||||
setButtonAct(num)
|
||||
}
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: 50, y: 30 }}>
|
||||
<WithDraggable isShow={true} pos={{ x: 1300, y: -950 }}>
|
||||
<div className={`modal-pop-wrap sm mount`}>
|
||||
<div className="modal-head">
|
||||
<h1 className="title">{getMessage('modal.canvas.setting')}</h1>
|
||||
@ -23,20 +30,22 @@ export default function SettingModal01(props) {
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className="modal-btn-wrap">
|
||||
<button className={`btn-frame modal ${buttonAct === 1 ? 'act' : ''}`} onClick={() => setButtonAct(1)}>
|
||||
<button className={`btn-frame modal ${buttonAct === 1 ? 'act' : ''}`} onClick={() => handleBtnClick(1)}>
|
||||
{getMessage('modal.canvas.setting.display')}
|
||||
</button>
|
||||
|
||||
<button className={`btn-frame modal ${buttonAct === 2 ? 'act' : ''}`} onClick={() => setButtonAct(2)}>
|
||||
<button className={`btn-frame modal ${buttonAct === 2 ? 'act' : ''}`} onClick={() => handleBtnClick(2)}>
|
||||
{getMessage('modal.canvas.setting.font.plan')}
|
||||
</button>
|
||||
<button className={`btn-frame modal ${buttonAct === 3 ? 'act' : ''}`} onClick={() => setButtonAct(3)}>
|
||||
{getMessage('modal.canvas.setting.grid')}
|
||||
</button>
|
||||
{canGridOptionSeletorValue && (
|
||||
<button className={`btn-frame modal ${buttonAct === 3 ? 'act' : ''}`} onClick={() => handleBtnClick(3)}>
|
||||
{getMessage('modal.canvas.setting.grid')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{buttonAct === 1 && <FirstOption />}
|
||||
{buttonAct === 2 && <SecondOption />}
|
||||
{buttonAct === 3 && <GridOption setShowDotLineGridModal={setShowDotLineGridModal} />}
|
||||
{buttonAct === 3 && <GridOption setShowDotLineGridModal={setShowDotLineGridModal} setShowColorPickerModal={setShowColorPickerModal} />}
|
||||
</div>
|
||||
</div>
|
||||
</WithDraggable>
|
||||
|
||||
@ -12,7 +12,7 @@ import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
|
||||
import dayjs from 'dayjs'
|
||||
import isLeapYear from 'dayjs/plugin/isLeapYear' // 윤년 판단 플러그인
|
||||
dayjs.extend(isLeapYear)
|
||||
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
export default function Stuff() {
|
||||
const stuffSearchParams = useRecoilValue(stuffSearchState)
|
||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||
@ -20,7 +20,9 @@ export default function Stuff() {
|
||||
const [curPage, setCurPage] = useState(1) //현재 페이지 번호
|
||||
const [defaultSize, setDefaultSize] = useState(100) //페이지 당 게시물 수
|
||||
const [defaultSortType, setDefaultSortType] = useState('R')
|
||||
const { get } = useAxios()
|
||||
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
const gridRef = useRef()
|
||||
|
||||
const [gridCount, setGridCount] = useState(0)
|
||||
@ -364,50 +366,46 @@ export default function Stuff() {
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="text-2xl">
|
||||
물건목록
|
||||
<span>
|
||||
전체 : {gridCount} // 선택 : {selectedRowDataCount}
|
||||
</span>
|
||||
<select className="select" onChange={onChangePerPage}>
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
</select>
|
||||
<select className="select" onChange={onChangeSortType}>
|
||||
<option value="R">최근 등록일</option>
|
||||
<option value="U">최근 수정일</option>
|
||||
</select>
|
||||
<div align="right">
|
||||
{/* <Button
|
||||
color="primary"
|
||||
onPress={() => {
|
||||
fnDeleteRowData(selectedRowData)
|
||||
}}
|
||||
>
|
||||
물건삭제
|
||||
</Button> */}
|
||||
{/* <Button
|
||||
color="primary"
|
||||
onPress={() => {
|
||||
addRowItems()
|
||||
}}
|
||||
>
|
||||
행추가
|
||||
</Button>
|
||||
<Button
|
||||
color="primary"
|
||||
onPress={() => {
|
||||
removeRowItems()
|
||||
}}
|
||||
>
|
||||
행삭제
|
||||
</Button> */}
|
||||
{/* 퍼블시작 */}
|
||||
<div className="sub-table-box">
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>물건목록</h3>
|
||||
<ul className="info-wrap">
|
||||
<li>
|
||||
전체
|
||||
<span>{gridCount}</span>
|
||||
</li>
|
||||
<li>
|
||||
선택
|
||||
<span className="red">{selectedRowDataCount}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="left-unit-box">
|
||||
<div className="select-box mr5" style={{ width: '110px' }}>
|
||||
<select className="select-light black" name="" id="" onChange={onChangeSortType}>
|
||||
<option value="R">최근 등록일</option>
|
||||
<option value="U">최근 수정일</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="select-box" style={{ width: '80px' }}>
|
||||
<select className="select-light black" name="" id="" onChange={onChangePerPage}>
|
||||
<option value="100">100</option>
|
||||
<option value="200">200</option>
|
||||
<option value="300">300</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ width: '100%', height: '100%' }}>
|
||||
<StuffQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} />
|
||||
<div className="grid-table-wrap">
|
||||
<div className="q-grid">
|
||||
<StuffQGrid {...gridProps} getSelectedRowdata={getSelectedRowdata} getCellDoubleClicked={getCellDoubleClicked} gridRef={gridRef} />
|
||||
<div className="pagination-wrap">페이징 컴포넌트예정</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 퍼블종료 */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -4,16 +4,20 @@ import React, { useState, useEffect } from 'react'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem, Select, SelectItem, Checkbox, Textarea, button } from '@nextui-org/react'
|
||||
import Link from 'next/link'
|
||||
import { del, get, post } from '@/lib/Axios'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { queryStringFormatter, isEmptyArray } from '@/util/common-utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
export default function StuffDetail() {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const { getMessage } = useMessage()
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
const { get, post, del } = useAxios(globalLocaleState)
|
||||
//form
|
||||
const formInitValue = {
|
||||
// 물건번호 T...(임시) R...(진짜)
|
||||
@ -63,7 +67,7 @@ export default function StuffDetail() {
|
||||
|
||||
useEffect(() => {
|
||||
if (objectNo) {
|
||||
console.log('수정화면')
|
||||
//console.log('수정화면')
|
||||
setEditMode('EDIT')
|
||||
|
||||
if (objectNo.substring(0, 1) === 'R') {
|
||||
@ -90,7 +94,7 @@ export default function StuffDetail() {
|
||||
//1차점 : X167
|
||||
get({ url: `/api/object/saleStore/X167/list` }).then((res) => {
|
||||
if (!isEmptyArray(res)) {
|
||||
console.log('판매점 결과:::::', res)
|
||||
// console.log('판매점 결과:::::', res)
|
||||
setSaleStoreList(res)
|
||||
//1차 판매점 자동완성 값 셋팅
|
||||
form.setValue('saleStoreId', res[0].saleStoreId)
|
||||
@ -99,9 +103,6 @@ export default function StuffDetail() {
|
||||
setOtherSaleStoreList([])
|
||||
}
|
||||
})
|
||||
} else {
|
||||
alert('삭제된 물건입니다')
|
||||
router.push('/management/stuff')
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@ -385,6 +386,312 @@ export default function StuffDetail() {
|
||||
return (
|
||||
<>
|
||||
{(editMode === 'NEW' && (
|
||||
<form onSubmit={handleSubmit(onValid)}>
|
||||
<div className="sub-table-box">
|
||||
<div className="promise-gudie">
|
||||
<span className="important">*</span> 필수 입력항목
|
||||
</div>
|
||||
<div className="infomation-table">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '200px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>
|
||||
담당자 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '500px' }}>
|
||||
<input type="text" className="input-light" {...form.register('dispCompanyName')} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
물건구분/물건명 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input type="radio" name="objectStatusId" value="0" id="objectStatus0" {...form.register('objectStatusId')} />
|
||||
<label htmlFor="objectStatus0">신축</label>
|
||||
</div>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input type="radio" name="objectStatusId" value="1" id="objectStatus1" {...form.register('objectStatusId')} />
|
||||
<label htmlFor="objectStatus0">기축</label>
|
||||
</div>
|
||||
<div className="input-wrap mr5" style={{ width: '545px' }}>
|
||||
<input type="text" className="input-light" {...form.register('objectName')} />
|
||||
</div>
|
||||
<div className="select-wrap" style={{ width: '120px' }}>
|
||||
<select name="objectNameOmit" {...register('objectNameOmit')}>
|
||||
<option value="">경칭공통코드선택</option>
|
||||
<option value="11">경칭11</option>
|
||||
<option value="22">경칭22</option>
|
||||
<option value="33">경칭33</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>물건명 후리가나</th>
|
||||
<td>
|
||||
<div className="input-wrap" style={{ width: '789px' }}>
|
||||
<input type="text" className="input-light" {...form.register('objectNameKana')} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<div className="flx-box">
|
||||
<div className="title">
|
||||
1차 판매점명 / ID
|
||||
<span className="important">*</span>
|
||||
</div>
|
||||
<div className="tooltips"></div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
<select className="select-light" name="" id=""></select>
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '216px' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
value={form.watch('saleStoreName')}
|
||||
{...form.register('saleStoreName')}
|
||||
readOnly
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
<div className="flx-box">
|
||||
<div className="title">
|
||||
2차 판매점명 / ID <span className="important">*</span>
|
||||
</div>
|
||||
<div className="tooltips"></div>
|
||||
</div>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap mr5" style={{ width: '567px' }}>
|
||||
<select className="select-light" name="" id=""></select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="input-wrap" style={{ width: '216px' }}>
|
||||
<input type="text" className="input-ligth" readOnly defaultValue={'선택한2차점 판매점명찍기 예정'} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
우편번호 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="input-wrap mr5" style={{ width: '200px' }}>
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
maxLength={7}
|
||||
{...form.register('zipNo', {
|
||||
minLength: { value: 7, message: '7자리만가능' },
|
||||
pattern: { value: /^[0-9]*$/g, message: '숫자만 입력' },
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
<Button className="btn-origin grey" isDisabled={!buttonValid} onClick={onSearchPostNumber}>
|
||||
주소검색
|
||||
</Button>
|
||||
<div className="guide">*우편번호 7자리를 입력한 후, 주소검색 버튼을 클릭해 주십시오</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
도도부현 / 주소 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="input-wrap mr5" style={{ width: '200px' }}>
|
||||
{/* {prefCodeList?.length > 0 && (
|
||||
<Select className="max-w-xs" selectedKeys={prefValue} isDisabled {...form.register('prefId')}>
|
||||
{prefCodeList.map((row) => {
|
||||
return <SelectItem key={row.prefId}>{row.prefName}</SelectItem>
|
||||
})}
|
||||
</Select>
|
||||
)} */}
|
||||
</div>
|
||||
<div className="input-wrap mr5" style={{ width: '580px' }}>
|
||||
<input type="text" className="input-light" value={form.watch('address')} {...form.register('address')} />
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
발전량시뮬레이션지역 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="select-wrap" style={{ width: '200px' }}></div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
기준풍속 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap mr10" style={{ width: '200px' }}>
|
||||
<select className="select-light" name="windSpeed" {...register('windSpeed')}>
|
||||
<option value="">기준풍속공통코드?</option>
|
||||
<option value="30">30</option>
|
||||
<option value="50">50</option>
|
||||
<option value="60">60</option>
|
||||
</select>
|
||||
</div>
|
||||
<span>m/s이하</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
수직적설량 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap mr10" style={{ width: '200px' }}></div>
|
||||
<span className="mr10">cm</span>
|
||||
<div className="d-check-box light">
|
||||
<input type="checkbox" />
|
||||
<label htmlFor="">한랭지대책시행</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
면조도구분 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input type="radio" name="surfaceType" value="III・IV" id="surfaceType0" {...form.register('surfaceType')} />
|
||||
<label htmlFor="surfaceType0">III・IV</label>
|
||||
</div>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input type="radio" name="surfaceType" value="Ⅱ" id="surfaceType1" {...form.register('surfaceType')} />
|
||||
<label htmlFor="surfaceType1">Ⅱ</label>
|
||||
</div>
|
||||
<div className="d-check-box light mr5">
|
||||
<input type="checkbox" />
|
||||
<label htmlFor="">염해지역용아이템사용</label>
|
||||
</div>
|
||||
<div className="tooltips"></div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>
|
||||
설치높이 <span className="important">*</span>
|
||||
</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="select-wrap mr10" style={{ width: '200px' }}>
|
||||
<select className="select-light" name="installHeight" {...register('installHeight')}>
|
||||
<option value="">설치높이공코</option>
|
||||
<option value="11">111</option>
|
||||
<option value="22">222</option>
|
||||
<option value="33">333</option>
|
||||
</select>
|
||||
</div>
|
||||
<span>m</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>계약조건</th>
|
||||
<td>
|
||||
<div className="flx-box">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input type="radio" name="powerConTerms" value="0" id="powerConTerms0" {...form.register('powerConTerms')} />
|
||||
<label htmlFor="powerConTerms0">잉여</label>
|
||||
</div>
|
||||
<div className="d-check-radio light mr10">
|
||||
<input type="radio" name="powerConTerms" value="1" id="powerConTerms1" {...form.register('powerConTerms')} />
|
||||
<label htmlFor="powerConTerms1">전량</label>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>메모</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input type="text" className="input-light" />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="sub-table-footer">
|
||||
{!isFormValid ? (
|
||||
<button className="btn-origin grey mr5" onClick={onTempSave}>
|
||||
New화면 임시저장
|
||||
</button>
|
||||
) : (
|
||||
<button type="submit" className="btn-origin navy mr5">
|
||||
NEW 화면 저장
|
||||
</button>
|
||||
)}
|
||||
<Link href="/management/stuff">
|
||||
<button type="button" className="btn-origin grey">
|
||||
NEW화면 물건목록이동
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
)) || (
|
||||
<>
|
||||
{objectNo.substring(0, 1) === 'R' ? (
|
||||
<>
|
||||
<Link href="/management/stuff">
|
||||
<button type="button" className="btn-origin grey">
|
||||
R상세:물건목록
|
||||
</button>
|
||||
</Link>
|
||||
<button type="submit" className="btn-origin navy mr5">
|
||||
R상세:저장
|
||||
</button>
|
||||
<button type="submit" className="btn-origin navy mr5" onClick={onDelete}>
|
||||
R상세:물건삭제
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Link href="/management/stuff">
|
||||
<button type="button" className="btn-origin grey">
|
||||
T상세:물건목록
|
||||
</button>
|
||||
</Link>
|
||||
<button type="submit" className="btn-origin navy mr5">
|
||||
T상세:저장
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* {(editMode === 'NEW' && (
|
||||
<form onSubmit={handleSubmit(onValid)}>
|
||||
<div>
|
||||
<div>(*필수 입력항목)</div>
|
||||
@ -610,7 +917,7 @@ export default function StuffDetail() {
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
)} */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
51
src/components/management/StuffHeader.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
'use client'
|
||||
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
export default function StuffHeader() {
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
const objectNo = searchParams.get('objectNo') //url에서 물건번호 꺼내서 바로 set
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
const [headerData, setHeaderData] = useState({})
|
||||
|
||||
useEffect(() => {
|
||||
get({ url: `/api/object/${objectNo}/detail` }).then((res) => {
|
||||
//console.log('res::', res)
|
||||
if (res != null && res != '') {
|
||||
console.log('헤더상세::::::::::', res)
|
||||
setHeaderData(res)
|
||||
} else {
|
||||
alert('삭제된 물건입니다')
|
||||
router.push('/management/stuff')
|
||||
}
|
||||
})
|
||||
}, [objectNo])
|
||||
|
||||
return (
|
||||
<div className="infomation-box-wrap">
|
||||
<div className="sub-table-box">
|
||||
<div className="info-title">물건번호</div>
|
||||
<div className="info-inner">
|
||||
{headerData.objectNo} <button className="copy-ico"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sub-table-box">
|
||||
<div className="info-title">사양확정일</div>
|
||||
<div className="info-inner">{headerData.specificationConfirmDate}</div>
|
||||
</div>
|
||||
<div className="sub-table-box">
|
||||
<div className="info-title">갱신일시</div>
|
||||
<div className="info-inner">{headerData.lastEditDatetime}</div>
|
||||
</div>
|
||||
<div className="sub-table-box">
|
||||
<div className="info-title">등록일</div>
|
||||
<div className="info-inner">{headerData.createDatetime}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -2,25 +2,36 @@
|
||||
|
||||
import React, { useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { Input, RadioGroup, Radio, Button, Autocomplete, AutocompleteItem } from '@nextui-org/react'
|
||||
import { Autocomplete, AutocompleteItem } from '@nextui-org/react'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
import { globalLocaleStore } from '@/store/localeAtom'
|
||||
|
||||
import RangeDatePicker from '@/components/common/datepicker/RangeDatePicker'
|
||||
import { useRecoilState, useResetRecoilState } from 'recoil'
|
||||
import { stuffSearchState } from '@/store/stuffAtom'
|
||||
import { isEmptyArray } from '@/util/common-utils'
|
||||
import { get } from '@/lib/Axios'
|
||||
import dayjs from 'dayjs'
|
||||
import isLeapYear from 'dayjs/plugin/isLeapYear' // 윤년 판단 플러그인
|
||||
dayjs.extend(isLeapYear)
|
||||
import Link from 'next/link'
|
||||
import SingleDatePicker from '../common/datepicker/SingleDatePicker'
|
||||
export default function StuffSearchCondition() {
|
||||
//달력 props 관련 날짜 셋팅
|
||||
const [dateRange, setDateRange] = useState([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')])
|
||||
const [startRangeDate, endRangeDate] = dateRange
|
||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||
|
||||
const rangeDatePickerProps = {
|
||||
startRangeDate, //시작일
|
||||
endRangeDate, //종료일
|
||||
setDateRange,
|
||||
const { get } = useAxios(globalLocaleState)
|
||||
|
||||
//달력 props 관련 날짜 셋팅
|
||||
const [startDate, setStartDate] = useState(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
const [endDate, setEndDate] = useState(dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
|
||||
const rangeDatePickerProps1 = {
|
||||
startDate, //시작일
|
||||
setStartDate,
|
||||
}
|
||||
|
||||
const rangeDatePickerProps2 = {
|
||||
startDate: endDate, //종료일
|
||||
setStartDate: setEndDate,
|
||||
}
|
||||
|
||||
//여기서 선택한 검색조건들을 recoil로 관리
|
||||
@ -40,7 +51,7 @@ export default function StuffSearchCondition() {
|
||||
const [schSelSaleStoreList, setSchSelSaleStoreList] = useState([]) //판매대리점 자동완성 SELECT
|
||||
// 조회
|
||||
const onSubmit = () => {
|
||||
let diff = dayjs(endRangeDate).diff(startRangeDate, 'day')
|
||||
let diff = dayjs(endDate).diff(startDate, 'day')
|
||||
if (diff > 366) {
|
||||
return alert('최대1년 조회 가능합니다.')
|
||||
}
|
||||
@ -55,8 +66,8 @@ export default function StuffSearchCondition() {
|
||||
schReceiveUser: stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser,
|
||||
schDispCompanyName: stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName,
|
||||
schDateType: stuffSearch?.schDateType ? stuffSearch.schDateType : dateType,
|
||||
schFromDt: dayjs(startRangeDate).format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(endRangeDate).format('YYYY-MM-DD'),
|
||||
schFromDt: dayjs(startDate).format('YYYY-MM-DD'),
|
||||
schToDt: dayjs(endDate).format('YYYY-MM-DD'),
|
||||
code: 'E',
|
||||
schSelSaleStoreId: stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId,
|
||||
startRow: stuffSearch?.startRow ? stuffSearch.startRow : 1,
|
||||
@ -76,7 +87,8 @@ export default function StuffSearchCondition() {
|
||||
setReceiveUser('')
|
||||
setDispCompanyName('')
|
||||
setDateType('U')
|
||||
setDateRange([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')])
|
||||
setStartDate(dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
setEndDate(dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
setSchSelSaleStoreId('')
|
||||
resetStuffRecoil()
|
||||
}
|
||||
@ -102,23 +114,207 @@ export default function StuffSearchCondition() {
|
||||
}
|
||||
}
|
||||
|
||||
//x로 날짜 비웠을때 기본값으로 셋팅
|
||||
useEffect(() => {
|
||||
if (!startRangeDate && !endRangeDate) {
|
||||
setDateRange([dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD')])
|
||||
}
|
||||
}, [startRangeDate, endRangeDate])
|
||||
|
||||
useEffect(() => {
|
||||
setDateRange([
|
||||
stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'),
|
||||
stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'),
|
||||
])
|
||||
setStartDate(stuffSearch?.schFromDt ? stuffSearch.schFromDt : dayjs(new Date()).add(-1, 'year').format('YYYY-MM-DD'))
|
||||
setEndDate(stuffSearch?.schToDt ? stuffSearch.schToDt : dayjs(new Date()).format('YYYY-MM-DD'))
|
||||
}, [stuffSearch])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div align="right">
|
||||
{/* 퍼블적용시작 */}
|
||||
<div className="sub-table-box">
|
||||
<div className="table-box-title-wrap">
|
||||
<div className="title-wrap">
|
||||
<h3>물건현황</h3>
|
||||
</div>
|
||||
<div className="left-unit-box">
|
||||
<Link href="/management/stuff/tempdetail">
|
||||
<button type="button" className="btn-origin navy mr5">
|
||||
물건신규등록버튼
|
||||
</button>
|
||||
</Link>
|
||||
<button type="button" className="btn-origin navy mr5" onClick={onSubmit}>
|
||||
조회
|
||||
</button>
|
||||
<button type="button" className="btn-origin grey" onClick={resetRecoil}>
|
||||
초기화
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="common-table">
|
||||
<table>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>물건번호</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="물건번호 입력"
|
||||
value={stuffSearch?.code === 'E' ? stuffSearch.schObjectNo : objectNo}
|
||||
onChange={(e) => {
|
||||
setObjectNo(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schObjectNo: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>판매대리점명</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="판매대리점ID 입력"
|
||||
value={stuffSearch?.schSaleStoreId ? stuffSearch.schSaleStoreId : saleStoreId}
|
||||
onChange={(e) => {
|
||||
setSaleStoreId(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreId: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>물건주소</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="물건주소 입력"
|
||||
value={stuffSearch?.schAddress ? stuffSearch.schAddress : address}
|
||||
onChange={(e) => {
|
||||
setAddress(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schAddress: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>물건명</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="물건명 입력"
|
||||
value={stuffSearch?.schObjectName ? stuffSearch.schObjectName : objectName}
|
||||
onChange={(e) => {
|
||||
setobjectName(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schObjectName: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>견적처</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="견적처 입력"
|
||||
value={stuffSearch?.schDispCompanyName ? stuffSearch.schDispCompanyName : dispCompanyName}
|
||||
onChange={(e) => {
|
||||
setDispCompanyName(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schDispCompanyName: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>판매대리점 선택</th>
|
||||
<td>
|
||||
{/* <div className="select-wrap">
|
||||
<select className="select-light" name="" id=""></select>
|
||||
</div> */}
|
||||
{schSelSaleStoreList?.length > 0 && (
|
||||
<Autocomplete
|
||||
className="max-w-xs"
|
||||
label="판매대리점선택"
|
||||
defaultItems={schSelSaleStoreList}
|
||||
selectedKey={stuffSearch?.schSelSaleStoreId ? stuffSearch.schSelSaleStoreId : schSelSaleStoreId}
|
||||
onSelectionChange={onSelectionChange}
|
||||
>
|
||||
{(option) => <AutocompleteItem key={option.saleStoreId}>{option.saleStoreName}</AutocompleteItem>}
|
||||
</Autocomplete>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>담당자</th>
|
||||
<td>
|
||||
<div className="input-wrap">
|
||||
<input
|
||||
type="text"
|
||||
className="input-light"
|
||||
placeholder="담당자 입력"
|
||||
value={stuffSearch?.schReceiveUser ? stuffSearch.schReceiveUser : receiveUser}
|
||||
onChange={(e) => {
|
||||
setReceiveUser(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schReceiveUser: e.target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<th>기간검색</th>
|
||||
<td colSpan={3}>
|
||||
<div className="form-flex-wrap">
|
||||
<div className="radio-wrap mr10">
|
||||
<div className="d-check-radio light mr10">
|
||||
<input
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_u"
|
||||
checked={stuffSearch?.schDateType === 'U' ? true : false}
|
||||
value={'U'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="radio_u">갱신일</label>
|
||||
</div>
|
||||
<div className="d-check-radio light">
|
||||
<input
|
||||
type="radio"
|
||||
name="radio_ptype"
|
||||
id="radio_r"
|
||||
checked={stuffSearch?.schDateType === 'R' ? true : false}
|
||||
value={'R'}
|
||||
onChange={(e) => {
|
||||
setDateType(e.target.value)
|
||||
setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
|
||||
}}
|
||||
/>
|
||||
<label htmlFor="ra05">등록일</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="date-picker-wrap">
|
||||
<div className="date-picker" style={{ flex: 1 }}>
|
||||
<SingleDatePicker {...rangeDatePickerProps1} />
|
||||
</div>
|
||||
<span> ~ </span>
|
||||
<div className="date-picker" style={{ flex: 1 }}>
|
||||
<SingleDatePicker {...rangeDatePickerProps2} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{/* 퍼블적용끝 */}
|
||||
{/* <div align="right">
|
||||
<Link href="/management/stuff/tempdetail">
|
||||
<button type="button">물건신규등록</button>
|
||||
</Link>
|
||||
@ -300,7 +496,7 @@ export default function StuffSearchCondition() {
|
||||
)
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div> */}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { distanceBetweenPoints } from '@/util/canvas-util'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { distanceBetweenPoints, getDegreeByChon } from '@/util/canvas-util'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import {
|
||||
adsorptionPointAddModeState,
|
||||
adsorptionPointModeState,
|
||||
@ -16,14 +16,18 @@ import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import {
|
||||
outerLineAngle1State,
|
||||
outerLineAngle2State,
|
||||
outerLineArrow1State,
|
||||
outerLineArrow2State,
|
||||
outerLineDiagonalState,
|
||||
outerLineFixState,
|
||||
outerLineLength1State,
|
||||
outerLineLength2State,
|
||||
outerLinePointsState,
|
||||
outerLineTypeState,
|
||||
} from '@/store/outerLineAtom'
|
||||
import { calculateAngle } from '@/util/qpolygon-utils'
|
||||
import { fabric } from 'fabric'
|
||||
|
||||
export function useOuterLineWall() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -33,6 +37,7 @@ export function useOuterLineWall() {
|
||||
const { addLine, removeLine } = useLine()
|
||||
const { tempGridMode } = useTempGrid()
|
||||
const { addPolygonByLines } = usePolygon()
|
||||
|
||||
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
||||
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
|
||||
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
|
||||
@ -42,28 +47,42 @@ export function useOuterLineWall() {
|
||||
const length1Ref = useRef(null)
|
||||
const length2Ref = useRef(null)
|
||||
const angle1Ref = useRef(null)
|
||||
const angle2Ref = useRef(null)
|
||||
const [length1, setLength1] = useRecoilState(outerLineLength1State)
|
||||
const [length2, setLength2] = useRecoilState(outerLineLength2State)
|
||||
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
|
||||
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
|
||||
const [points, setPoints] = useRecoilState(outerLinePointsState)
|
||||
const [type, setType] = useRecoilState(outerLineTypeState)
|
||||
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
|
||||
const [angle2, setAngle2] = useRecoilState(outerLineAngle2State)
|
||||
const [outerLineDiagonalLength, setOuterLineDiagonalLength] = useRecoilState(outerLineDiagonalState)
|
||||
const arrow1Ref = useRef(arrow1)
|
||||
const arrow2Ref = useRef(arrow2)
|
||||
|
||||
const setOuterLineFix = useSetRecoilState(outerLineFixState)
|
||||
|
||||
const outerLineDiagonalLengthRef = useRef(null)
|
||||
|
||||
const isFix = useRef(false)
|
||||
|
||||
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
|
||||
const closeModalFn = useRef(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (adsorptionPointAddMode || tempGridMode) {
|
||||
return
|
||||
}
|
||||
removeMouseEvent('mouse:down', mouseDown)
|
||||
|
||||
if (points.length === 0) {
|
||||
// 만약 포인트가 없다면 모든 라인과 텍스트를 삭제 후 outerLines에서 point를 뽑아 points에 넣어준다.
|
||||
const lengthTxts = canvas?.getObjects().filter((obj) => obj.name === 'lengthTxt')
|
||||
lengthTxts.forEach((txt) => {
|
||||
canvas?.remove(txt)
|
||||
})
|
||||
}
|
||||
|
||||
addCanvasMouseEventListener('mouse:down', mouseDown)
|
||||
clear()
|
||||
return () => {
|
||||
removeAllMouseEventListeners()
|
||||
}
|
||||
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
|
||||
|
||||
useEffect(() => {
|
||||
@ -75,9 +94,8 @@ export function useOuterLineWall() {
|
||||
}, [arrow2])
|
||||
|
||||
useEffect(() => {
|
||||
removeAllDocumentEventListeners()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
clear()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
}, [type])
|
||||
|
||||
const clear = () => {
|
||||
@ -88,6 +106,9 @@ export function useOuterLineWall() {
|
||||
setArrow2('')
|
||||
|
||||
setAngle1(0)
|
||||
setAngle2(0)
|
||||
|
||||
setOuterLineDiagonalLength(0)
|
||||
}
|
||||
|
||||
const mouseDown = (e) => {
|
||||
@ -143,14 +164,14 @@ export function useOuterLineWall() {
|
||||
|
||||
canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint'))
|
||||
|
||||
// point가 변경 될때마다 이벤트 리스너를 제거하고 다시 등록
|
||||
removeAllDocumentEventListeners()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
|
||||
if (points.length === 0) {
|
||||
setOuterLineFix(true)
|
||||
removeAllDocumentEventListeners()
|
||||
return
|
||||
}
|
||||
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
|
||||
if (points.length === 1) {
|
||||
const point = new fabric.Circle({
|
||||
radius: 5,
|
||||
@ -164,6 +185,7 @@ export function useOuterLineWall() {
|
||||
|
||||
canvas?.add(point)
|
||||
} else {
|
||||
setOuterLineFix(false)
|
||||
points.forEach((point, idx) => {
|
||||
if (idx === 0) {
|
||||
return
|
||||
@ -174,6 +196,13 @@ export function useOuterLineWall() {
|
||||
const lastPoint = points[points.length - 1]
|
||||
const firstPoint = points[0]
|
||||
|
||||
if (isFix.current) {
|
||||
removeAllMouseEventListeners()
|
||||
removeAllDocumentEventListeners()
|
||||
canvas?.renderAll()
|
||||
closeModalFn.current(false)
|
||||
}
|
||||
|
||||
if (points.length < 3) {
|
||||
return
|
||||
}
|
||||
@ -241,7 +270,7 @@ export function useOuterLineWall() {
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
idx: idx,
|
||||
selectable: false,
|
||||
selectable: true,
|
||||
name: 'outerLine',
|
||||
})
|
||||
}
|
||||
@ -261,7 +290,26 @@ export function useOuterLineWall() {
|
||||
}
|
||||
|
||||
// 직각 완료될 경우 확인
|
||||
const checkRightAngle = () => {
|
||||
const checkRightAngle = (direction) => {
|
||||
const activeElem = document.activeElement
|
||||
|
||||
const canDirection =
|
||||
direction === '↓' || direction === '↑'
|
||||
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
|
||||
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
|
||||
|
||||
if (activeElem === length1Ref.current || activeElem === angle1Ref.current) {
|
||||
setArrow1(direction)
|
||||
arrow1Ref.current = direction
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
|
||||
if (!canDirection) {
|
||||
return
|
||||
}
|
||||
setArrow2(direction)
|
||||
arrow2Ref.current = direction
|
||||
}
|
||||
|
||||
const length1Num = Number(length1Ref.current.value) / 10
|
||||
const length2Num = Number(length2Ref.current.value) / 10
|
||||
|
||||
@ -332,6 +380,191 @@ export function useOuterLineWall() {
|
||||
}
|
||||
}
|
||||
|
||||
//이구배 완료될 경우 확인 ↓, ↑, ←, →
|
||||
const checkDoublePitch = (direction) => {
|
||||
const activeElem = document.activeElement
|
||||
|
||||
const canDirection =
|
||||
direction === '↓' || direction === '↑'
|
||||
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
|
||||
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
|
||||
|
||||
if (activeElem === length1Ref.current || activeElem === angle1Ref.current) {
|
||||
setArrow1(direction)
|
||||
arrow1Ref.current = direction
|
||||
angle2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
|
||||
if (!canDirection) {
|
||||
return
|
||||
}
|
||||
setArrow2(direction)
|
||||
arrow2Ref.current = direction
|
||||
}
|
||||
|
||||
const angle1Value = angle1Ref.current.value
|
||||
const angle2Value = angle2Ref.current.value
|
||||
const length1Value = length1Ref.current.value
|
||||
const length2Value = length2Ref.current.value
|
||||
|
||||
const arrow1Value = arrow1Ref.current
|
||||
const arrow2Value = arrow2Ref.current
|
||||
|
||||
if (angle1Value !== 0 && length1Value !== 0 && angle2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
|
||||
if (arrow1Value === '↓' && arrow2Value === '→') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '↓' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '→') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length1Value / 10, y: prev[prev.length - 1].y - length2Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↓') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '←' && arrow2Value === '↓') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '←' && arrow2Value === '↑') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
})
|
||||
}
|
||||
|
||||
angle1Ref.current.focus()
|
||||
}
|
||||
}
|
||||
|
||||
//대각선 완료될 경우 확인
|
||||
const checkDiagonal = (direction) => {
|
||||
const activeElem = document.activeElement
|
||||
|
||||
const canDirection =
|
||||
direction === '↓' || direction === '↑'
|
||||
? arrow1Ref.current === '←' || arrow1Ref.current === '→'
|
||||
: arrow1Ref.current === '↓' || arrow1Ref.current === '↑'
|
||||
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1(direction)
|
||||
arrow1Ref.current = direction
|
||||
} else if (activeElem === length2Ref.current || activeElem === angle2Ref.current) {
|
||||
if (!canDirection) {
|
||||
return
|
||||
}
|
||||
setArrow2(direction)
|
||||
arrow2Ref.current = direction
|
||||
}
|
||||
|
||||
const diagonalLength = outerLineDiagonalLengthRef.current.value // 대각선 길이
|
||||
|
||||
const length1Value = length1Ref.current.value
|
||||
|
||||
const arrow1Value = arrow1Ref.current
|
||||
const arrow2Value = arrow2Ref.current
|
||||
|
||||
const getLength2 = () => {
|
||||
return Math.floor(Math.sqrt(diagonalLength ** 2 - length1Value ** 2))
|
||||
}
|
||||
|
||||
const length2Value = getLength2()
|
||||
|
||||
console.log(length2Value)
|
||||
|
||||
if (diagonalLength !== 0 && length1Value !== 0 && arrow1Value !== '') {
|
||||
setLength2(getLength2())
|
||||
length2Ref.current.focus()
|
||||
}
|
||||
|
||||
if (length1Value !== 0 && length2Value !== 0 && arrow1Value !== '' && arrow2Value !== '') {
|
||||
if (arrow1Value === '↓' && arrow2Value === '→') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '↓' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y + length1Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '→') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '↑' && arrow2Value === '←') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x - length2Value / 10, y: prev[prev.length - 1].y - length1Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↓') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [...prev, { x: prev[prev.length - 1].x + length1Value / 10, y: prev[prev.length - 1].y + length2Value / 10 }]
|
||||
})
|
||||
} else if (arrow1Value === '→' && arrow2Value === '↑') {
|
||||
setPoints((prev) => {
|
||||
if (prev.length === 0) {
|
||||
return []
|
||||
}
|
||||
return [
|
||||
...prev,
|
||||
{
|
||||
x: prev[prev.length - 1].x + length1Value / 10,
|
||||
y: prev[prev.length - 1].y - length2Value / 10,
|
||||
},
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const keydown = {
|
||||
outerLine: (e) => {
|
||||
if (points.length === 0) {
|
||||
@ -411,75 +644,52 @@ export function useOuterLineWall() {
|
||||
switch (key) {
|
||||
case 'Down': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowDown': {
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('↓')
|
||||
arrow1Ref.current = '↓'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
||||
break
|
||||
}
|
||||
setArrow2('↓')
|
||||
arrow2Ref.current = '↓'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
checkRightAngle('↓')
|
||||
break
|
||||
}
|
||||
case 'Up': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowUp':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('↑')
|
||||
arrow1Ref.current = '↑'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
||||
break
|
||||
}
|
||||
setArrow2('↑')
|
||||
arrow2Ref.current = '↑'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
checkRightAngle('↑')
|
||||
break
|
||||
case 'Left': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowLeft':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('←')
|
||||
arrow1Ref.current = '←'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
||||
break
|
||||
}
|
||||
setArrow2('←')
|
||||
arrow2Ref.current = '←'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
checkRightAngle('←')
|
||||
break
|
||||
case 'Right': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowRight':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('→')
|
||||
arrow1Ref.current = '→'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
||||
break
|
||||
}
|
||||
setArrow2('→')
|
||||
arrow2Ref.current = '→'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
checkRightAngle('→')
|
||||
break
|
||||
}
|
||||
},
|
||||
leeGubae: (e) => {
|
||||
console.log('leegubae')
|
||||
doublePitch: (e) => {
|
||||
if (points.length === 0) {
|
||||
return
|
||||
}
|
||||
const key = e.key
|
||||
switch (key) {
|
||||
case 'Down': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowDown': {
|
||||
checkDoublePitch('↓')
|
||||
break
|
||||
}
|
||||
case 'Up': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowUp':
|
||||
checkDoublePitch('↑')
|
||||
break
|
||||
case 'Left': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowLeft':
|
||||
checkDoublePitch('←')
|
||||
break
|
||||
case 'Right': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowRight':
|
||||
checkDoublePitch('→')
|
||||
break
|
||||
}
|
||||
},
|
||||
angle: (e) => {
|
||||
if (points.length === 0) {
|
||||
return
|
||||
}
|
||||
const key = e.key
|
||||
switch (key) {
|
||||
case 'Enter': {
|
||||
@ -501,7 +711,30 @@ export function useOuterLineWall() {
|
||||
}
|
||||
},
|
||||
diagonalLine: (e) => {
|
||||
console.log('diagonalLine')
|
||||
if (points.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
const key = e.key
|
||||
switch (key) {
|
||||
case 'Down': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowDown': {
|
||||
checkDiagonal('↓')
|
||||
break
|
||||
}
|
||||
case 'Up': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowUp':
|
||||
checkDiagonal('↑')
|
||||
break
|
||||
case 'Left': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowLeft':
|
||||
checkDiagonal('←')
|
||||
break
|
||||
case 'Right': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowRight':
|
||||
checkDiagonal('→')
|
||||
break
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@ -513,7 +746,7 @@ export function useOuterLineWall() {
|
||||
setPoints((prev) => prev.slice(0, prev.length - 1))
|
||||
}
|
||||
|
||||
const handleFix = () => {
|
||||
const handleFix = (fn) => {
|
||||
if (points.length < 3) {
|
||||
return
|
||||
}
|
||||
@ -541,6 +774,9 @@ export function useOuterLineWall() {
|
||||
setPoints((prev) => {
|
||||
return [...prev, { x: prev[0].x, y: prev[0].y }]
|
||||
})
|
||||
|
||||
isFix.current = true
|
||||
closeModalFn.current = fn
|
||||
}
|
||||
|
||||
return {
|
||||
@ -558,6 +794,15 @@ export function useOuterLineWall() {
|
||||
setArrow2,
|
||||
arrow1Ref,
|
||||
arrow2Ref,
|
||||
angle1,
|
||||
setAngle1,
|
||||
angle1Ref,
|
||||
angle2,
|
||||
setAngle2,
|
||||
angle2Ref,
|
||||
outerLineDiagonalLength,
|
||||
setOuterLineDiagonalLength,
|
||||
outerLineDiagonalLengthRef,
|
||||
type,
|
||||
setType,
|
||||
handleFix,
|
||||
|
||||
160
src/hooks/roofcover/usePropertiesSetting.js
Normal file
@ -0,0 +1,160 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { LINE_TYPE } from '@/common/common'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, currentObjectState } from '@/store/canvasAtom'
|
||||
import { useMode } from '@/hooks/useMode'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
|
||||
export function usePropertiesSetting() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const currentObject = useRecoilValue(currentObjectState)
|
||||
|
||||
const { drawRoofPolygon } = useMode()
|
||||
|
||||
const { addPolygonByLines } = usePolygon()
|
||||
const { removeLine } = useLine()
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentObject) {
|
||||
return
|
||||
}
|
||||
if (currentObject.name !== 'outerLine') {
|
||||
return
|
||||
}
|
||||
|
||||
const type = currentObject.attributes?.type
|
||||
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
|
||||
lines.forEach((line) => {
|
||||
const lineType = line.attributes?.type
|
||||
if (!lineType) {
|
||||
line.set({
|
||||
stroke: '#000000',
|
||||
strokeWidth: 4,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
if (!type) {
|
||||
currentObject.set({
|
||||
stroke: '#EA10AC',
|
||||
strokeWidth: 4,
|
||||
})
|
||||
}
|
||||
}, [currentObject])
|
||||
|
||||
const history = useRef([])
|
||||
|
||||
const handleSetEaves = () => {
|
||||
const selectedLine = canvas?.getActiveObject()
|
||||
if (!selectedLine) {
|
||||
return
|
||||
}
|
||||
selectedLine.set({
|
||||
stroke: '#45CD7D',
|
||||
strokeWidth: 4,
|
||||
attributes: {
|
||||
offset: 50,
|
||||
type: LINE_TYPE.WALLLINE.EAVES,
|
||||
pitch: 4,
|
||||
},
|
||||
})
|
||||
history.current.push(selectedLine)
|
||||
nextLineFocus(selectedLine)
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const handleSetGable = () => {
|
||||
const selectedLine = canvas?.getActiveObject()
|
||||
if (!selectedLine) {
|
||||
return
|
||||
}
|
||||
selectedLine.set({
|
||||
stroke: '#3FBAE6',
|
||||
strokeWidth: 4,
|
||||
attributes: {
|
||||
offset: 30,
|
||||
type: LINE_TYPE.WALLLINE.GABLE,
|
||||
},
|
||||
})
|
||||
history.current.push(selectedLine)
|
||||
nextLineFocus(selectedLine)
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const nextLineFocus = (selectedLine) => {
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
const index = lines.findIndex((line) => line === selectedLine)
|
||||
|
||||
const nextLine = lines[index + 1] || lines[0]
|
||||
if (!nextLine.attributes?.type) {
|
||||
canvas.setActiveObject(nextLine)
|
||||
} else {
|
||||
//activeObject 해제
|
||||
canvas.discardActiveObject()
|
||||
}
|
||||
}
|
||||
|
||||
const handleRollback = () => {
|
||||
if (history.current.length === 0) {
|
||||
return
|
||||
}
|
||||
const lastLine = history.current.pop()
|
||||
|
||||
lastLine.set({
|
||||
stroke: '#000000',
|
||||
strokeWidth: 4,
|
||||
})
|
||||
|
||||
canvas.setActiveObject(lastLine)
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const handleFix = () => {
|
||||
if (!confirm('외벽선 속성 설정을 완료하시겠습니까?')) {
|
||||
return
|
||||
}
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
|
||||
lines.forEach((line) => {
|
||||
line.set({
|
||||
attributes: line.attributes ? line.attributes : { offset: 0, type: LINE_TYPE.WALLLINE.WALL },
|
||||
stroke: '#000000',
|
||||
strokeWidth: 4,
|
||||
})
|
||||
removeLine(line)
|
||||
})
|
||||
|
||||
const wall = addPolygonByLines(lines, { name: 'WallLine', fill: 'transparent', stroke: 'black' })
|
||||
|
||||
wall.lines = [...lines]
|
||||
|
||||
drawRoofPolygon(wall)
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const closeModal = (fn) => {
|
||||
if (!confirm('외벽선 속성 설정을 종료 하시겠습니까?')) {
|
||||
return
|
||||
}
|
||||
|
||||
const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
|
||||
lines.forEach((line) => {
|
||||
line.set({
|
||||
attributes: { offset: 0, type: LINE_TYPE.WALLLINE.WALL },
|
||||
stroke: '#000000',
|
||||
strokeWidth: 4,
|
||||
})
|
||||
})
|
||||
|
||||
canvas.renderAll()
|
||||
fn(false)
|
||||
}
|
||||
|
||||
return { handleSetEaves, handleSetGable, handleRollback, handleFix, closeModal }
|
||||
}
|
||||
@ -26,7 +26,7 @@ export function useAdsorptionPoint() {
|
||||
top: pointer.y - 3,
|
||||
x: pointer.x,
|
||||
y: pointer.y,
|
||||
selectable: false,
|
||||
selectable: true,
|
||||
name: 'adsorptionPoint',
|
||||
})
|
||||
|
||||
|
||||
@ -79,5 +79,9 @@ export function useAxios(lang = '') {
|
||||
.catch(console.error)
|
||||
}
|
||||
|
||||
return { get, promiseGet, post, promisePost, put, promisePut, patch, del }
|
||||
const promiseDel = async ({ url }) => {
|
||||
return await getInstances(url).delete(url)
|
||||
}
|
||||
|
||||
return { get, promiseGet, post, promisePost, put, promisePut, patch, del, promiseDel }
|
||||
}
|
||||
|
||||
@ -36,6 +36,7 @@ export function useCanvas(id) {
|
||||
|
||||
setCanvas(c)
|
||||
setCanvasForEvent(c)
|
||||
attachDefaultEventOnCanvas()
|
||||
|
||||
return () => {
|
||||
// c.dispose()
|
||||
@ -45,7 +46,6 @@ export function useCanvas(id) {
|
||||
|
||||
useEffect(() => {
|
||||
// canvas 사이즈가 변경되면 다시
|
||||
attachDefaultEventOnCanvas()
|
||||
}, [canvasSize])
|
||||
|
||||
useEffect(() => {
|
||||
@ -91,6 +91,13 @@ export function useCanvas(id) {
|
||||
// settings for all canvas in the app
|
||||
fabric.Object.prototype.transparentCorners = false
|
||||
fabric.Object.prototype.id = uuidv4()
|
||||
fabric.Object.prototype.selectable = true
|
||||
fabric.Object.prototype.lockMovementX = true
|
||||
fabric.Object.prototype.lockMovementY = true
|
||||
fabric.Object.prototype.lockRotation = true
|
||||
fabric.Object.prototype.lockScalingX = true
|
||||
fabric.Object.prototype.lockScalingY = true
|
||||
|
||||
fabric.Object.prototype.cornerColor = '#2BEBC8'
|
||||
fabric.Object.prototype.cornerStyle = 'rect'
|
||||
fabric.Object.prototype.cornerStrokeColor = '#2BEBC8'
|
||||
|
||||
@ -6,6 +6,7 @@ import { gridColorState } from '@/store/gridAtom'
|
||||
|
||||
export function useDotLineGrid() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const gridColor = useRecoilValue(gridColorState)
|
||||
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
|
||||
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
|
||||
|
||||
@ -1,38 +1,31 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import {
|
||||
adsorptionPointAddModeState,
|
||||
adsorptionPointModeState,
|
||||
adsorptionRangeState,
|
||||
canvasState,
|
||||
canvasZoomState,
|
||||
currentMenuState,
|
||||
} from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
|
||||
import { canvasState, canvasZoomState, currentMenuState, textModeState } from '@/store/canvasAtom'
|
||||
import { fabric } from 'fabric'
|
||||
import { calculateDistance, calculateIntersection, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
|
||||
import { calculateDistance, distanceBetweenPoints, findClosestPoint } from '@/util/canvas-util'
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useMouse } from '@/hooks/useMouse'
|
||||
import { useDotLineGrid } from '@/hooks/useDotLineGrid'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
|
||||
export function useEvent() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const currentMenu = useRecoilValue(currentMenuState)
|
||||
const keyboardEventListeners = useRef([])
|
||||
const documentEventListeners = useRef([])
|
||||
const mouseEventListeners = useRef([])
|
||||
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
|
||||
const setCanvasZoom = useSetRecoilState(canvasZoomState)
|
||||
|
||||
const { adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, getAdsorptionPoints, adsorptionPointAddModeStateEvent } = useAdsorptionPoint()
|
||||
const { dotLineGridSetting, interval, getClosestLineGrid } = useDotLineGrid()
|
||||
const { tempGridModeStateLeftClickEvent, tempGridMode, tempGridRightClickEvent } = useTempGrid()
|
||||
|
||||
const textMode = useRecoilValue(textModeState)
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) {
|
||||
return
|
||||
}
|
||||
removeAllMouseEventListeners()
|
||||
removeAllDocumentEventListeners()
|
||||
|
||||
removeAllMouseEventListeners()
|
||||
/**
|
||||
* wheelEvent
|
||||
*/
|
||||
@ -47,6 +40,7 @@ export function useEvent() {
|
||||
addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent)
|
||||
addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent)
|
||||
addDocumentEventListener('keydown', document, defaultKeyboardEvent)
|
||||
addDocumentEventListener('contextmenu', document, defaultContextMenuEvent)
|
||||
if (adsorptionPointAddMode) {
|
||||
addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent)
|
||||
}
|
||||
@ -193,13 +187,14 @@ export function useEvent() {
|
||||
}
|
||||
|
||||
const addCanvasMouseEventListener = (eventType, handler) => {
|
||||
canvas.off(eventType)
|
||||
canvas.on(eventType, handler)
|
||||
mouseEventListeners.current.push({ eventType, handler })
|
||||
}
|
||||
|
||||
const removeAllMouseEventListeners = () => {
|
||||
mouseEventListeners.current.forEach(({ eventType, handler }) => {
|
||||
canvas.off(eventType, handler)
|
||||
canvas.off(eventType)
|
||||
})
|
||||
mouseEventListeners.current.length = 0 // 배열 초기화
|
||||
}
|
||||
@ -211,24 +206,36 @@ export function useEvent() {
|
||||
* @param handler
|
||||
*/
|
||||
const addDocumentEventListener = (eventType, element, handler) => {
|
||||
removeDocumentEvent(eventType)
|
||||
element.addEventListener(eventType, handler)
|
||||
keyboardEventListeners.current.push({ eventType, element, handler })
|
||||
documentEventListeners.current.push({ eventType, element, handler })
|
||||
}
|
||||
|
||||
/**
|
||||
* document에 등록되는 event 제거
|
||||
*/
|
||||
const removeAllDocumentEventListeners = () => {
|
||||
keyboardEventListeners.current.forEach(({ eventType, element, handler }) => {
|
||||
documentEventListeners.current.forEach(({ eventType, element, handler }) => {
|
||||
element.removeEventListener(eventType, handler)
|
||||
})
|
||||
keyboardEventListeners.current.length = 0 // 배열 초기화
|
||||
documentEventListeners.current.length = 0 // 배열 초기화
|
||||
}
|
||||
|
||||
const removeMouseEvent = (type, handler) => {
|
||||
const removeMouseEvent = (type) => {
|
||||
mouseEventListeners.current = mouseEventListeners.current.filter((event) => {
|
||||
if (event.type === type && event.handler === handler) {
|
||||
canvas.off(type, handler)
|
||||
if (event.eventType === type) {
|
||||
canvas.off(type, event.handler)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
const removeDocumentEvent = (type) => {
|
||||
documentEventListeners.current = documentEventListeners.current.filter((event) => {
|
||||
if (event.eventType === type) {
|
||||
console.log(type)
|
||||
event.element.removeEventListener(type, event.handler)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
|
||||
export const useLine = () => {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -7,13 +8,13 @@ export const useLine = () => {
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
|
||||
const addLine = (points = [], options) => {
|
||||
const line = new fabric.Line(points, {
|
||||
const line = new QLine(points, {
|
||||
...options,
|
||||
selectable: options.selectable ?? false,
|
||||
fontSize: fontSize,
|
||||
fontFamily: fontFamily,
|
||||
})
|
||||
|
||||
canvas?.add(line)
|
||||
addLineText(line)
|
||||
return line
|
||||
}
|
||||
|
||||
@ -38,6 +39,7 @@ export const useLine = () => {
|
||||
const removeLine = (line) => {
|
||||
removeLineText(line)
|
||||
canvas?.remove(line)
|
||||
canvas?.renderAll()
|
||||
}
|
||||
|
||||
const removeLineText = (line) => {
|
||||
|
||||
@ -39,6 +39,7 @@ import * as turf from '@turf/turf'
|
||||
import { INPUT_TYPE, Mode } from '@/common/common'
|
||||
import { m } from 'framer-motion'
|
||||
import { set } from 'react-hook-form'
|
||||
import { FaWineGlassEmpty } from 'react-icons/fa6'
|
||||
|
||||
export function useMode() {
|
||||
const [mode, setMode] = useRecoilState(modeState)
|
||||
@ -1266,7 +1267,8 @@ export function useMode() {
|
||||
historyPoints.current = []
|
||||
|
||||
const wall = makePolygon(null, sort)
|
||||
wall.set({ name: 'wall' })
|
||||
wall.name = 'wall'
|
||||
// wall.set({ name: 'wall' })
|
||||
|
||||
return wall
|
||||
}
|
||||
@ -1507,344 +1509,253 @@ export function useMode() {
|
||||
*벽 지붕 외곽선 생성 polygon을 입력받아 만들기
|
||||
*/
|
||||
const handleOuterlinesTest2 = (polygon, offset = 50) => {
|
||||
const offsetPoints = offsetPolygon(polygon.points, offset)
|
||||
const roof = drawRoofPolygon(polygon) //지붕 그리기
|
||||
roof.drawHelpLine()
|
||||
// roof.divideLine()
|
||||
}
|
||||
|
||||
function inwardEdgeNormal(vertex1, vertex2) {
|
||||
// Assuming that polygon vertices are in clockwise order
|
||||
const dx = vertex2.x - vertex1.x
|
||||
const dy = vertex2.y - vertex1.y
|
||||
const edgeLength = Math.sqrt(dx * dx + dy * dy)
|
||||
|
||||
return {
|
||||
x: -dy / edgeLength,
|
||||
y: dx / edgeLength,
|
||||
}
|
||||
}
|
||||
|
||||
function outwardEdgeNormal(vertex1, vertex2) {
|
||||
const n = inwardEdgeNormal(vertex1, vertex2)
|
||||
|
||||
return {
|
||||
x: -n.x,
|
||||
y: -n.y,
|
||||
}
|
||||
}
|
||||
|
||||
function createRoofPolygon(vertices) {
|
||||
const edges = []
|
||||
let minX = vertices.length > 0 ? vertices[0].x : undefined
|
||||
let minY = vertices.length > 0 ? vertices[0].y : undefined
|
||||
let maxX = minX
|
||||
let maxY = minY
|
||||
|
||||
for (let i = 0; i < vertices.length; i++) {
|
||||
const vertex1 = vertices[i]
|
||||
const vertex2 = vertices[(i + 1) % vertices.length]
|
||||
|
||||
const outwardNormal = outwardEdgeNormal(vertex1, vertex2)
|
||||
|
||||
const inwardNormal = inwardEdgeNormal(vertex1, vertex2)
|
||||
|
||||
const edge = {
|
||||
vertex1,
|
||||
vertex2,
|
||||
index: i,
|
||||
outwardNormal,
|
||||
inwardNormal,
|
||||
}
|
||||
|
||||
edges.push(edge)
|
||||
|
||||
const x = vertices[i].x
|
||||
const y = vertices[i].y
|
||||
minX = Math.min(x, minX)
|
||||
minY = Math.min(y, minY)
|
||||
maxX = Math.max(x, maxX)
|
||||
maxY = Math.max(y, maxY)
|
||||
}
|
||||
|
||||
return {
|
||||
vertices,
|
||||
edges,
|
||||
minX,
|
||||
minY,
|
||||
maxX,
|
||||
maxY,
|
||||
}
|
||||
}
|
||||
|
||||
function createOffsetEdge(edge, dx, dy) {
|
||||
return {
|
||||
vertex1: {
|
||||
x: edge.vertex1.x + dx,
|
||||
y: edge.vertex1.y + dy,
|
||||
},
|
||||
vertex2: {
|
||||
x: edge.vertex2.x + dx,
|
||||
y: edge.vertex2.y + dy,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function edgesIntersection(edgeA, edgeB) {
|
||||
const den =
|
||||
(edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex2.x - edgeA.vertex1.x) -
|
||||
(edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex2.y - edgeA.vertex1.y)
|
||||
|
||||
if (den === 0) {
|
||||
return null // lines are parallel or coincident
|
||||
}
|
||||
|
||||
const ua =
|
||||
((edgeB.vertex2.x - edgeB.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) -
|
||||
(edgeB.vertex2.y - edgeB.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) /
|
||||
den
|
||||
|
||||
const ub =
|
||||
((edgeA.vertex2.x - edgeA.vertex1.x) * (edgeA.vertex1.y - edgeB.vertex1.y) -
|
||||
(edgeA.vertex2.y - edgeA.vertex1.y) * (edgeA.vertex1.x - edgeB.vertex1.x)) /
|
||||
den
|
||||
|
||||
// Edges are not intersecting but the lines defined by them are
|
||||
const isIntersectionOutside = ua < 0 || ub < 0 || ua > 1 || ub > 1
|
||||
|
||||
return {
|
||||
x: edgeA.vertex1.x + ua * (edgeA.vertex2.x - edgeA.vertex1.x),
|
||||
y: edgeA.vertex1.y + ua * (edgeA.vertex2.y - edgeA.vertex1.y),
|
||||
isIntersectionOutside,
|
||||
}
|
||||
}
|
||||
|
||||
function createMarginPolygon(polygon, lines, arcSegments = 0) {
|
||||
const offsetEdges = []
|
||||
|
||||
polygon.edges.forEach((edge, i) => {
|
||||
const offset = lines[i % lines.length].attributes.offset
|
||||
const dx = edge.outwardNormal.x * offset
|
||||
const dy = edge.outwardNormal.y * offset
|
||||
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
||||
})
|
||||
|
||||
const vertices = []
|
||||
|
||||
offsetEdges.forEach((thisEdge, i) => {
|
||||
const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
|
||||
const vertex = edgesIntersection(prevEdge, thisEdge)
|
||||
if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
|
||||
vertices.push({
|
||||
x: vertex.x,
|
||||
y: vertex.y,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const marginPolygon = createRoofPolygon(vertices)
|
||||
marginPolygon.offsetEdges = offsetEdges
|
||||
return marginPolygon
|
||||
}
|
||||
|
||||
function createPaddingPolygon(polygon, lines, arcSegments = 0) {
|
||||
const offsetEdges = []
|
||||
|
||||
polygon.edges.forEach((edge, i) => {
|
||||
const offset = lines[i % lines.length].attributes.offset
|
||||
const dx = edge.inwardNormal.x * offset
|
||||
const dy = edge.inwardNormal.y * offset
|
||||
offsetEdges.push(createOffsetEdge(edge, dx, dy))
|
||||
})
|
||||
|
||||
const vertices = []
|
||||
|
||||
offsetEdges.forEach((thisEdge, i) => {
|
||||
const prevEdge = offsetEdges[(i + offsetEdges.length - 1) % offsetEdges.length]
|
||||
const vertex = edgesIntersection(prevEdge, thisEdge)
|
||||
if (vertex && (!vertex.isIntersectionOutside || arcSegments < 1)) {
|
||||
vertices.push({
|
||||
x: vertex.x,
|
||||
y: vertex.y,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const paddingPolygon = createRoofPolygon(vertices)
|
||||
paddingPolygon.offsetEdges = offsetEdges
|
||||
return paddingPolygon
|
||||
}
|
||||
|
||||
const drawRoofPolygon = (wall) => {
|
||||
// TODO [ljyoung] : offset 입력 처리 후 제거 해야함.
|
||||
/*wall.lines.forEach((line, index) => {
|
||||
if (index === wall.lines.length - 1 || index === 3) {
|
||||
line.attributes = {
|
||||
type: 'gable',
|
||||
offset: 30,
|
||||
}
|
||||
} else {
|
||||
line.attributes = {
|
||||
type: 'hip',
|
||||
offset: 50,
|
||||
}
|
||||
}
|
||||
})*/
|
||||
|
||||
const polygon = createRoofPolygon(wall.points)
|
||||
const originPolygon = new QPolygon(wall.points, { fontSize: 0 })
|
||||
let offsetPolygon
|
||||
|
||||
let result = createMarginPolygon(polygon, wall.lines).vertices
|
||||
const allPointsOutside = result.every((point) => !originPolygon.inPolygon(point))
|
||||
|
||||
if (allPointsOutside) {
|
||||
offsetPolygon = createMarginPolygon(polygon, wall.lines).vertices
|
||||
} else {
|
||||
offsetPolygon = createPaddingPolygon(polygon, wall.lines).vertices
|
||||
}
|
||||
|
||||
const roof = makePolygon(
|
||||
offsetPoints.map((point) => {
|
||||
offsetPolygon.map((point) => {
|
||||
return { x1: point.x, y1: point.y }
|
||||
}),
|
||||
)
|
||||
roof.name = 'roofBase'
|
||||
roof.setWall(polygon)
|
||||
roof.setWall(wall)
|
||||
|
||||
roof.lines.forEach((line, index) => {
|
||||
line.attributes = { type: wall.lines[index].attributes.type }
|
||||
})
|
||||
|
||||
setRoof(roof)
|
||||
setWall(polygon)
|
||||
setWall(wall)
|
||||
|
||||
roof.drawHelpLine()
|
||||
roof.divideLine()
|
||||
}
|
||||
|
||||
const drawRoofPolygon = (wall, offset = 50) => {
|
||||
console.log(wall)
|
||||
let points = wall.points,
|
||||
expandedPoints = []
|
||||
|
||||
let minX = points[0].x,
|
||||
minY = points[0].y,
|
||||
maxX = points[0].x,
|
||||
maxY = points[0].y
|
||||
|
||||
points.forEach((point) => {
|
||||
if (point.x < minX) minX = point.x
|
||||
if (point.y < minY) minY = point.y
|
||||
if (point.x > maxX) maxX = point.x
|
||||
if (point.y > maxY) maxY = point.y
|
||||
})
|
||||
|
||||
console.log(points)
|
||||
points.forEach((point, index) => {
|
||||
const prevIndex = index === 0 ? points.length - 1 : index - 1
|
||||
const nextIndex = index === points.length - 1 ? 0 : index + 1
|
||||
point.direction = getDirectionByPoint(point, points[nextIndex])
|
||||
point.length = Math.abs(point.x - points[nextIndex].x) === 0 ? Math.abs(point.y - points[nextIndex].y) : Math.abs(point.x - points[nextIndex].x)
|
||||
// point.degree = Math.round(getDegreeBetweenTwoLines(points[prevIndex], point, points[nextIndex]))
|
||||
})
|
||||
console.log('points : ', points)
|
||||
|
||||
points.forEach((currentWall, index) => {
|
||||
let prevWall = points[index === 0 ? points.length - 1 : index - 1]
|
||||
let nextWall = points[index === points.length - 1 ? 0 : index + 1]
|
||||
let isStartPointIn = minX < currentWall.x && currentWall.x < maxX && minY < currentWall.y && currentWall.y < maxY
|
||||
// let isEndPointIn = minX < currentWall.x2 && currentWall.x2 < maxX && minY < currentWall.y2 && currentWall.y2 < maxY
|
||||
|
||||
if (prevWall.direction !== nextWall.direction) {
|
||||
if (currentWall.direction === 'top') {
|
||||
console.log('prevWall degree : ', 45)
|
||||
if (prevWall.direction === 'right') {
|
||||
let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + addLength,
|
||||
y: currentWall.y + addLength,
|
||||
})
|
||||
}
|
||||
if (prevWall.direction === 'left') {
|
||||
console.log('prevWall degree : ', 225)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'bottom') {
|
||||
if (prevWall.direction === 'right') {
|
||||
console.log('prevWall degree : ', 45)
|
||||
let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset)
|
||||
console.log(currentWall.x, '-', offset, '+', addLength)
|
||||
console.log('addLength : ', addLength)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + addLength,
|
||||
y: currentWall.y - addLength,
|
||||
})
|
||||
}
|
||||
if (prevWall.direction === 'left') {
|
||||
console.log('prevWall degree : ', 315)
|
||||
let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset)
|
||||
console.log(currentWall.x, '-', offset, '+', addLength)
|
||||
console.log('addLength : ', addLength)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'right') {
|
||||
if (prevWall.direction === 'top') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 135)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 315)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (prevWall.direction === 'bottom') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 45)
|
||||
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 225)
|
||||
let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset)
|
||||
console.log('addLength : ', addLength)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'left') {
|
||||
if (prevWall.direction === 'top') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 225)
|
||||
let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset)
|
||||
console.log('addLength : ', addLength)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 45)
|
||||
|
||||
let addLength = getLineOffsetPoint(prevWall, currentWall, nextWall, offset)
|
||||
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (prevWall.direction === 'bottom') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 315)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 135)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('else :::: ')
|
||||
if (currentWall.direction === 'top') {
|
||||
if (prevWall.direction === 'right') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 315)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 135)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (prevWall.direction === 'left') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 45)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 225)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'bottom') {
|
||||
if (prevWall.direction === 'right') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 225)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 45)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'right') {
|
||||
if (prevWall.direction === 'top') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 135)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 315)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (prevWall.direction === 'bottom') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 225)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 45)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'left') {
|
||||
if (prevWall.direction === 'top') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 225)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 45)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (prevWall.direction === 'bottom') {
|
||||
if (isStartPointIn) {
|
||||
console.log('prevWall degree : ', 135)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x + offset,
|
||||
y: currentWall.y + offset,
|
||||
})
|
||||
} else {
|
||||
console.log('prevWall degree : ', 315)
|
||||
expandedPoints.push({
|
||||
x: currentWall.x - offset,
|
||||
y: currentWall.y - offset,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
console.log('expandedPoints : ', expandedPoints)
|
||||
|
||||
/*const roof = new fabric.Polygon(expandedPoints, {
|
||||
fill: 'transparent',
|
||||
stroke: 'red',
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
fontSize: fontSize,
|
||||
name: 'QPolygon1',
|
||||
})
|
||||
|
||||
roof.wall = wall
|
||||
canvas?.add(roof)*/
|
||||
|
||||
// roof.drawHelpLine()
|
||||
return roof
|
||||
}
|
||||
|
||||
/**
|
||||
* 구하려는 라인의 x1,y1좌표가 기준
|
||||
* @param line1 이전 라인
|
||||
* @param line2 현재 라인
|
||||
* @param line3 다음 라인
|
||||
* @param offset
|
||||
* @returns {number}
|
||||
* 라인 사이가 지붕골 인지 확인.
|
||||
* @param polygon
|
||||
* @param line1
|
||||
* @param line2
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const getLineOffsetPoint = (line1, line2, line3, offset) => {
|
||||
//밑변
|
||||
let a = Math.abs(line1.x - line2.x)
|
||||
//빗변
|
||||
let c = Math.sqrt(Math.abs(line1.x - line2.x) ** 2 + Math.abs(line1.y - line2.y) ** 2)
|
||||
const checkValley = (polygon, line1, line2) => {
|
||||
let points = [
|
||||
{ x: line1.x1, y: line1.y1 },
|
||||
{ x: line1.x2, y: line1.y2 },
|
||||
{ x: line2.x1, y: line2.y1 },
|
||||
{ x: line2.x2, y: line2.y2 },
|
||||
]
|
||||
points = Array.from(new Set(points.map((point) => JSON.stringify(point)))).map((point) => JSON.parse(point))
|
||||
const centroidX = points.reduce((acc, point) => acc + point.x, 0) / points.length
|
||||
const centroidY = points.reduce((acc, point) => acc + point.y, 0) / points.length
|
||||
|
||||
console.log(a, c)
|
||||
//밑변과 빗변사이의 각도
|
||||
let alphaDegree = getDegreeBetweenTwoLines(line1, line2, line3)
|
||||
alphaDegree = alphaDegree <= 90 ? alphaDegree : 180 - alphaDegree
|
||||
alphaDegree = 90 - alphaDegree
|
||||
console.log('alphaDegree : ', alphaDegree)
|
||||
let isValley = true
|
||||
const pPoints = polygon.points
|
||||
pPoints.forEach((point, index) => {
|
||||
if (index < pPoints.length - 1) {
|
||||
let j = index + 1
|
||||
let xi = pPoints[index].x + polygon.left,
|
||||
yi = pPoints[index].y + polygon.top
|
||||
let xj = pPoints[j].x + polygon.left,
|
||||
yj = pPoints[j].y + polygon.top
|
||||
|
||||
// console.log('Math.tan(alphaDegree * (Math.PI / 180)) : ', Math.tan(alphaDegree * (Math.PI / 180)))
|
||||
console.log(Math.round(offset * Math.tan(alphaDegree * (Math.PI / 180))))
|
||||
|
||||
const angle = getDegreeBetweenTwoLines(line1, line2, line3)
|
||||
const side1 = line1.length
|
||||
const side2 = line2.length
|
||||
const side3 = Math.sqrt(side1 ** 2 + side2 ** 2 - 2 * side1 * side2 * Math.cos(angle * (Math.PI / 180)))
|
||||
const beta = Math.round(Math.asin((side2 * Math.sin(angle * (Math.PI / 180))) / side3) * (180 / Math.PI) * 10) / 10
|
||||
const alpha = 180 - angle - beta
|
||||
|
||||
console.log('angle : ', angle, 'alpha : ', alpha, 'beta : ', beta)
|
||||
|
||||
const h = side2 * Math.sin(alpha * (Math.PI / 180))
|
||||
const h_new = h + offset
|
||||
console.log('빗변까지 길이 : ', h, 'offset 적용된 빗변까지 길이 : ', h_new)
|
||||
const newAdjacent = h_new / Math.sin(angle * (Math.PI / 180))
|
||||
console.log('offset 적용된 밑변 : ', newAdjacent)
|
||||
|
||||
// offset = Math.sqrt(diffAdjacent ** 2 + diffAdjacent ** 2) / 2
|
||||
console.log('offset : ', offset)
|
||||
return offset
|
||||
let intersect = yi > centroidY !== yj > centroidY && centroidX < ((xj - xi) * (centroidY - yi)) / (yj - yi) + xi
|
||||
if (intersect) isValley = !isValley
|
||||
}
|
||||
})
|
||||
return isValley
|
||||
}
|
||||
|
||||
/**
|
||||
@ -4941,6 +4852,12 @@ export function useMode() {
|
||||
)
|
||||
}
|
||||
|
||||
const coordToTurfPolygon = (points) => {
|
||||
const coordinates = points.map((point) => [point.x, point.y])
|
||||
coordinates.push(coordinates[0])
|
||||
return turf.polygon([coordinates])
|
||||
}
|
||||
|
||||
/**
|
||||
* trestle에서 영역을 가져와 mouse:move 이벤트로 해당 영역에 진입했을때 booleanPointInPolygon 로 진입여부를 확인
|
||||
* 확인 후 셀을 이동시킴
|
||||
@ -4948,59 +4865,191 @@ export function useMode() {
|
||||
const drawCellManualInTrestle = () => {
|
||||
const trestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'trestle') //가대를 가져옴
|
||||
|
||||
const dormerTrestlePolygons = canvas?.getObjects().filter((obj) => obj.name === 'dormerTrestle') //도머 객체
|
||||
|
||||
if (trestlePolygons.length !== 0) {
|
||||
let lastPointPosition = { x: 0, y: 0 }
|
||||
let fabricPolygon = null
|
||||
let inside = false
|
||||
let turfPolygon
|
||||
let manualDrawCells = drewRoofCells //
|
||||
let manualDrawCells = drewRoofCells // 앞에서 자동으로 했을때 추가됨
|
||||
let direction
|
||||
|
||||
canvas.on('mouse:move', (e) => {
|
||||
//마우스 이벤트 삭제 후 재추가
|
||||
const mousePoint = canvas.getPointer(e.e)
|
||||
const turfPoint = turf.point([mousePoint.x, mousePoint.y])
|
||||
|
||||
for (let i = 0; i < trestlePolygons.length; i++) {
|
||||
turfPolygon = polygonToTurfPolygon(trestlePolygons[i])
|
||||
if (turf.booleanPointInPolygon(turfPoint, turfPolygon)) {
|
||||
//turf에 보면 폴리곤안에 포인트가 있는지 함수가 있다
|
||||
const direction = trestlePolygons[i].direction //도형의 방향
|
||||
let width = direction === 'south' || direction === 'north' ? 172.2 : 113.4
|
||||
let height = direction === 'south' || direction === 'north' ? 113.4 : 172.2
|
||||
if (Math.abs(mousePoint.x - lastPointPosition.x) >= 5 || Math.abs(mousePoint.y - lastPointPosition.y) >= 5) {
|
||||
let isDrawing = false
|
||||
direction = trestlePolygons[i].direction //도형의 방향
|
||||
let width = direction === 'south' || direction === 'north' ? 172 : 113
|
||||
let height = direction === 'south' || direction === 'north' ? 113 : 172
|
||||
|
||||
if (isDrawing) return
|
||||
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tmpCell')) //움직일때 일단 지워가면서 움직임
|
||||
const points = [
|
||||
{ x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 },
|
||||
{ x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 },
|
||||
{ x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 },
|
||||
{ x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 },
|
||||
]
|
||||
|
||||
const points = [
|
||||
{ x: mousePoint.x - width / 2, y: mousePoint.y - height / 2 },
|
||||
{ x: mousePoint.x + width / 2, y: mousePoint.y - height / 2 },
|
||||
{ x: mousePoint.x + width / 2, y: mousePoint.y + height / 2 },
|
||||
{ x: mousePoint.x - width / 2, y: mousePoint.y + height / 2 },
|
||||
]
|
||||
const turfPoints = coordToTurfPolygon(points)
|
||||
|
||||
fabricPolygon = new QPolygon(points, {
|
||||
fill: '#BFFD9F',
|
||||
stroke: 'black',
|
||||
selectable: false, // 선택 가능하게 설정
|
||||
lockMovementX: true, // X 축 이동 잠금
|
||||
lockMovementY: true, // Y 축 이동 잠금
|
||||
lockRotation: true, // 회전 잠금
|
||||
lockScalingX: true, // X 축 크기 조정 잠금
|
||||
lockScalingY: true, // Y 축 크기 조정 잠금
|
||||
opacity: 0.8,
|
||||
parentId: trestlePolygons[i].parentId,
|
||||
name: 'tmpCell',
|
||||
if (turf.booleanWithin(turfPoints, turfPolygon)) {
|
||||
let isDrawing = false
|
||||
|
||||
if (isDrawing) return
|
||||
canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'tmpCell')) //움직일때 일단 지워가면서 움직임
|
||||
|
||||
fabricPolygon = new fabric.Rect({
|
||||
fill: 'white',
|
||||
stroke: 'black',
|
||||
strokeWidth: 1,
|
||||
width: width,
|
||||
height: height,
|
||||
left: mousePoint.x - width / 2,
|
||||
top: mousePoint.y - height / 2,
|
||||
selectable: false,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
opacity: 0.8,
|
||||
name: 'tmpCell',
|
||||
parentId: trestlePolygons[i].parentId,
|
||||
})
|
||||
|
||||
canvas?.add(fabricPolygon) //움직여가면서 추가됨
|
||||
|
||||
/**
|
||||
* 스냅기능
|
||||
*/
|
||||
let snapDistance = 10
|
||||
let cellSnapDistance = 20
|
||||
|
||||
const trestleLeft = trestlePolygons[i].left
|
||||
const trestleTop = trestlePolygons[i].top
|
||||
const trestleRight = trestleLeft + trestlePolygons[i].width * trestlePolygons[i].scaleX
|
||||
const trestleBottom = trestleTop + trestlePolygons[i].height * trestlePolygons[i].scaleY
|
||||
const bigCenterY = (trestleTop + trestleTop + trestlePolygons[i].height) / 2
|
||||
|
||||
// 작은 폴리곤의 경계 좌표 계산
|
||||
const smallLeft = fabricPolygon.left
|
||||
const smallTop = fabricPolygon.top
|
||||
const smallRight = smallLeft + fabricPolygon.width * fabricPolygon.scaleX
|
||||
const smallBottom = smallTop + fabricPolygon.height * fabricPolygon.scaleY
|
||||
const smallCenterX = smallLeft + (fabricPolygon.width * fabricPolygon.scaleX) / 2
|
||||
const smallCenterY = smallTop + (fabricPolygon.height * fabricPolygon.scaleX) / 2
|
||||
|
||||
/**
|
||||
* 미리 깔아놓은 셀이 있을때 셀에 흡착됨
|
||||
*/
|
||||
if (manualDrawCells) {
|
||||
manualDrawCells.forEach((cell) => {
|
||||
const holdCellLeft = cell.left
|
||||
const holdCellTop = cell.top
|
||||
const holdCellRight = holdCellLeft + cell.width * cell.scaleX
|
||||
const holdCellBottom = holdCellTop + cell.height * cell.scaleY
|
||||
const holdCellCenterX = holdCellLeft + (cell.width * cell.scaleX) / 2
|
||||
const holdCellCenterY = holdCellTop + (cell.height * cell.scaleY) / 2
|
||||
|
||||
//설치된 셀에 좌측에 스냅
|
||||
if (Math.abs(smallRight - holdCellLeft) < snapDistance) {
|
||||
fabricPolygon.left = holdCellLeft - width - 0.5
|
||||
}
|
||||
|
||||
//설치된 셀에 우측에 스냅
|
||||
if (Math.abs(smallLeft - holdCellRight) < snapDistance) {
|
||||
fabricPolygon.left = holdCellRight + 0.5
|
||||
}
|
||||
|
||||
//설치된 셀에 위쪽에 스냅
|
||||
if (Math.abs(smallBottom - holdCellTop) < snapDistance) {
|
||||
fabricPolygon.top = holdCellTop - height - 0.5
|
||||
}
|
||||
|
||||
//설치된 셀에 밑쪽에 스냅
|
||||
if (Math.abs(smallTop - holdCellBottom) < snapDistance) {
|
||||
fabricPolygon.top = holdCellBottom + 0.5
|
||||
}
|
||||
//가운데 -> 가운데
|
||||
if (Math.abs(smallCenterX - holdCellCenterX) < cellSnapDistance) {
|
||||
fabricPolygon.left = holdCellCenterX - width / 2
|
||||
}
|
||||
//왼쪽 -> 가운데
|
||||
if (Math.abs(smallLeft - holdCellCenterX) < cellSnapDistance) {
|
||||
fabricPolygon.left = holdCellCenterX
|
||||
}
|
||||
// 오른쪽 -> 가운데
|
||||
if (Math.abs(smallRight - holdCellCenterX) < cellSnapDistance) {
|
||||
fabricPolygon.left = holdCellCenterX - width
|
||||
}
|
||||
//세로 가운데 -> 가운데
|
||||
if (Math.abs(smallCenterY - holdCellCenterY) < cellSnapDistance) {
|
||||
fabricPolygon.top = holdCellCenterY - height / 2
|
||||
}
|
||||
//위쪽 -> 가운데
|
||||
if (Math.abs(smallTop - holdCellCenterY) < cellSnapDistance) {
|
||||
fabricPolygon.top = holdCellCenterY
|
||||
}
|
||||
//아랫쪽 -> 가운데
|
||||
if (Math.abs(smallBottom - holdCellCenterY) < cellSnapDistance) {
|
||||
fabricPolygon.top = holdCellCenterY - height
|
||||
}
|
||||
})
|
||||
|
||||
canvas?.add(fabricPolygon) //움직여가면서 추가됨
|
||||
lastPointPosition = { x: mousePoint.x, y: mousePoint.y }
|
||||
}
|
||||
|
||||
// 위쪽 변에 스냅
|
||||
if (Math.abs(smallTop - trestleTop) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop
|
||||
}
|
||||
|
||||
// 아래쪽 변에 스냅
|
||||
if (Math.abs(smallTop + fabricPolygon.height * fabricPolygon.scaleY - (trestleTop + trestlePolygons[i].height)) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop + trestlePolygons[i].height - fabricPolygon.height * fabricPolygon.scaleY
|
||||
}
|
||||
|
||||
// 왼쪽변에 스냅
|
||||
if (Math.abs(smallLeft - trestleLeft) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft
|
||||
}
|
||||
//오른쪽 변에 스냅
|
||||
if (Math.abs(smallRight - trestleRight) < snapDistance) {
|
||||
fabricPolygon.left = trestleRight - fabricPolygon.width * fabricPolygon.scaleX
|
||||
}
|
||||
|
||||
if (direction === 'south' || direction === 'north') {
|
||||
// 모듈왼쪽이 세로중앙선에 붙게 스냅
|
||||
if (Math.abs(smallLeft - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2
|
||||
}
|
||||
|
||||
// 모듈이 가운데가 세로중앙선에 붙게 스냅
|
||||
if (Math.abs(smallCenterX - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2 - (fabricPolygon.width * fabricPolygon.scaleX) / 2
|
||||
}
|
||||
|
||||
// 모듈오른쪽이 세로중앙선에 붙게 스냅
|
||||
if (Math.abs(smallRight - (trestleLeft + trestlePolygons[i].width / 2)) < snapDistance) {
|
||||
fabricPolygon.left = trestleLeft + trestlePolygons[i].width / 2 - fabricPolygon.width * fabricPolygon.scaleX
|
||||
}
|
||||
} else {
|
||||
// 모듈이 가로중앙선에 스냅
|
||||
if (Math.abs(smallTop + fabricPolygon.height / 2 - bigCenterY) < snapDistance) {
|
||||
fabricPolygon.top = bigCenterY - fabricPolygon.height / 2
|
||||
}
|
||||
|
||||
if (Math.abs(smallTop - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop + trestlePolygons[i].height / 2
|
||||
}
|
||||
// 모듈 밑면이 가로중앙선에 스냅
|
||||
if (Math.abs(smallBottom - (trestleTop + trestlePolygons[i].height / 2)) < snapDistance) {
|
||||
fabricPolygon.top = trestleTop + trestlePolygons[i].height / 2 - fabricPolygon.height * fabricPolygon.scaleY
|
||||
}
|
||||
}
|
||||
|
||||
fabricPolygon.setCoords()
|
||||
canvas?.renderAll()
|
||||
inside = true
|
||||
break
|
||||
} else {
|
||||
inside = false
|
||||
}
|
||||
@ -5013,17 +5062,47 @@ export function useMode() {
|
||||
})
|
||||
|
||||
canvas?.on('mouse:up', (e) => {
|
||||
let isIntersection = true
|
||||
if (!inside) return
|
||||
if (fabricPolygon) {
|
||||
const turfCellPolygon = polygonToTurfPolygon(fabricPolygon)
|
||||
const rectPoints = [
|
||||
{ x: fabricPolygon.left + 0.5, y: fabricPolygon.top + 0.5 },
|
||||
{ x: fabricPolygon.left + 0.5 + fabricPolygon.width * fabricPolygon.scaleX, y: fabricPolygon.top + 0.5 },
|
||||
{
|
||||
x: fabricPolygon.left + fabricPolygon.width * fabricPolygon.scaleX + 0.5,
|
||||
y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5,
|
||||
},
|
||||
{ x: fabricPolygon.left + 0.5, y: fabricPolygon.top + fabricPolygon.height * fabricPolygon.scaleY + 0.5 },
|
||||
]
|
||||
|
||||
if (turf.booleanWithin(turfCellPolygon, turfPolygon)) {
|
||||
fabricPolygon.set({ points: rectPoints })
|
||||
const tempTurfModule = polygonToTurfPolygon(fabricPolygon)
|
||||
|
||||
if (dormerTrestlePolygons) {
|
||||
dormerTrestlePolygons.forEach((dormerTrestle) => {
|
||||
const dormerTurfPolygon = polygonToTurfPolygon(dormerTrestle)
|
||||
|
||||
const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule]))
|
||||
|
||||
if (intersection) {
|
||||
alert('도머위에 모듈을 올릴 수 없습니다.')
|
||||
isIntersection = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (!isIntersection) return
|
||||
|
||||
fabricPolygon.setCoords() //좌표 재정렬
|
||||
|
||||
if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
|
||||
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
|
||||
const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(turfCellPolygon, polygonToTurfPolygon(cell)))
|
||||
|
||||
const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(cell)))
|
||||
if (!isOverlap) {
|
||||
//안겹치면 넣는다
|
||||
fabricPolygon.set({ name: 'cell' })
|
||||
fabricPolygon.setCoords()
|
||||
fabricPolygon.set({ name: 'cell', fill: '#BFFD9F' })
|
||||
manualDrawCells.push(fabricPolygon)
|
||||
} else {
|
||||
alert('셀끼리 겹치면 안되죠?')
|
||||
@ -5104,29 +5183,6 @@ export function useMode() {
|
||||
const cols = Math.floor((bbox[2] - bbox[0]) / width)
|
||||
const rows = Math.floor((bbox[3] - bbox[1]) / height)
|
||||
|
||||
// console.log('bbox', bbox)
|
||||
|
||||
const boxes = []
|
||||
const installedCellsArray = []
|
||||
|
||||
for (let x = bbox[0]; x < bbox[2]; x += width) {
|
||||
for (let y = bbox[1]; y < bbox[3]; y += height) {
|
||||
const box = turf.polygon([
|
||||
[
|
||||
[x, y],
|
||||
[x + width, y],
|
||||
[x + width, y + height],
|
||||
[x, y + height],
|
||||
[x, y],
|
||||
],
|
||||
])
|
||||
|
||||
if (turf.booleanWithin(box, turfTrestlePolygon)) {
|
||||
boxes.push(box)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let col = 0; col <= cols; col++) {
|
||||
for (let row = 0; row <= rows; row++) {
|
||||
let x = 0,
|
||||
@ -5176,20 +5232,6 @@ export function useMode() {
|
||||
|
||||
const squarePolygon = turf.polygon([square])
|
||||
|
||||
// console.log('turfTrestlePolygon', turfTrestlePolygon)
|
||||
// console.log('squarePolygon', squarePolygon)
|
||||
|
||||
const areaSize = turf.area(turfTrestlePolygon)
|
||||
|
||||
// console.log('areaSize', areaSize)
|
||||
const objSize = turf.area(squarePolygon)
|
||||
|
||||
// console.log('objSize', objSize)
|
||||
|
||||
const maxObject = Math.floor(areaSize / objSize)
|
||||
|
||||
// console.log('maxObjectSize', maxObject)
|
||||
|
||||
const disjointFromTrestle = turf.booleanContains(turfTrestlePolygon, squarePolygon) || turf.booleanWithin(squarePolygon, turfTrestlePolygon)
|
||||
|
||||
if (disjointFromTrestle) {
|
||||
@ -5230,6 +5272,8 @@ export function useMode() {
|
||||
lockScalingY: true, // Y 축 크기 조정 잠금
|
||||
opacity: 0.8,
|
||||
parentId: trestle.parentId,
|
||||
lineCol: col,
|
||||
lineRow: row,
|
||||
})
|
||||
canvas?.add(fabricPolygon)
|
||||
drawCellsArray.push(fabricPolygon)
|
||||
|
||||
@ -2,18 +2,15 @@ import { useRecoilState } from 'recoil'
|
||||
import { canvasState, currentCanvasPlanState, initCanvasPlansState } from '@/store/canvasAtom'
|
||||
import { useAxios } from '@/hooks/useAxios'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { toastUp } from '@/hooks/useToast'
|
||||
import { sessionStore } from '@/store/commonAtom'
|
||||
import { useState } from 'react'
|
||||
import { useSwal } from '@/hooks/useSwal'
|
||||
|
||||
export function usePlan() {
|
||||
const [canvas, setCanvas] = useRecoilState(canvasState)
|
||||
const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
|
||||
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
|
||||
const { swalFire } = useSwal()
|
||||
const { getMessage } = useMessage()
|
||||
const { get, promisePost, promisePut } = useAxios()
|
||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||
const [userId, setUserId] = useState(sessionState.userId)
|
||||
const { get, promisePost, promisePut, promiseDel } = useAxios()
|
||||
|
||||
/**
|
||||
* 마우스 포인터의 가이드라인을 제거합니다.
|
||||
@ -103,14 +100,14 @@ export function usePlan() {
|
||||
|
||||
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData })
|
||||
.then((res) => {
|
||||
toastUp({ message: getMessage('common.message.save'), type: 'success' }) // 성공 시 메세지 없음
|
||||
swalFire({ text: getMessage('common.message.save') })
|
||||
console.log('[PUT] canvas-statuses res :::::::: %o', res)
|
||||
setInitCanvasPlans((initCanvasPlans) =>
|
||||
initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)),
|
||||
)
|
||||
})
|
||||
.catch((error) => {
|
||||
toastUp({ message: error.message, type: 'error' })
|
||||
swalFire({ text: error.message, icon: 'error' })
|
||||
console.error('[PUT] canvas-statuses error :::::::: %o', error)
|
||||
})
|
||||
} else {
|
||||
@ -124,11 +121,11 @@ export function usePlan() {
|
||||
|
||||
await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData })
|
||||
.then((res) => {
|
||||
toastUp({ message: getMessage('common.message.save'), type: 'success' }) // 성공 시 메세지 없음
|
||||
swalFire({ text: getMessage('common.message.save') })
|
||||
console.log('[POST] canvas-statuses response :::::::: %o', res)
|
||||
})
|
||||
.catch((error) => {
|
||||
toastUp({ message: error.message, type: 'error' })
|
||||
swalFire({ text: error.message, icon: 'error' })
|
||||
console.error('[POST] canvas-statuses res error :::::::: %o', error)
|
||||
})
|
||||
}
|
||||
@ -137,7 +134,7 @@ export function usePlan() {
|
||||
/**
|
||||
* objectNo에 해당하는 canvas 목록을 조회하는 함수
|
||||
*/
|
||||
const getCanvasByObjectNo = async (objectNo) => {
|
||||
const getCanvasByObjectNo = async (userId, objectNo) => {
|
||||
return get({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}/${userId}` }).then((res) =>
|
||||
res.map((item) => ({
|
||||
id: item.id,
|
||||
@ -149,11 +146,26 @@ export function usePlan() {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* id에 해당하는 canvas 데이터를 삭제하는 함수
|
||||
*/
|
||||
const delCanvasById = (id) => {
|
||||
return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-id/${id}` })
|
||||
}
|
||||
|
||||
/**
|
||||
* objectNo에 해당하는 canvas 데이터들을 삭제하는 함수
|
||||
*/
|
||||
const delCanvasByObjectNo = (objectNo) => {
|
||||
return promiseDel({ url: `/api/canvas-management/canvas-statuses/by-object/${objectNo}` })
|
||||
}
|
||||
|
||||
return {
|
||||
canvas,
|
||||
removeMouseLines,
|
||||
saveCanvas,
|
||||
addCanvas,
|
||||
getCanvasByObjectNo,
|
||||
delCanvasById,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
import { fabric } from 'fabric'
|
||||
import { getDirectionByPoint } from '@/util/canvas-util'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
|
||||
export const usePolygon = () => {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -9,19 +10,22 @@ export const usePolygon = () => {
|
||||
const fontFamily = useRecoilValue(fontFamilyState)
|
||||
|
||||
const addPolygon = (points, options) => {
|
||||
const polygon = new fabric.Polygon(points, {
|
||||
const polygon = new QPolygon(points, {
|
||||
...options,
|
||||
selectable: options.selectable ?? false,
|
||||
fontSize: fontSize,
|
||||
fontFamily: fontFamily,
|
||||
selectable: true,
|
||||
})
|
||||
|
||||
canvas?.add(polygon)
|
||||
addLengthText(polygon)
|
||||
|
||||
return polygon
|
||||
}
|
||||
|
||||
const addPolygonByLines = (lines, options) => {
|
||||
const points = createPolygonPointsFromOuterLines(lines)
|
||||
|
||||
addPolygon(points, {
|
||||
return addPolygon(points, {
|
||||
...options,
|
||||
})
|
||||
}
|
||||
@ -39,10 +43,11 @@ export const usePolygon = () => {
|
||||
const degree = (Math.atan2(dy, dx) * 180) / Math.PI
|
||||
|
||||
// Create new text object if it doesn't exist
|
||||
const text = new fabric.IText(length.toString(), {
|
||||
const text = new fabric.Text(length.toString(), {
|
||||
left: midPoint.x,
|
||||
top: midPoint.y,
|
||||
fontSize: fontSize,
|
||||
fontFamily: fontFamily,
|
||||
parentId: polygon.id,
|
||||
minX: Math.min(start.x, end.x),
|
||||
maxX: Math.max(start.x, end.x),
|
||||
|
||||
@ -15,6 +15,27 @@
|
||||
"header.stem": "ステム",
|
||||
"plan.menu.plan.drawing": "도면작성",
|
||||
"plan.menu.placement.surface.initial.setting": "配置面 初期設定",
|
||||
"modal.placement.initial.setting.plan.drawing.size.stuff": "寸法入力による物品作成",
|
||||
"modal.placement.initial.setting.plan.": "図面の作成方法",
|
||||
"modal.placement.initial.setting.size": "寸法入力方法",
|
||||
"modal.placement.initial.setting.size.info": "寸法入力方法案内",
|
||||
"modal.placement.initial.setting.size.roof": "複視図入力",
|
||||
"modal.placement.initial.setting.size.roof.info": "平面の外壁線と立面の屋根勾配に基づいて作画する場合に選択",
|
||||
"modal.placement.initial.setting.size.actual": "実測値入力",
|
||||
"modal.placement.initial.setting.size.actual.info": "現地屋根の外周寸法を入力して作画する場合選択",
|
||||
"modal.placement.initial.setting.size.none.pitch": "陸上屋根",
|
||||
"modal.placement.initial.setting.size.none.pitch.info": "傾斜のない平面形状の屋根にパネルを配置する場合に選択",
|
||||
"modal.placement.initial.setting.roof.angle.setting": "屋根角度設定",
|
||||
"modal.placement.initial.setting.roof.pitch": "傾斜",
|
||||
"modal.placement.initial.setting.roof.angle": "角度",
|
||||
"modal.placement.initial.setting.roof.material": "屋根材の選択(単位:mm)",
|
||||
"modal.placement.initial.setting.roof.material.info": "対応可能な屋根材や足場は限定されますので、必ず事前マニュアルをご確認ください。",
|
||||
"modal.placement.initial.setting.rafter": "垂木の間隔",
|
||||
"modal.roof.shape.setting": "屋根形状の設定",
|
||||
"modal.roof.shape.setting.ridge": "龍丸屋根",
|
||||
"modal.roof.shape.setting.patten.a": "Aパターン",
|
||||
"modal.roof.shape.setting.patten.b": "Bパターン",
|
||||
"modal.roof.shape.setting.side": "別に設定",
|
||||
"plan.menu.roof.cover": "지붕덮개",
|
||||
"plan.menu.roof.cover.outline.drawing": "外壁線を描",
|
||||
"plan.menu.roof.cover.roof.shape.setting": "屋根形状設定",
|
||||
@ -32,6 +53,8 @@
|
||||
"modal.cover.outline.arrow": "方向 (矢印)",
|
||||
"modal.cover.outline.fix": "外壁線確定",
|
||||
"modal.cover.outline.rollback": "一変戦に戻る",
|
||||
"modal.cover.outline.finish": "設定完了",
|
||||
"common.setting.finish": "設定完了",
|
||||
"modal.cover.outline.remove": "外壁の削除",
|
||||
"modal.cover.outline.select.move": "外壁の選択、移動",
|
||||
"plan.menu.roof.cover.roof.setting": "屋根形状設定",
|
||||
@ -83,6 +106,7 @@
|
||||
"modal.grid.copy.info": "間隔を設定し、コピー方向を選択します",
|
||||
"modal.grid.copy.length": "長さ",
|
||||
"modal.grid.copy.save": "保存",
|
||||
"modal.common.save": "保存",
|
||||
"modal.canvas.setting.font.plan.edit": "フォントとサイズの変更",
|
||||
"modal.canvas.setting.font.plan.edit.word": "文字フォントの変更",
|
||||
"modal.canvas.setting.font.plan.edit.flow": "フロー方向フォントの変更",
|
||||
@ -115,6 +139,11 @@
|
||||
"modal.canvas.setting.first.option.border": "ボーダーのみ",
|
||||
"modal.canvas.setting.first.option.line": "ラインハッチ",
|
||||
"modal.canvas.setting.first.option.all": "All painted",
|
||||
"modal.canvas.setting.wallline.properties.setting": "外壁のプロパティの設定",
|
||||
"modal.canvas.setting.wallline.properties.setting.info": "※属性を変更する外壁線を選択し、軒で設定またはケラバで設定 ボタンをクリックして設定値を適用します。",
|
||||
"modal.canvas.setting.wallline.properties.setting.eaves": "軒で設定",
|
||||
"modal.canvas.setting.wallline.properties.setting.edge": "ケラバに設定",
|
||||
"setting": "設定",
|
||||
"common.message.no.data": "No data",
|
||||
"common.message.no.dataDown": "ダウンロードするデータがありません",
|
||||
"common.message.noData": "表示するデータがありません",
|
||||
@ -203,6 +232,10 @@
|
||||
"common.message.password.init.success": "パスワード [{0}] に初期化されました。",
|
||||
"common.message.no.edit.save": "この文書は変更できません。",
|
||||
"common.require": "필수",
|
||||
"commons.west": "立つ",
|
||||
"commons.east": "ドン",
|
||||
"commons.south": "M",
|
||||
"commons.north": "北",
|
||||
"site.name": "Q.CAST III",
|
||||
"site.sub_name": "태양광 발전 시스템 도면관리 사이트",
|
||||
"login": "로그인",
|
||||
@ -261,5 +294,22 @@
|
||||
"stuff.gridHeader.dispCompanyName": "견적처",
|
||||
"stuff.gridHeader.receiveUser": "담당자",
|
||||
"stuff.gridHeader.specDate": "사양확인",
|
||||
"stuff.gridHeader.createDatetime": "등록일"
|
||||
"stuff.gridHeader.createDatetime": "등록일",
|
||||
"slope": "傾斜",
|
||||
"eaves.offset": "軒の",
|
||||
"gable.offset": "ケラバ出幅",
|
||||
"size": "寸",
|
||||
"eaves": "軒",
|
||||
"gable": "ケラバ",
|
||||
"wall": "壁",
|
||||
"hipandgable": "八作屋根",
|
||||
"jerkinhead": "半折",
|
||||
"shed": "片側の流れ",
|
||||
"apply": "適用",
|
||||
"has.sleeve": "袖あり",
|
||||
"has.not.sleeve": "袖なし",
|
||||
"jerkinhead.width": "半折先幅",
|
||||
"jerkinhead.slope": "半折先傾斜",
|
||||
"shed.width": "片流幅",
|
||||
"windage.width": "漂流の出幅"
|
||||
}
|
||||
|
||||
@ -15,6 +15,27 @@
|
||||
"header.stem": "Stem",
|
||||
"plan.menu.plan.drawing": "도면작성",
|
||||
"plan.menu.placement.surface.initial.setting": "배치면 초기 설정",
|
||||
"modal.placement.initial.setting.plan.drawing": "도면 작성방법",
|
||||
"modal.placement.initial.setting.plan.drawing.size.stuff": "치수 입력에 의한 물건 작성",
|
||||
"modal.placement.initial.setting.size": "치수 입력방법",
|
||||
"modal.placement.initial.setting.size.info": "치수 입력방법 안내",
|
||||
"modal.placement.initial.setting.size.roof": "복시도 입력",
|
||||
"modal.placement.initial.setting.size.roof.info": "평면의 외벽선과 입면의 지붕 구배를 바탕으로 작화할 경우 선택",
|
||||
"modal.placement.initial.setting.size.actual": "실측값 입력",
|
||||
"modal.placement.initial.setting.size.actual.info": "현지 지붕의 외주 치수를 입력하여 작화하는 경우 선택",
|
||||
"modal.placement.initial.setting.size.none.pitch": "육지붕",
|
||||
"modal.placement.initial.setting.size.none.pitch.info": "경사가 없는 평면 형태의 지붕에 패널을 배치할 경우 선택",
|
||||
"modal.placement.initial.setting.roof.angle.setting": "지붕각도 설정",
|
||||
"modal.placement.initial.setting.roof.pitch": "경사",
|
||||
"modal.placement.initial.setting.roof.angle": "각도",
|
||||
"modal.placement.initial.setting.roof.material": "지붕재 선택(단위: mm)",
|
||||
"modal.placement.initial.setting.roof.material.info": "대응 가능한 지붕재 및 발판은 한정되므로 반드시 사전 매뉴얼을 확인하십시오.",
|
||||
"modal.placement.initial.setting.rafter": "서까래",
|
||||
"modal.roof.shape.setting": "지붕형상 설정",
|
||||
"modal.roof.shape.setting.ridge": "용마루",
|
||||
"modal.roof.shape.setting.patten.a": "A 패턴",
|
||||
"modal.roof.shape.setting.patten.b": "A 패턴",
|
||||
"modal.roof.shape.setting.side": "변별로 설정",
|
||||
"plan.menu.roof.cover": "지붕덮개",
|
||||
"plan.menu.roof.cover.outline.drawing": "외벽선 그리기",
|
||||
"plan.menu.roof.cover.roof.shape.setting": "지붕형상 설정",
|
||||
@ -36,6 +57,9 @@
|
||||
"modal.cover.outline.arrow": "방향(화살표)",
|
||||
"modal.cover.outline.fix": "외벽선 확정",
|
||||
"modal.cover.outline.rollback": "일변전으로 돌아가기",
|
||||
"modal.cover.outline.finish": "설정완료",
|
||||
"common.setting.finish": "설정완료",
|
||||
"common.setting.rollback": "일변전으로 돌아가기",
|
||||
"modal.cover.outline.remove": "외벽 제거",
|
||||
"modal.cover.outline.select.move": "외벽 선택, 이동",
|
||||
"plan.menu.placement.surface": "배치면",
|
||||
@ -84,6 +108,7 @@
|
||||
"modal.grid.copy.info": "간격을 설정하고 복사 방향을 선택하십시오",
|
||||
"modal.grid.copy.length": "길이",
|
||||
"modal.grid.copy.save": "저장",
|
||||
"modal.common.save": "저장",
|
||||
"modal.canvas.setting.font.plan.edit": "글꼴 및 크기 변경",
|
||||
"modal.canvas.setting.font.plan.edit.word": "문자 글꼴 변경",
|
||||
"modal.canvas.setting.font.plan.edit.flow": "흐름 방향 글꼴 변경",
|
||||
@ -116,6 +141,11 @@
|
||||
"modal.canvas.setting.first.option.border": "테두리만",
|
||||
"modal.canvas.setting.first.option.line": "라인해치",
|
||||
"modal.canvas.setting.first.option.all": "All painted",
|
||||
"modal.canvas.setting.wallline.properties.setting": "외벽선 속성 설정",
|
||||
"modal.canvas.setting.wallline.properties.setting.info": "※ 속성을 변경할 외벽선을 선택하고, 처마로 설정 또는 케라바로 설정\n 버튼을 클릭하여 설정값을 적용하십시오.\n",
|
||||
"modal.canvas.setting.wallline.properties.setting.eaves": "처마로 설정",
|
||||
"modal.canvas.setting.wallline.properties.setting.edge": "케라바로 설정",
|
||||
"setting": "설정",
|
||||
"common.message.no.data": "No data",
|
||||
"common.message.no.dataDown": "No data to download",
|
||||
"common.message.noData": "No data to display",
|
||||
@ -204,6 +234,10 @@
|
||||
"common.message.password.init.success": "비밀번호 [{0}]로 초기화 되었습니다.",
|
||||
"common.message.no.edit.save": "This document cannot be changed.",
|
||||
"common.require": "필수",
|
||||
"commons.west": "서",
|
||||
"commons.east": "동",
|
||||
"commons.south": "남",
|
||||
"commons.north": "북",
|
||||
"site.name": "Q.CAST III",
|
||||
"site.sub_name": "태양광 발전 시스템 도면관리 사이트",
|
||||
"login": "로그인",
|
||||
@ -262,5 +296,22 @@
|
||||
"stuff.gridHeader.dispCompanyName": "견적처",
|
||||
"stuff.gridHeader.receiveUser": "담당자",
|
||||
"stuff.gridHeader.specDate": "사양확인",
|
||||
"stuff.gridHeader.createDatetime": "등록일"
|
||||
"stuff.gridHeader.createDatetime": "등록일",
|
||||
"slope": "경사",
|
||||
"eaves.offset": "처마 출폭",
|
||||
"gable.offset": "케라바 출폭",
|
||||
"size": "치수",
|
||||
"eaves": "처마",
|
||||
"gable": "케라바",
|
||||
"wall": "벽",
|
||||
"hipandgable": "팔작지붕",
|
||||
"jerkinhead": "반절처",
|
||||
"shed": "한쪽흐름",
|
||||
"apply": "적용",
|
||||
"has.sleeve": "소매 있음",
|
||||
"has.not.sleeve": "소매 없음",
|
||||
"jerkinhead.width": "반절처 폭",
|
||||
"jerkinhead.slope": "반절처 경사",
|
||||
"shed.width": "한쪽흐름 폭",
|
||||
"windage.width": "편류의 출폭"
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { atom, selector } from 'recoil'
|
||||
import { MENU } from '@/common/common'
|
||||
import { outerLineFixState, outerLinePointsState } from '@/store/outerLineAtom'
|
||||
|
||||
export const canvasState = atom({
|
||||
key: 'canvasState',
|
||||
@ -267,3 +268,18 @@ export const tempGridModeState = atom({
|
||||
key: 'tempGridModeState',
|
||||
default: false,
|
||||
})
|
||||
|
||||
export const textModeState = atom({
|
||||
key: 'textModeState',
|
||||
default: false,
|
||||
})
|
||||
|
||||
export const canGridOptionSeletor = selector({
|
||||
key: 'canGridOptionSeletor',
|
||||
get: ({ get }) => {
|
||||
const points = get(outerLinePointsState)
|
||||
const currentMenu = get(currentMenuState)
|
||||
const outerLineFix = get(outerLineFixState)
|
||||
return points.length === 0 || outerLineFix
|
||||
},
|
||||
})
|
||||
|
||||
@ -2,5 +2,5 @@ import { atom } from 'recoil'
|
||||
|
||||
export const gridColorState = atom({
|
||||
key: 'gridColorState',
|
||||
default: '#000000',
|
||||
default: '#FF0000',
|
||||
})
|
||||
|
||||
@ -63,3 +63,8 @@ export const outerLinePointsState = atom({
|
||||
key: 'outerLinePointsState',
|
||||
default: [],
|
||||
})
|
||||
|
||||
export const outerLineFixState = atom({
|
||||
key: 'outerLineFixState',
|
||||
default: false,
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { atom } from 'recoil'
|
||||
import { atom, selector } from 'recoil'
|
||||
|
||||
export const settingModalFirstOptionsState = atom({
|
||||
key: 'settingModalFirstOptions',
|
||||
|
||||
@ -133,12 +133,16 @@ button{
|
||||
// margin
|
||||
.mt5{margin-top: 5px !important;}
|
||||
.mt10{margin-top: 10px !important;}
|
||||
.mt15{margin-top: 15px !important;}
|
||||
.mb5{margin-bottom: 5px !important;}
|
||||
.mb10{margin-bottom: 10px !important;}
|
||||
.mb15{margin-bottom: 15px !important;}
|
||||
.mr5{margin-right: 5px !important;}
|
||||
.mr10{margin-right: 10px !important;}
|
||||
.mr15{margin-right: 15px !important;}
|
||||
.ml5{margin-left: 5px !important;}
|
||||
.ml10{margin-left: 10px !important;}
|
||||
.ml15{margin-left: 15px !important;}
|
||||
|
||||
// button
|
||||
.btn-frame{
|
||||
@ -189,6 +193,22 @@ button{
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
&.sub-tab{
|
||||
height: 30px;
|
||||
padding: 0 10px;
|
||||
line-height: 28px;
|
||||
font-family: 'Noto Sans JP', sans-serif;
|
||||
background-color: #2D2D2D;
|
||||
border: 1px solid #393939;
|
||||
color: #aaa;
|
||||
&.act,
|
||||
&:hover{
|
||||
background-color: #414E6C;
|
||||
border: 1px solid #414E6C;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
&:hover,
|
||||
&.act{
|
||||
background-color: #1083E3;
|
||||
@ -244,7 +264,7 @@ button{
|
||||
min-width: 100px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 10px;
|
||||
padding: 0 25px 0 10px;
|
||||
background-color: #373737;
|
||||
border: 1px solid #3F3F3F;
|
||||
border-radius: 2px;
|
||||
@ -263,11 +283,14 @@ button{
|
||||
clip-path:inset(0 0 100% 0);
|
||||
width: calc(100% + 2px);
|
||||
padding: 8px 0;
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
background-color: #373737;
|
||||
border: 1px solid #3F3F3F;
|
||||
border-radius: 2px;
|
||||
transition: all 0.17s ease-in-out;
|
||||
visibility: hidden;
|
||||
z-index: 999;
|
||||
.select-item{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -283,6 +306,18 @@ button{
|
||||
background-color: #2C2C2C;
|
||||
}
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
background-color: transparent;
|
||||
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #5a5a5a;
|
||||
border-radius: 10px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
&::after{
|
||||
content: '';
|
||||
@ -372,6 +407,9 @@ input[type=text]{
|
||||
&.block{
|
||||
width: 100%;
|
||||
}
|
||||
&:read-only{
|
||||
color: #AAA;
|
||||
}
|
||||
}
|
||||
&.input-light{
|
||||
display: block;
|
||||
@ -586,6 +624,7 @@ input[type=text]{
|
||||
}
|
||||
&.pop{
|
||||
label{
|
||||
font-size: 12px;
|
||||
&:before{
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
@ -1,13 +1,6 @@
|
||||
import { fabric } from 'fabric'
|
||||
import { QLine } from '@/components/fabric/QLine'
|
||||
import {
|
||||
calculateIntersection,
|
||||
distanceBetweenPoints,
|
||||
findClosestPoint,
|
||||
getAdjacent,
|
||||
getDirectionByPoint,
|
||||
getRoofHypotenuse,
|
||||
} from '@/util/canvas-util'
|
||||
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, getDirectionByPoint } from '@/util/canvas-util'
|
||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||
import * as turf from '@turf/turf'
|
||||
|
||||
@ -1194,314 +1187,294 @@ function calculateAngleBetweenLines(line1, line2) {
|
||||
|
||||
// Calculate the angle in radians and then convert to degrees
|
||||
const angleInRadians = Math.acos(cosTheta)
|
||||
const angleInDegrees = (angleInRadians * 180) / Math.PI
|
||||
|
||||
return angleInDegrees
|
||||
return (angleInRadians * 180) / Math.PI
|
||||
}
|
||||
|
||||
export const drawHippedRoof = (polygon, chon) => {
|
||||
drawRoofRidge(polygon, chon)
|
||||
const hasNonParallelLines = polygon.lines.filter((line) => line.x1 !== line.x2 && line.y1 !== line.y2)
|
||||
if (hasNonParallelLines.length > 0) {
|
||||
alert('대각선이 존재합니다.')
|
||||
return
|
||||
}
|
||||
drawRidgeRoof(polygon, chon)
|
||||
drawHips(polygon)
|
||||
connectLinePoint(polygon)
|
||||
}
|
||||
|
||||
const drawRoofRidge = (polygon, chon) => {
|
||||
const points = []
|
||||
polygon.wall.points.forEach((point) => points.push({ x: point.x, y: point.y }))
|
||||
console.log('points : ', points)
|
||||
|
||||
/**
|
||||
*
|
||||
* @param polygon
|
||||
* @param chon
|
||||
*/
|
||||
const drawRidgeRoof = (polygon, chon) => {
|
||||
const walls = polygon.wall.lines // 외벽의 라인
|
||||
const roofs = polygon.lines // 지붕의 라인
|
||||
let ridgeWall = []
|
||||
walls.forEach((wall, index) => {
|
||||
let currentRoof, prevWall, currentWall, nextWall
|
||||
let ridgeRoof = []
|
||||
|
||||
if (index === 0) {
|
||||
prevWall = walls[walls.length - 1]
|
||||
} else {
|
||||
prevWall = walls[index - 1]
|
||||
}
|
||||
currentRoof = roofs[index]
|
||||
currentWall = walls[index]
|
||||
roofs.forEach((currentRoof, index) => {
|
||||
let prevRoof,
|
||||
nextRoof,
|
||||
currentWall = walls[index]
|
||||
|
||||
if (index === walls.length - 1) {
|
||||
nextWall = walls[0]
|
||||
} else if (index === walls.length) {
|
||||
nextWall = walls[1]
|
||||
} else {
|
||||
nextWall = walls[index + 1]
|
||||
}
|
||||
prevRoof = index === 0 ? walls[walls.length - 1] : walls[index - 1]
|
||||
nextRoof = index === walls.length - 1 ? walls[0] : index === walls.length ? walls[1] : walls[index + 1]
|
||||
|
||||
if (prevWall.direction !== nextWall.direction && currentWall.length < currentRoof.length) {
|
||||
ridgeWall.push({ index: index, wall: currentWall, length: currentWall.length })
|
||||
if (prevRoof.direction !== nextRoof.direction && currentWall.length <= currentRoof.length) {
|
||||
ridgeRoof.push({ index: index, roof: currentRoof, length: currentRoof.length })
|
||||
}
|
||||
})
|
||||
|
||||
// 지붕의 길이가 짧은 순으로 정렬
|
||||
ridgeWall.sort((a, b) => a.length - b.length)
|
||||
ridgeRoof.sort((a, b) => a.length - b.length)
|
||||
|
||||
ridgeWall.forEach((item) => {
|
||||
if (getMaxRidge(walls.length) > polygon.ridges.length) {
|
||||
ridgeRoof.forEach((item) => {
|
||||
if (getMaxRidge(roofs.length) > polygon.ridges.length) {
|
||||
let index = item.index,
|
||||
// currentRoof = roofs[index],
|
||||
beforePrevWall,
|
||||
prevWall,
|
||||
currentWall = item.wall,
|
||||
nextWall,
|
||||
afterNextWall
|
||||
beforePrevRoof,
|
||||
prevRoof,
|
||||
currentRoof = item.roof,
|
||||
nextRoof,
|
||||
afterNextRoof
|
||||
let startXPoint, startYPoint, endXPoint, endYPoint
|
||||
|
||||
if (index === 0) {
|
||||
prevWall = walls[walls.length - 1]
|
||||
} else {
|
||||
prevWall = walls[index - 1]
|
||||
}
|
||||
prevRoof = index === 0 ? roofs[walls.length - 1] : roofs[index - 1]
|
||||
nextRoof = index === roofs.length - 1 ? roofs[0] : index === roofs.length ? roofs[1] : roofs[index + 1]
|
||||
|
||||
if (index === 0) {
|
||||
beforePrevWall = walls[roofs.length - 2]
|
||||
} else if (index === 1) {
|
||||
beforePrevWall = walls[roofs.length - 1]
|
||||
} else {
|
||||
beforePrevWall = walls[index - 2]
|
||||
}
|
||||
beforePrevRoof = index <= 1 ? roofs[roofs.length - 2 + index] : roofs[index - 2]
|
||||
afterNextRoof = index >= roofs.length - 2 ? roofs[(index + 2) % roofs.length] : roofs[index + 2]
|
||||
|
||||
if (index === walls.length - 1) {
|
||||
nextWall = walls[0]
|
||||
} else if (index === walls.length) {
|
||||
nextWall = walls[1]
|
||||
} else {
|
||||
nextWall = walls[index + 1]
|
||||
}
|
||||
const anotherRoof = roofs.filter((roof) => roof !== currentRoof && roof !== prevRoof && roof !== nextRoof)
|
||||
|
||||
if (index === walls.length - 2) {
|
||||
afterNextWall = walls[0]
|
||||
} else if (index === walls.length - 1) {
|
||||
afterNextWall = walls[1]
|
||||
} else if (index === walls.length) {
|
||||
afterNextWall = walls[2]
|
||||
} else {
|
||||
afterNextWall = walls[index + 2]
|
||||
}
|
||||
let xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)), //x가 같은 내부선
|
||||
yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevRoof, currentRoof, nextRoof, roof)) //y가 같은 내부선
|
||||
|
||||
const anotherRoof = walls.filter((wall) => wall !== currentWall && wall !== prevWall && wall !== nextWall)
|
||||
let acrossRoof, xEqualInnerLines, yEqualInnerLines
|
||||
xEqualInnerLines = anotherRoof.filter((roof) => roof.x1 === roof.x2 && isInnerLine(prevWall, currentWall, nextWall, roof)) //x가 같은 내부선
|
||||
yEqualInnerLines = anotherRoof.filter((roof) => roof.y1 === roof.y2 && isInnerLine(prevWall, currentWall, nextWall, roof)) //y가 같은 내부선
|
||||
let ridgeBaseLength = currentRoof.length / 2, // 지붕의 기반 길이
|
||||
ridgeMaxLength = Math.min(prevRoof.length, nextRoof.length), // 지붕의 최대 길이. 이전, 다음 벽 중 짧은 길이
|
||||
ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - currentRoof.length // 맞은편 벽까지의 길이 - 지붕의 기반 길이
|
||||
|
||||
let ridgeBaseLength = currentWall.length / 2, // 지붕의 기반 길이
|
||||
ridgeMaxLength = Math.min(prevWall.length, nextWall.length), // 지붕의 최대 길이. 이전, 다음 벽 중 짧은 길이
|
||||
ridgeAcrossLength = Math.max(prevWall.length, nextWall.length) - currentWall.length // 맞은편 벽까지의 길이 - 지붕의 기반 길이
|
||||
|
||||
acrossRoof = anotherRoof
|
||||
let acrossRoof = anotherRoof
|
||||
.filter((roof) => {
|
||||
if (currentWall.direction === 'top' && roof.direction === 'bottom') {
|
||||
if (nextWall.direction === 'right' && roof.x1 > currentWall.x1) {
|
||||
return roof
|
||||
}
|
||||
if (nextWall.direction === 'left' && roof.x1 < currentWall.x1) {
|
||||
if (roof.x1 === roof.x2) {
|
||||
if ((nextRoof.direction === 'right' && roof.x1 > currentRoof.x1) || (nextRoof.direction === 'left' && roof.x1 < currentRoof.x1)) {
|
||||
return roof
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'right' && roof.direction === 'left') {
|
||||
if (nextWall.direction === 'top' && roof.y1 < currentWall.y1) {
|
||||
return roof
|
||||
}
|
||||
if (nextWall.direction === 'bottom' && roof.y1 > currentWall.y1) {
|
||||
if (roof.y1 === roof.y2) {
|
||||
if ((nextRoof.direction === 'top' && roof.y1 < currentRoof.y1) || (nextRoof.direction === 'bottom' && roof.y1 > currentRoof.y1)) {
|
||||
return roof
|
||||
}
|
||||
}
|
||||
})
|
||||
.reduce((prev, current) => {
|
||||
let hasBetweenWall = false
|
||||
if (current.direction === 'top' || current.direction === 'bottom') {
|
||||
hasBetweenWall = walls
|
||||
.filter((wall) => wall !== current && wall !== currentWall)
|
||||
let hasBetweenRoof = false
|
||||
if (current.x1 === current.x2) {
|
||||
hasBetweenRoof = roofs
|
||||
.filter((roof) => roof !== current && roof !== currentRoof)
|
||||
.some((line) => {
|
||||
let currentY2 = currentWall.y2
|
||||
let currentY2 = currentRoof.y2
|
||||
if (yEqualInnerLines.length > 0) {
|
||||
yEqualInnerLines.forEach((line) => {
|
||||
currentY2 = Math.abs(currentWall.y1 - currentY2) < Math.abs(currentWall.y1 - line.y1) ? currentY2 : line.y1
|
||||
currentY2 = Math.abs(currentRoof.y1 - currentY2) < Math.abs(currentRoof.y1 - line.y1) ? currentY2 : line.y1
|
||||
})
|
||||
}
|
||||
const isY1Between = (line.y1 > currentWall.y1 && line.y1 < currentY2) || (line.y1 > currentY2 && line.y1 < currentWall.y1)
|
||||
const isY2Between = (line.y2 > currentWall.y1 && line.y2 < currentY2) || (line.y2 > currentY2 && line.y2 < currentWall.y1)
|
||||
const isX1Between = (line.x1 > currentWall.x1 && line.x1 < current.x1) || (line.x1 > currentWall.x1 && line.x1 < current.x1)
|
||||
const isX2Between = (line.x2 > currentWall.x1 && line.x2 < current.x1) || (line.x2 > currentWall.x1 && line.x2 < current.x1)
|
||||
const isY1Between = (line.y1 > currentRoof.y1 && line.y1 < currentY2) || (line.y1 > currentY2 && line.y1 < currentRoof.y1)
|
||||
const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < currentY2) || (line.y2 > currentY2 && line.y2 < currentRoof.y1)
|
||||
const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < current.x1) || (line.x1 > currentRoof.x1 && line.x1 < current.x1)
|
||||
const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < current.x1) || (line.x2 > currentRoof.x1 && line.x2 < current.x1)
|
||||
return isY1Between && isY2Between && isX1Between && isX2Between
|
||||
})
|
||||
}
|
||||
if (current.direction === 'right' || current.direction === 'left') {
|
||||
hasBetweenWall = walls
|
||||
.filter((wall) => wall !== current && wall !== currentWall)
|
||||
if (current.y1 === current.y2) {
|
||||
hasBetweenRoof = walls
|
||||
.filter((roof) => roof !== current && roof !== currentRoof)
|
||||
.some((line) => {
|
||||
let currentX2 = currentWall.x2
|
||||
let currentX2 = currentRoof.x2
|
||||
if (xEqualInnerLines.length > 0) {
|
||||
xEqualInnerLines.forEach((line) => {
|
||||
currentX2 = Math.abs(currentWall.x1 - currentX2) < Math.abs(currentWall.x1 - line.x1) ? currentX2 : line.x1
|
||||
currentX2 = Math.abs(currentRoof.x1 - currentX2) < Math.abs(currentRoof.x1 - line.x1) ? currentX2 : line.x1
|
||||
})
|
||||
}
|
||||
const isX1Between = (line.x1 > currentWall.x1 && line.x1 < currentX2) || (line.x1 > currentX2 && line.x1 < currentWall.x1)
|
||||
const isX2Between = (line.x2 > currentWall.x1 && line.x2 < currentX2) || (line.x2 > currentX2 && line.x2 < currentWall.x1)
|
||||
const isY1Between = (line.y1 > currentWall.y1 && line.y1 < current.y1) || (line.y1 > currentWall.y1 && line.y1 < current.y1)
|
||||
const isY2Between = (line.y2 > currentWall.y1 && line.y2 < current.y1) || (line.y2 > currentWall.y1 && line.y2 < current.y1)
|
||||
const isX1Between = (line.x1 > currentRoof.x1 && line.x1 < currentX2) || (line.x1 > currentX2 && line.x1 < currentRoof.x1)
|
||||
const isX2Between = (line.x2 > currentRoof.x1 && line.x2 < currentX2) || (line.x2 > currentX2 && line.x2 < currentRoof.x1)
|
||||
const isY1Between = (line.y1 > currentRoof.y1 && line.y1 < current.y1) || (line.y1 > currentRoof.y1 && line.y1 < current.y1)
|
||||
const isY2Between = (line.y2 > currentRoof.y1 && line.y2 < current.y1) || (line.y2 > currentRoof.y1 && line.y2 < current.y1)
|
||||
|
||||
return isX1Between && isX2Between && isY1Between && isY2Between
|
||||
})
|
||||
}
|
||||
|
||||
if (prev !== undefined) {
|
||||
if (currentWall.direction === 'top' || currentWall.direction === 'bottom') {
|
||||
return Math.abs(currentWall.y1 - prev.y1) > Math.abs(currentWall.y1 - current.y1) ? prev : current
|
||||
if (currentRoof.x1 === currentRoof.x2) {
|
||||
return Math.abs(currentRoof.y1 - prev.y1) > Math.abs(currentRoof.y1 - current.y1) ? prev : current
|
||||
}
|
||||
if (currentWall.direction === 'right' || currentWall.direction === 'left') {
|
||||
return Math.abs(currentWall.x1 - prev.x1) > Math.abs(currentWall.x1 - current.x1) ? prev : current
|
||||
if (currentRoof.y1 === currentRoof.y2) {
|
||||
return Math.abs(currentRoof.x1 - prev.x1) > Math.abs(currentRoof.x1 - current.x1) ? prev : current
|
||||
}
|
||||
} else {
|
||||
if (!hasBetweenWall) {
|
||||
return current
|
||||
if (!hasBetweenRoof) {
|
||||
if (currentRoof.x1 === currentRoof.x2) {
|
||||
return Math.sign(currentRoof.y1 - currentRoof.y2) !== Math.sign(current.y1 - current.y2) ? current : undefined
|
||||
}
|
||||
if (currentRoof.y1 === currentRoof.y2) {
|
||||
return Math.sign(currentRoof.x1 - currentRoof.x2) !== Math.sign(current.x1 - current.x2) ? current : undefined
|
||||
}
|
||||
return undefined
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}, undefined)
|
||||
|
||||
if (acrossRoof !== undefined) {
|
||||
if (currentWall.direction === 'top' || currentWall.direction === 'bottom') {
|
||||
if (ridgeAcrossLength < Math.abs(currentWall.x1 - acrossRoof.x1)) {
|
||||
ridgeAcrossLength = Math.abs(currentWall.x1 - acrossRoof.x1) - currentWall.length
|
||||
if (currentRoof.x1 === currentRoof.x2) {
|
||||
if (ridgeAcrossLength < Math.abs(currentRoof.x1 - acrossRoof.x1)) {
|
||||
ridgeAcrossLength = Math.abs(currentRoof.x1 - acrossRoof.x1) - currentRoof.length
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'right' || currentWall.direction === 'left') {
|
||||
if (ridgeAcrossLength < Math.abs(currentWall.y1 - acrossRoof.y1)) {
|
||||
ridgeAcrossLength = Math.abs(currentWall.y1 - acrossRoof.y1) - currentWall.length
|
||||
if (currentRoof.y1 === currentRoof.y2) {
|
||||
if (ridgeAcrossLength < Math.abs(currentRoof.y1 - acrossRoof.y1)) {
|
||||
ridgeAcrossLength = Math.abs(currentRoof.y1 - acrossRoof.y1) - currentRoof.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ridgeBaseLength > 0 && ridgeMaxLength > 0 && ridgeAcrossLength > 0) {
|
||||
let ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
||||
if (currentWall.direction === 'top' || currentWall.direction === 'bottom') {
|
||||
startXPoint = currentWall.x1 + (nextWall.direction === 'right' ? 1 : -1) * ridgeBaseLength
|
||||
startYPoint = currentWall.y1 + (currentWall.direction === 'top' ? -1 : 1) * ridgeBaseLength
|
||||
endXPoint = startXPoint + (nextWall.direction === 'right' ? 1 : -1) * ridgeLength
|
||||
if (currentRoof.x1 === currentRoof.x2) {
|
||||
startXPoint = currentRoof.x1 + (nextRoof.direction === 'right' ? 1 : -1) * ridgeBaseLength
|
||||
startYPoint = currentRoof.y1 + (currentRoof.direction === 'top' ? -1 : 1) * ridgeBaseLength
|
||||
endXPoint = startXPoint + (nextRoof.direction === 'right' ? 1 : -1) * ridgeLength
|
||||
endYPoint = startYPoint
|
||||
let adjustY
|
||||
if (currentWall.direction === 'top') {
|
||||
if (afterNextWall.direction === 'bottom' && beforePrevWall.direction === 'bottom') {
|
||||
if (currentRoof.direction === 'top') {
|
||||
if (afterNextRoof.direction === 'bottom' && beforePrevRoof.direction === 'bottom') {
|
||||
adjustY =
|
||||
Math.abs(currentWall.x1 - afterNextWall.x1) < Math.abs(currentWall.x1 - beforePrevWall.x1) ? afterNextWall.y2 : beforePrevWall.y1
|
||||
} else if (afterNextWall.direction === 'bottom' && afterNextWall.y2 > currentWall.y2 && afterNextWall.y2 < currentWall.y1) {
|
||||
adjustY = afterNextWall.y2
|
||||
} else if (beforePrevWall.direction === 'bottom' && beforePrevWall.y1 > currentWall.y2 && beforePrevWall.y1 < currentWall.y1) {
|
||||
adjustY = beforePrevWall.y1
|
||||
Math.abs(currentRoof.x1 - afterNextRoof.x1) < Math.abs(currentRoof.x1 - beforePrevRoof.x1) ? afterNextRoof.y2 : beforePrevRoof.y1
|
||||
} else if (afterNextRoof.direction === 'bottom' && afterNextRoof.y2 > currentRoof.y2 && afterNextRoof.y2 < currentRoof.y1) {
|
||||
adjustY = afterNextRoof.y2
|
||||
} else if (beforePrevRoof.direction === 'bottom' && beforePrevRoof.y1 > currentRoof.y2 && beforePrevRoof.y1 < currentRoof.y1) {
|
||||
adjustY = beforePrevRoof.y1
|
||||
}
|
||||
if (adjustY) {
|
||||
startYPoint = currentWall.y1 - Math.abs(currentWall.y1 - adjustY) / 2
|
||||
startYPoint = currentRoof.y1 - Math.abs(currentRoof.y1 - adjustY) / 2
|
||||
endYPoint = startYPoint
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'bottom') {
|
||||
if (afterNextWall.direction === 'top' && beforePrevWall.direction === 'top') {
|
||||
if (currentRoof.direction === 'bottom') {
|
||||
if (afterNextRoof.direction === 'top' && beforePrevRoof.direction === 'top') {
|
||||
adjustY =
|
||||
Math.abs(currentWall.x1 - afterNextWall.x1) < Math.abs(currentWall.x1 - beforePrevWall.x1) ? afterNextWall.y2 : beforePrevWall.y1
|
||||
} else if (afterNextWall.direction === 'top' && afterNextWall.y2 < currentWall.y2 && afterNextWall.y2 > currentWall.y1) {
|
||||
adjustY = afterNextWall.y2
|
||||
} else if (beforePrevWall.direction === 'top' && beforePrevWall.y1 < currentWall.y2 && beforePrevWall.y1 > currentWall.y1) {
|
||||
adjustY = beforePrevWall.y1
|
||||
Math.abs(currentRoof.x1 - afterNextRoof.x1) < Math.abs(currentRoof.x1 - beforePrevRoof.x1) ? afterNextRoof.y2 : beforePrevRoof.y1
|
||||
} else if (afterNextRoof.direction === 'top' && afterNextRoof.y2 < currentRoof.y2 && afterNextRoof.y2 > currentRoof.y1) {
|
||||
adjustY = afterNextRoof.y2
|
||||
} else if (beforePrevRoof.direction === 'top' && beforePrevRoof.y1 < currentRoof.y2 && beforePrevRoof.y1 > currentRoof.y1) {
|
||||
adjustY = beforePrevRoof.y1
|
||||
}
|
||||
if (adjustY) {
|
||||
startYPoint = currentWall.y1 + Math.abs(currentWall.y1 - adjustY) / 2
|
||||
startYPoint = currentRoof.y1 + Math.abs(currentRoof.y1 - adjustY) / 2
|
||||
endYPoint = startYPoint
|
||||
}
|
||||
}
|
||||
if (yEqualInnerLines.length > 0) {
|
||||
yEqualInnerLines.reduce((prev, current) => {
|
||||
if (prev !== undefined) {
|
||||
return Math.abs(currentWall.y1 - prev.y1) < Math.abs(currentWall.y1 - current.y1) ? prev : current
|
||||
return Math.abs(currentRoof.y1 - prev.y1) < Math.abs(currentRoof.y1 - current.y1) ? prev : current
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
}, undefined)
|
||||
startYPoint =
|
||||
Math.abs(currentWall.y1 - startYPoint) * 2 <= Math.abs(currentWall.y1 - yEqualInnerLines[0].y1)
|
||||
Math.abs(currentRoof.y1 - startYPoint) * 2 <= Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1)
|
||||
? startYPoint
|
||||
: Math.abs(currentWall.y1 - yEqualInnerLines[0].y1)
|
||||
: Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1)
|
||||
endYPoint = startYPoint
|
||||
ridgeAcrossLength = Math.max(prevWall.length, nextWall.length) - Math.abs(currentWall.y1 - startYPoint) * 2
|
||||
ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - Math.abs(currentRoof.y1 - startYPoint) * 2
|
||||
if (
|
||||
//yEqualInnerLines 이 다음 벽보다 안쪽에 있을때
|
||||
Math.abs(currentWall.y1 - yEqualInnerLines[0].y1) <= Math.abs(currentWall.y1 - nextWall.y1) &&
|
||||
Math.abs(currentWall.x1 - yEqualInnerLines[0].x2) >= Math.abs(currentWall.x1 - nextWall.x2)
|
||||
Math.abs(currentRoof.y1 - yEqualInnerLines[0].y1) <= Math.abs(currentRoof.y1 - nextRoof.y1) &&
|
||||
Math.abs(currentRoof.x1 - yEqualInnerLines[0].x2) >= Math.abs(currentRoof.x1 - nextRoof.x2)
|
||||
) {
|
||||
ridgeMaxLength = Math.abs(currentWall.x1 - yEqualInnerLines[0].x2)
|
||||
ridgeMaxLength = Math.abs(currentRoof.x1 - yEqualInnerLines[0].x2)
|
||||
}
|
||||
ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
||||
startXPoint = currentWall.x1 + (nextWall.direction === 'right' ? 1 : -1) * Math.abs(currentWall.y1 - startYPoint)
|
||||
endXPoint = startXPoint + (nextWall.direction === 'right' ? 1 : -1) * ridgeLength
|
||||
startXPoint = currentRoof.x1 + (nextRoof.direction === 'right' ? 1 : -1) * Math.abs(currentRoof.y1 - startYPoint)
|
||||
endXPoint = startXPoint + (nextRoof.direction === 'right' ? 1 : -1) * ridgeLength
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'left' || currentWall.direction === 'right') {
|
||||
startXPoint = currentWall.x1 + (currentWall.direction === 'left' ? -1 : 1) * ridgeBaseLength
|
||||
startYPoint = currentWall.y1 + (nextWall.direction === 'bottom' ? 1 : -1) * ridgeBaseLength
|
||||
if (currentRoof.y1 === currentRoof.y2) {
|
||||
startXPoint = currentRoof.x1 + (currentRoof.direction === 'left' ? -1 : 1) * ridgeBaseLength
|
||||
startYPoint = currentRoof.y1 + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeBaseLength
|
||||
endXPoint = startXPoint
|
||||
endYPoint = startYPoint + (nextWall.direction === 'bottom' ? 1 : -1) * ridgeLength
|
||||
endYPoint = startYPoint + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeLength
|
||||
|
||||
let adjustX
|
||||
if (currentWall.direction === 'right') {
|
||||
if (afterNextWall.direction === 'left' && beforePrevWall.direction === 'left') {
|
||||
if (currentRoof.direction === 'right') {
|
||||
if (afterNextRoof.direction === 'left' && beforePrevRoof.direction === 'left') {
|
||||
adjustX =
|
||||
Math.abs(currentWall.y1 - afterNextWall.y1) < Math.abs(currentWall.y1 - beforePrevWall.y1) ? afterNextWall.x2 : beforePrevWall.x1
|
||||
} else if (afterNextWall.direction === 'left' && afterNextWall.x2 < currentWall.x2 && afterNextWall.x2 > currentWall.x1) {
|
||||
adjustX = afterNextWall.x2
|
||||
} else if (beforePrevWall.direction === 'left' && beforePrevWall.x1 < currentWall.x2 && beforePrevWall.x1 > currentWall.x1) {
|
||||
adjustX = beforePrevWall.x1
|
||||
Math.abs(currentRoof.y1 - afterNextRoof.y1) < Math.abs(currentRoof.y1 - beforePrevRoof.y1) ? afterNextRoof.x2 : beforePrevRoof.x1
|
||||
} else if (afterNextRoof.direction === 'left' && afterNextRoof.x2 < currentRoof.x2 && afterNextRoof.x2 > currentRoof.x1) {
|
||||
adjustX = afterNextRoof.x2
|
||||
} else if (beforePrevRoof.direction === 'left' && beforePrevRoof.x1 < currentRoof.x2 && beforePrevRoof.x1 > currentRoof.x1) {
|
||||
adjustX = beforePrevRoof.x1
|
||||
}
|
||||
if (adjustX) {
|
||||
startXPoint = currentWall.x1 + Math.abs(currentWall.x1 - adjustX) / 2
|
||||
startXPoint = currentRoof.x1 + Math.abs(currentRoof.x1 - adjustX) / 2
|
||||
endXPoint = startXPoint
|
||||
}
|
||||
}
|
||||
if (currentWall.direction === 'left') {
|
||||
if (afterNextWall.direction === 'right' && beforePrevWall.direction === 'right') {
|
||||
if (currentRoof.direction === 'left') {
|
||||
if (afterNextRoof.direction === 'right' && beforePrevRoof.direction === 'right') {
|
||||
adjustX =
|
||||
Math.abs(currentWall.y1 - afterNextWall.y1) < Math.abs(currentWall.y1 - beforePrevWall.y1) ? afterNextWall.x2 : beforePrevWall.x1
|
||||
} else if (afterNextWall.direction === 'right' && afterNextWall.x2 > currentWall.x2 && afterNextWall.x2 < currentWall.x1) {
|
||||
adjustX = afterNextWall.x2
|
||||
} else if (beforePrevWall.direction === 'right' && beforePrevWall.x1 > currentWall.x2 && beforePrevWall.x1 < currentWall.x1) {
|
||||
adjustX = beforePrevWall.x1
|
||||
Math.abs(currentRoof.y1 - afterNextRoof.y1) < Math.abs(currentRoof.y1 - beforePrevRoof.y1) ? afterNextRoof.x2 : beforePrevRoof.x1
|
||||
} else if (afterNextRoof.direction === 'right' && afterNextRoof.x2 > currentRoof.x2 && afterNextRoof.x2 < currentRoof.x1) {
|
||||
adjustX = afterNextRoof.x2
|
||||
} else if (beforePrevRoof.direction === 'right' && beforePrevRoof.x1 > currentRoof.x2 && beforePrevRoof.x1 < currentRoof.x1) {
|
||||
adjustX = beforePrevRoof.x1
|
||||
}
|
||||
if (adjustX) {
|
||||
startXPoint = currentWall.x1 - Math.abs(currentWall.x1 - adjustX) / 2
|
||||
startXPoint = currentRoof.x1 - Math.abs(currentRoof.x1 - adjustX) / 2
|
||||
endXPoint = startXPoint
|
||||
}
|
||||
}
|
||||
if (xEqualInnerLines.length > 0) {
|
||||
xEqualInnerLines.reduce((prev, current) => {
|
||||
if (prev !== undefined) {
|
||||
return Math.abs(currentWall.x1 - prev.x1) < Math.abs(currentWall.x1 - current.x1) ? prev : current
|
||||
return Math.abs(currentRoof.x1 - prev.x1) < Math.abs(currentRoof.x1 - current.x1) ? prev : current
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
}, undefined)
|
||||
startXPoint =
|
||||
Math.abs(currentWall.x1 - startXPoint) * 2 <= Math.abs(currentWall.x1 - xEqualInnerLines[0].x1)
|
||||
Math.abs(currentRoof.x1 - startXPoint) * 2 <= Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1)
|
||||
? startXPoint
|
||||
: Math.abs(currentWall.x1 - xEqualInnerLines[0].x1)
|
||||
: Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1)
|
||||
endXPoint = startXPoint
|
||||
ridgeAcrossLength = Math.max(prevWall.length, nextWall.length) - Math.abs(currentWall.x1 - startXPoint) * 2
|
||||
ridgeAcrossLength = Math.max(prevRoof.length, nextRoof.length) - Math.abs(currentRoof.x1 - startXPoint) * 2
|
||||
if (
|
||||
//xEqualInnerLines 이 다음 벽보다 안쪽에 있을때
|
||||
Math.abs(currentWall.x1 - xEqualInnerLines[0].x1) <= Math.abs(currentWall.x1 - nextWall.x1) &&
|
||||
Math.abs(currentWall.y1 - xEqualInnerLines[0].y2) >= Math.abs(currentWall.y1 - nextWall.y2)
|
||||
Math.abs(currentRoof.x1 - xEqualInnerLines[0].x1) <= Math.abs(currentRoof.x1 - nextRoof.x1) &&
|
||||
Math.abs(currentRoof.y1 - xEqualInnerLines[0].y2) >= Math.abs(currentRoof.y1 - nextRoof.y2)
|
||||
) {
|
||||
ridgeMaxLength = Math.abs(currentWall.y1 - xEqualInnerLines[0].y2)
|
||||
ridgeMaxLength = Math.abs(currentRoof.y1 - xEqualInnerLines[0].y2)
|
||||
}
|
||||
ridgeLength = Math.min(ridgeMaxLength, ridgeAcrossLength)
|
||||
startYPoint = currentWall.y1 + (nextWall.direction === 'bottom' ? 1 : -1) * Math.abs(currentWall.x1 - startXPoint)
|
||||
endYPoint = startYPoint + (nextWall.direction === 'bottom' ? 1 : -1) * ridgeLength
|
||||
startYPoint = currentRoof.y1 + (nextRoof.direction === 'bottom' ? 1 : -1) * Math.abs(currentRoof.x1 - startXPoint)
|
||||
endYPoint = startYPoint + (nextRoof.direction === 'bottom' ? 1 : -1) * ridgeLength
|
||||
}
|
||||
}
|
||||
}
|
||||
const currentWall = walls[index]
|
||||
if (currentWall.attributes.type === 'gable') {
|
||||
if (currentRoof.x1 === currentRoof.x2) {
|
||||
startXPoint = currentRoof.x1
|
||||
}
|
||||
if (currentRoof.y1 === currentRoof.y2) {
|
||||
startYPoint = currentRoof.y1
|
||||
}
|
||||
}
|
||||
|
||||
// 마루 그리기
|
||||
if (!(startXPoint === undefined && startYPoint === undefined && endXPoint === undefined && endYPoint === undefined)) {
|
||||
if (startXPoint !== undefined && startYPoint !== undefined && endXPoint !== undefined && endYPoint !== undefined) {
|
||||
const ridge = new QLine(
|
||||
[Math.min(startXPoint, endXPoint), Math.min(startYPoint, endYPoint), Math.max(startXPoint, endXPoint), Math.max(startYPoint, endYPoint)],
|
||||
{
|
||||
@ -1762,7 +1735,6 @@ const drawHips = (polygon) => {
|
||||
}
|
||||
}
|
||||
if (ridge.x1 === ridge.x2) {
|
||||
console.log('세로방향 마루')
|
||||
//위쪽 좌표 기준 45, 315도 방향 라인확인
|
||||
leftTop = polygon.lines
|
||||
.filter((line) => line.x1 < ridge.x1 && line.y1 < ridge.y1 && Math.abs(line.x1 - ridge.x1) === Math.abs(line.y1 - ridge.y1))
|
||||
@ -2229,8 +2201,9 @@ const drawHips = (polygon) => {
|
||||
})
|
||||
|
||||
// 마루와 연결되지 않은 hip을 그린다.
|
||||
polygon.lines.forEach((line, index) => {
|
||||
/*polygon.lines.forEach((line, index) => {
|
||||
if (!isAlreadyHip(polygon, line)) {
|
||||
console.log(' 확인 : ', line)
|
||||
let prevLine, currentLine, nextLine
|
||||
if (index === 0) {
|
||||
prevLine = polygon.lines[polygon.lines.length - 1]
|
||||
@ -2264,6 +2237,7 @@ const drawHips = (polygon) => {
|
||||
|
||||
let acrossLine = getAcrossLine(polygon, currentLine, dVector)
|
||||
let hypotenuse, adjacent
|
||||
console.log(acrossLine)
|
||||
|
||||
if (getLineDirection(prevLine) === getLineDirection(nextLine)) {
|
||||
hypotenuse = Math.round(getRoofHypotenuse(Math.abs(currentLine.x1 - acrossLine.x1) / 2))
|
||||
@ -2304,8 +2278,7 @@ const drawHips = (polygon) => {
|
||||
polygon.hips.push(hip)
|
||||
polygon.innerLines.push(hip)
|
||||
}
|
||||
})
|
||||
console.log('polygon.hips : ', polygon.hips)
|
||||
})*/
|
||||
}
|
||||
|
||||
const getPointInPolygon = (polygon, point, isInclude = false) => {
|
||||
@ -2332,7 +2305,7 @@ const getPointInPolygon = (polygon, point, isInclude = false) => {
|
||||
*/
|
||||
const getAcrossLine = (polygon, currentLine, dVector) => {
|
||||
let acrossLine
|
||||
|
||||
console.log('dVector : ', dVector)
|
||||
switch (dVector) {
|
||||
case 45:
|
||||
acrossLine = polygon.lines
|
||||
@ -2404,6 +2377,24 @@ const connectLinePoint = (polygon) => {
|
||||
let missedPoints = []
|
||||
//마루
|
||||
polygon.ridges.forEach((ridge) => {
|
||||
if (ridge.x1 === ridge.x2) {
|
||||
if (
|
||||
polygon.lines
|
||||
.filter((roof) => roof.y1 === roof.y2)
|
||||
.filter((roof) => roof.y1 === ridge.y1 || roof.y1 === ridge.y2 || roof.y2 === ridge.y1 || roof.y2 === ridge.y2).length > 0
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (ridge.y1 === ridge.y2) {
|
||||
if (
|
||||
polygon.lines
|
||||
.filter((roof) => roof.x1 === roof.x2)
|
||||
.filter((roof) => roof.x1 === ridge.x1 || roof.x1 === ridge.x2 || roof.x2 === ridge.x1 || roof.x2 === ridge.x2).length > 0
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (polygon.hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1).length < 2) {
|
||||
missedPoints.push({ x: ridge.x1, y: ridge.y1 })
|
||||
}
|
||||
@ -2412,10 +2403,6 @@ const connectLinePoint = (polygon) => {
|
||||
}
|
||||
})
|
||||
|
||||
console.log('polygon.ridges : ', polygon.ridges)
|
||||
|
||||
console.log('missedPoints : ', missedPoints)
|
||||
|
||||
//추녀마루
|
||||
polygon.hips.forEach((hip) => {
|
||||
let count = 0
|
||||
@ -2428,7 +2415,6 @@ const connectLinePoint = (polygon) => {
|
||||
})
|
||||
|
||||
let missedLine = []
|
||||
console.log('missedPoints : ', missedPoints)
|
||||
|
||||
//중복포인트제거
|
||||
missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
|
||||
@ -2490,14 +2476,10 @@ const connectLinePoint = (polygon) => {
|
||||
|
||||
missedPoints = [...new Set(missedPoints.map((line) => JSON.stringify(line)))].map((line) => JSON.parse(line))
|
||||
|
||||
console.log(missedPoints)
|
||||
|
||||
missedPoints.forEach((p1) => {
|
||||
let p2 = missedPoints
|
||||
.filter((p) => !(p.x === p1.x && p.y === p1.y))
|
||||
.reduce((prev, current) => {
|
||||
console.log('current : ', current)
|
||||
console.log('prev : ', prev)
|
||||
if (prev !== undefined) {
|
||||
return Math.abs(current.x - p1.x) + Math.abs(current.y - p1.y) < Math.abs(prev.x - p1.x) + Math.abs(prev.y - p1.y) ? current : prev
|
||||
} else {
|
||||
@ -2506,7 +2488,6 @@ const connectLinePoint = (polygon) => {
|
||||
}, undefined)
|
||||
|
||||
if (p2 !== undefined) {
|
||||
console.log(p1.x, p2.x, p1.y, p2.y)
|
||||
if (p1.x === p2.x && p1.y < p2.y) {
|
||||
missedLine.push({ x1: p1.x, y1: p1.y, x2: p2.x, y2: p2.y })
|
||||
}
|
||||
@ -2584,7 +2565,6 @@ const connectLinePoint = (polygon) => {
|
||||
strokeWidth: 1,
|
||||
name: 'ridgeLine',
|
||||
})
|
||||
console.log('newRidge : ', newRidge)
|
||||
if (polygon.ridges.filter((r) => newRidge.x1 === r.x1 && newRidge.y1 === r.y1 && newRidge.x2 === r.x2 && newRidge.y2 === r.y2).length === 0) {
|
||||
polygon.canvas.remove(ridge)
|
||||
polygon.canvas.remove(ridge2)
|
||||
@ -2679,12 +2659,14 @@ const getLineDirection = (line) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const changeAllGableRoof = (polygon, offset, canvas) => {
|
||||
export const changeAllHipAndGableRoof = (polygon, offset, canvas) => {
|
||||
const roof = polygon.filter((p) => p.name === 'roofBase')[0] // 지붕
|
||||
const roofLines = roof.lines // 지붕의 라인
|
||||
const ridges = roof.ridges // 마루의 라인
|
||||
const hips = roof.hips // 추녀마루의 라인
|
||||
|
||||
console.log('roofLines : ', roofLines)
|
||||
|
||||
ridges.forEach((ridge) => {
|
||||
let ridgeHip1 = hips.filter((hip) => hip.x2 === ridge.x1 && hip.y2 === ridge.y1)
|
||||
let ridgeHip2 = hips.filter((hip) => hip.x2 === ridge.x2 && hip.y2 === ridge.y2)
|
||||
@ -2699,7 +2681,7 @@ export const changeAllGableRoof = (polygon, offset, canvas) => {
|
||||
(roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) ||
|
||||
(roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1)
|
||||
) {
|
||||
gableLines.push(setGableRoof(polygon, ridge, ridgeHip1[0], ridgeHip1[1], offset, canvas))
|
||||
gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip1[0], ridgeHip1[1], offset, canvas))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -2713,7 +2695,7 @@ export const changeAllGableRoof = (polygon, offset, canvas) => {
|
||||
(roofLine.x1 === x1 && roofLine.y1 === y1 && roofLine.x2 === x2 && roofLine.y2 === y2) ||
|
||||
(roofLine.x1 === x2 && roofLine.y1 === y2 && roofLine.x2 === x1 && roofLine.y2 === y1)
|
||||
) {
|
||||
gableLines.push(setGableRoof(polygon, ridge, ridgeHip2[0], ridgeHip2[1], offset, canvas))
|
||||
gableLines.push(setHipAndGableRoof(roof, ridge, ridgeHip2[0], ridgeHip2[1], offset, canvas))
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -2725,7 +2707,17 @@ export const changeAllGableRoof = (polygon, offset, canvas) => {
|
||||
// splitPolygonWithLines(roof)
|
||||
}
|
||||
|
||||
const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
/**
|
||||
* 모임지붕 -> 팔작지붕 변경
|
||||
* @param roof
|
||||
* @param ridge
|
||||
* @param hip1
|
||||
* @param hip2
|
||||
* @param offset
|
||||
* @param canvas
|
||||
* @returns {*}
|
||||
*/
|
||||
const setHipAndGableRoof = (roof, ridge, hip1, hip2, offset, canvas) => {
|
||||
let x1 = hip1.x1,
|
||||
y1 = hip1.y1
|
||||
let gableLine, diffOffset
|
||||
@ -2741,7 +2733,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
})
|
||||
|
||||
gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
@ -2772,7 +2764,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
y2: ridge.y2,
|
||||
})
|
||||
gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
@ -2805,7 +2797,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
y2: ridge.y2,
|
||||
})
|
||||
gableLine = new QLine([ridge.x1 - offset, ridge.y1, ridge.x1 + offset, ridge.y1], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
@ -2837,7 +2829,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
})
|
||||
|
||||
gableLine = new QLine([ridge.x2 - offset, ridge.y2, ridge.x2 + offset, ridge.y2], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
@ -2870,7 +2862,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
y2: ridge.y2,
|
||||
})
|
||||
gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
@ -2901,7 +2893,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
y2: ridge.y2,
|
||||
})
|
||||
gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
@ -2934,7 +2926,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
y2: ridge.y2,
|
||||
})
|
||||
gableLine = new QLine([ridge.x2, ridge.y2 - offset, ridge.x2, ridge.y2 + offset], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
@ -2965,7 +2957,7 @@ const setGableRoof = (polygon, ridge, hip1, hip2, offset, canvas) => {
|
||||
y2: ridge.y2,
|
||||
})
|
||||
gableLine = new QLine([ridge.x1, ridge.y1 - offset, ridge.x1, ridge.y1 + offset], {
|
||||
fontSize: polygon.fontSize,
|
||||
fontSize: roof.fontSize,
|
||||
stroke: 'blue',
|
||||
strokeWidth: 1,
|
||||
name: 'gableLine',
|
||||
|
||||