# Conflicts:
#	src/components/floor-plan/modal/grid/DotLineGrid.jsx
#	src/components/floor-plan/modal/setting01/GridOption.jsx
This commit is contained in:
changkyu choi 2024-10-04 10:52:58 +09:00
commit 96a9b460dc
86 changed files with 4819 additions and 1543 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View File

@ -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>
</>
)

View File

@ -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>
</>
)

View File

@ -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>
</>
)

View File

@ -53,8 +53,9 @@ export const Mode = {
export const LINE_TYPE = {
WALLLINE: {
/**
* 처마 / 캐라바 / / 팔작지붕 / 반절처 / 한쪽흐름
* 없음 / 처마 / 캐라바 / / 팔작지붕 / 반절처 / 한쪽흐름
*/
DEFAULT: 'default',
EAVES: 'eaves',
GABLE: 'gable',
WALL: 'wall',

View File

@ -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,

View File

@ -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 {

View File

@ -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>
)}

View File

@ -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() {

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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>
</>

View File

@ -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)
}
}

View File

@ -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>

View 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>
)
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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">

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>
)
}

View File

@ -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>

View File

@ -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>

View File

@ -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>
)
}

View File

@ -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>
)
}

View 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>
)
}

View File

@ -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>
)
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View File

@ -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>
</>
)
}

View File

@ -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>
</>
)
}

View File

@ -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>
</>
)
}

View File

@ -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>
</>
)
}

View File

@ -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>
</>
)
}

View File

@ -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>
</>
)
}

View File

@ -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)
}
}

View File

@ -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>

View File

@ -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>
{/* 퍼블종료 */}
</>
)
}

View File

@ -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">IIIIV</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() {
</>
)}
</>
)}
)} */}
</>
)
}

View 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>
)
}

View File

@ -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> */}
</>
)
}

View File

@ -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,

View 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 }
}

View File

@ -26,7 +26,7 @@ export function useAdsorptionPoint() {
top: pointer.y - 3,
x: pointer.x,
y: pointer.y,
selectable: false,
selectable: true,
name: 'adsorptionPoint',
})

View File

@ -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 }
}

View File

@ -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'

View File

@ -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) // 가로 세로 간격

View File

@ -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

View File

@ -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) => {

View File

@ -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)

View File

@ -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,
}
}

View File

@ -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),

View File

@ -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": "漂流の出幅"
}

View File

@ -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": "편류의 출폭"
}

View File

@ -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
},
})

View File

@ -2,5 +2,5 @@ import { atom } from 'recoil'
export const gridColorState = atom({
key: 'gridColorState',
default: '#000000',
default: '#FF0000',
})

View File

@ -63,3 +63,8 @@ export const outerLinePointsState = atom({
key: 'outerLinePointsState',
default: [],
})
export const outerLineFixState = atom({
key: 'outerLineFixState',
default: false,
})

View File

@ -1,4 +1,4 @@
import { atom } from 'recoil'
import { atom, selector } from 'recoil'
export const settingModalFirstOptionsState = atom({
key: 'settingModalFirstOptions',

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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',