Merge remote-tracking branch 'origin/main-demo' into decal-list
This commit is contained in:
BIN
app/src/assets/image/fallback/fallback decal 1.png
Normal file
BIN
app/src/assets/image/fallback/fallback decal 1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
app/src/assets/image/fallback/fallback decal.png
Normal file
BIN
app/src/assets/image/fallback/fallback decal.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
353
app/src/components/icons/AssetTypeIcons.tsx
Normal file
353
app/src/components/icons/AssetTypeIcons.tsx
Normal file
@@ -0,0 +1,353 @@
|
||||
export const ForkLiftIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_6014_655)">
|
||||
<g filter="url(#filter0_f_6014_655)">
|
||||
<path
|
||||
d="M4.97826 12.4473C4.1581 12.4473 3.49243 13.112 3.49243 13.9331C3.49243 14.753 4.15813 15.4177 4.97826 15.4177C5.79818 15.4177 6.46284 14.753 6.46284 13.9331C6.46284 13.1119 5.79815 12.4473 4.97826 12.4473ZM4.97826 14.4544C4.69055 14.4544 4.45567 14.2208 4.45567 13.9331C4.45567 13.6454 4.69052 13.4105 4.97826 13.4105C5.26573 13.4105 5.49957 13.6454 5.49957 13.9331C5.49957 14.2208 5.26573 14.4544 4.97826 14.4544Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M11.3257 5.81717C11.1156 5.08596 10.4467 4.58203 9.6857 4.58203H5.20523C4.84291 4.58203 4.5491 4.87583 4.5491 5.23816V9.41249H4.07715C3.26935 9.41249 2.6145 10.0673 2.6145 10.8751V13.4258H2.88458C3.11388 12.4815 3.96429 11.7774 4.9782 11.7774C5.99161 11.7774 6.84123 12.4815 7.07052 13.4258H8.05798C8.13812 12.1123 9.22862 11.0679 10.5618 11.0679C11.2884 11.0679 11.9421 11.3804 12.4009 11.876V11.7487V10.3279C12.4009 9.8179 12.3293 9.31021 12.1885 8.82012L11.3257 5.81717ZM10.1793 8.21563L9.49446 8.6286L10.5313 10.6025H8.67763L7.81739 9.41249H6.07579V5.66572H9.6857C9.96187 5.66572 10.208 5.85115 10.2844 6.1165L11.1471 9.11919C11.2058 9.32329 11.2487 9.53116 11.2769 9.74131L10.1793 8.21563Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M10.5616 11.7383C9.54538 11.7383 8.7207 12.5617 8.7207 13.5792C8.7207 14.5948 9.54538 15.4183 10.5616 15.4183C11.5772 15.4183 12.4007 14.5948 12.4007 13.5792C12.4007 12.5617 11.5772 11.7383 10.5616 11.7383ZM10.5616 14.225C10.2051 14.225 9.91402 13.9357 9.91402 13.5792C9.91402 13.2226 10.2051 12.9316 10.5616 12.9316C10.9176 12.9316 11.2074 13.2226 11.2074 13.5792C11.2074 13.9357 10.9176 14.225 10.5616 14.225Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M14.8484 14.1482C14.3396 14.1482 13.9269 13.7355 13.9269 13.2268V4.58203H12.7197V15.0314H18.679V14.1482H14.8484Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M9.85501 7.42463L8.88497 8.09259C8.78745 8.15961 8.76301 8.2929 8.83003 8.39041C8.89704 8.48768 9.03033 8.51237 9.12785 8.44535L10.0979 7.77714C10.1954 7.71012 10.2199 7.57681 10.1529 7.47957C10.0858 7.38206 9.95231 7.35733 9.85501 7.42463Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M4.97826 12.4473C4.1581 12.4473 3.49243 13.112 3.49243 13.9331C3.49243 14.753 4.15813 15.4177 4.97826 15.4177C5.79818 15.4177 6.46284 14.753 6.46284 13.9331C6.46284 13.1119 5.79815 12.4473 4.97826 12.4473ZM4.97826 14.4544C4.69055 14.4544 4.45567 14.2208 4.45567 13.9331C4.45567 13.6454 4.69052 13.4105 4.97826 13.4105C5.26573 13.4105 5.49957 13.6454 5.49957 13.9331C5.49957 14.2208 5.26573 14.4544 4.97826 14.4544Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M11.3257 5.81717C11.1156 5.08596 10.4467 4.58203 9.6857 4.58203H5.20523C4.84291 4.58203 4.5491 4.87583 4.5491 5.23816V9.41249H4.07715C3.26935 9.41249 2.6145 10.0673 2.6145 10.8751V13.4258H2.88458C3.11388 12.4815 3.96429 11.7774 4.9782 11.7774C5.99161 11.7774 6.84123 12.4815 7.07052 13.4258H8.05798C8.13812 12.1123 9.22862 11.0679 10.5618 11.0679C11.2884 11.0679 11.9421 11.3804 12.4009 11.876V11.7487V10.3279C12.4009 9.8179 12.3293 9.31021 12.1885 8.82012L11.3257 5.81717ZM10.1793 8.21563L9.49446 8.6286L10.5313 10.6025H8.67763L7.81739 9.41249H6.07579V5.66572H9.6857C9.96187 5.66572 10.208 5.85115 10.2844 6.1165L11.1471 9.11919C11.2058 9.32329 11.2487 9.53116 11.2769 9.74131L10.1793 8.21563Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M10.5616 11.7383C9.54538 11.7383 8.7207 12.5617 8.7207 13.5792C8.7207 14.5948 9.54538 15.4183 10.5616 15.4183C11.5772 15.4183 12.4007 14.5948 12.4007 13.5792C12.4007 12.5617 11.5772 11.7383 10.5616 11.7383ZM10.5616 14.225C10.2051 14.225 9.91402 13.9357 9.91402 13.5792C9.91402 13.2226 10.2051 12.9316 10.5616 12.9316C10.9176 12.9316 11.2074 13.2226 11.2074 13.5792C11.2074 13.9357 10.9176 14.225 10.5616 14.225Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M14.8484 14.1482C14.3396 14.1482 13.9269 13.7355 13.9269 13.2268V4.58203H12.7197V15.0314H18.679V14.1482H14.8484Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M9.85501 7.42463L8.88497 8.09259C8.78745 8.15961 8.76301 8.2929 8.83003 8.39041C8.89704 8.48768 9.03033 8.51237 9.12785 8.44535L10.0979 7.77714C10.1954 7.71012 10.2199 7.57681 10.1529 7.47957C10.0858 7.38206 9.95231 7.35733 9.85501 7.42463Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_f_6014_655"
|
||||
x="2.14674"
|
||||
y="4.11427"
|
||||
width="17"
|
||||
height="11.7715"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="0.233881"
|
||||
result="effect1_foregroundBlur_6014_655"
|
||||
/>
|
||||
</filter>
|
||||
<clipPath id="clip0_6014_655">
|
||||
<rect
|
||||
width="18.7105"
|
||||
height="18.7105"
|
||||
fill="white"
|
||||
transform="translate(0.644775 0.644531)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ConveyorIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clip-path="url(#clip0_352_182)">
|
||||
<path
|
||||
d="M8.16336 9.72707H8.82742C8.80273 9.66301 8.7893 9.59363 8.7893 9.52144V6.32707C8.7893 6.1677 8.85492 6.02238 8.95961 5.91738C9.06586 5.8127 9.21086 5.74707 9.3693 5.74707H12.8659C13.0255 5.74707 13.1705 5.81238 13.2755 5.91738C13.3805 6.02238 13.4455 6.16707 13.4455 6.32707V9.52144C13.4455 9.59363 13.4321 9.66301 13.4074 9.72707H15.918C16.4902 9.72707 17.0105 9.96113 17.3874 10.3377C17.764 10.7146 17.998 11.2349 17.998 11.8071C17.998 12.3793 17.764 12.8996 17.3871 13.2761C17.0105 13.653 16.4896 13.8874 15.918 13.8874H4.07836C3.50617 13.8874 2.98586 13.6533 2.60867 13.2761C2.23211 12.8996 1.99805 12.3793 1.99805 11.8071C1.99805 11.2361 2.23242 10.7158 2.60961 10.3383C2.8793 10.0677 3.22305 9.87082 3.60742 9.78113C3.56655 9.70067 3.54524 9.61169 3.54523 9.52144V6.32707C3.54523 6.1677 3.61086 6.02238 3.71555 5.91738C3.82148 5.8127 3.9668 5.74707 4.12492 5.74707H7.6218C7.78148 5.74707 7.92617 5.81238 8.03117 5.91738C8.13617 6.02238 8.20148 6.16707 8.20148 6.32707V9.52144C8.20148 9.59363 8.18805 9.66301 8.16336 9.72707ZM15.6268 6.7652L16.823 7.89488L15.5705 9.08207L15.183 8.67488L15.6805 8.20363L14.1824 8.20582V7.64426L15.7365 7.64207L15.2415 7.17457L15.6268 6.7652ZM5.28961 6.6077H6.46648L6.48617 8.05176L5.8543 7.66645L5.26117 8.04238L5.28961 6.6077ZM7.6218 6.2402H4.12492C4.10117 6.2402 4.0793 6.24988 4.06367 6.26551C4.04762 6.282 4.03856 6.30406 4.03836 6.32707V9.52144C4.03836 9.54551 4.04773 9.56738 4.06305 9.58269C4.07961 9.59832 4.10117 9.60801 4.12492 9.60801H7.6218C7.64492 9.60801 7.6668 9.59801 7.68273 9.58238C7.69836 9.56644 7.70836 9.54457 7.70836 9.52144V6.32707C7.70836 6.30301 7.69867 6.28145 7.68305 6.26582C7.66742 6.24988 7.64555 6.2402 7.6218 6.2402ZM10.5334 6.6077H11.7102L11.7299 8.05176L11.098 7.66645L10.5046 8.04238L10.5334 6.6077ZM12.8659 6.2402H9.3693C9.34523 6.2402 9.32336 6.24988 9.30773 6.26551C9.29169 6.282 9.28262 6.30406 9.28242 6.32707V9.52144C9.28242 9.54551 9.29211 9.56738 9.30711 9.58269C9.32367 9.59832 9.34523 9.60801 9.3693 9.60801H12.8659C12.889 9.60801 12.9109 9.59801 12.9268 9.58238C12.9424 9.56644 12.9524 9.54457 12.9524 9.52144V6.32707C12.9524 6.30301 12.9427 6.28145 12.9271 6.26582C12.9115 6.24988 12.8896 6.2402 12.8659 6.2402ZM14.7877 11.0064C15.2299 11.0064 15.5887 11.3649 15.5887 11.8071C15.5887 12.2493 15.2299 12.608 14.7877 12.608C14.3455 12.608 13.9871 12.2493 13.9871 11.8071C13.9871 11.3649 14.3455 11.0064 14.7877 11.0064ZM11.5946 11.0064C12.0368 11.0064 12.3952 11.3649 12.3952 11.8071C12.3952 12.2493 12.0368 12.608 11.5946 12.608C11.1521 12.608 10.7937 12.2493 10.7937 11.8071C10.7937 11.3649 11.1521 11.0064 11.5946 11.0064ZM8.40148 11.0064C8.84367 11.0064 9.20242 11.3649 9.20242 11.8071C9.20242 12.2493 8.84367 12.608 8.40148 12.608C7.9593 12.608 7.60086 12.2493 7.60086 11.8071C7.60086 11.3649 7.9593 11.0064 8.40148 11.0064ZM5.20836 11.0064C5.65055 11.0064 6.0093 11.3649 6.0093 11.8071C6.0093 12.2493 5.65055 12.608 5.20836 12.608C4.76617 12.608 4.40742 12.2493 4.40742 11.8071C4.40742 11.3649 4.76617 11.0064 5.20836 11.0064ZM15.918 10.3608H4.07836C3.68086 10.3608 3.3193 10.5239 3.05711 10.7861C2.7943 11.0474 2.63211 11.4089 2.63211 11.8071C2.63211 12.2043 2.79492 12.5661 3.05711 12.828C3.3193 13.0905 3.68117 13.2533 4.07836 13.2533H15.918C16.3149 13.2533 16.6768 13.0902 16.939 12.828C17.2012 12.5658 17.3643 12.2043 17.3643 11.8071C17.3643 11.4099 17.2012 11.0483 16.939 10.7861C16.6768 10.5239 16.3152 10.3608 15.918 10.3608Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M9.2334 9.76172L9.15527 6.13672H13.1396V9.76172H9.2334Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M3.9209 9.76172L3.84277 6.13672H7.82715V9.76172H3.9209Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M11.7178 6.60645H10.5303L10.499 8.05957L11.0928 7.66895L11.7334 8.05957L11.7178 6.60645Z"
|
||||
fill="#494949"
|
||||
/>
|
||||
<path
|
||||
d="M6.43555 6.60645H5.24805L5.2168 8.05957L5.81055 7.66895L6.45117 8.05957L6.43555 6.60645Z"
|
||||
fill="#494949"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_352_182">
|
||||
<rect
|
||||
width="16"
|
||||
height="8.14031"
|
||||
fill="white"
|
||||
transform="translate(1.99902 5.74707)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const RoboticArmIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M13.9796 9.68306L10.6387 7.13761L12.5478 5.22852L15.2523 8.25124L13.9796 9.68306Z"
|
||||
fill="white"
|
||||
/>
|
||||
<rect
|
||||
x="3.07955"
|
||||
y="15.1713"
|
||||
width="9.06818"
|
||||
height="2.06818"
|
||||
rx="0.238636"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
stroke-width="0.159091"
|
||||
/>
|
||||
<rect
|
||||
x="4.59277"
|
||||
y="10.9551"
|
||||
width="2.86364"
|
||||
height="4.13636"
|
||||
fill="white"
|
||||
/>
|
||||
<rect
|
||||
x="5.22656"
|
||||
y="12.5459"
|
||||
width="0.636364"
|
||||
height="2.22727"
|
||||
rx="0.318182"
|
||||
fill="#494949"
|
||||
/>
|
||||
<rect
|
||||
x="11.2617"
|
||||
y="16.0898"
|
||||
width="0.636364"
|
||||
height="2.22727"
|
||||
rx="0.318182"
|
||||
transform="rotate(88.7682 11.2617 16.0898)"
|
||||
fill="#494949"
|
||||
/>
|
||||
<rect
|
||||
x="5.71387"
|
||||
y="8.08301"
|
||||
width="4.85107"
|
||||
height="2.86364"
|
||||
transform="rotate(-46.048 5.71387 8.08301)"
|
||||
fill="white"
|
||||
/>
|
||||
<circle
|
||||
cx="6.02326"
|
||||
cy="10.161"
|
||||
r="1.98864"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
stroke-width="0.159091"
|
||||
/>
|
||||
<circle
|
||||
cx="10.4764"
|
||||
cy="5.06916"
|
||||
r="1.98864"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
stroke-width="0.159091"
|
||||
/>
|
||||
<circle cx="6.02379" cy="10.1615" r="1.11364" fill="#494949" />
|
||||
<circle cx="10.4769" cy="5.06871" r="1.11364" fill="#494949" />
|
||||
<path
|
||||
d="M14.9316 8.17188C15.5026 8.17188 15.9656 8.63511 15.9658 9.20605V10.2402H13.8975V9.20605C13.8977 8.63522 14.3608 8.17207 14.9316 8.17188Z"
|
||||
fill="white"
|
||||
stroke="white"
|
||||
stroke-width="0.159091"
|
||||
/>
|
||||
<path
|
||||
d="M14.4561 10.1602H15.7295C16.3442 10.1604 16.8426 10.6587 16.8428 11.2734V13.291C16.8427 13.4108 16.7153 13.4875 16.6094 13.4316L15.834 13.0215V11.6855C15.8338 11.4222 15.6208 11.2092 15.3574 11.209H14.8799C14.6164 11.209 14.4025 11.4221 14.4023 11.6855V13.0205L13.5742 13.4424C13.4684 13.4963 13.3428 13.4186 13.3428 13.2998V11.2734C13.3429 10.6586 13.8412 10.1603 14.4561 10.1602Z"
|
||||
stroke="white"
|
||||
stroke-width="0.318182"
|
||||
/>
|
||||
<rect
|
||||
x="8.43457"
|
||||
y="6.18359"
|
||||
width="0.636364"
|
||||
height="2.22727"
|
||||
rx="0.318182"
|
||||
transform="rotate(46.5101 8.43457 6.18359)"
|
||||
fill="#494949"
|
||||
/>
|
||||
<rect
|
||||
x="14.0859"
|
||||
y="7.48438"
|
||||
width="0.636364"
|
||||
height="1.34988"
|
||||
rx="0.318182"
|
||||
transform="rotate(131.749 14.0859 7.48438)"
|
||||
fill="#494949"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const MachineIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="21"
|
||||
height="20"
|
||||
viewBox="0 0 21 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.9369 3.38379V5.39368H10.7644V4.55635H9.5918V3.38379H11.9369Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M8.07979 12.0938H6.90723V16.2816H8.07979V12.0938Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M5.90694 12.0938H4.73438V16.2816H5.90694V12.0938Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M9.59346 4.55635V5.8964H8.4209V3.38379H9.59346V4.55635Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M6.40843 5.89694C6.96354 5.89694 7.41354 5.44693 7.41354 4.89183C7.41354 4.33672 6.96354 3.88672 6.40843 3.88672C5.85332 3.88672 5.40332 4.33672 5.40332 4.89183C5.40332 5.44693 5.85332 5.89694 6.40843 5.89694Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M11.418 5.08984V4.08984"
|
||||
stroke="#494949"
|
||||
stroke-width="0.5"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M10.7646 7.9055L10.9324 6.90039H12.2724L12.4399 7.9055H13.1097V9.91539H10.0947V7.9055H10.7646Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M14.7864 5.39258H13.7812V6.73295H14.7864V5.39258Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path d="M16.6304 16.2832H15.29V17.4558H16.6304V16.2832Z" fill="white" />
|
||||
<path
|
||||
d="M7.58266 16.2832V17.4558H4.23242V16.2832H4.73481H5.90737H7.07994H7.58266Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path d="M14.7864 13.9365H8.4209V15.1091H14.7864V13.9365Z" fill="white" />
|
||||
<path
|
||||
d="M7.08048 10.585V12.0925H5.90791H4.73535V10.585H7.08048Z"
|
||||
fill="#494949"
|
||||
/>
|
||||
<path
|
||||
d="M14.7862 11.2557H8.42069V13.9358V15.1083V16.2809H7.58337H7.08065V12.093V10.5855H4.73552V12.093V16.2809H4.23313H3.39551V7.23531H4.40059H8.42069V9.91537H10.096H13.1109H14.7862V6.73292V5.39258H17.1314V16.2809H16.629H15.2886H14.7862V15.1083V13.9358V11.2557Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M8.42343 5.89613V7.23615H4.40332V2.5459H8.4234V3.38355L8.42343 5.89613ZM7.41865 4.89102C7.41865 4.33596 6.96861 3.88591 6.41354 3.88591C5.85847 3.88591 5.40843 4.33596 5.40843 4.89102C5.40843 5.44609 5.85847 5.89613 6.41354 5.89613C6.96861 5.89613 7.41865 5.44609 7.41865 4.89102Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M7.41862 8.15625H4.40332V8.65963H7.41862V8.15625Z"
|
||||
fill="#494949"
|
||||
/>
|
||||
<path
|
||||
d="M7.41862 9.16211H4.40332V9.66549H7.41862V9.16211Z"
|
||||
fill="#494949"
|
||||
/>
|
||||
<path
|
||||
d="M6.41496 3.63379C5.722 3.63379 5.1582 4.19758 5.1582 4.89057C5.1582 5.58356 5.722 6.14736 6.41496 6.14736C7.10795 6.14736 7.67174 5.58356 7.67174 4.89057C7.67174 4.19758 7.10798 3.63379 6.41496 3.63379ZM6.41496 5.64401C5.99953 5.64401 5.66159 5.30603 5.66159 4.8906C5.66159 4.47518 5.99953 4.1372 6.41496 4.1372C6.83039 4.1372 7.16836 4.47518 7.16836 4.8906C7.16836 5.30603 6.83039 5.64401 6.41496 5.64401Z"
|
||||
fill="#494949"
|
||||
/>
|
||||
<path
|
||||
d="M6.74732 4.63965H6.07715V5.14303H6.74732V4.63965Z"
|
||||
fill="#494949"
|
||||
/>
|
||||
<path
|
||||
d="M8.91992 5.08984V4.08984"
|
||||
stroke="#494949"
|
||||
stroke-width="0.5"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M12.3857 9.08984H10.8857"
|
||||
stroke="#494949"
|
||||
stroke-width="0.5"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M15.9199 6.09082V12.5908"
|
||||
stroke="#494949"
|
||||
stroke-width="0.5"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
type TypeBasedAssetIconsProps = {
|
||||
assetType: string;
|
||||
};
|
||||
|
||||
export function TypeBasedAssetIcons({ assetType }: TypeBasedAssetIconsProps) {
|
||||
console.log("assetType: ", assetType);
|
||||
return (
|
||||
<div>
|
||||
{assetType === "machine" && <MachineIcon />}
|
||||
{assetType === "vehicle" && <ForkLiftIcon />}
|
||||
{assetType === "transfer" && <ConveyorIcon />}
|
||||
{assetType === "roboticArm" && <RoboticArmIcon />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1697,93 +1697,6 @@ export const TargetIcon = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const ForkLiftIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<g clipPath="url(#clip0_6014_655)">
|
||||
<g filter="url(#filter0_f_6014_655)">
|
||||
<path
|
||||
d="M4.97826 12.4473C4.1581 12.4473 3.49243 13.112 3.49243 13.9331C3.49243 14.753 4.15813 15.4177 4.97826 15.4177C5.79818 15.4177 6.46284 14.753 6.46284 13.9331C6.46284 13.1119 5.79815 12.4473 4.97826 12.4473ZM4.97826 14.4544C4.69055 14.4544 4.45567 14.2208 4.45567 13.9331C4.45567 13.6454 4.69052 13.4105 4.97826 13.4105C5.26573 13.4105 5.49957 13.6454 5.49957 13.9331C5.49957 14.2208 5.26573 14.4544 4.97826 14.4544Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M11.3257 5.81717C11.1156 5.08596 10.4467 4.58203 9.6857 4.58203H5.20523C4.84291 4.58203 4.5491 4.87583 4.5491 5.23816V9.41249H4.07715C3.26935 9.41249 2.6145 10.0673 2.6145 10.8751V13.4258H2.88458C3.11388 12.4815 3.96429 11.7774 4.9782 11.7774C5.99161 11.7774 6.84123 12.4815 7.07052 13.4258H8.05798C8.13812 12.1123 9.22862 11.0679 10.5618 11.0679C11.2884 11.0679 11.9421 11.3804 12.4009 11.876V11.7487V10.3279C12.4009 9.8179 12.3293 9.31021 12.1885 8.82012L11.3257 5.81717ZM10.1793 8.21563L9.49446 8.6286L10.5313 10.6025H8.67763L7.81739 9.41249H6.07579V5.66572H9.6857C9.96187 5.66572 10.208 5.85115 10.2844 6.1165L11.1471 9.11919C11.2058 9.32329 11.2487 9.53116 11.2769 9.74131L10.1793 8.21563Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M10.5616 11.7383C9.54538 11.7383 8.7207 12.5617 8.7207 13.5792C8.7207 14.5948 9.54538 15.4183 10.5616 15.4183C11.5772 15.4183 12.4007 14.5948 12.4007 13.5792C12.4007 12.5617 11.5772 11.7383 10.5616 11.7383ZM10.5616 14.225C10.2051 14.225 9.91402 13.9357 9.91402 13.5792C9.91402 13.2226 10.2051 12.9316 10.5616 12.9316C10.9176 12.9316 11.2074 13.2226 11.2074 13.5792C11.2074 13.9357 10.9176 14.225 10.5616 14.225Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M14.8484 14.1482C14.3396 14.1482 13.9269 13.7355 13.9269 13.2268V4.58203H12.7197V15.0314H18.679V14.1482H14.8484Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M9.85501 7.42463L8.88497 8.09259C8.78745 8.15961 8.76301 8.2929 8.83003 8.39041C8.89704 8.48768 9.03033 8.51237 9.12785 8.44535L10.0979 7.77714C10.1954 7.71012 10.2199 7.57681 10.1529 7.47957C10.0858 7.38206 9.95231 7.35733 9.85501 7.42463Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M4.97826 12.4473C4.1581 12.4473 3.49243 13.112 3.49243 13.9331C3.49243 14.753 4.15813 15.4177 4.97826 15.4177C5.79818 15.4177 6.46284 14.753 6.46284 13.9331C6.46284 13.1119 5.79815 12.4473 4.97826 12.4473ZM4.97826 14.4544C4.69055 14.4544 4.45567 14.2208 4.45567 13.9331C4.45567 13.6454 4.69052 13.4105 4.97826 13.4105C5.26573 13.4105 5.49957 13.6454 5.49957 13.9331C5.49957 14.2208 5.26573 14.4544 4.97826 14.4544Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M11.3257 5.81717C11.1156 5.08596 10.4467 4.58203 9.6857 4.58203H5.20523C4.84291 4.58203 4.5491 4.87583 4.5491 5.23816V9.41249H4.07715C3.26935 9.41249 2.6145 10.0673 2.6145 10.8751V13.4258H2.88458C3.11388 12.4815 3.96429 11.7774 4.9782 11.7774C5.99161 11.7774 6.84123 12.4815 7.07052 13.4258H8.05798C8.13812 12.1123 9.22862 11.0679 10.5618 11.0679C11.2884 11.0679 11.9421 11.3804 12.4009 11.876V11.7487V10.3279C12.4009 9.8179 12.3293 9.31021 12.1885 8.82012L11.3257 5.81717ZM10.1793 8.21563L9.49446 8.6286L10.5313 10.6025H8.67763L7.81739 9.41249H6.07579V5.66572H9.6857C9.96187 5.66572 10.208 5.85115 10.2844 6.1165L11.1471 9.11919C11.2058 9.32329 11.2487 9.53116 11.2769 9.74131L10.1793 8.21563Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M10.5616 11.7383C9.54538 11.7383 8.7207 12.5617 8.7207 13.5792C8.7207 14.5948 9.54538 15.4183 10.5616 15.4183C11.5772 15.4183 12.4007 14.5948 12.4007 13.5792C12.4007 12.5617 11.5772 11.7383 10.5616 11.7383ZM10.5616 14.225C10.2051 14.225 9.91402 13.9357 9.91402 13.5792C9.91402 13.2226 10.2051 12.9316 10.5616 12.9316C10.9176 12.9316 11.2074 13.2226 11.2074 13.5792C11.2074 13.9357 10.9176 14.225 10.5616 14.225Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M14.8484 14.1482C14.3396 14.1482 13.9269 13.7355 13.9269 13.2268V4.58203H12.7197V15.0314H18.679V14.1482H14.8484Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M9.85501 7.42463L8.88497 8.09259C8.78745 8.15961 8.76301 8.2929 8.83003 8.39041C8.89704 8.48768 9.03033 8.51237 9.12785 8.44535L10.0979 7.77714C10.1954 7.71012 10.2199 7.57681 10.1529 7.47957C10.0858 7.38206 9.95231 7.35733 9.85501 7.42463Z"
|
||||
fill="white"
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<filter
|
||||
id="filter0_f_6014_655"
|
||||
x="2.14674"
|
||||
y="4.11427"
|
||||
width="17"
|
||||
height="11.7715"
|
||||
filterUnits="userSpaceOnUse"
|
||||
colorInterpolationFilters="sRGB"
|
||||
>
|
||||
<feFlood floodOpacity="0" result="BackgroundImageFix" />
|
||||
<feBlend
|
||||
mode="normal"
|
||||
in="SourceGraphic"
|
||||
in2="BackgroundImageFix"
|
||||
result="shape"
|
||||
/>
|
||||
<feGaussianBlur
|
||||
stdDeviation="0.233881"
|
||||
result="effect1_foregroundBlur_6014_655"
|
||||
/>
|
||||
</filter>
|
||||
<clipPath id="clip0_6014_655">
|
||||
<rect
|
||||
width="18.7105"
|
||||
height="18.7105"
|
||||
fill="white"
|
||||
transform="translate(0.644775 0.644531)"
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
export const RightHalfFillCircleIcon = () => {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react";
|
||||
import Search from "../../ui/inputs/Search";
|
||||
import { getCategoryAsset } from "../../../services/factoryBuilder/asset/assets/getCategoryAsset";
|
||||
import { fetchAssets } from "../../../services/marketplace/fetchAssets";
|
||||
import { useDecalStore, useSelectedItem } from "../../../store/builder/store";
|
||||
import {
|
||||
useDecalStore,
|
||||
useDroppedDecal,
|
||||
useSelectedItem,
|
||||
} from "../../../store/builder/store";
|
||||
|
||||
// images -------------------
|
||||
import vehicle from "../../../assets/image/categories/vehicles.png";
|
||||
@@ -17,11 +21,11 @@ import decal from "../../../assets/image/categories/decal.png";
|
||||
import SkeletonUI from "../../templates/SkeletonUI";
|
||||
import {
|
||||
AlertIcon,
|
||||
ArrowIcon,
|
||||
DecalInfoIcon,
|
||||
HangTagIcon,
|
||||
NavigationIcon,
|
||||
} from "../../icons/ExportCommonIcons";
|
||||
import { assert } from "console";
|
||||
import { getCategoryDecals } from "../../../services/factoryBuilder/asset/decals/getCategoryDecals";
|
||||
// -------------------------------------
|
||||
|
||||
@@ -46,6 +50,7 @@ interface CategoryListProp {
|
||||
}
|
||||
const Assets: React.FC = () => {
|
||||
const { setSelectedItem } = useSelectedItem();
|
||||
const { setDroppedDecal } = useDroppedDecal();
|
||||
const [searchValue, setSearchValue] = useState<string>("");
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
|
||||
const [categoryAssets, setCategoryAssets] = useState<AssetProp[]>([]);
|
||||
@@ -146,7 +151,12 @@ const Assets: React.FC = () => {
|
||||
echo.error("failed to fetch assets");
|
||||
setisLoading(false);
|
||||
}
|
||||
|
||||
if (asset === "Decals") {
|
||||
fetchCategoryDecals("Safety");
|
||||
}
|
||||
};
|
||||
|
||||
const fetchCategoryDecals = async (asset: any) => {
|
||||
setisLoading(true);
|
||||
// setSelectedCategory(asset);
|
||||
@@ -185,7 +195,10 @@ const Assets: React.FC = () => {
|
||||
<div className="assets-result">
|
||||
<div className="assets-wrapper">
|
||||
<div className="searched-content">
|
||||
<p>Results for {searchValue}</p>
|
||||
<p>
|
||||
Results for{" "}
|
||||
<span className="search-for">'{searchValue}'</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="assets-container">
|
||||
{selectedCategory == "Decals" ? (
|
||||
@@ -291,7 +304,7 @@ const Assets: React.FC = () => {
|
||||
if (selectedCategory) {
|
||||
return (
|
||||
<div className="assets-wrapper">
|
||||
<h2>
|
||||
<h2 className="header">
|
||||
{selectedCategory}
|
||||
<button
|
||||
className="back-button"
|
||||
@@ -302,7 +315,10 @@ const Assets: React.FC = () => {
|
||||
setCategoryAssets([]);
|
||||
}}
|
||||
>
|
||||
← Back
|
||||
<div className="back-arrow">
|
||||
<ArrowIcon />
|
||||
</div>
|
||||
Back
|
||||
</button>
|
||||
</h2>
|
||||
|
||||
@@ -327,7 +343,6 @@ const Assets: React.FC = () => {
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{selectedCategory !== "Decals" && !selectedSubCategory ? (
|
||||
<div className="assets-container">
|
||||
{categoryAssets?.map((asset: any, index: number) => (
|
||||
@@ -386,15 +401,11 @@ const Assets: React.FC = () => {
|
||||
alt={asset.decalName}
|
||||
className="asset-image"
|
||||
onPointerDown={() => {
|
||||
setSelectedItem({
|
||||
name: asset.decalName,
|
||||
id: asset.id,
|
||||
type:
|
||||
asset.type === "undefined"
|
||||
? undefined
|
||||
: asset.type,
|
||||
setDroppedDecal({
|
||||
category: asset.category,
|
||||
// subType: asset.subType,
|
||||
decalName: asset.decalName,
|
||||
decalImage: asset.decalImage,
|
||||
decalId: asset.id,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
@@ -423,7 +434,7 @@ const Assets: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="assets-wrapper">
|
||||
<h2>Categories</h2>
|
||||
<h2 className="categories-header">Categories</h2>
|
||||
<div className="categories-container">
|
||||
{Array.from(
|
||||
new Set(categoryList.map((asset) => asset.category))
|
||||
|
||||
@@ -1,29 +1,13 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import Header from "./Header";
|
||||
import useModuleStore, {
|
||||
useSubModuleStore,
|
||||
} from "../../../store/useModuleStore";
|
||||
import {
|
||||
AnalysisIcon,
|
||||
FilePackageIcon,
|
||||
MechanicsIcon,
|
||||
PropertiesIcon,
|
||||
SimulationIcon,
|
||||
} from "../../icons/SimulationIcons";
|
||||
import useModuleStore, { useSubModuleStore } from "../../../store/useModuleStore";
|
||||
import { AnalysisIcon, FilePackageIcon, MechanicsIcon, PropertiesIcon, SimulationIcon, } from "../../icons/SimulationIcons";
|
||||
import { useToggleStore } from "../../../store/useUIToggleStore";
|
||||
import Visualization from "./visualization/Visualization";
|
||||
import Analysis from "./analysis/Analysis";
|
||||
import Simulations from "./simulation/Simulations";
|
||||
import useVersionHistoryVisibleStore, {
|
||||
useDecalStore,
|
||||
useSaveVersion,
|
||||
useSelectedFloorItem,
|
||||
useToolMode,
|
||||
} from "../../../store/builder/store";
|
||||
import {
|
||||
useSelectedEventData,
|
||||
useSelectedEventSphere,
|
||||
} from "../../../store/simulation/useSimulationStore";
|
||||
import useVersionHistoryVisibleStore, { useSaveVersion, useSelectedFloorItem, useToolMode } from "../../../store/builder/store";
|
||||
import { useSelectedEventData, useSelectedEventSphere, } from "../../../store/simulation/useSimulationStore";
|
||||
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
|
||||
import GlobalProperties from "./properties/GlobalProperties";
|
||||
import AssetProperties from "./properties/AssetProperties";
|
||||
@@ -35,319 +19,281 @@ import WallProperties from "./properties/WallProperties";
|
||||
import FloorProperties from "./properties/FloorProperties";
|
||||
import SelectedWallProperties from "./properties/SelectedWallProperties";
|
||||
import SelectedFloorProperties from "./properties/SelectedFloorProperties";
|
||||
import SelectedDecalProperties from "./properties/SelectedDecalProperties";
|
||||
import SelectedAisleProperties from "./properties/SelectedAisleProperties";
|
||||
import ResourceManagement from "./resourceManagement/ResourceManagement";
|
||||
import DecalProperties from "./properties/DecalProperties";
|
||||
|
||||
type DisplayComponent =
|
||||
| "versionHistory"
|
||||
| "globalProperties"
|
||||
| "aisleProperties"
|
||||
| "wallProperties"
|
||||
| "floorProperties"
|
||||
| "assetProperties"
|
||||
| "selectedWallProperties"
|
||||
| "selectedFloorProperties"
|
||||
| "zoneProperties"
|
||||
| "simulations"
|
||||
| "mechanics"
|
||||
| "analysis"
|
||||
| "visualization"
|
||||
| "selectedDecalProperties"
|
||||
| "resourceManagement"
|
||||
| "none";
|
||||
| "versionHistory"
|
||||
| "globalProperties"
|
||||
| "aisleProperties"
|
||||
| "wallProperties"
|
||||
| "floorProperties"
|
||||
| "assetProperties"
|
||||
| "selectedWallProperties"
|
||||
| "selectedFloorProperties"
|
||||
| "selectedDecalProperties"
|
||||
| "selectedAisleProperties"
|
||||
| "zoneProperties"
|
||||
| "simulations"
|
||||
| "mechanics"
|
||||
| "analysis"
|
||||
| "visualization"
|
||||
| "resourceManagement"
|
||||
| "none";
|
||||
|
||||
const SideBarRight: React.FC = () => {
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toggleUIRight } = useToggleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { subModule, setSubModule } = useSubModuleStore();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { viewVersionHistory, setVersionHistoryVisible } =
|
||||
useVersionHistoryVisibleStore();
|
||||
const { isVersionSaved } = useSaveVersion();
|
||||
const { selectedSubCategory } = useDecalStore();
|
||||
const { selectedDecal } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toggleUIRight } = useToggleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { subModule, setSubModule } = useSubModuleStore();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedWall, selectedFloor, selectedAisle } = useBuilderStore();
|
||||
const { selectedEventData } = useSelectedEventData();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { viewVersionHistory, setVersionHistoryVisible } = useVersionHistoryVisibleStore();
|
||||
const { isVersionSaved } = useSaveVersion();
|
||||
|
||||
const [displayComponent, setDisplayComponent] =
|
||||
useState<DisplayComponent>("none");
|
||||
const [displayComponent, setDisplayComponent] = useState<DisplayComponent>("none");
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "simulation") setSubModule("properties");
|
||||
if (activeModule === "simulation") setSubModule("simulations");
|
||||
}, [activeModule, setSubModule]);
|
||||
useEffect(() => {
|
||||
if (activeModule !== "simulation") setSubModule("properties");
|
||||
if (activeModule === "simulation") setSubModule("simulations");
|
||||
}, [activeModule, setSubModule]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
activeModule !== "mechanics" &&
|
||||
selectedEventData &&
|
||||
selectedEventSphere
|
||||
) {
|
||||
setSubModule("mechanics");
|
||||
} else if (!selectedEventData && !selectedEventSphere) {
|
||||
if (activeModule === "simulation") {
|
||||
setSubModule("simulations");
|
||||
}
|
||||
}
|
||||
if (activeModule !== "simulation") {
|
||||
setSubModule("properties");
|
||||
}
|
||||
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule === "visualization") {
|
||||
setDisplayComponent("visualization");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isVersionSaved && activeModule === "simulation") {
|
||||
if (subModule === "simulations") {
|
||||
setDisplayComponent("simulations");
|
||||
return;
|
||||
}
|
||||
if (subModule === "mechanics") {
|
||||
setDisplayComponent("mechanics");
|
||||
return;
|
||||
}
|
||||
if (subModule === "analysis") {
|
||||
setDisplayComponent("analysis");
|
||||
return;
|
||||
}
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (activeModule === "simulation" || activeModule === "builder") {
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (subModule === "properties" && activeModule !== "visualization") {
|
||||
if (selectedFloorItem) {
|
||||
setDisplayComponent("assetProperties");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!selectedFloorItem &&
|
||||
!selectedFloor &&
|
||||
!selectedAisle &&
|
||||
selectedWall
|
||||
) {
|
||||
setDisplayComponent("selectedWallProperties");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!selectedFloorItem &&
|
||||
!selectedWall &&
|
||||
!selectedAisle &&
|
||||
selectedFloor
|
||||
) {
|
||||
setDisplayComponent("selectedFloorProperties");
|
||||
return;
|
||||
}
|
||||
if (viewVersionHistory) {
|
||||
setDisplayComponent("versionHistory");
|
||||
return;
|
||||
}
|
||||
if (selectedSubCategory) {
|
||||
setDisplayComponent("selectedDecalProperties");
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!selectedFloorItem &&
|
||||
!selectedFloor &&
|
||||
!selectedWall &&
|
||||
!selectedSubCategory
|
||||
) {
|
||||
if (toolMode === "Aisle") {
|
||||
setDisplayComponent("aisleProperties");
|
||||
return;
|
||||
useEffect(() => {
|
||||
if (activeModule !== "mechanics" && selectedEventData && selectedEventSphere) {
|
||||
setSubModule("mechanics");
|
||||
} else if (!selectedEventData && !selectedEventSphere) {
|
||||
if (activeModule === "simulation") {
|
||||
setSubModule("simulations");
|
||||
}
|
||||
}
|
||||
if (toolMode === "Wall") {
|
||||
setDisplayComponent("wallProperties");
|
||||
return;
|
||||
if (activeModule !== "simulation") {
|
||||
setSubModule("properties");
|
||||
}
|
||||
if (toolMode === "Floor") {
|
||||
setDisplayComponent("floorProperties");
|
||||
return;
|
||||
}, [activeModule, selectedEventData, selectedEventSphere, setSubModule]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule === "visualization") {
|
||||
setDisplayComponent("visualization");
|
||||
return;
|
||||
}
|
||||
setDisplayComponent("globalProperties");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
subModule === "zoneProperties" &&
|
||||
(activeModule === "builder" || activeModule === "simulation")
|
||||
) {
|
||||
setDisplayComponent("zoneProperties");
|
||||
return;
|
||||
}
|
||||
if (!isVersionSaved && activeModule === "simulation") {
|
||||
if (subModule === "simulations") {
|
||||
setDisplayComponent("simulations");
|
||||
return;
|
||||
}
|
||||
if (subModule === "mechanics") {
|
||||
setDisplayComponent("mechanics");
|
||||
return;
|
||||
}
|
||||
if (subModule === "analysis") {
|
||||
setDisplayComponent("analysis");
|
||||
return;
|
||||
}
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setDisplayComponent("none");
|
||||
}, [
|
||||
viewVersionHistory,
|
||||
activeModule,
|
||||
subModule,
|
||||
isVersionSaved,
|
||||
selectedFloorItem,
|
||||
selectedWall,
|
||||
selectedFloor,
|
||||
selectedAisle,
|
||||
toolMode,
|
||||
selectedSubCategory,
|
||||
]);
|
||||
if (activeModule === "simulation" || activeModule === "builder") {
|
||||
if (subModule === "resourceManagement") {
|
||||
setDisplayComponent("resourceManagement");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const renderComponent = () => {
|
||||
switch (displayComponent) {
|
||||
case "versionHistory":
|
||||
return <VersionHistory />;
|
||||
case "globalProperties":
|
||||
return <GlobalProperties />;
|
||||
case "aisleProperties":
|
||||
return <AisleProperties />;
|
||||
case "wallProperties":
|
||||
return <WallProperties />;
|
||||
case "floorProperties":
|
||||
return <FloorProperties />;
|
||||
case "assetProperties":
|
||||
return <AssetProperties />;
|
||||
case "selectedWallProperties":
|
||||
return <SelectedWallProperties />;
|
||||
case "selectedFloorProperties":
|
||||
return <SelectedFloorProperties />;
|
||||
case "zoneProperties":
|
||||
return <ZoneProperties />;
|
||||
case "simulations":
|
||||
return <Simulations />;
|
||||
case "mechanics":
|
||||
return <EventProperties />;
|
||||
case "analysis":
|
||||
return <Analysis />;
|
||||
case "visualization":
|
||||
return <Visualization />;
|
||||
case "selectedDecalProperties":
|
||||
return <DecalProperties />;
|
||||
case "resourceManagement":
|
||||
return <ResourceManagement />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
if (subModule === "properties" && activeModule !== "visualization") {
|
||||
if (selectedFloorItem) {
|
||||
setDisplayComponent("assetProperties");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedFloor && !selectedAisle && !selectedDecal && selectedWall) {
|
||||
setDisplayComponent("selectedWallProperties");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedWall && !selectedAisle && !selectedDecal && selectedFloor) {
|
||||
setDisplayComponent("selectedFloorProperties");
|
||||
return;
|
||||
}
|
||||
if (viewVersionHistory && !selectedFloorItem && !selectedWall && !selectedAisle && !selectedFloor && !selectedDecal) {
|
||||
setDisplayComponent("versionHistory");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedFloor && !selectedAisle && !selectedWall && selectedDecal) {
|
||||
setDisplayComponent("selectedDecalProperties");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal && selectedAisle) {
|
||||
setDisplayComponent("selectedAisleProperties");
|
||||
return;
|
||||
}
|
||||
if (!selectedFloorItem && !selectedFloor && !selectedWall && !selectedDecal && !selectedAisle) {
|
||||
if (toolMode === "Aisle") {
|
||||
setDisplayComponent("aisleProperties");
|
||||
return;
|
||||
}
|
||||
if (toolMode === "Wall") {
|
||||
setDisplayComponent("wallProperties");
|
||||
return;
|
||||
}
|
||||
if (toolMode === "Floor") {
|
||||
setDisplayComponent("floorProperties");
|
||||
return;
|
||||
}
|
||||
setDisplayComponent("globalProperties");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`sidebar-right-wrapper ${
|
||||
toggleUIRight && (!isVersionSaved || activeModule !== "simulation")
|
||||
? "open"
|
||||
: "closed"
|
||||
}`}
|
||||
>
|
||||
<Header />
|
||||
{toggleUIRight && (
|
||||
<>
|
||||
{(!isVersionSaved || activeModule !== "simulation") && (
|
||||
<div className="sidebar-actions-container">
|
||||
{activeModule !== "simulation" && (
|
||||
if (subModule === "zoneProperties" && (activeModule === "builder" || activeModule === "simulation")) {
|
||||
setDisplayComponent("zoneProperties");
|
||||
return;
|
||||
}
|
||||
|
||||
setDisplayComponent("none");
|
||||
}, [viewVersionHistory, activeModule, subModule, isVersionSaved, selectedFloorItem, selectedWall, selectedFloor, selectedAisle, toolMode, selectedDecal]);
|
||||
|
||||
const renderComponent = () => {
|
||||
switch (displayComponent) {
|
||||
case "versionHistory":
|
||||
return <VersionHistory />;
|
||||
case "globalProperties":
|
||||
return <GlobalProperties />;
|
||||
case "aisleProperties":
|
||||
return <AisleProperties />;
|
||||
case "wallProperties":
|
||||
return <WallProperties />;
|
||||
case "floorProperties":
|
||||
return <FloorProperties />;
|
||||
case "assetProperties":
|
||||
return <AssetProperties />;
|
||||
case "zoneProperties":
|
||||
return <ZoneProperties />;
|
||||
case "selectedWallProperties":
|
||||
return <SelectedWallProperties />;
|
||||
case "selectedFloorProperties":
|
||||
return <SelectedFloorProperties />;
|
||||
case "selectedDecalProperties":
|
||||
return <SelectedDecalProperties />;
|
||||
case "selectedAisleProperties":
|
||||
return <SelectedAisleProperties />;
|
||||
case "simulations":
|
||||
return <Simulations />;
|
||||
case "mechanics":
|
||||
return <EventProperties />;
|
||||
case "analysis":
|
||||
return <Analysis />;
|
||||
case "visualization":
|
||||
return <Visualization />;
|
||||
case "resourceManagement":
|
||||
return <ResourceManagement />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`sidebar-right-wrapper ${toggleUIRight && (!isVersionSaved || activeModule !== "simulation") ? "open" : "closed"}`}>
|
||||
<Header />
|
||||
{toggleUIRight && (
|
||||
<>
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "properties" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("properties");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">properties</div>
|
||||
<PropertiesIcon isActive={subModule === "properties"} />
|
||||
</button>
|
||||
{(!isVersionSaved || activeModule !== "simulation") && (
|
||||
<div className="sidebar-actions-container">
|
||||
{activeModule !== "simulation" && (
|
||||
<>
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${subModule === "properties" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("properties");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">properties</div>
|
||||
<PropertiesIcon isActive={subModule === "properties"} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<button
|
||||
id="sidebar-action-list-simulation"
|
||||
className={`sidebar-action-list ${subModule === "simulations" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("simulations");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">simulations</div>
|
||||
<SimulationIcon isActive={subModule === "simulations"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-mechanics"
|
||||
className={`sidebar-action-list ${subModule === "mechanics" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("mechanics");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">mechanics</div>
|
||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-analysis"
|
||||
className={`sidebar-action-list ${subModule === "analysis" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("analysis");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">analysis</div>
|
||||
<AnalysisIcon isActive={subModule === "analysis"} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(activeModule === "builder" ||
|
||||
activeModule === "simulation") && (
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${subModule === "resourceManagement" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("resourceManagement");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">Resource Management</div>
|
||||
<FilePackageIcon
|
||||
isActive={subModule === "resourceManagement"}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayComponent !== "none" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
{renderComponent()}
|
||||
{/* <ResourceManagement /> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeModule === "simulation" && (
|
||||
<>
|
||||
<button
|
||||
id="sidebar-action-list-simulation"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "simulations" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("simulations");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">simulations</div>
|
||||
<SimulationIcon isActive={subModule === "simulations"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-mechanics"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "mechanics" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("mechanics");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">mechanics</div>
|
||||
<MechanicsIcon isActive={subModule === "mechanics"} />
|
||||
</button>
|
||||
<button
|
||||
id="sidebar-action-list-analysis"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "analysis" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("analysis");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">analysis</div>
|
||||
<AnalysisIcon isActive={subModule === "analysis"} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{(activeModule === "builder" ||
|
||||
activeModule === "simulation") && (
|
||||
<button
|
||||
id="sidebar-action-list-properties"
|
||||
className={`sidebar-action-list ${
|
||||
subModule === "resourceManagement" ? "active" : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
setSubModule("resourceManagement");
|
||||
setVersionHistoryVisible(false);
|
||||
}}
|
||||
>
|
||||
<div className="tooltip">Resource Management</div>
|
||||
<FilePackageIcon
|
||||
isActive={subModule === "resourceManagement"}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{displayComponent !== "none" && (
|
||||
<div className="sidebar-right-container">
|
||||
<div className="sidebar-right-content-container">
|
||||
{renderComponent()}
|
||||
{/* <ResourceManagement /> */}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SideBarRight;
|
||||
|
||||
@@ -1,30 +1,46 @@
|
||||
import React from "react";
|
||||
|
||||
interface RotationInputProps {
|
||||
onChange: (value: string) => void; // Callback for value change
|
||||
placeholder?: string; // Optional placeholder
|
||||
type?: string; // Input type (e.g., text, number, email)
|
||||
heading?: string;
|
||||
label?: string;
|
||||
onChange: (value: string) => void;
|
||||
placeholder?: string;
|
||||
type?: string;
|
||||
value?: number;
|
||||
disabled?: boolean;
|
||||
min?: number;
|
||||
max?: number;
|
||||
step?: number;
|
||||
}
|
||||
|
||||
const RotationInput: React.FC<RotationInputProps> = ({
|
||||
label = "Rotate :",
|
||||
heading = "Rotation",
|
||||
onChange,
|
||||
placeholder = "Enter value", // Default placeholder
|
||||
type = "number", // Default type
|
||||
value = "number",
|
||||
placeholder = "Enter value",
|
||||
type = "number",
|
||||
value,
|
||||
disabled = false,
|
||||
min,
|
||||
max,
|
||||
step,
|
||||
}) => {
|
||||
return (
|
||||
<div className="custom-input-container">
|
||||
<div className="header">Rotation</div>
|
||||
<div className="header">{heading}</div>
|
||||
<div className="inputs-container" style={{ display: "block" }}>
|
||||
<div className="input-container">
|
||||
<div className="custom-input-label">Rotate : </div>
|
||||
<div className="custom-input-label">{label}</div>
|
||||
<input
|
||||
className="custom-input-field"
|
||||
type={type}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
placeholder={placeholder}
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
min={min}
|
||||
max={max}
|
||||
step={step}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,24 +21,25 @@ interface TextureItem {
|
||||
texture: string;
|
||||
}
|
||||
|
||||
export const aisleTextureList: TextureItem[] = [
|
||||
{ color: "yellow", id: "#FBE50E", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "gray", id: "#6F6F7A", brief: "basic", texture: "" },
|
||||
{ color: "green", id: "#43C06D", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "orange", id: "#FF711B", brief: "material flow", texture: "" },
|
||||
{ color: "blue", id: "#488EF6", brief: "vehicle paths", texture: "" },
|
||||
{ color: "purple", id: "#AF52DE", brief: "material flow", texture: "" },
|
||||
{ color: "red", id: "#FF3B30", brief: "safety zone", texture: "" },
|
||||
{ color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" },
|
||||
{ color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" },
|
||||
{ color: "white-black", id: "white-black", brief: "utility aisles", texture: "" },
|
||||
];
|
||||
|
||||
const AisleProperties: React.FC = () => {
|
||||
const [collapsePresets, setCollapsePresets] = useState(false);
|
||||
const [collapseTexture, setCollapseTexture] = useState(true);
|
||||
|
||||
const { aisleType, aisleWidth, aisleColor, dashLength, gapLength, dotRadius, aisleLength, isFlipped, setAisleType, setAisleColor, setAisleWidth, setDashLength, setGapLength, setDotRadius, setAisleLength, setIsFlipped } = useBuilderStore();
|
||||
|
||||
const aisleTextureList: TextureItem[] = [
|
||||
{ color: "yellow", id: "yellow", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "gray", id: "gray", brief: "basic", texture: "" },
|
||||
{ color: "green", id: "green", brief: "pedestrian walkways", texture: "" },
|
||||
{ color: "orange", id: "orange", brief: "material flow", texture: "" },
|
||||
{ color: "blue", id: "blue", brief: "vehicle paths", texture: "" },
|
||||
{ color: "purple", id: "purple", brief: "material flow", texture: "" },
|
||||
{ color: "red", id: "red", brief: "safety zone", texture: "" },
|
||||
{ color: "bright green", id: "#66FF00", brief: "safety zone", texture: "" },
|
||||
{ color: "yellow-black", id: "yellow-black", brief: "utility aisles", texture: "" },
|
||||
{ color: "white-black", id: "white-black", brief: "utility aisles", texture: "" },
|
||||
];
|
||||
|
||||
const aisleTypes: {
|
||||
name: string;
|
||||
@@ -285,7 +286,12 @@ const AisleProperties: React.FC = () => {
|
||||
onClick={() => setAisleColor(val.id)}
|
||||
aria-pressed={aisleColor === val.id}
|
||||
>
|
||||
<div className="texture-display">{val.texture}</div>
|
||||
<div
|
||||
className={`texture-display ${val.id}`}
|
||||
style={{ background: val.id }}
|
||||
>
|
||||
{val.texture}
|
||||
</div>
|
||||
<div className="aisle-color">{val.color}</div>
|
||||
<div className="aisle-brief">{`( ${val.brief} )`}</div>
|
||||
</button>
|
||||
|
||||
@@ -32,9 +32,9 @@ const AssetProperties: React.FC = () => {
|
||||
setUserData([]);
|
||||
};
|
||||
|
||||
const handleUserDataChange = (id: number, newValue: string) => {};
|
||||
const handleUserDataChange = (id: number, newValue: string) => { };
|
||||
|
||||
const handleRemoveUserData = (id: number) => {};
|
||||
const handleRemoveUserData = (id: number) => { };
|
||||
|
||||
const handleAnimationClick = (animation: string) => {
|
||||
if (selectedFloorItem) {
|
||||
@@ -57,14 +57,16 @@ const AssetProperties: React.FC = () => {
|
||||
<section>
|
||||
{objectPosition && (
|
||||
<PositionInput
|
||||
onChange={() => {}}
|
||||
disabled={true}
|
||||
onChange={() => { }}
|
||||
value1={parseFloat(objectPosition.x.toFixed(5))}
|
||||
value2={parseFloat(objectPosition.z.toFixed(5))}
|
||||
/>
|
||||
)}
|
||||
{objectRotation && (
|
||||
<RotationInput
|
||||
onChange={() => {}}
|
||||
disabled={true}
|
||||
onChange={() => { }}
|
||||
value={parseFloat(objectRotation.y.toFixed(5))}
|
||||
/>
|
||||
)}
|
||||
@@ -107,7 +109,7 @@ const AssetProperties: React.FC = () => {
|
||||
if (asset.modelUuid !== selectedFloorItem.uuid || !asset.animations)
|
||||
return (
|
||||
i === 0 && (
|
||||
<div className="no-animation">
|
||||
<div className="no-animation" key={i}>
|
||||
Looks like there are no preset animations yet. Stay tuned for
|
||||
future additions!
|
||||
</div>
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import {
|
||||
LayeringBottomIcon,
|
||||
LayeringTopIcon,
|
||||
} from "../../../icons/ExportCommonIcons";
|
||||
import InputRange from "../../../ui/inputs/InputRange";
|
||||
import RotationInput from "../customInput/RotationInput";
|
||||
import Vector3Input from "../customInput/Vector3Input";
|
||||
|
||||
const DecalProperties = () => {
|
||||
return (
|
||||
<div className="decal-transformation-container">
|
||||
<div className="header">Decal Propertis</div>
|
||||
<section>
|
||||
<RotationInput
|
||||
onChange={() => {}}
|
||||
value={10}
|
||||
/>
|
||||
<Vector3Input
|
||||
onChange={(value) => console.log(value)}
|
||||
header="Scale"
|
||||
value={[0, 0, 0] as [number, number, number]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<InputRange
|
||||
label="Opacity"
|
||||
value={1}
|
||||
min={0}
|
||||
step={0.1}
|
||||
max={1}
|
||||
onChange={(value: number) => console.log(value)}
|
||||
key={"6"}
|
||||
/>
|
||||
|
||||
<div className="transformation-wrapper opacity">
|
||||
<div className="transformation-header">Layering</div>
|
||||
|
||||
<div className="layers-list">
|
||||
<button className="layer-move-btn">
|
||||
<LayeringBottomIcon />
|
||||
</button>
|
||||
<button className="layer-move-btn">
|
||||
<LayeringTopIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecalProperties;
|
||||
@@ -0,0 +1,529 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import InputWithDropDown from "../../../ui/inputs/InputWithDropDown";
|
||||
import { ArrowIcon } from "../../../icons/ExportCommonIcons";
|
||||
|
||||
// image imports
|
||||
import Arc from "../../../../assets/image/aisleTypes/Arc.png";
|
||||
import Arrow from "../../../../assets/image/aisleTypes/Arrow.png";
|
||||
import Arrows from "../../../../assets/image/aisleTypes/Arrows.png";
|
||||
import Circle from "../../../../assets/image/aisleTypes/Circle.png";
|
||||
import Dashed from "../../../../assets/image/aisleTypes/Dashed.png";
|
||||
import Directional from "../../../../assets/image/aisleTypes/Directional.png";
|
||||
import Dotted from "../../../../assets/image/aisleTypes/Dotted.png";
|
||||
import Solid from "../../../../assets/image/aisleTypes/Solid.png";
|
||||
import InputToggle from "../../../ui/inputs/InputToggle";
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
|
||||
import { useSocketStore } from "../../../../store/builder/store";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
import { aisleTextureList } from "./AisleProperties";
|
||||
|
||||
const SelectedAisleProperties: React.FC = () => {
|
||||
const [collapsePresets, setCollapsePresets] = useState(false);
|
||||
const [collapseTexture, setCollapseTexture] = useState(true);
|
||||
const { aisleStore } = useSceneContext();
|
||||
const { getAisleById, updateAisle, setDashedAisleProperties, setDottedAisleProperties, setArrowsAisleProperties, setArcAisleWidth, setColor } = aisleStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
const [selectedAisleData, setSelectedAisleData] = useState<Aisle | undefined>();
|
||||
|
||||
const { selectedAisle, setSelectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
const aisleData = getAisleById(selectedAisle?.aisleMesh?.uuid || "");
|
||||
setSelectedAisleData(aisleData);
|
||||
}, [selectedAisle, getAisleById]);
|
||||
|
||||
if (!selectedAisleData) return null;
|
||||
|
||||
const updateBackend = (updatedAisle: Aisle) => {
|
||||
if (updatedAisle && projectId) {
|
||||
|
||||
// API
|
||||
|
||||
// upsertAisleApi(updatedAisle.aisleUuid, updatedAisle.points, updatedAisle.type, projectId, selectedVersion?.versionId || '');
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization,
|
||||
aisleUuid: updatedAisle.aisleUuid,
|
||||
points: updatedAisle.points,
|
||||
type: updatedAisle.type
|
||||
}
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
}
|
||||
|
||||
const aisleTypes: {
|
||||
name: string;
|
||||
type: AisleTypes;
|
||||
id: string;
|
||||
thumbnail: string;
|
||||
}[] = [
|
||||
{ name: "Solid", type: "solid-aisle", id: "1", thumbnail: Solid },
|
||||
{ name: "Dotted", type: "dotted-aisle", id: "2", thumbnail: Dotted },
|
||||
{ name: "Dashed", type: "dashed-aisle", id: "3", thumbnail: Dashed },
|
||||
{ name: "Arrow", type: "arrow-aisle", id: "4", thumbnail: Arrow },
|
||||
{ name: "Continuous Arrows", type: "arrows-aisle", id: "5", thumbnail: Arrows },
|
||||
{ name: "Directional", type: "junction-aisle", id: "6", thumbnail: Directional },
|
||||
{ name: "Arc", type: "arc-aisle", id: "7", thumbnail: Arc },
|
||||
{ name: "Circle", type: "circle-aisle", id: "8", thumbnail: Circle },
|
||||
];
|
||||
|
||||
const createAisleTypeObject = (newType: AisleTypes, currentType: AisleType): AisleType => {
|
||||
switch (newType) {
|
||||
case 'solid-aisle':
|
||||
return {
|
||||
aisleType: 'solid-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1
|
||||
} as SolidAisle;
|
||||
|
||||
case 'dashed-aisle':
|
||||
return {
|
||||
aisleType: 'dashed-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1,
|
||||
dashLength: 'dashLength' in currentType ? (currentType as DashedAisle).dashLength : 0.5,
|
||||
gapLength: 'gapLength' in currentType ? (currentType as DashedAisle).gapLength : 0.3
|
||||
} as DashedAisle;
|
||||
|
||||
case 'dotted-aisle':
|
||||
return {
|
||||
aisleType: 'dotted-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
dotRadius: 'dotRadius' in currentType ? (currentType as DottedAisle).dotRadius : 0.1,
|
||||
gapLength: 'gapLength' in currentType ? (currentType as DottedAisle).gapLength : 0.3
|
||||
} as DottedAisle;
|
||||
|
||||
case 'arrow-aisle':
|
||||
return {
|
||||
aisleType: 'arrow-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1
|
||||
} as ArrowAisle;
|
||||
|
||||
case 'arrows-aisle':
|
||||
return {
|
||||
aisleType: 'arrows-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1,
|
||||
aisleLength: 'aisleLength' in currentType ? (currentType as ArrowsAisle).aisleLength : 0.6,
|
||||
gapLength: 'gapLength' in currentType ? (currentType as ArrowsAisle).gapLength : 0.3
|
||||
} as ArrowsAisle;
|
||||
|
||||
case 'arc-aisle':
|
||||
return {
|
||||
aisleType: 'arc-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1,
|
||||
isFlipped: 'isFlipped' in currentType ? (currentType as ArcAisle).isFlipped : false
|
||||
} as ArcAisle;
|
||||
|
||||
case 'circle-aisle':
|
||||
return {
|
||||
aisleType: 'circle-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1
|
||||
} as CircleAisle;
|
||||
|
||||
case 'junction-aisle':
|
||||
return {
|
||||
aisleType: 'junction-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 'aisleWidth' in currentType ? currentType.aisleWidth : 0.1,
|
||||
isFlipped: 'isFlipped' in currentType ? (currentType as JunctionAisle).isFlipped : false
|
||||
} as JunctionAisle;
|
||||
|
||||
default:
|
||||
return {
|
||||
aisleType: 'solid-aisle',
|
||||
aisleColor: currentType.aisleColor,
|
||||
aisleWidth: 0.1
|
||||
} as SolidAisle;
|
||||
}
|
||||
};
|
||||
|
||||
const handleAisleTypeChange = (newType: AisleTypes) => {
|
||||
if (!selectedAisle?.aisleData) return;
|
||||
const newAisleType = createAisleTypeObject(newType, selectedAisleData.type);
|
||||
|
||||
const updatedAisle = updateAisle(selectedAisleData.aisleUuid, {
|
||||
type: newAisleType
|
||||
});
|
||||
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: null });
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: newAisleType
|
||||
});
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
};
|
||||
|
||||
const handleColorChange = (value: AisleColors) => {
|
||||
const updatedAisle = setColor(selectedAisleData.aisleUuid, value);
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...selectedAisleData.type,
|
||||
aisleColor: value
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
};
|
||||
|
||||
const handleAisleWidthChange = (value: string) => {
|
||||
const width = parseFloat(value);
|
||||
if (!isNaN(width) && selectedAisleData.type.aisleType !== 'dotted-aisle') {
|
||||
const updatedAisle = updateAisle(selectedAisleData.aisleUuid, {
|
||||
type: {
|
||||
...selectedAisleData.type,
|
||||
aisleWidth: width
|
||||
}
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...selectedAisleData.type,
|
||||
aisleWidth: width
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDashLengthChange = (value: string) => {
|
||||
const length = parseFloat(value);
|
||||
if (!isNaN(length) && selectedAisleData.type.aisleType === 'dashed-aisle') {
|
||||
const updatedAisle = setDashedAisleProperties(selectedAisleData.aisleUuid, {
|
||||
dashLength: length
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...(selectedAisleData.type as DashedAisle),
|
||||
dashLength: length
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleGapLengthChange = (value: string) => {
|
||||
const length = parseFloat(value);
|
||||
if (!isNaN(length) && (selectedAisleData.type.aisleType === 'dashed-aisle' || selectedAisleData.type.aisleType === 'dotted-aisle' || selectedAisleData.type.aisleType === 'arrows-aisle')) {
|
||||
if (selectedAisleData.type.aisleType === 'dashed-aisle') {
|
||||
const updatedAisle = setDashedAisleProperties(selectedAisleData.aisleUuid, {
|
||||
gapLength: length
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...(selectedAisleData.type as DashedAisle),
|
||||
gapLength: length
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
} else if (selectedAisleData.type.aisleType === 'dotted-aisle') {
|
||||
const updatedAisle = setDottedAisleProperties(selectedAisleData.aisleUuid, {
|
||||
gapLength: length
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...(selectedAisleData.type as DottedAisle),
|
||||
gapLength: length
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
} else if (selectedAisleData.type.aisleType === 'arrows-aisle') {
|
||||
const updatedAisle = setArrowsAisleProperties(selectedAisleData.aisleUuid, {
|
||||
gapLength: length
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...(selectedAisleData.type as ArrowsAisle),
|
||||
gapLength: length
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleDotRadiusChange = (value: string) => {
|
||||
const radius = parseFloat(value);
|
||||
if (!isNaN(radius) && selectedAisleData.type.aisleType === 'dotted-aisle') {
|
||||
const updatedAisle = setDottedAisleProperties(selectedAisleData.aisleUuid, {
|
||||
dotRadius: radius
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...(selectedAisleData.type as DottedAisle),
|
||||
dotRadius: radius
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleAisleLengthChange = (value: string) => {
|
||||
const length = parseFloat(value);
|
||||
if (!isNaN(length) && selectedAisleData.type.aisleType === 'arrows-aisle') {
|
||||
const updatedAisle = setArrowsAisleProperties(selectedAisleData.aisleUuid, {
|
||||
aisleLength: length
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...(selectedAisleData.type as ArrowsAisle),
|
||||
aisleLength: length
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleIsFlippedChange = () => {
|
||||
if (selectedAisleData.type.aisleType === 'arc-aisle' || selectedAisleData.type.aisleType === 'junction-aisle') {
|
||||
const currentType = selectedAisleData.type as ArcAisle | JunctionAisle;
|
||||
const currentFlipped = currentType.isFlipped || false;
|
||||
const updatedAisle = setArcAisleWidth(selectedAisleData.aisleUuid, {
|
||||
isFlipped: !currentFlipped
|
||||
});
|
||||
|
||||
setSelectedAisleData({
|
||||
...selectedAisleData,
|
||||
type: {
|
||||
...currentType,
|
||||
isFlipped: !currentFlipped
|
||||
}
|
||||
})
|
||||
|
||||
if (updatedAisle) {
|
||||
updateBackend(updatedAisle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const renderAdvancedProperties = () => {
|
||||
switch (selectedAisleData.type.aisleType) {
|
||||
case 'dashed-aisle':
|
||||
const dashedType = selectedAisleData.type as DashedAisle;
|
||||
return (
|
||||
<>
|
||||
<InputWithDropDown
|
||||
label="Dash Length"
|
||||
value={`${dashedType.dashLength}`}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleDashLengthChange}
|
||||
/>
|
||||
<InputWithDropDown
|
||||
label="Gap Length"
|
||||
value={`${dashedType.gapLength}`}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleGapLengthChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case 'dotted-aisle':
|
||||
const dottedType = selectedAisleData.type as DottedAisle;
|
||||
return (
|
||||
<>
|
||||
<InputWithDropDown
|
||||
label="Dot Radius"
|
||||
value={`${dottedType.dotRadius}`}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleDotRadiusChange}
|
||||
/>
|
||||
<InputWithDropDown
|
||||
label="Gap Length"
|
||||
value={`${dottedType.gapLength}`}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleGapLengthChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case 'arrows-aisle':
|
||||
const arrowsType = selectedAisleData.type as ArrowsAisle;
|
||||
return (
|
||||
<>
|
||||
<InputWithDropDown
|
||||
label="Arrow Length"
|
||||
value={`${arrowsType.aisleLength}`}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleAisleLengthChange}
|
||||
/>
|
||||
<InputWithDropDown
|
||||
label="Gap Length"
|
||||
value={`${arrowsType.gapLength}`}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleGapLengthChange}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
case 'junction-aisle':
|
||||
case 'arc-aisle':
|
||||
const flippedType = selectedAisleData.type as ArcAisle | JunctionAisle;
|
||||
return (
|
||||
<InputToggle
|
||||
inputKey="Flip Aisle"
|
||||
label="Flip Aisle"
|
||||
value={flippedType.isFlipped || false}
|
||||
onClick={handleIsFlippedChange}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="aisle-properties-container">
|
||||
<div className="header">Properties</div>
|
||||
|
||||
{/* Basic Properties */}
|
||||
<section>
|
||||
{selectedAisleData.type.aisleType !== 'dotted-aisle' &&
|
||||
<InputWithDropDown
|
||||
label="Aisle Width"
|
||||
value={`${selectedAisleData.type.aisleWidth || 0.5}`}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={2}
|
||||
onChange={handleAisleWidthChange}
|
||||
/>
|
||||
}
|
||||
{renderAdvancedProperties()}
|
||||
</section>
|
||||
|
||||
{/* Presets */}
|
||||
<section>
|
||||
<button
|
||||
className="header"
|
||||
onClick={() => setCollapsePresets(!collapsePresets)}
|
||||
aria-expanded={!collapsePresets}
|
||||
>
|
||||
<div className="value">Presets</div>
|
||||
<div className="icon">
|
||||
<ArrowIcon />
|
||||
</div>
|
||||
</button>
|
||||
{!collapsePresets && (
|
||||
<div className="presets-list-container">
|
||||
{aisleTypes.map((val) => (
|
||||
<div className="preset-list" key={val.id}>
|
||||
<button
|
||||
className={`thumbnail ${selectedAisleData.type.aisleType === val.type ? "selected" : ""}`}
|
||||
title={val.name}
|
||||
onClick={() => handleAisleTypeChange(val.type)}
|
||||
>
|
||||
<img src={val.thumbnail} alt="" />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
{/* Texture */}
|
||||
<section>
|
||||
<button
|
||||
className="header"
|
||||
onClick={() => setCollapseTexture(!collapseTexture)}
|
||||
aria-expanded={!collapseTexture}
|
||||
>
|
||||
<div className="value">Aisle Texture</div>
|
||||
<div className="icon" style={{ rotate: collapseTexture ? "" : "-90deg" }}>
|
||||
<ArrowIcon />
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{collapseTexture && (
|
||||
<div className="aisle-texture-container">
|
||||
{aisleTextureList.map((val) => (
|
||||
<button
|
||||
key={val.id}
|
||||
title={val.brief || val.id}
|
||||
className={`aisle-list ${selectedAisleData.type.aisleColor === val.id ? "selected" : ""}`}
|
||||
onClick={() => handleColorChange(val.id)}
|
||||
aria-pressed={selectedAisleData.type.aisleColor === val.id}
|
||||
>
|
||||
<div
|
||||
className={`texture-display ${val.id}`}
|
||||
style={{ background: val.id }}
|
||||
>
|
||||
{val.texture}
|
||||
</div>
|
||||
<div className="aisle-color">{val.color}</div>
|
||||
<div className="aisle-brief">{`( ${val.brief} )`}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectedAisleProperties;
|
||||
@@ -0,0 +1,188 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useVersionContext } from "../../../../modules/builder/version/versionContext";
|
||||
import { useSceneContext } from "../../../../modules/scene/sceneContext";
|
||||
import { useBuilderStore } from "../../../../store/builder/useBuilderStore";
|
||||
import { LayeringBottomIcon, LayeringTopIcon } from "../../../icons/ExportCommonIcons";
|
||||
import { useSocketStore } from "../../../../store/builder/store";
|
||||
import InputRange from "../../../ui/inputs/InputRange";
|
||||
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
// import { upsertWallApi } from "../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
// import { upsertFloorApi } from "../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
|
||||
const SelectedDecalProperties = () => {
|
||||
const { selectedDecal, setSelectedDecal } = useBuilderStore();
|
||||
const { wallStore, floorStore } = useSceneContext();
|
||||
const { updateDecal: updateDecalInWall } = wallStore();
|
||||
const { updateDecal: updateDecalInFloor } = floorStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
|
||||
const updateBackend = (updatedData: Wall | Floor) => {
|
||||
if ('wallUuid' in updatedData) {
|
||||
if (projectId && updatedData) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
} else if ('floorUuid' in updatedData) {
|
||||
if (projectId && updatedData) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleRotationChange = (value: number) => {
|
||||
if (!selectedDecal) return;
|
||||
const updatedDecal = { ...selectedDecal.decalData, decalRotation: value };
|
||||
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
|
||||
|
||||
if ('wallUuid' in selectedDecal.decalData.decalType) {
|
||||
const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedWall) updateBackend(updatedWall);
|
||||
} else if ('floorUuid' in selectedDecal.decalData.decalType) {
|
||||
const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedFloor) updateBackend(updatedFloor);
|
||||
}
|
||||
}
|
||||
|
||||
const handleScaleChange = (value: number) => {
|
||||
if (!selectedDecal) return;
|
||||
const updatedDecal = { ...selectedDecal.decalData, decalScale: value };
|
||||
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
|
||||
|
||||
if ('wallUuid' in selectedDecal.decalData.decalType) {
|
||||
const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedWall) updateBackend(updatedWall);
|
||||
} else if ('floorUuid' in selectedDecal.decalData.decalType) {
|
||||
const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedFloor) updateBackend(updatedFloor);
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpacityChange = (value: number) => {
|
||||
if (!selectedDecal) return;
|
||||
const updatedDecal = { ...selectedDecal.decalData, decalOpacity: value };
|
||||
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
|
||||
|
||||
if ('wallUuid' in selectedDecal.decalData.decalType) {
|
||||
const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedWall) updateBackend(updatedWall);
|
||||
} else if ('floorUuid' in selectedDecal.decalData.decalType) {
|
||||
const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedFloor) updateBackend(updatedFloor);
|
||||
}
|
||||
}
|
||||
|
||||
const handleLayerChange = (direction: "up" | "down") => {
|
||||
if (!selectedDecal) return;
|
||||
|
||||
const position: [number, number, number] = [...(selectedDecal.decalData.decalPosition || [0, 0, 0]),];
|
||||
|
||||
if (direction === "up") {
|
||||
position[2] = Math.abs(position[2]);
|
||||
} else {
|
||||
position[2] = -Math.abs(position[2]);
|
||||
}
|
||||
|
||||
const updatedDecal: Decal = { ...selectedDecal.decalData, decalPosition: position, };
|
||||
|
||||
setSelectedDecal({ ...selectedDecal, decalData: updatedDecal });
|
||||
|
||||
if ("wallUuid" in selectedDecal.decalData.decalType) {
|
||||
const updatedWall = updateDecalInWall(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedWall) updateBackend(updatedWall);
|
||||
} else if ("floorUuid" in selectedDecal.decalData.decalType) {
|
||||
const updatedFloor = updateDecalInFloor(updatedDecal.decalUuid, updatedDecal);
|
||||
if (updatedFloor) updateBackend(updatedFloor);
|
||||
}
|
||||
};
|
||||
|
||||
if (!selectedDecal) return null;
|
||||
|
||||
return (
|
||||
<div className="decal-transformation-container">
|
||||
<div className="header">Decal Properties</div>
|
||||
<section>
|
||||
<InputRange
|
||||
label="Rotation"
|
||||
value={selectedDecal.decalData.decalRotation || 0}
|
||||
min={0}
|
||||
max={360}
|
||||
step={1}
|
||||
onChange={(value: number) => handleRotationChange(value)}
|
||||
/>
|
||||
|
||||
<InputRange
|
||||
label="Scale"
|
||||
value={selectedDecal.decalData.decalScale || 1}
|
||||
min={0.1}
|
||||
max={5}
|
||||
step={0.1}
|
||||
onChange={(value: number) => handleScaleChange(value)}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<InputRange
|
||||
label="Opacity"
|
||||
value={selectedDecal.decalData.decalOpacity || 1}
|
||||
min={0.1}
|
||||
step={0.1}
|
||||
max={1}
|
||||
onChange={(value: number) => handleOpacityChange(value)}
|
||||
/>
|
||||
|
||||
<div className="transformation-wrapper opacity">
|
||||
<div className="transformation-header">Layering</div>
|
||||
|
||||
<div className="layers-list">
|
||||
<button
|
||||
className="layer-move-btn"
|
||||
onClick={() => handleLayerChange("down")}
|
||||
>
|
||||
<LayeringBottomIcon />
|
||||
</button>
|
||||
<button
|
||||
className="layer-move-btn"
|
||||
onClick={() => handleLayerChange("up")}
|
||||
>
|
||||
<LayeringTopIcon />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectedDecalProperties;
|
||||
@@ -1,18 +1,19 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
// import NavigateCatagory from '../../NavigateCatagory'
|
||||
import { EyeIcon, ForkLiftIcon, KebabIcon, LocationPinIcon, RightHalfFillCircleIcon } from '../../../../../icons/ExportCommonIcons';
|
||||
import { EyeIcon, KebabIcon, LocationPinIcon, RightHalfFillCircleIcon } from '../../../../../icons/ExportCommonIcons';
|
||||
import assetImage from "../../../../../../assets/image/asset-image.png"
|
||||
import { useSceneContext } from '../../../../../../modules/scene/sceneContext';
|
||||
import { useProductContext } from '../../../../../../modules/simulation/products/productContext';
|
||||
import RenameInput from '../../../../../ui/inputs/RenameInput';
|
||||
import { useResourceManagementId } from '../../../../../../store/builder/store';
|
||||
import { TypeBasedAssetIcons } from '../../../../../icons/AssetTypeIcons';
|
||||
const AssetManagement = () => {
|
||||
// const [selectedCategory, setSelectedCategory] = useState("All Assets");
|
||||
const [expandedAssetId, setExpandedAssetId] = useState<string | null>(null);
|
||||
const [assets, setAssets] = useState<any[]>([]);
|
||||
|
||||
const { productStore } = useSceneContext();
|
||||
const { products, getProductById } = productStore();
|
||||
const { getProductById } = productStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { setResourceManagementId } = useResourceManagementId();
|
||||
@@ -30,6 +31,7 @@ const AssetManagement = () => {
|
||||
grouped[asset.modelName] = {
|
||||
id: asset.modelUuid,
|
||||
name: asset.modelName,
|
||||
type: asset.type,
|
||||
model: asset.modelCode || "N/A",
|
||||
status: asset.status || "Online",
|
||||
usageRate: asset.usageRate || 15,
|
||||
@@ -46,6 +48,7 @@ const AssetManagement = () => {
|
||||
|
||||
setAssets(Object.values(grouped));
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}, [selectedProduct]);
|
||||
|
||||
function handleRenameAsset(newName: string) {
|
||||
@@ -130,7 +133,7 @@ const AssetManagement = () => {
|
||||
<img className='asset-image' src={asset.image} alt="" />
|
||||
</>
|
||||
:
|
||||
<div className="icon"><ForkLiftIcon /></div>
|
||||
<div className="icon"><TypeBasedAssetIcons assetType={asset.type}/></div>
|
||||
}
|
||||
<div className="asset-details-container">
|
||||
<div className="asset-details" >
|
||||
@@ -210,16 +213,10 @@ const AssetManagement = () => {
|
||||
<div className="value">{expandedAssetId === asset.id ? "View Less" : "View More"}</div>
|
||||
<div className="icon"><KebabIcon /></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
))}
|
||||
</div >
|
||||
</>
|
||||
|
||||
@@ -146,6 +146,12 @@ const Tools: React.FC = () => {
|
||||
case "draw-floor":
|
||||
is2D && setToolMode("Floor");
|
||||
break;
|
||||
case "move":
|
||||
if (!is2D) setToolMode("Move-Asset");
|
||||
break;
|
||||
case "rotate":
|
||||
if (!is2D) setToolMode("Rotate-Asset");
|
||||
break;
|
||||
case "measure":
|
||||
setToolMode("MeasurementScale");
|
||||
break;
|
||||
@@ -368,7 +374,7 @@ const Tools: React.FC = () => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
{activeModule !== "visualization" && (
|
||||
{toggleThreeD && activeModule !== "visualization" && (
|
||||
<>
|
||||
<div className="split"></div>
|
||||
<div className="transform-tools">
|
||||
|
||||
14
app/src/modules/builder/Decal/decal.tsx
Normal file
14
app/src/modules/builder/Decal/decal.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import DecalCreator from './decalCreator/decalCreator'
|
||||
|
||||
function Decal() {
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<DecalCreator />
|
||||
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Decal
|
||||
152
app/src/modules/builder/Decal/decalCreator/decalCreator.tsx
Normal file
152
app/src/modules/builder/Decal/decalCreator/decalCreator.tsx
Normal file
@@ -0,0 +1,152 @@
|
||||
import { MathUtils } from 'three';
|
||||
import { useEffect } from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useDroppedDecal, useSocketStore } from '../../../../store/builder/store';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
|
||||
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
|
||||
|
||||
function DecalCreator() {
|
||||
const { wallStore, floorStore } = useSceneContext();
|
||||
const { addDecal: addDecalOnWall, getWallById } = wallStore();
|
||||
const { addDecal: addDecalOnFloor, getFloorById } = floorStore();
|
||||
const { droppedDecal } = useDroppedDecal();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
const { controls, gl, pointer, camera, raycaster, scene } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onDrop = (event: DragEvent) => {
|
||||
if (droppedDecal) {
|
||||
pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
|
||||
pointer.y = -(event.clientY / window.innerHeight) * 2 + 1;
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||
const wallIntersect = intersects.find(i => i.object.userData && i.object.userData.wallUuid);
|
||||
const floorIntersect = intersects.find(i => i.object.userData && i.object.userData.floorUuid);
|
||||
console.log('wallIntersect: ', wallIntersect);
|
||||
|
||||
if (wallIntersect) {
|
||||
const wall = getWallById(wallIntersect.object.userData.wallUuid);
|
||||
if (!wall) return;
|
||||
|
||||
const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone());
|
||||
|
||||
const decal: Decal = {
|
||||
decalUuid: MathUtils.generateUUID(),
|
||||
decalName: droppedDecal.decalName,
|
||||
decalId: droppedDecal.decalId,
|
||||
decalType: {
|
||||
type: 'Wall',
|
||||
wallUuid: wallIntersect.object.userData.wallUuid,
|
||||
},
|
||||
decalPosition: [point.x, point.y, (wall.wallThickness / 2 + 0.001) * (wallIntersect.normal?.z || 1)],
|
||||
decalRotation: 0,
|
||||
decalOpacity: 1,
|
||||
decalScale: 1,
|
||||
}
|
||||
|
||||
addDecalOnWall(wallIntersect.object.userData.wallUuid, decal);
|
||||
|
||||
setTimeout(() => {
|
||||
const updatedWall = getWallById(wallIntersect.object.userData.wallUuid);
|
||||
if (updatedWall) {
|
||||
if (projectId && updatedWall) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
} else if (floorIntersect) {
|
||||
const floor = getFloorById(floorIntersect.object.userData.floorUuid);
|
||||
if (!floor) return;
|
||||
|
||||
const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone());
|
||||
|
||||
const decal: Decal = {
|
||||
decalUuid: MathUtils.generateUUID(),
|
||||
decalName: droppedDecal.decalName,
|
||||
decalId: droppedDecal.decalId,
|
||||
decalType: {
|
||||
type: 'Floor',
|
||||
floorUuid: floorIntersect.object.userData.floorUuid,
|
||||
},
|
||||
decalPosition: [point.x, point.y, -0.001],
|
||||
decalRotation: 0,
|
||||
decalOpacity: 1,
|
||||
decalScale: 1,
|
||||
}
|
||||
|
||||
addDecalOnFloor(floorIntersect.object.userData.floorUuid, decal);
|
||||
|
||||
setTimeout(() => {
|
||||
const updatedFloor = getFloorById(floorIntersect.object.userData.floorUuid);
|
||||
if (projectId && updatedFloor) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onDragOver = (event: any) => {
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
if (activeModule === "builder") {
|
||||
canvasElement.addEventListener("drop", onDrop);
|
||||
canvasElement.addEventListener("dragover", onDragOver);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("drop", onDrop);
|
||||
canvasElement.removeEventListener("dragover", onDragOver);
|
||||
};
|
||||
}, [droppedDecal, camera, activeModule, controls]);
|
||||
|
||||
return (
|
||||
<>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DecalCreator
|
||||
@@ -1,50 +0,0 @@
|
||||
import * as THREE from 'three';
|
||||
import { Decal } from '@react-three/drei'
|
||||
import { useLoader } from '@react-three/fiber';
|
||||
import { useToggleView } from '../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
|
||||
import defaultMaterial from '../../../assets/textures/floor/wall-tex.png';
|
||||
import useModuleStore from '../../../store/useModuleStore';
|
||||
|
||||
function DecalInstance({ visible = true, decal, zPosition = decal.decalPosition[2] }: { visible?: boolean, decal: Decal, zPosition?: number }) {
|
||||
const { setSelectedWall, setSelectedFloor, selectedDecal, setSelectedDecal } = useBuilderStore();
|
||||
const { togglView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const material = useLoader(THREE.TextureLoader, defaultMaterial);
|
||||
|
||||
return (
|
||||
<Decal
|
||||
// debug
|
||||
visible={visible}
|
||||
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
||||
rotation={[0, 0, decal.decalRotation]}
|
||||
scale={[decal.decalScale, decal.decalScale, 0.01]}
|
||||
userData={decal}
|
||||
onClick={(e) => {
|
||||
if (visible && !togglView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedDecal(e.object);
|
||||
setSelectedWall(null);
|
||||
setSelectedFloor(null);
|
||||
}
|
||||
}
|
||||
}}
|
||||
onPointerMissed={() => {
|
||||
if (selectedDecal && selectedDecal.userData.decalUuid === decal.decalUuid) {
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<meshBasicMaterial
|
||||
map={material}
|
||||
side={THREE.DoubleSide}
|
||||
polygonOffset
|
||||
polygonOffsetFactor={-1}
|
||||
/>
|
||||
</Decal>
|
||||
)
|
||||
}
|
||||
|
||||
export default DecalInstance
|
||||
172
app/src/modules/builder/Decal/decalInstance/decalInstance.tsx
Normal file
172
app/src/modules/builder/Decal/decalInstance/decalInstance.tsx
Normal file
@@ -0,0 +1,172 @@
|
||||
import * as THREE from 'three';
|
||||
import { Decal } from '@react-three/drei'
|
||||
import { useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { retrieveImage, storeImage } from '../../../../utils/indexDB/idbUtils';
|
||||
|
||||
import defaultMaterial from '../../../../assets/image/fallback/fallback decal 1.png';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useDecalEventHandlers } from '../eventHandler/useDecalEventHandlers';
|
||||
|
||||
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
|
||||
|
||||
function DecalInstance({ parent, visible = true, decal, zPosition = decal.decalPosition[2] }: { parent: Wall | Floor; visible?: boolean, decal: Decal, zPosition?: number }) {
|
||||
const url_Backend_dwinzo = `http://${process.env.REACT_APP_SERVER_MARKETPLACE_URL}`;
|
||||
const { selectedDecal, deletableDecal, setSelectedDecal, setDeletableDecal } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const decalRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedDecal?.decalData.decalUuid === decal.decalUuid && !selectedDecal.decalMesh) {
|
||||
setSelectedDecal({ decalData: selectedDecal.decalData, decalMesh: decalRef.current });
|
||||
}
|
||||
}, [selectedDecal])
|
||||
|
||||
const { handlePointerMissed, handlePointerLeave, handleClick, handlePointerDown, handlePointerEnter } = useDecalEventHandlers({ parent, decal, visible });
|
||||
|
||||
const [texture, setTexture] = useState<THREE.Texture | null>(null);
|
||||
|
||||
const logDecalStatus = (decalId: string, status: string) => {
|
||||
// console.log(decalId, status);
|
||||
}
|
||||
|
||||
const loadDefaultTexture = () => {
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
textureLoader.load(
|
||||
defaultMaterial,
|
||||
(fallbackTex) => {
|
||||
fallbackTex.name = "default-decal";
|
||||
setTexture(fallbackTex);
|
||||
logDecalStatus(decal.decalId, 'default-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
console.error("Error loading default decal texture:", error);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const loadDecalTexture = async (decalId: string) => {
|
||||
|
||||
try {
|
||||
const cachedTexture = THREE.Cache.get(decalId);
|
||||
if (cachedTexture) {
|
||||
setTexture(cachedTexture);
|
||||
logDecalStatus(decalId, 'cache-loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
const indexedDBTexture = await retrieveImage(decalId);
|
||||
if (indexedDBTexture) {
|
||||
const blobUrl = URL.createObjectURL(indexedDBTexture);
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
textureLoader.load(
|
||||
blobUrl,
|
||||
(tex) => {
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
tex.name = decalId;
|
||||
THREE.Cache.add(decalId, tex);
|
||||
setTexture(tex);
|
||||
logDecalStatus(decalId, 'indexedDB-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
console.error(`Error loading texture from IndexedDB:`, error);
|
||||
URL.revokeObjectURL(blobUrl);
|
||||
loadFromBackend(decalId);
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
loadFromBackend(decalId);
|
||||
} catch (error) {
|
||||
console.error("Error loading decal texture:", error);
|
||||
loadDefaultTexture();
|
||||
}
|
||||
};
|
||||
|
||||
const loadFromBackend = (decalId: string) => {
|
||||
|
||||
const textureUrl = `${url_Backend_dwinzo}/api/v1/DecalImage/${decalId}`;
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
|
||||
textureLoader.load(
|
||||
textureUrl,
|
||||
async (tex) => {
|
||||
tex.name = decalId;
|
||||
THREE.Cache.add(decalId, tex);
|
||||
setTexture(tex);
|
||||
logDecalStatus(decalId, 'backend-loaded');
|
||||
|
||||
try {
|
||||
const response = await fetch(textureUrl);
|
||||
const blob = await response.blob();
|
||||
await storeImage(decalId, blob);
|
||||
} catch (error) {
|
||||
console.error("Error storing texture in IndexedDB:", error);
|
||||
}
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
echo.error(`Error loading texture from backend: ${decal.decalName}`);
|
||||
loadDefaultTexture();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (decal.decalId) {
|
||||
loadDecalTexture(decal.decalId);
|
||||
} else {
|
||||
loadDefaultTexture();
|
||||
}
|
||||
}, [decal.decalId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
if (toolMode !== 'cursor') {
|
||||
if (selectedDecal) setSelectedDecal(null);
|
||||
}
|
||||
if (toolMode !== '3D-Delete') {
|
||||
if (deletableDecal) setDeletableDecal(null);
|
||||
}
|
||||
} else {
|
||||
if (selectedDecal) setSelectedDecal(null);
|
||||
if (deletableDecal) setDeletableDecal(null);
|
||||
}
|
||||
}, [toggleView, toolMode, activeModule, selectedDecal, deletableDecal]);
|
||||
|
||||
return (
|
||||
<Decal
|
||||
// debug
|
||||
visible={visible}
|
||||
ref={decalRef}
|
||||
position={[decal.decalPosition[0], decal.decalPosition[1], zPosition]}
|
||||
rotation={[0, 0, decal.decalRotation * (Math.PI / 180)]}
|
||||
scale={[(decal.decalType.type === 'Floor' || zPosition < 0) ? -decal.decalScale : decal.decalScale, decal.decalScale, 0.01]}
|
||||
userData={decal}
|
||||
onPointerDown={(e) => { if (e.button === 0) handlePointerDown(e) }}
|
||||
onClick={(e) => { handleClick(e) }}
|
||||
onPointerEnter={(e) => { handlePointerEnter(e) }}
|
||||
onPointerLeave={(e) => { handlePointerLeave(e) }}
|
||||
onPointerMissed={() => handlePointerMissed()}
|
||||
>
|
||||
<meshBasicMaterial
|
||||
map={texture}
|
||||
side={THREE.DoubleSide}
|
||||
polygonOffset
|
||||
polygonOffsetFactor={-1}
|
||||
transparent
|
||||
opacity={decal.decalOpacity}
|
||||
/>
|
||||
</Decal>
|
||||
)
|
||||
}
|
||||
|
||||
export default DecalInstance
|
||||
@@ -0,0 +1,301 @@
|
||||
import * as THREE from 'three';
|
||||
import { CameraControls } from '@react-three/drei';
|
||||
import { ThreeEvent, useFrame, useThree } from '@react-three/fiber';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useSocketStore, useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { getUserData } from '../../../../functions/getUserData';
|
||||
import { useVersionContext } from '../../version/versionContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
|
||||
// import { upsertWallApi } from '../../../../services/factoryBuilder/wall/upsertWallApi';
|
||||
// import { upsertFloorApi } from '../../../../services/factoryBuilder/floor/upsertFloorApi';
|
||||
|
||||
export function useDecalEventHandlers({
|
||||
parent,
|
||||
decal,
|
||||
visible,
|
||||
}: {
|
||||
parent: Wall | Floor;
|
||||
decal: Decal;
|
||||
visible: boolean;
|
||||
}) {
|
||||
const { wallStore, floorStore } = useSceneContext();
|
||||
const { removeDecal: removeDecalInWall, updateDecalPosition: updateDecalPositionInWall, getWallById, addDecal: addDecalToWall } = wallStore();
|
||||
const { removeDecal: removeDecalInFloor, updateDecalPosition: updateDecalPositionInFloor, getFloorById, addDecal: addDecalToFloor } = floorStore();
|
||||
const { setSelectedWall, setSelectedFloor, setSelectedDecal, setDeletableDecal, deletableDecal, selectedDecal, setDecalDragState, decalDragState } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
const { raycaster, pointer, camera, scene, gl, controls } = useThree();
|
||||
|
||||
useFrame(() => {
|
||||
if (activeModule !== 'builder' || toggleView || !decalDragState.isDragging || !selectedDecal || selectedDecal.decalData.decalUuid !== decal.decalUuid) return;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||
|
||||
const wallIntersect = intersects.find(i => i.object.userData?.wallUuid);
|
||||
const floorIntersect = intersects.find(i => i.object.userData?.floorUuid);
|
||||
|
||||
let offset = decalDragState.dragOffset || new THREE.Vector3(0, 0, 0);
|
||||
|
||||
if (wallIntersect) {
|
||||
const wallUuid = wallIntersect.object.userData.wallUuid;
|
||||
const point = wallIntersect.object.worldToLocal(wallIntersect.point.clone());
|
||||
|
||||
if ("wallUuid" in parent && parent.wallUuid === wallUuid && decal.decalType.type === 'Wall') {
|
||||
updateDecalPositionInWall(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]);
|
||||
} else if (decal.decalType.type === 'Wall' && wallUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
|
||||
const addedDecal = addDecalToWall(wallUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]],
|
||||
decalType: { type: 'Wall', wallUuid: wallUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
} else if (decal.decalType.type === 'Floor' && wallUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
const wall = getWallById(wallUuid);
|
||||
if (!wall) return;
|
||||
|
||||
const addedDecal = addDecalToWall(wallUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, wall.wallThickness / 2 + 0.001],
|
||||
decalType: { type: 'Wall', wallUuid: wallUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
}
|
||||
} else if (floorIntersect) {
|
||||
const floorUuid = floorIntersect.object.userData.floorUuid;
|
||||
const point = floorIntersect.object.worldToLocal(floorIntersect.point.clone());
|
||||
|
||||
if ("floorUuid" in parent && parent.floorUuid === floorUuid && decal.decalType.type === 'Floor') {
|
||||
updateDecalPositionInFloor(decal.decalUuid, [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]]);
|
||||
} else if (decal.decalType.type === 'Floor' && floorUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
|
||||
const addedDecal = addDecalToFloor(floorUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, decal.decalPosition[2]],
|
||||
decalType: { type: 'Floor', floorUuid: floorUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
} else if (decal.decalType.type === 'Wall' && floorUuid) {
|
||||
deleteDecal(decal.decalUuid, parent);
|
||||
const floor = getFloorById(floorUuid);
|
||||
if (!floor) return;
|
||||
|
||||
const addedDecal = addDecalToFloor(floorUuid, {
|
||||
...decal,
|
||||
decalPosition: [point.x + offset.x, point.y + offset.y, -0.001],
|
||||
decalType: { type: 'Floor', floorUuid: floorUuid }
|
||||
});
|
||||
|
||||
if (addedDecal) {
|
||||
setSelectedDecal({ decalMesh: null, decalData: addedDecal })
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const handlePointerUp = (e: PointerEvent) => {
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
if (decalDragState.isDragging) {
|
||||
setDecalDragState(false, null, null);
|
||||
|
||||
if ('wallUuid' in parent) {
|
||||
setTimeout(() => {
|
||||
const updatedWall = getWallById(parent.wallUuid);
|
||||
if (updatedWall) {
|
||||
if (projectId && updatedWall) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
} else if ('floorUuid' in parent) {
|
||||
setTimeout(() => {
|
||||
const updatedFloor = parent;
|
||||
|
||||
if (projectId && updatedFloor) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const deleteDecal = (decalUuid: string, parent: Wall | Floor) => {
|
||||
if ('wallUuid' in parent) {
|
||||
const updatedWall = removeDecalInWall(decalUuid);
|
||||
|
||||
if (projectId && updatedWall) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedWall);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedWall,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
} else if ('floorUuid' in parent) {
|
||||
const updatedFloor = removeDecalInFloor(decalUuid);
|
||||
|
||||
if (projectId && updatedFloor) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedFloor);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedFloor,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handlePointerDown = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid && toolMode === 'cursor') {
|
||||
e.stopPropagation();
|
||||
setDecalDragState(true, decal.decalUuid, null);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
}
|
||||
setSelectedDecal({ decalMesh: e.object, decalData: decal });
|
||||
setSelectedWall(null);
|
||||
setSelectedFloor(null);
|
||||
|
||||
const localIntersect = e.object.worldToLocal(e.point.clone());
|
||||
let dragOffset = new THREE.Vector3(decal.decalPosition[0] - localIntersect.x, decal.decalPosition[1] - localIntersect.y, 0);
|
||||
setDecalDragState(true, decal.decalUuid, dragOffset);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleClick = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
if (toolMode === 'cursor') {
|
||||
setSelectedDecal({ decalMesh: e.object, decalData: decal });
|
||||
setSelectedWall(null);
|
||||
setSelectedFloor(null);
|
||||
} else if (toolMode === '3D-Delete') {
|
||||
deleteDecal(e.object.userData.decalUuid, parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerEnter = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
if (toolMode === '3D-Delete') {
|
||||
setDeletableDecal(e.object);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerLeave = (e: ThreeEvent<MouseEvent>) => {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.decalUuid) {
|
||||
e.stopPropagation();
|
||||
if (toolMode === '3D-Delete' && deletableDecal && deletableDecal?.userData.decalUuid === e.object.userData.decalUuid) {
|
||||
setDeletableDecal(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerMissed = () => {
|
||||
if (selectedDecal && selectedDecal.decalMesh && selectedDecal.decalMesh.userData.decalUuid === decal.decalUuid) {
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
if (activeModule === 'builder' && !toggleView && selectedDecal && selectedDecal.decalData.decalUuid === decal.decalUuid) {
|
||||
canvasElement.addEventListener('pointerup', handlePointerUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('pointerup', handlePointerUp);
|
||||
};
|
||||
}, [gl, activeModule, toggleView, selectedDecal, camera, controls, visible, parent, decal, decalDragState]);
|
||||
|
||||
return {
|
||||
handlePointerDown,
|
||||
handleClick,
|
||||
handlePointerEnter,
|
||||
handlePointerLeave,
|
||||
handlePointerMissed,
|
||||
deleteDecal
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function ArcAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const arc = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arc-aisle') return null;
|
||||
@@ -63,8 +69,8 @@ function ArcAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const arrow = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'arrow-aisle') return null;
|
||||
@@ -50,8 +56,8 @@ function ArrowAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Instances, Instance } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const { arrowGeometry, arrowInstances } = useMemo(() => {
|
||||
const result = {
|
||||
@@ -68,8 +74,8 @@ function ArrowsAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const circle = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'circle-aisle') return null;
|
||||
@@ -38,8 +44,8 @@ function CircleAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Instances, Instance } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const dashInstances = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dashed-aisle') return [];
|
||||
@@ -43,8 +49,8 @@ function DashedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Instance, Instances } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const dotPositions = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'dotted-aisle') return [];
|
||||
@@ -27,8 +33,8 @@ function DottedAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,13 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const arrows = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'junction-aisle') return null;
|
||||
@@ -85,8 +91,8 @@ function JunctionAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { Extrude } from '@react-three/drei';
|
||||
import * as Constants from '../../../../../../types/world/worldConstants';
|
||||
import { useToolMode } from '../../../../../../store/builder/store';
|
||||
@@ -8,7 +8,14 @@ import { useBuilderStore } from '../../../../../../store/builder/useBuilderStore
|
||||
function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
const aisleRef = useRef<THREE.Group>(null);
|
||||
const { toolMode } = useToolMode();
|
||||
const { setSelectedAisle, hoveredPoint } = useBuilderStore();
|
||||
const { setSelectedAisle, hoveredPoint, selectedAisle } = useBuilderStore();
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAisle?.aisleData.aisleUuid === aisle.aisleUuid && !selectedAisle.aisleMesh) {
|
||||
setSelectedAisle({ aisleData: selectedAisle.aisleData, aisleMesh: aisleRef.current });
|
||||
}
|
||||
}, [selectedAisle])
|
||||
|
||||
const shape = useMemo(() => {
|
||||
if (aisle.points.length < 2 || aisle.type.aisleType !== 'solid-aisle') return null;
|
||||
|
||||
@@ -35,8 +42,8 @@ function SolidAisle({ aisle }: { readonly aisle: Aisle }) {
|
||||
}, [aisle]);
|
||||
|
||||
const handleClick = () => {
|
||||
if (toolMode === 'move' && !hoveredPoint) {
|
||||
setSelectedAisle(aisleRef.current);
|
||||
if ((toolMode === 'move' || toolMode === 'cursor') && !hoveredPoint) {
|
||||
setSelectedAisle({ aisleMesh: aisleRef.current, aisleData: aisle });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -363,6 +363,7 @@ function AssetsGroup({ plane }: { readonly plane: RefMesh }) {
|
||||
setLeft(relativeX);
|
||||
}
|
||||
};
|
||||
|
||||
const onMouseUp = (evt: any) => {
|
||||
setIsRenameMode(false);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function useModelEventHandlers({
|
||||
const { socket } = useSocketStore();
|
||||
const { eventStore, productStore, assetStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { getAssetById, removeAsset } = assetStore();
|
||||
const { removeAsset } = assetStore();
|
||||
const { zoneAssetId, setZoneAssetId } = useZoneAssetId();
|
||||
const { resourceManagementId, setResourceManagementId } = useResourceManagementId();
|
||||
const { removeEvent, getEventByModelUuid } = eventStore();
|
||||
@@ -77,6 +77,7 @@ export function useModelEventHandlers({
|
||||
}
|
||||
|
||||
}, [zoneAssetId])
|
||||
|
||||
useEffect(() => {
|
||||
if (!resourceManagementId) return
|
||||
if (resourceManagementId === asset.modelUuid) {
|
||||
|
||||
@@ -88,6 +88,10 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
}
|
||||
}, [gltfScene]);
|
||||
|
||||
const logModelStatus = (modelId: string, status: string) => {
|
||||
// console.log(modelId, status);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Calculate Bounding Box
|
||||
const calculateBoundingBox = (scene: THREE.Object3D) => {
|
||||
@@ -103,6 +107,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
clone.animations = cachedModel.animations || [];
|
||||
setGltfScene(clone);
|
||||
calculateBoundingBox(clone);
|
||||
logModelStatus(assetId, 'cache-loaded');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -118,6 +123,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
THREE.Cache.add(assetId, gltf);
|
||||
setGltfScene(gltf.scene.clone());
|
||||
calculateBoundingBox(gltf.scene);
|
||||
logModelStatus(assetId, 'indexedDB-loaded');
|
||||
},
|
||||
undefined,
|
||||
(error) => {
|
||||
@@ -140,6 +146,7 @@ function Model({ asset, isRendered, loader }: { readonly asset: Asset, isRendere
|
||||
THREE.Cache.add(assetId, gltf);
|
||||
setGltfScene(gltf.scene.clone());
|
||||
calculateBoundingBox(gltf.scene);
|
||||
logModelStatus(assetId, 'backend-loaded');
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
|
||||
@@ -33,13 +33,14 @@ import AssetsGroup from "./asset/assetsGroup";
|
||||
import DxfFile from "./dfx/LoadBlueprint";
|
||||
import AislesGroup from "./aisle/aislesGroup";
|
||||
import WallGroup from "./wall/wallGroup";
|
||||
import WallAssetGroup from "./wallAsset/wallAssetGroup";
|
||||
import FloorGroup from "./floor/floorGroup";
|
||||
import ZoneGroup from "./zone/zoneGroup";
|
||||
import Decal from "./Decal/decal";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useBuilderStore } from "../../store/builder/useBuilderStore";
|
||||
import { getUserData } from "../../functions/getUserData";
|
||||
import WallAssetGroup from "./wallAsset/wallAssetGroup";
|
||||
|
||||
export default function Builder() {
|
||||
const state = useThree<Types.ThreeState>();
|
||||
@@ -106,6 +107,8 @@ export default function Builder() {
|
||||
</Geometry>
|
||||
</mesh>
|
||||
|
||||
<Decal />
|
||||
|
||||
<AislesGroup />
|
||||
|
||||
<FloorGroup />
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useMemo } from "react";
|
||||
import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, } from "three";
|
||||
import { Shape, Vector2, DoubleSide, TextureLoader, RepeatWrapping, SRGBColorSpace, NoColorSpace, ExtrudeGeometry, Vector3, Euler, } from "three";
|
||||
import { useLoader } from "@react-three/fiber";
|
||||
import { Extrude } from "@react-three/drei";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
import { useBuilderStore } from "../../../../../store/builder/useBuilderStore";
|
||||
import { useToggleView } from "../../../../../store/builder/store";
|
||||
import * as Constants from "../../../../../types/world/worldConstants";
|
||||
|
||||
import DecalInstance from "../../../Decal/decalInstance/decalInstance";
|
||||
|
||||
import texturePath from "../../../../../assets/textures/floor/white.png";
|
||||
import texturePathDark from "../../../../../assets/textures/floor/black.png";
|
||||
import material1 from "../../../../../assets/textures/floor/factory wall texture.jpg";
|
||||
@@ -28,7 +29,7 @@ import material4MetalicMap from "../../../../../assets/textures/floor/tex3/metal
|
||||
import material4NormalMap from "../../../../../assets/textures/floor/tex3/metal_plate_nor_gl_1k.png";
|
||||
|
||||
function FloorInstance({ floor }: { floor: Floor }) {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedFloor, setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
||||
const savedTheme = localStorage.getItem("theme");
|
||||
@@ -67,20 +68,26 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
},
|
||||
};
|
||||
|
||||
const shape = useMemo(() => {
|
||||
const shape = new Shape();
|
||||
const shapeData = useMemo(() => {
|
||||
const points = floor.points.map((p) => new Vector2(p.position[0], p.position[2]));
|
||||
if (points.length < 3) return null;
|
||||
shape.moveTo(points[0].x, points[0].y);
|
||||
for (let i = 1; i < points.length; i++) {
|
||||
shape.lineTo(points[i].x, points[i].y);
|
||||
|
||||
const centroidX = points.reduce((sum, p) => sum + p.x, 0) / points.length;
|
||||
const centroidY = points.reduce((sum, p) => sum + p.y, 0) / points.length;
|
||||
|
||||
const relativePoints = points.map((p) => new Vector2(p.x - centroidX, p.y - centroidY));
|
||||
|
||||
const shape = new Shape();
|
||||
shape.moveTo(relativePoints[0].x, relativePoints[0].y);
|
||||
for (let i = 1; i < relativePoints.length; i++) {
|
||||
shape.lineTo(relativePoints[i].x, relativePoints[i].y);
|
||||
}
|
||||
return shape;
|
||||
|
||||
return { shape, center: [centroidX, centroidY] };
|
||||
}, [floor]);
|
||||
|
||||
const textureScale = Constants.floorConfig.textureScale;
|
||||
|
||||
// Helper function to handle texture maps and filter out null values
|
||||
function getMaterialMaps(material: any, defaultMap: any) {
|
||||
const materialMap = material.map || defaultMap;
|
||||
const normalMap = material.normalMap || null;
|
||||
@@ -90,26 +97,18 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
return [materialMap, normalMap, roughnessMap, metalnessMap].filter((texture): texture is string => texture !== null);
|
||||
}
|
||||
|
||||
// Default material map
|
||||
const defaultMaterialMap = materials["Default Material"].map;
|
||||
|
||||
// Get top and side material maps
|
||||
const topMaterial = materials[floor.topMaterial];
|
||||
const sideMaterial = materials[floor.sideMaterial];
|
||||
|
||||
// Get the filtered lists for top and side textures
|
||||
const topTexturesList = getMaterialMaps(topMaterial, defaultMaterialMap);
|
||||
const sideTexturesList = getMaterialMaps(sideMaterial, defaultMaterialMap);
|
||||
|
||||
// Use loader to load top and side textures
|
||||
const [topTexture, topNormalTexture, topRoughnessTexture, topMetalicTexture] = useLoader(TextureLoader, topTexturesList);
|
||||
|
||||
const [sideTexture, sideNormalTexture, sideRoughnessTexture, sideMetalicTexture] = useLoader(TextureLoader, sideTexturesList);
|
||||
|
||||
// Early exit if materials are missing
|
||||
if (!materials[floor.topMaterial] || !materials[floor.sideMaterial]) return null;
|
||||
|
||||
// Combine and pair textures with their corresponding material
|
||||
const textureMaterialMap = [
|
||||
{
|
||||
textures: [
|
||||
@@ -131,7 +130,6 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
},
|
||||
];
|
||||
|
||||
// Apply texture settings
|
||||
textureMaterialMap.forEach(({ textures, materialKey }) => {
|
||||
const tileScale = materials[materialKey]?.textureTileScale ?? [
|
||||
textureScale,
|
||||
@@ -143,23 +141,39 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
tex.wrapS = tex.wrapT = RepeatWrapping;
|
||||
tex.repeat.set(tileScale[0], tileScale[1]);
|
||||
tex.anisotropy = 16;
|
||||
// First texture is always the color map (use SRGB), others should be linear
|
||||
tex.colorSpace = idx < 1 ? SRGBColorSpace : NoColorSpace;
|
||||
});
|
||||
});
|
||||
|
||||
if (!shape) return null;
|
||||
const geometry = useMemo(() => {
|
||||
if (!shapeData) return null;
|
||||
return new ExtrudeGeometry(shapeData.shape, {
|
||||
depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1,
|
||||
bevelEnabled: floor.isBeveled,
|
||||
bevelSegments: floor.bevelStrength,
|
||||
bevelOffset: -0.1,
|
||||
bevelSize: 0.1,
|
||||
bevelThickness: 0.1,
|
||||
});
|
||||
}, [shapeData, floor]);
|
||||
|
||||
if (!geometry) return null;
|
||||
|
||||
return (
|
||||
<mesh
|
||||
castShadow
|
||||
receiveShadow
|
||||
geometry={geometry}
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
rotation={[Math.PI / 2, 0, 0]}
|
||||
position={[0, !floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2, 0,]}
|
||||
position={[
|
||||
shapeData?.center[0] ?? 0,
|
||||
!floor.isBeveled ? floor.floorDepth - 0.1 : floor.floorDepth - 0.2,
|
||||
shapeData?.center[1] ?? 0,
|
||||
]}
|
||||
userData={floor}
|
||||
onDoubleClick={(e) => {
|
||||
if (!togglView && activeModule === "builder") {
|
||||
if (!toggleView && activeModule === "builder") {
|
||||
if (e.object.userData.floorUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedFloor(e.object);
|
||||
@@ -173,41 +187,32 @@ function FloorInstance({ floor }: { floor: Floor }) {
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Extrude
|
||||
name={`Floor-${floor.floorUuid}`}
|
||||
args={[shape,
|
||||
{
|
||||
depth: !floor.isBeveled ? floor.floorDepth : floor.floorDepth - 0.1,
|
||||
bevelEnabled: floor.isBeveled,
|
||||
bevelSegments: floor.bevelStrength,
|
||||
bevelOffset: -0.1,
|
||||
bevelSize: 0.1,
|
||||
bevelThickness: 0.1,
|
||||
},
|
||||
]}
|
||||
userData={floor}
|
||||
>
|
||||
<meshPhysicalMaterial
|
||||
attach="material-0"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={topTexture}
|
||||
roughnessMap={topRoughnessTexture}
|
||||
metalnessMap={topMetalicTexture}
|
||||
normalMap={topNormalTexture}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
<meshStandardMaterial
|
||||
attach="material-1"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={sideTexture?.clone()}
|
||||
roughnessMap={sideRoughnessTexture?.clone()}
|
||||
metalnessMap={sideMetalicTexture?.clone()}
|
||||
normalMap={sideNormalTexture?.clone()}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
</Extrude>
|
||||
<meshPhysicalMaterial
|
||||
attach="material-0"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={topTexture}
|
||||
roughnessMap={topRoughnessTexture}
|
||||
metalnessMap={topMetalicTexture}
|
||||
normalMap={topNormalTexture}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
<meshStandardMaterial
|
||||
attach="material-1"
|
||||
color={Constants.floorConfig.defaultColor}
|
||||
map={sideTexture?.clone()}
|
||||
roughnessMap={sideRoughnessTexture?.clone()}
|
||||
metalnessMap={sideMetalicTexture?.clone()}
|
||||
normalMap={sideNormalTexture?.clone()}
|
||||
roughness={1.5}
|
||||
metalness={1.0}
|
||||
side={DoubleSide}
|
||||
/>
|
||||
|
||||
{floor.decals.map((decal) => (
|
||||
<DecalInstance parent={floor} key={decal.decalUuid} decal={decal} />
|
||||
))}
|
||||
</mesh>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,21 +2,36 @@ import React, { useEffect, useMemo } from 'react';
|
||||
import { Vector3 } from 'three';
|
||||
import { Html } from '@react-three/drei';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import { useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import Line from '../../line/line';
|
||||
import Point from '../../point/point';
|
||||
import FloorInstance from './Instance/floorInstance';
|
||||
import Floor2DInstance from './Instance/floor2DInstance';
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
|
||||
function FloorInstances() {
|
||||
const { floorStore } = useSceneContext();
|
||||
const { floors } = floorStore();
|
||||
const { setSelectedFloor, selectedFloor } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('floors: ', floors);
|
||||
}, [floors])
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
if (toolMode !== 'cursor') {
|
||||
if (selectedFloor) setSelectedFloor(null);
|
||||
}
|
||||
} else {
|
||||
if (selectedFloor) setSelectedFloor(null);
|
||||
}
|
||||
}, [toggleView, toolMode, activeModule, selectedFloor]);
|
||||
|
||||
const allPoints = useMemo(() => {
|
||||
const points: Point[] = [];
|
||||
const seenUuids = new Set<string>();
|
||||
|
||||
@@ -10,7 +10,7 @@ import FloorInstances from './Instances/floorInstances';
|
||||
import { getFloorsApi } from '../../../services/factoryBuilder/floor/getFloorsApi';
|
||||
|
||||
function FloorGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedFloor, setSelectedDecal } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
@@ -21,11 +21,11 @@ function FloorGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
if (toggleView || activeModule !== 'builder') {
|
||||
setSelectedFloor(null);
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, activeTool])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useToggleView, useWallVisibility } from '../../../../../store/builder/s
|
||||
import { useBuilderStore } from '../../../../../store/builder/useBuilderStore';
|
||||
import * as Constants from '../../../../../types/world/worldConstants';
|
||||
|
||||
import DecalInstance from '../../../Decal/decalInstance';
|
||||
import DecalInstance from '../../../Decal/decalInstance/decalInstance';
|
||||
|
||||
import defaultMaterial from '../../../../../assets/textures/floor/wall-tex.png';
|
||||
import material1 from '../../../../../assets/textures/floor/factory wall texture.jpg';
|
||||
@@ -21,7 +21,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
const { wallAssets, getWallAssetsByWall, setVisibility } = wallAssetStore();
|
||||
const assets = getWallAssetsByWall(wall.wallUuid);
|
||||
const { selectedWall, setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { camera } = useThree();
|
||||
const { wallVisibility } = useWallVisibility();
|
||||
@@ -118,10 +118,11 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
>
|
||||
{(assets.length > 0 || (walls[0].wallUuid === wall.wallUuid && wallAssets.length > 0)) ?
|
||||
<Base
|
||||
name={`BaseWall${wall.wallUuid}`}
|
||||
castShadow
|
||||
receiveShadow
|
||||
ref={meshRef}
|
||||
geometry={geometry}
|
||||
geometry={visible ? geometry : new THREE.BoxGeometry(0, 0)}
|
||||
position={[centerX, centerY, centerZ]}
|
||||
rotation={[0, -angle, 0]}
|
||||
userData={wall}
|
||||
@@ -154,7 +155,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
userData={wall}
|
||||
name={`WallReference_${wall.wallUuid}`}
|
||||
onDoubleClick={(e) => {
|
||||
if (visible && !togglView && activeModule === 'builder') {
|
||||
if (visible && !toggleView && activeModule === 'builder') {
|
||||
if (e.object.userData.wallUuid) {
|
||||
e.stopPropagation();
|
||||
setSelectedWall(e.object);
|
||||
@@ -171,7 +172,7 @@ function Wall({ wall }: { readonly wall: Wall }) {
|
||||
<MeshDiscardMaterial />
|
||||
|
||||
{wall.decals.map((decal) => (
|
||||
<DecalInstance zPosition={wall.wallThickness / 2 + 0.001} visible={visible} key={decal.decalUuid} decal={decal} />
|
||||
<DecalInstance parent={wall} visible={visible} key={decal.decalUuid} decal={decal} />
|
||||
))}
|
||||
</mesh>
|
||||
</mesh>
|
||||
|
||||
@@ -2,8 +2,9 @@ import React, { useEffect, useMemo } from 'react';
|
||||
import { DoubleSide, RepeatWrapping, Shape, SRGBColorSpace, TextureLoader, Vector2, Vector3 } from 'three';
|
||||
import { Html, Extrude } from '@react-three/drei';
|
||||
import { useLoader } from '@react-three/fiber';
|
||||
import { useBuilderStore } from '../../../../store/builder/useBuilderStore';
|
||||
import { useSceneContext } from '../../../scene/sceneContext';
|
||||
import { useToggleView } from '../../../../store/builder/store';
|
||||
import { useToggleView, useToolMode } from '../../../../store/builder/store';
|
||||
import { useWallClassification } from './instance/helpers/useWallClassification';
|
||||
import Line from '../../line/line';
|
||||
import Point from '../../point/point';
|
||||
@@ -12,17 +13,31 @@ import * as Constants from '../../../../types/world/worldConstants';
|
||||
|
||||
import texturePath from "../../../../assets/textures/floor/white.png";
|
||||
import texturePathDark from "../../../../assets/textures/floor/black.png";
|
||||
import useModuleStore from '../../../../store/useModuleStore';
|
||||
|
||||
function WallInstances() {
|
||||
const { wallStore } = useSceneContext();
|
||||
const { setSelectedWall, selectedWall } = useBuilderStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { walls } = wallStore();
|
||||
const { rooms } = useWallClassification(walls);
|
||||
const { toggleView } = useToggleView();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('walls: ', walls);
|
||||
}, [walls])
|
||||
|
||||
useEffect(() => {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
if (toolMode !== 'cursor') {
|
||||
if (selectedWall) setSelectedWall(null);
|
||||
}
|
||||
} else {
|
||||
if (selectedWall) setSelectedWall(null);
|
||||
}
|
||||
}, [toggleView, toolMode, activeModule, selectedWall]);
|
||||
|
||||
const allPoints = useMemo(() => {
|
||||
const points: Point[] = [];
|
||||
const seenUuids = new Set<string>();
|
||||
|
||||
@@ -11,7 +11,7 @@ import WallInstances from './Instances/wallInstances';
|
||||
import { getWallsApi } from '../../../services/factoryBuilder/wall/getWallsApi';
|
||||
|
||||
function WallGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedWall, setSelectedDecal } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
@@ -22,11 +22,11 @@ function WallGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
if (toggleView || activeModule !== 'builder') {
|
||||
setSelectedWall(null);
|
||||
setSelectedDecal(null);
|
||||
}
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, activeTool])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -18,10 +18,10 @@ const calculateAssetTransformationOnWall = (
|
||||
|
||||
const projection = initialWallNormalized.clone().multiplyScalar(dotProduct);
|
||||
const perpendicular = new THREE.Vector3().subVectors(assetVector, projection);
|
||||
const distanceFromWall = perpendicular.length();
|
||||
const distanceInWall = perpendicular.length();
|
||||
|
||||
const crossProduct = new THREE.Vector3().crossVectors(initialWallNormalized, perpendicular).y;
|
||||
const signedDistance = distanceFromWall * (crossProduct >= 0 ? 1 : -1);
|
||||
const signedDistance = distanceInWall * (crossProduct >= 0 ? 1 : -1);
|
||||
|
||||
const percentage = Math.max(0, Math.min(1, dotProduct / initialWallLength));
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||
const { raycaster, pointer, camera, scene, controls, gl } = useThree();
|
||||
const { wallStore, wallAssetStore } = useSceneContext();
|
||||
const { walls, getWallById } = wallStore();
|
||||
const { updateWallAsset, removeWallAsset } = wallAssetStore();
|
||||
const { updateWallAsset, removeWallAsset, getWallAssetById } = wallAssetStore();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { activeModule } = useModuleStore();
|
||||
@@ -116,33 +116,14 @@ function WallAssetInstance({ wallAsset }: { wallAsset: WallAsset }) {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerUp = (e: PointerEvent) => {
|
||||
draggingRef.current = false;
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
if (draggingRef.current) {
|
||||
draggingRef.current = false;
|
||||
if (controls) {
|
||||
(controls as any).enabled = true;
|
||||
}
|
||||
|
||||
if (selectedWallAsset) {
|
||||
pointer.x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1;
|
||||
|
||||
raycaster.setFromCamera(pointer, camera);
|
||||
const intersects = raycaster.intersectObjects(scene.children, true);
|
||||
const intersect = intersects.find((i: any) => i.object.name.includes('WallReference'));
|
||||
|
||||
if (intersect && intersect.object.userData.wallUuid && selectedWallAsset.userData.modelUuid === wallAsset.modelUuid) {
|
||||
const newPoint = closestPointOnLineSegment(
|
||||
new THREE.Vector3(intersect.point.x, 0, intersect.point.z),
|
||||
new THREE.Vector3(...intersect.object.userData.points[0].position),
|
||||
new THREE.Vector3(...intersect.object.userData.points[1].position)
|
||||
);
|
||||
|
||||
const wallRotation = intersect.object.rotation.clone();
|
||||
|
||||
const updatedWallAsset = updateWallAsset(wallAsset.modelUuid, {
|
||||
wallUuid: intersect.object.userData.wallUuid,
|
||||
position: [newPoint.x, wallAsset.wallAssetType === 'fixedMove' ? 0 : intersect.point.y, newPoint.z],
|
||||
rotation: [wallRotation.x, wallRotation.y, wallRotation.z],
|
||||
});
|
||||
if (selectedWallAsset) {
|
||||
const updatedWallAsset = getWallAssetById(wallAsset.modelUuid);
|
||||
|
||||
if (projectId && updatedWallAsset) {
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import closestPointOnLineSegment from '../line/helpers/getClosestPointOnLineSegm
|
||||
function WallAssetCreator() {
|
||||
const { socket } = useSocketStore();
|
||||
const { pointer, camera, raycaster, scene, gl } = useThree();
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { wallAssetStore } = useSceneContext();
|
||||
const { addWallAsset } = wallAssetStore();
|
||||
@@ -84,7 +84,7 @@ function WallAssetCreator() {
|
||||
}
|
||||
};
|
||||
|
||||
if (!togglView && activeModule === 'builder') {
|
||||
if (!toggleView && activeModule === 'builder') {
|
||||
canvasElement.addEventListener('drop', onDrop);
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ function WallAssetCreator() {
|
||||
canvasElement.removeEventListener('drop', onDrop);
|
||||
};
|
||||
|
||||
}, [gl, camera, togglView, activeModule, socket, selectedItem, setSelectedItem]);
|
||||
}, [gl, camera, toggleView, activeModule, socket, selectedItem, setSelectedItem]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useActiveTool, useToggleView } from '../../../store/builder/store';
|
||||
import { useToggleView, useToolMode } from '../../../store/builder/store';
|
||||
import { useBuilderStore } from '../../../store/builder/useBuilderStore';
|
||||
import { useVersionContext } from '../version/versionContext';
|
||||
import { useSceneContext } from '../../scene/sceneContext';
|
||||
@@ -10,10 +10,10 @@ import WallAssetInstances from './Instances/wallAssetInstances'
|
||||
import { getWallAssetsApi } from '../../../services/factoryBuilder/asset/wallAsset/getWallAssetsApi';
|
||||
|
||||
function WallAssetGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { setSelectedFloorAsset, setDeletableWallAsset } = useBuilderStore();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedWallAsset, setDeletableWallAsset } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { wallAssetStore } = useSceneContext();
|
||||
@@ -21,11 +21,11 @@ function WallAssetGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
setSelectedFloorAsset(null);
|
||||
if (toggleView || activeModule !== 'builder' || toolMode !== 'cursor') {
|
||||
setSelectedWallAsset(null);
|
||||
}
|
||||
setDeletableWallAsset(null);
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, toolMode])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -11,7 +11,7 @@ import ZoneInstances from './Instances/zoneInstances';
|
||||
import { getZonesApi } from '../../../services/factoryBuilder/zone/getZonesApi';
|
||||
|
||||
function ZoneGroup() {
|
||||
const { togglView } = useToggleView();
|
||||
const { toggleView } = useToggleView();
|
||||
const { setSelectedZone } = useBuilderStore();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { activeTool } = useActiveTool();
|
||||
@@ -22,10 +22,10 @@ function ZoneGroup() {
|
||||
const { projectId } = useParams();
|
||||
|
||||
useEffect(() => {
|
||||
if (togglView || activeModule !== 'builder') {
|
||||
if (toggleView || activeModule !== 'builder') {
|
||||
setSelectedZone(null);
|
||||
}
|
||||
}, [togglView, activeModule, activeTool])
|
||||
}, [toggleView, activeModule, activeTool])
|
||||
|
||||
useEffect(() => {
|
||||
if (projectId && selectedVersion) {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useKeyboardControls } from "@react-three/drei";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import { useCamMode, useToggleView } from "../../../store/builder/store";
|
||||
import { useKeyboardControls } from "@react-three/drei";
|
||||
import switchToThirdPerson from "./switchToThirdPerson";
|
||||
import switchToFirstPerson from "./switchToFirstPerson";
|
||||
|
||||
import switchToThirdPerson from "./functions/switchToThirdPerson";
|
||||
import switchToFirstPerson from "./functions/switchToFirstPerson";
|
||||
import { detectModifierKeys } from "../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { firstPersonCamera } from "./firstPersonCamera";
|
||||
import { firstPersonCamera } from "./functions/firstPersonCamera";
|
||||
|
||||
const CamMode: React.FC = () => {
|
||||
const { camMode, setCamMode } = useCamMode();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import * as CONSTANTS from "../../../../types/world/worldConstants";
|
||||
|
||||
interface FirstPersonCameraProps {
|
||||
setIsTransitioning?: (value: boolean) => void;
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
|
||||
export default async function switchToFirstPerson(
|
||||
controls: any,
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as THREE from 'three';
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
import * as CONSTANTS from '../../../../types/world/worldConstants';
|
||||
|
||||
export default async function switchToThirdPerson(
|
||||
controls: any,
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Socket } from "socket.io-client";
|
||||
import * as THREE from "three";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
|
||||
export default function updateCamPosition(
|
||||
controls: any,
|
||||
@@ -3,20 +3,24 @@ import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import type { CameraControls } from "@react-three/drei";
|
||||
|
||||
export const useCameraShortcuts = (controlsRef: React.RefObject<CameraControls>) => {
|
||||
const { camera } = useThree();
|
||||
const CameraShortcutsControls = () => {
|
||||
const { camera, controls } = useThree();
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (!controlsRef.current) return;
|
||||
if (!controls) return;
|
||||
|
||||
// get current distance from camera to target
|
||||
const cc = controls as CameraControls;
|
||||
|
||||
// get current target
|
||||
const target = new THREE.Vector3();
|
||||
controlsRef.current.getTarget(target);
|
||||
cc.getTarget(target);
|
||||
|
||||
const distance = camera.position.distanceTo(target);
|
||||
let pos: THREE.Vector3 | null = null;
|
||||
|
||||
const dir = new THREE.Vector3().subVectors(camera.position, target).normalize();
|
||||
|
||||
switch (e.key) {
|
||||
case "1": // Front
|
||||
pos = new THREE.Vector3(0, 0, distance).add(target);
|
||||
@@ -27,13 +31,24 @@ export const useCameraShortcuts = (controlsRef: React.RefObject<CameraControls>)
|
||||
case "7": // Top
|
||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
||||
break;
|
||||
case "9": // Back
|
||||
pos = new THREE.Vector3(0, 0, -distance).add(target);
|
||||
case "9": {
|
||||
// Opposite view logic
|
||||
if (Math.abs(dir.z) > Math.abs(dir.x) && Math.abs(dir.z) > Math.abs(dir.y)) {
|
||||
// Currently looking Front/Back → flip Z
|
||||
pos = new THREE.Vector3(0, 0, -Math.sign(dir.z) * distance).add(target);
|
||||
} else if (Math.abs(dir.x) > Math.abs(dir.z) && Math.abs(dir.x) > Math.abs(dir.y)) {
|
||||
// Currently looking Right/Left → flip X
|
||||
pos = new THREE.Vector3(-Math.sign(dir.x) * distance, 0, 0).add(target);
|
||||
} else {
|
||||
// Currently looking Top/Bottom → stay Top
|
||||
pos = new THREE.Vector3(0, distance, 0).add(target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos) {
|
||||
controlsRef.current.setLookAt(
|
||||
cc.setLookAt(
|
||||
pos.x, pos.y, pos.z, // camera position
|
||||
target.x, target.y, target.z, // keep same target
|
||||
true // smooth transition
|
||||
@@ -43,5 +58,9 @@ export const useCameraShortcuts = (controlsRef: React.RefObject<CameraControls>)
|
||||
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [controlsRef, camera]);
|
||||
}, [controls, camera]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default CameraShortcutsControls;
|
||||
@@ -1,41 +1,14 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { CameraControls, Html, ScreenSpace } from "@react-three/drei";
|
||||
import {
|
||||
useContextActionStore,
|
||||
useRenameModeStore,
|
||||
useSelectedAssets,
|
||||
} from "../../../../store/builder/store";
|
||||
import { useContextActionStore, useRenameModeStore, useSelectedAssets, useToggleView, useToolMode, } from "../../../../store/builder/store";
|
||||
import ContextMenu from "../../../../components/ui/menu/contextMenu";
|
||||
import useModuleStore from "../../../../store/useModuleStore";
|
||||
|
||||
function ContextControls() {
|
||||
const { gl, controls } = useThree();
|
||||
const [canRender, setCanRender] = useState(false);
|
||||
const [visibility, setVisibility] = useState({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
setVisibility({
|
||||
const { gl, controls } = useThree();
|
||||
const [canRender, setCanRender] = useState(false);
|
||||
const [visibility, setVisibility] = useState({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
@@ -49,195 +22,219 @@ function ContextControls() {
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else if (selectedAssets.length > 1) {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: true,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: false,
|
||||
flipX: false,
|
||||
flipZ: false,
|
||||
move: false,
|
||||
rotate: false,
|
||||
duplicate: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: false,
|
||||
});
|
||||
}
|
||||
}, [selectedAssets]);
|
||||
});
|
||||
const [menuPosition, setMenuPosition] = useState({ x: 0, y: 0 });
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedAssets } = useSelectedAssets();
|
||||
const { setContextAction } = useContextActionStore();
|
||||
const { setIsRenameMode } = useRenameModeStore();
|
||||
const rightDrag = useRef(false);
|
||||
const isRightMouseDown = useRef(false);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerDown = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = true;
|
||||
rightDrag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
if (isRightMouseDown.current) {
|
||||
rightDrag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerUp = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleContextClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (rightDrag.current) return;
|
||||
if (selectedAssets.length > 0) {
|
||||
setMenuPosition({
|
||||
x: event.clientX - gl.domElement.width / 2,
|
||||
y: event.clientY - gl.domElement.height / 2,
|
||||
});
|
||||
setCanRender(true);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 1) {
|
||||
setVisibility({
|
||||
rename: true,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else if (selectedAssets.length > 1) {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: true,
|
||||
flipX: true,
|
||||
flipZ: true,
|
||||
move: true,
|
||||
rotate: true,
|
||||
duplicate: true,
|
||||
copy: true,
|
||||
paste: true,
|
||||
modifier: false,
|
||||
group: true,
|
||||
array: false,
|
||||
delete: true,
|
||||
});
|
||||
} else {
|
||||
setVisibility({
|
||||
rename: false,
|
||||
focus: false,
|
||||
flipX: false,
|
||||
flipZ: false,
|
||||
move: false,
|
||||
rotate: false,
|
||||
duplicate: false,
|
||||
copy: false,
|
||||
paste: false,
|
||||
modifier: false,
|
||||
group: false,
|
||||
array: false,
|
||||
delete: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const onPointerDown = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = true;
|
||||
rightDrag.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerMove = () => {
|
||||
if (isRightMouseDown.current) {
|
||||
rightDrag.current = true;
|
||||
}
|
||||
};
|
||||
|
||||
const onPointerUp = (evt: any) => {
|
||||
if (evt.button === 2) {
|
||||
isRightMouseDown.current = false;
|
||||
}
|
||||
};
|
||||
|
||||
const handleContextClick = (event: MouseEvent) => {
|
||||
event.preventDefault();
|
||||
if (rightDrag.current) return;
|
||||
if (selectedAssets.length > 0) {
|
||||
setMenuPosition({ x: event.clientX - gl.domElement.width / 2, y: event.clientY - gl.domElement.height / 2, });
|
||||
setCanRender(true);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = false;
|
||||
}
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0 && !toggleView && activeModule === "builder" && toolMode === 'cursor') {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("contextmenu", handleContextClick);
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setMenuPosition({ x: 0, y: 0 });
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("contextmenu", handleContextClick);
|
||||
};
|
||||
}, [controls, gl, selectedAssets, toggleView, activeModule, toolMode]);
|
||||
|
||||
const handleAssetRename = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
}
|
||||
setContextAction("renameAsset");
|
||||
setIsRenameMode(true);
|
||||
};
|
||||
const handleAssetFocus = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("focusAsset");
|
||||
};
|
||||
const handleAssetMove = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("moveAsset");
|
||||
};
|
||||
const handleAssetRotate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("rotateAsset");
|
||||
};
|
||||
const handleAssetCopy = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("copyAsset");
|
||||
};
|
||||
const handleAssetPaste = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("pasteAsset");
|
||||
};
|
||||
const handleAssetDelete = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("deleteAsset");
|
||||
};
|
||||
const handleAssetDuplicate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("duplicateAsset");
|
||||
};
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
canvasElement.addEventListener("contextmenu", handleContextClick);
|
||||
} else {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setMenuPosition({ x: 0, y: 0 });
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.removeEventListener("pointermove", onPointerMove);
|
||||
canvasElement.removeEventListener("pointerup", onPointerUp);
|
||||
canvasElement.removeEventListener("contextmenu", handleContextClick);
|
||||
};
|
||||
}, [controls, gl, selectedAssets]);
|
||||
|
||||
const handleAssetRename = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("renameAsset");
|
||||
setIsRenameMode(true);
|
||||
};
|
||||
const handleAssetFocus = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("focusAsset");
|
||||
};
|
||||
const handleAssetMove = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("moveAsset");
|
||||
};
|
||||
const handleAssetRotate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("rotateAsset");
|
||||
};
|
||||
const handleAssetCopy = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("copyAsset");
|
||||
};
|
||||
const handleAssetPaste = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("pasteAsset");
|
||||
};
|
||||
const handleAssetDelete = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("deleteAsset");
|
||||
};
|
||||
const handleAssetDuplicate = () => {
|
||||
setCanRender(false);
|
||||
if (controls) {
|
||||
(controls as CameraControls).enabled = true;
|
||||
}
|
||||
setContextAction("duplicateAsset");
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{canRender && (
|
||||
<ScreenSpace depth={1}>
|
||||
<Html
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: menuPosition.y,
|
||||
left: menuPosition.x,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<ContextMenu
|
||||
visibility={visibility}
|
||||
onRename={() => handleAssetRename()}
|
||||
onFocus={() => handleAssetFocus()}
|
||||
onFlipX={() => console.log("Flip to X")}
|
||||
onFlipZ={() => console.log("Flip to Z")}
|
||||
onMove={() => handleAssetMove()}
|
||||
onRotate={() => handleAssetRotate()}
|
||||
onDuplicate={() => handleAssetDuplicate()}
|
||||
onCopy={() => handleAssetCopy()}
|
||||
onPaste={() => handleAssetPaste()}
|
||||
onGroup={() => console.log("Group")}
|
||||
onArray={() => console.log("Array")}
|
||||
onDelete={() => handleAssetDelete()}
|
||||
/>
|
||||
</Html>
|
||||
</ScreenSpace>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
{canRender && (
|
||||
<ScreenSpace depth={1}>
|
||||
<Html
|
||||
style={{
|
||||
position: "fixed",
|
||||
top: menuPosition.y,
|
||||
left: menuPosition.x,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<ContextMenu
|
||||
visibility={visibility}
|
||||
onRename={() => handleAssetRename()}
|
||||
onFocus={() => handleAssetFocus()}
|
||||
onFlipX={() => console.log("Flip to X")}
|
||||
onFlipZ={() => console.log("Flip to Z")}
|
||||
onMove={() => handleAssetMove()}
|
||||
onRotate={() => handleAssetRotate()}
|
||||
onDuplicate={() => handleAssetDuplicate()}
|
||||
onCopy={() => handleAssetCopy()}
|
||||
onPaste={() => handleAssetPaste()}
|
||||
onGroup={() => console.log("Group")}
|
||||
onArray={() => console.log("Array")}
|
||||
onDelete={() => handleAssetDelete()}
|
||||
/>
|
||||
</Html>
|
||||
</ScreenSpace>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ContextControls;
|
||||
|
||||
@@ -3,22 +3,22 @@ import { useRef, useEffect } from "react";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import * as THREE from "three";
|
||||
import * as CONSTANTS from '../../../types/world/worldConstants';
|
||||
|
||||
import { useSocketStore, useToggleView, useResetCamera } from "../../../store/builder/store";
|
||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||
import updateCamPosition from "../camera/updateCameraPosition";
|
||||
|
||||
import CamMode from "../camera/camMode";
|
||||
import SwitchView from "../camera/switchView";
|
||||
import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D";
|
||||
import TransformControl from "./transformControls/transformControls";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
|
||||
import ContextControls from "./contextControls/contextControls";
|
||||
import TransformControl from "./transformControls/transformControls";
|
||||
import SelectionControls2D from "./selectionControls/selection2D/selectionControls2D";
|
||||
import SelectionControls3D from "./selectionControls/selection3D/selectionControls3D";
|
||||
import UndoRedo2DControls from "./undoRedoControls/undoRedo2D/undoRedo2DControls";
|
||||
import UndoRedo3DControls from "./undoRedoControls/undoRedo3D/undoRedo3DControls";
|
||||
import { useCameraShortcuts } from "../../../hooks/useCameraShortcuts";
|
||||
import CameraShortcutsControls from "../camera/shortcutsControls/cameraShortcutsControls";
|
||||
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../functions/getUserData";
|
||||
import { getCamera } from "../../../services/factoryBuilder/camera/getCameraApi";
|
||||
import updateCamPosition from "../camera/functions/updateCameraPosition";
|
||||
|
||||
export default function Controls() {
|
||||
const controlsRef = useRef<CameraControls>(null);
|
||||
@@ -117,7 +117,6 @@ export default function Controls() {
|
||||
stopInterval();
|
||||
};
|
||||
}, [toggleView, state, socket]);
|
||||
useCameraShortcuts(controlsRef);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -140,6 +139,8 @@ export default function Controls() {
|
||||
|
||||
<CamMode />
|
||||
|
||||
<CameraShortcutsControls/>
|
||||
|
||||
</CameraControls>
|
||||
|
||||
<SelectionControls3D />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode, } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
@@ -12,6 +12,7 @@ import { useProductContext } from "../../../../simulation/products/productContex
|
||||
import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
@@ -20,6 +21,8 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
const plane = useMemo(() => new THREE.Plane(new THREE.Vector3(0, 1, 0), 0), []);
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
@@ -162,7 +165,7 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
}
|
||||
};
|
||||
|
||||
if (!toggleView) {
|
||||
if (!toggleView && toolMode === 'cursor') {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
@@ -178,7 +181,14 @@ function MoveControls3D({ boundingBoxRef }: any) {
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
||||
}, [camera, controls, scene, toggleView, toolMode, selectedAssets, socket, pastedObjects, duplicatedObjects, movedObjects, rotatedObjects, keyEvent, initialStates]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||
resetToInitialPositions();
|
||||
setMovedObjects([]);
|
||||
}
|
||||
}, [activeModule, toolMode, toggleView]);
|
||||
|
||||
const calculateDragOffset = useCallback((point: THREE.Object3D, hitPoint: THREE.Vector3) => {
|
||||
const pointPosition = new THREE.Vector3().copy(point.position);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as THREE from "three";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView } from "../../../../../store/builder/store";
|
||||
import { useContextActionStore, useSelectedAssets, useSocketStore, useToggleView, useToolMode } from "../../../../../store/builder/store";
|
||||
import * as Types from "../../../../../types/world/worldTypes";
|
||||
import { upsertProductOrEventApi } from "../../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
import { useParams } from "react-router-dom";
|
||||
@@ -11,6 +11,7 @@ import { useSceneContext } from "../../../sceneContext";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { detectModifierKeys } from "../../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { handleAssetRotationSnap } from "./functions/handleAssetRotationSnap";
|
||||
import useModuleStore from "../../../../../store/useModuleStore";
|
||||
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
@@ -18,6 +19,8 @@ function RotateControls3D() {
|
||||
const { camera, gl, scene, pointer, raycaster } = useThree();
|
||||
|
||||
const { toggleView } = useToggleView();
|
||||
const { toolMode } = useToolMode();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
@@ -34,6 +37,7 @@ function RotateControls3D() {
|
||||
const [initialRotations, setInitialRotations] = useState<Record<string, THREE.Euler>>({});
|
||||
const [initialPositions, setInitialPositions] = useState<Record<string, THREE.Vector3>>({});
|
||||
const [isRotating, setIsRotating] = useState(false);
|
||||
const [isIndividualRotating, setIsIndividualRotating] = useState(false);
|
||||
const prevPointerPosition = useRef<THREE.Vector2 | null>(null);
|
||||
const rotationCenter = useRef<THREE.Vector3 | null>(null);
|
||||
const mouseButtonsDown = useRef<{ left: boolean; right: boolean }>({ left: false, right: false, });
|
||||
@@ -117,6 +121,12 @@ function RotateControls3D() {
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key.toLowerCase() === 'i') {
|
||||
if (rotatedObjects.length > 0) {
|
||||
setIsIndividualRotating(!isIndividualRotating);
|
||||
}
|
||||
}
|
||||
|
||||
if (event.key.toLowerCase() === "escape") {
|
||||
event.preventDefault();
|
||||
resetToInitialRotations();
|
||||
@@ -137,7 +147,7 @@ function RotateControls3D() {
|
||||
prevPointerPosition.current = currentPointer.clone();
|
||||
};
|
||||
|
||||
if (!toggleView) {
|
||||
if (!toggleView && toolMode === 'cursor') {
|
||||
canvasElement.addEventListener("pointerdown", onPointerDown);
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
@@ -152,7 +162,14 @@ function RotateControls3D() {
|
||||
canvasElement.removeEventListener("keydown", onKeyDown);
|
||||
canvasElement?.removeEventListener("keyup", onKeyUp);
|
||||
};
|
||||
}, [camera, scene, toggleView, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations]);
|
||||
}, [camera, scene, toggleView, toolMode, selectedAssets, rotatedObjects, pastedObjects, duplicatedObjects, movedObjects, keyEvent, initialPositions, initialRotations, isIndividualRotating]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||
resetToInitialRotations();
|
||||
setRotatedObjects([]);
|
||||
}
|
||||
}, [activeModule, toolMode, toggleView]);
|
||||
|
||||
const resetToInitialRotations = useCallback(() => {
|
||||
setTimeout(() => {
|
||||
@@ -204,10 +221,12 @@ function RotateControls3D() {
|
||||
wasShiftHeldRef
|
||||
});
|
||||
|
||||
const relativePosition = new THREE.Vector3().subVectors(obj.position, center);
|
||||
const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
relativePosition.applyMatrix4(rotationMatrix);
|
||||
obj.position.copy(center).add(relativePosition);
|
||||
if (!isIndividualRotating) {
|
||||
const relativePosition = new THREE.Vector3().subVectors(obj.position, center);
|
||||
const rotationMatrix = new THREE.Matrix4().makeRotationY(angleDelta);
|
||||
relativePosition.applyMatrix4(rotationMatrix);
|
||||
obj.position.copy(center).add(relativePosition);
|
||||
}
|
||||
|
||||
const rotationQuat = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), angleDelta);
|
||||
obj.quaternion.multiply(rotationQuat);
|
||||
@@ -384,8 +403,9 @@ function RotateControls3D() {
|
||||
}
|
||||
|
||||
setIsRotating(false);
|
||||
setIsIndividualRotating(false);
|
||||
clearSelection();
|
||||
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId]);
|
||||
}, [rotatedObjects, eventStore, productStore, selectedProduct, updateBackend, projectId, updateAsset, organization, socket, selectedVersion, userId, initialPositions, initialRotations]);
|
||||
|
||||
const clearSelection = () => {
|
||||
setPastedObjects([]);
|
||||
@@ -393,6 +413,7 @@ function RotateControls3D() {
|
||||
setMovedObjects([]);
|
||||
setRotatedObjects([]);
|
||||
setSelectedAssets([]);
|
||||
setIsIndividualRotating(false);
|
||||
};
|
||||
|
||||
return null;
|
||||
|
||||
@@ -16,22 +16,23 @@ import DuplicationControls3D from "./duplicationControls3D";
|
||||
import CopyPasteControls3D from "./copyPasteControls3D";
|
||||
import MoveControls3D from "./moveControls3D";
|
||||
import RotateControls3D from "./rotateControls3D";
|
||||
import TransformControls3D from "./transformControls3D";
|
||||
|
||||
// import { deleteFloorItem } from '../../../../../services/factoryBuilder/asset/floorAsset/deleteFloorItemApi';
|
||||
|
||||
const SelectionControls3D: React.FC = () => {
|
||||
const { camera, controls, gl, scene, raycaster, pointer } = useThree();
|
||||
const { toggleView } = useToggleView();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const boundingBoxRef = useRef<THREE.Mesh>();
|
||||
const { activeModule } = useModuleStore();
|
||||
const { socket } = useSocketStore();
|
||||
const { contextAction, setContextAction } = useContextActionStore()
|
||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D } = undoRedo3DStore();
|
||||
const { removeAsset, getAssetById, movedObjects, rotatedObjects, copiedObjects, pastedObjects, duplicatedObjects, setPastedObjects, setDuplicatedObjects } = assetStore();
|
||||
const selectionBox = useMemo(() => new SelectionBox(camera, scene), [camera, scene]);
|
||||
const { toolMode } = useToolMode();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
@@ -202,7 +203,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
rightClickMoved.current = false;
|
||||
};
|
||||
|
||||
if (!toggleView && activeModule === "builder" && toolMode === 'cursor') {
|
||||
if (!toggleView && activeModule === "builder" && (toolMode === 'cursor' || toolMode === 'Move-Asset' || toolMode === 'Rotate-Asset')) {
|
||||
helper.enabled = true;
|
||||
canvasElement.addEventListener("pointermove", onPointerMove);
|
||||
canvasElement.addEventListener("pointerup", onPointerUp);
|
||||
@@ -227,7 +228,7 @@ const SelectionControls3D: React.FC = () => {
|
||||
}, [camera, controls, scene, toggleView, selectedAssets, copiedObjects, pastedObjects, duplicatedObjects, movedObjects, socket, rotatedObjects, activeModule, toolMode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (activeModule !== "builder" || toolMode !== 'cursor' || toggleView) {
|
||||
if (activeModule !== "builder" || (toolMode !== 'cursor' && toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset') || toggleView) {
|
||||
clearSelection();
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -381,6 +382,8 @@ const SelectionControls3D: React.FC = () => {
|
||||
|
||||
<CopyPasteControls3D />
|
||||
|
||||
<TransformControls3D />
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
import * as THREE from 'three';
|
||||
import { useRef, useEffect, useState, useCallback } from 'react';
|
||||
import { useThree } from '@react-three/fiber';
|
||||
import { TransformControls } from '@react-three/drei';
|
||||
import { useSelectedAssets, useSocketStore, useToolMode } from '../../../../../store/builder/store';
|
||||
import { useProductContext } from '../../../../simulation/products/productContext';
|
||||
import { useVersionContext } from '../../../../builder/version/versionContext';
|
||||
import { useSceneContext } from '../../../sceneContext';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { getUserData } from '../../../../../functions/getUserData';
|
||||
import { upsertProductOrEventApi } from '../../../../../services/simulation/products/UpsertProductOrEventApi';
|
||||
import { detectModifierKeys } from '../../../../../utils/shortcutkeys/detectModifierKeys';
|
||||
// import { setAssetsApi } from '../../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi';
|
||||
|
||||
function TransformControls3D() {
|
||||
const { selectedAssets, setSelectedAssets } = useSelectedAssets();
|
||||
const { toolMode } = useToolMode();
|
||||
const { camera, scene, gl } = useThree();
|
||||
const transformControlsRef = useRef<any>(null);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [keyEvent, setKeyEvent] = useState<"Ctrl" | "Shift" | "Ctrl+Shift" | "">("");
|
||||
const { socket } = useSocketStore();
|
||||
const { selectedProductStore } = useProductContext();
|
||||
const { selectedProduct } = selectedProductStore();
|
||||
const { assetStore, eventStore, productStore, undoRedo3DStore } = useSceneContext();
|
||||
const { push3D, subscribeUndoRedo } = undoRedo3DStore();
|
||||
const { updateAsset, getAssetById } = assetStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { projectId } = useParams();
|
||||
const pivotPointRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||
const initialPositionsRef = useRef<Map<string, THREE.Vector3>>(new Map());
|
||||
const initialRotationsRef = useRef<Map<string, THREE.Euler>>(new Map());
|
||||
const initialPivotPositionRef = useRef<THREE.Vector3>(new THREE.Vector3());
|
||||
const initialQuaternionsRef = useRef<Map<string, THREE.Quaternion>>(new Map());
|
||||
const tempObjectRef = useRef<THREE.Object3D>(new THREE.Object3D());
|
||||
|
||||
const updateBackend = useCallback((
|
||||
productName: string,
|
||||
productUuid: string,
|
||||
projectId: string,
|
||||
eventData: any
|
||||
) => {
|
||||
upsertProductOrEventApi({
|
||||
productName: productName,
|
||||
productUuid: productUuid,
|
||||
projectId: projectId,
|
||||
eventDatas: eventData,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
});
|
||||
}, [selectedVersion]);
|
||||
|
||||
const recalcGizmo = useCallback(() => {
|
||||
if (selectedAssets.length === 0) return;
|
||||
|
||||
const bbox = new THREE.Box3();
|
||||
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||
bbox.getCenter(pivotPointRef.current);
|
||||
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||
|
||||
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||
tempObjectRef.current.scale.set(1, 1, 1);
|
||||
|
||||
initialPositionsRef.current.clear();
|
||||
initialRotationsRef.current.clear();
|
||||
initialQuaternionsRef.current.clear();
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||
});
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = subscribeUndoRedo(() => {
|
||||
setTimeout(() => {
|
||||
recalcGizmo();
|
||||
}, 10);
|
||||
});
|
||||
return unsubscribe;
|
||||
}, [subscribeUndoRedo, recalcGizmo]);
|
||||
|
||||
const handleTransformationComplete = useCallback(() => {
|
||||
if (selectedAssets.length === 0) return;
|
||||
|
||||
const updatedAssets = [...selectedAssets];
|
||||
setSelectedAssets(updatedAssets);
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
const asset = getAssetById(obj.uuid);
|
||||
if (!asset) return;
|
||||
|
||||
updateAsset(asset.modelUuid, {
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
|
||||
if (asset.eventData) {
|
||||
const eventData = eventStore.getState().getEventByModelUuid(asset.modelUuid);
|
||||
const productData = productStore.getState().getEventByModelUuid(
|
||||
selectedProduct.productUuid,
|
||||
asset.modelUuid
|
||||
);
|
||||
|
||||
if (eventData) {
|
||||
eventStore.getState().updateEvent(asset.modelUuid, {
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
}
|
||||
|
||||
if (productData) {
|
||||
const event = productStore
|
||||
.getState()
|
||||
.updateEvent(
|
||||
selectedProduct.productUuid,
|
||||
asset.modelUuid,
|
||||
{
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
}
|
||||
);
|
||||
|
||||
if (event) {
|
||||
updateBackend(
|
||||
selectedProduct.productName,
|
||||
selectedProduct.productUuid,
|
||||
projectId || '',
|
||||
event
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//REST
|
||||
|
||||
// setAssetsApi(
|
||||
// organization,
|
||||
// asset.modelUuid,
|
||||
// asset.modelName,
|
||||
// [obj.position.x, 0, obj.position.z],
|
||||
// { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
// asset.assetId,
|
||||
// false,
|
||||
// true,
|
||||
// );
|
||||
|
||||
//SOCKET
|
||||
|
||||
const data = {
|
||||
organization,
|
||||
modelUuid: asset.modelUuid,
|
||||
modelName: asset.modelName,
|
||||
assetId: asset.assetId,
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z },
|
||||
isLocked: false,
|
||||
isVisible: true,
|
||||
socketId: socket.id,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
projectId
|
||||
};
|
||||
|
||||
socket.emit("v1:model-asset:add", data);
|
||||
|
||||
});
|
||||
}, [selectedAssets, setSelectedAssets, getAssetById, updateAsset, eventStore, productStore, selectedProduct, updateBackend, projectId, organization, socket, selectedVersion, userId, push3D]);
|
||||
|
||||
useEffect(() => {
|
||||
const temp = tempObjectRef.current;
|
||||
scene.add(temp);
|
||||
return () => {
|
||||
scene.remove(temp);
|
||||
};
|
||||
}, [scene]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedAssets.length === 0) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const bbox = new THREE.Box3();
|
||||
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||
bbox.getCenter(pivotPointRef.current);
|
||||
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||
|
||||
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||
tempObjectRef.current.scale.set(1, 1, 1);
|
||||
|
||||
initialPositionsRef.current.clear();
|
||||
initialRotationsRef.current.clear();
|
||||
initialQuaternionsRef.current.clear();
|
||||
selectedAssets.forEach(obj => {
|
||||
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||
});
|
||||
|
||||
setVisible(true);
|
||||
}, [selectedAssets]);
|
||||
|
||||
useEffect(() => {
|
||||
const controls = transformControlsRef.current;
|
||||
if (!controls || !visible) return;
|
||||
|
||||
controls.attach(tempObjectRef.current);
|
||||
controls.setMode(toolMode === 'Move-Asset' ? 'translate' : 'rotate');
|
||||
|
||||
const onObjectChange = () => {
|
||||
const temp = tempObjectRef.current;
|
||||
if (!temp) return;
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
const initialPos = initialPositionsRef.current.get(obj.uuid);
|
||||
const initialQuat = initialQuaternionsRef.current.get(obj.uuid);
|
||||
if (!initialPos || !initialQuat) return;
|
||||
|
||||
if (controls.mode === 'translate') {
|
||||
const delta = new THREE.Vector3().copy(temp.position).sub(initialPivotPositionRef.current);
|
||||
obj.position.copy(initialPos).add(delta);
|
||||
} else if (controls.mode === 'rotate') {
|
||||
const deltaQuat = temp.quaternion.clone();
|
||||
const relPos = initialPos.clone().sub(initialPivotPositionRef.current);
|
||||
relPos.applyQuaternion(deltaQuat);
|
||||
obj.position.copy(initialPivotPositionRef.current).add(relPos);
|
||||
obj.quaternion.copy(deltaQuat).multiply(initialQuat);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
const assetsToUpdate: AssetData[] = [];
|
||||
const undoActions: UndoRedo3DAction[] = [];
|
||||
|
||||
selectedAssets.forEach((obj) => {
|
||||
const asset = getAssetById(obj.uuid);
|
||||
if (!asset) return;
|
||||
|
||||
const initialPos = initialPositionsRef.current.get(obj.uuid);
|
||||
const initialRot = initialRotationsRef.current.get(obj.uuid);
|
||||
if (!initialPos || !initialRot) return;
|
||||
|
||||
const assetData: Asset = {
|
||||
...asset,
|
||||
position: [initialPos.x, initialPos.y, initialPos.z],
|
||||
rotation: [initialRot.x, initialRot.y, initialRot.z],
|
||||
};
|
||||
|
||||
const newData: Asset = {
|
||||
...asset,
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
};
|
||||
|
||||
assetsToUpdate.push({
|
||||
type: "Asset",
|
||||
assetData,
|
||||
newData,
|
||||
timeStap: new Date().toISOString(),
|
||||
});
|
||||
|
||||
updateAsset(asset.modelUuid, {
|
||||
position: [obj.position.x, obj.position.y, obj.position.z],
|
||||
rotation: [obj.rotation.x, obj.rotation.y, obj.rotation.z],
|
||||
});
|
||||
});
|
||||
|
||||
if (assetsToUpdate.length > 0) {
|
||||
if (assetsToUpdate.length === 1) {
|
||||
undoActions.push({
|
||||
module: "builder",
|
||||
actionType: "Asset-Update",
|
||||
asset: assetsToUpdate[0],
|
||||
});
|
||||
} else {
|
||||
undoActions.push({
|
||||
module: "builder",
|
||||
actionType: "Assets-Update",
|
||||
assets: assetsToUpdate,
|
||||
});
|
||||
}
|
||||
|
||||
push3D({
|
||||
type: "Scene",
|
||||
actions: undoActions,
|
||||
});
|
||||
}
|
||||
|
||||
selectedAssets.forEach(obj => {
|
||||
initialPositionsRef.current.set(obj.uuid, obj.position.clone());
|
||||
initialRotationsRef.current.set(obj.uuid, obj.rotation.clone());
|
||||
initialQuaternionsRef.current.set(obj.uuid, obj.quaternion.clone());
|
||||
});
|
||||
|
||||
if (selectedAssets.length > 0) {
|
||||
const bbox = new THREE.Box3();
|
||||
selectedAssets.forEach(obj => bbox.expandByObject(obj));
|
||||
bbox.getCenter(pivotPointRef.current);
|
||||
initialPivotPositionRef.current.copy(pivotPointRef.current);
|
||||
|
||||
tempObjectRef.current.position.copy(pivotPointRef.current);
|
||||
tempObjectRef.current.rotation.set(0, 0, 0);
|
||||
}
|
||||
|
||||
recalcGizmo();
|
||||
handleTransformationComplete();
|
||||
};
|
||||
|
||||
controls.addEventListener('objectChange', onObjectChange);
|
||||
controls.addEventListener('mouseUp', onMouseUp);
|
||||
|
||||
return () => {
|
||||
controls.removeEventListener('objectChange', onObjectChange);
|
||||
controls.removeEventListener('mouseUp', onMouseUp);
|
||||
controls.detach();
|
||||
};
|
||||
}, [selectedAssets, toolMode, visible, handleTransformationComplete]);
|
||||
|
||||
useEffect(() => {
|
||||
const canvasElement = gl.domElement;
|
||||
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (keyCombination !== keyEvent) {
|
||||
if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
} else {
|
||||
setKeyEvent("");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const onKeyUp = (event: KeyboardEvent) => {
|
||||
const keyCombination = detectModifierKeys(event);
|
||||
|
||||
if (keyCombination === "") {
|
||||
setKeyEvent("");
|
||||
} else if (keyCombination === "Ctrl" || keyCombination === "Ctrl+Shift" || keyCombination === "Shift") {
|
||||
setKeyEvent(keyCombination);
|
||||
}
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
canvasElement.addEventListener('keydown', handleKeyDown);
|
||||
canvasElement.addEventListener('keyup', onKeyUp);
|
||||
}
|
||||
|
||||
return () => {
|
||||
canvasElement.removeEventListener('keydown', handleKeyDown);
|
||||
canvasElement.removeEventListener('keyup', onKeyUp);
|
||||
}
|
||||
}, [gl, visible, keyEvent]);
|
||||
|
||||
if (!visible || (toolMode !== 'Move-Asset' && toolMode !== 'Rotate-Asset')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<TransformControls
|
||||
space="world"
|
||||
translationSnap={keyEvent === 'Ctrl' ? 0.5 : keyEvent === 'Ctrl+Shift' ? 0.1 : 0}
|
||||
rotationSnap={keyEvent === 'Ctrl' ? THREE.MathUtils.degToRad(15) : keyEvent === 'Ctrl+Shift' ? THREE.MathUtils.degToRad(5) : 0}
|
||||
ref={transformControlsRef}
|
||||
camera={camera}
|
||||
showX
|
||||
showY
|
||||
showZ
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default TransformControls3D;
|
||||
@@ -1,17 +1,17 @@
|
||||
import { TransformControls } from "@react-three/drei";
|
||||
import * as THREE from "three";
|
||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { TransformControls } from "@react-three/drei";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useProductContext } from "../../../simulation/products/productContext";
|
||||
import { getUserData } from "../../../../functions/getUserData";
|
||||
import { useSceneContext } from "../../sceneContext";
|
||||
import { useVersionContext } from "../../../builder/version/versionContext";
|
||||
import { useSelectedFloorItem, useObjectPosition, useObjectRotation, useActiveTool, useSocketStore } from "../../../../store/builder/store";
|
||||
|
||||
import { detectModifierKeys } from "../../../../utils/shortcutkeys/detectModifierKeys";
|
||||
import { upsertProductOrEventApi } from "../../../../services/simulation/products/UpsertProductOrEventApi";
|
||||
// import { setAssetsApi } from "../../../../services/factoryBuilder/asset/floorAsset/setAssetsApi";
|
||||
|
||||
export default function TransformControl() {
|
||||
const state = useThree();
|
||||
|
||||
@@ -20,7 +20,7 @@ function UndoRedo3DControls() {
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
|
||||
useEffect(() => {
|
||||
// console.log(undoStack, redoStack);
|
||||
console.log(undoStack, redoStack);
|
||||
}, [undoStack, redoStack]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,25 +1,19 @@
|
||||
import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
|
||||
import { useThree } from "@react-three/fiber";
|
||||
import { BlendFunction } from "postprocessing";
|
||||
import {
|
||||
useDeletableFloorItem,
|
||||
useSelectedWallItem,
|
||||
useSelectedFloorItem,
|
||||
} from "../../../store/builder/store";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
import { useDeletableEventSphere, useSelectedEventSphere, useSelectedPoints } from "../../../store/simulation/useSimulationStore";
|
||||
import { useEffect } from "react";
|
||||
import { BlendFunction } from "postprocessing";
|
||||
import { DepthOfField, Bloom, EffectComposer, N8AO, Outline } from "@react-three/postprocessing";
|
||||
import { useDeletableFloorItem, useSelectedWallItem, useSelectedFloorItem, } from "../../../store/builder/store";
|
||||
import { useDeletableEventSphere, useSelectedEventSphere, useSelectedPoints } from "../../../store/simulation/useSimulationStore";
|
||||
import { useBuilderStore } from "../../../store/builder/useBuilderStore";
|
||||
import * as CONSTANTS from "../../../types/world/worldConstants";
|
||||
|
||||
export default function PostProcessing() {
|
||||
const { scene } = useThree();
|
||||
const { selectedPoints } = useSelectedPoints();
|
||||
const { deletableFloorItem } = useDeletableFloorItem();
|
||||
const { selectedWallItem } = useSelectedWallItem();
|
||||
const { selectedFloorItem } = useSelectedFloorItem();
|
||||
const { selectedEventSphere } = useSelectedEventSphere();
|
||||
const { deletableEventSphere } = useDeletableEventSphere();
|
||||
const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset } = useBuilderStore();
|
||||
const { selectedAisle, selectedWall, selectedDecal, selectedFloor, selectedWallAsset, deletableWallAsset, deletableDecal } = useBuilderStore();
|
||||
|
||||
function flattenChildren(children: any[]) {
|
||||
const allChildren: any[] = [];
|
||||
@@ -68,6 +62,10 @@ export default function PostProcessing() {
|
||||
// console.log('selectedPoints: ', selectedPoints);
|
||||
}, [selectedPoints])
|
||||
|
||||
useEffect(() => {
|
||||
// console.log('deletableDecal: ', deletableDecal);
|
||||
}, [deletableDecal])
|
||||
|
||||
return (
|
||||
<EffectComposer autoClear={false}>
|
||||
<N8AO
|
||||
@@ -122,7 +120,7 @@ export default function PostProcessing() {
|
||||
)}
|
||||
{selectedAisle && (
|
||||
<Outline
|
||||
selection={flattenChildren(selectedAisle.children)}
|
||||
selection={flattenChildren(selectedAisle.aisleMesh?.children || [])}
|
||||
selectionLayer={10}
|
||||
width={2000}
|
||||
blendFunction={BlendFunction.ALPHA}
|
||||
@@ -167,7 +165,7 @@ export default function PostProcessing() {
|
||||
)}
|
||||
{selectedDecal && (
|
||||
<Outline
|
||||
selection={selectedDecal}
|
||||
selection={selectedDecal.decalMesh || undefined}
|
||||
selectionLayer={10}
|
||||
width={2000}
|
||||
blendFunction={BlendFunction.ALPHA}
|
||||
@@ -180,6 +178,21 @@ export default function PostProcessing() {
|
||||
xRay={true}
|
||||
/>
|
||||
)}
|
||||
{deletableDecal && (
|
||||
<Outline
|
||||
selection={deletableDecal}
|
||||
selectionLayer={10}
|
||||
width={3000}
|
||||
blendFunction={BlendFunction.ALPHA}
|
||||
edgeStrength={5}
|
||||
resolutionScale={2}
|
||||
pulseSpeed={0}
|
||||
visibleEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
|
||||
hiddenEdgeColor={CONSTANTS.outlineConfig.assetDeleteColor}
|
||||
blur={true}
|
||||
xRay={true}
|
||||
/>
|
||||
)}
|
||||
{deletableFloorItem && (
|
||||
<Outline
|
||||
selection={flattenChildren(deletableFloorItem.children)}
|
||||
|
||||
@@ -128,6 +128,7 @@ export const useSocketStore = create<any>((set: any, get: any) => ({
|
||||
// });
|
||||
// },
|
||||
// }));
|
||||
|
||||
export const useLoadingProgress = create<{
|
||||
loadingProgress: number;
|
||||
setLoadingProgress: (x: number) => void;
|
||||
@@ -162,6 +163,21 @@ export const useSelectedItem = create<any>((set: any) => ({
|
||||
setSelectedItem: (x: any) => set(() => ({ selectedItem: x })),
|
||||
}));
|
||||
|
||||
type DroppedDecalType = {
|
||||
category: string;
|
||||
decalName: string;
|
||||
decalImage: string;
|
||||
decalId: string;
|
||||
};
|
||||
|
||||
export const useDroppedDecal = create<{
|
||||
droppedDecal: DroppedDecalType | null;
|
||||
setDroppedDecal: (x: DroppedDecalType | null) => void;
|
||||
}>((set) => ({
|
||||
droppedDecal: null,
|
||||
setDroppedDecal: (x) => set({ droppedDecal: x }),
|
||||
}));
|
||||
|
||||
export const useNavMesh = create<any>((set: any) => ({
|
||||
navMesh: null,
|
||||
setNavMesh: (x: any) => set({ navMesh: x }),
|
||||
@@ -663,6 +679,6 @@ interface DecalStore {
|
||||
|
||||
// Create the Zustand store with types
|
||||
export const useDecalStore = create<DecalStore>((set) => ({
|
||||
selectedSubCategory: null,
|
||||
selectedSubCategory: 'Safety',
|
||||
setSelectedSubCategory: (subCategory: string | null) => set({ selectedSubCategory: subCategory }),
|
||||
}));
|
||||
|
||||
@@ -3,43 +3,42 @@ import { immer } from "zustand/middleware/immer";
|
||||
|
||||
interface AisleStore {
|
||||
aisles: Aisles;
|
||||
setAisles: (aisles: Aisles) => void;
|
||||
addAisle: (aisle: Aisle) => void;
|
||||
updateAisle: (uuid: string, updated: Partial<Aisle>) => void;
|
||||
removeAisle: (uuid: string) => void;
|
||||
setAisles: (aisles: Aisles) => Aisles;
|
||||
addAisle: (aisle: Aisle) => Aisle;
|
||||
updateAisle: (uuid: string, updated: Partial<Aisle>) => Aisle | undefined;
|
||||
removeAisle: (uuid: string) => Aisle | undefined;
|
||||
removePoint: (uuid: string) => Aisles;
|
||||
clearAisles: () => void;
|
||||
clearAisles: () => Aisles;
|
||||
setPosition: (
|
||||
pointUuid: string,
|
||||
position: [number, number, number]
|
||||
) => Aisle | undefined;
|
||||
setLayer: (pointUuid: string, layer: number) => void;
|
||||
setColor: (aisleUuid: string, color: AisleColors) => void;
|
||||
setLayer: (pointUuid: string, layer: number) => Aisle | undefined;
|
||||
setColor: (aisleUuid: string, color: AisleColors) => Aisle | undefined;
|
||||
|
||||
// Type-specific setters
|
||||
setSolidAisleWidth: (aisleUuid: string, width: number) => void;
|
||||
setSolidAisleWidth: (aisleUuid: string, width: number) => Aisle | undefined;
|
||||
setDashedAisleProperties: (
|
||||
aisleUuid: string,
|
||||
props: { aisleWidth?: number; dashLength?: number; gapLength?: number }
|
||||
) => void;
|
||||
) => Aisle | undefined;
|
||||
setDottedAisleProperties: (
|
||||
aisleUuid: string,
|
||||
props: { dotRadius?: number; gapLength?: number }
|
||||
) => void;
|
||||
setArrowAisleWidth: (aisleUuid: string, width: number) => void;
|
||||
) => Aisle | undefined;
|
||||
setArrowAisleWidth: (aisleUuid: string, width: number) => Aisle | undefined;
|
||||
setArrowsAisleProperties: (
|
||||
aisleUuid: string,
|
||||
props: { aisleWidth?: number; aisleLength?: number; gapLength?: number }
|
||||
) => void;
|
||||
) => Aisle | undefined;
|
||||
setArcAisleWidth: (
|
||||
aisleUuid: string,
|
||||
props: { aisleWidth?: number; isFlipped: boolean }
|
||||
) => void;
|
||||
setCircleAisleWidth: (aisleUuid: string, width: number) => void;
|
||||
) => Aisle | undefined;
|
||||
setCircleAisleWidth: (aisleUuid: string, width: number) => Aisle | undefined;
|
||||
setJunctionAisleProperties: (
|
||||
aisleUuid: string,
|
||||
props: { aisleWidth?: number; isFlipped: boolean }
|
||||
) => void;
|
||||
) => Aisle | undefined;
|
||||
|
||||
getAisleById: (uuid: string) => Aisle | undefined;
|
||||
getAislesByPointId: (uuid: string) => Aisle[] | [];
|
||||
@@ -53,28 +52,43 @@ export const createAisleStore = () => {
|
||||
immer((set, get) => ({
|
||||
aisles: [],
|
||||
|
||||
setAisles: (aisles) =>
|
||||
setAisles: (aisles) => {
|
||||
set((state) => {
|
||||
state.aisles = aisles;
|
||||
}),
|
||||
});
|
||||
return aisles;
|
||||
},
|
||||
|
||||
addAisle: (aisle) =>
|
||||
addAisle: (aisle) => {
|
||||
set((state) => {
|
||||
state.aisles.push(aisle);
|
||||
}),
|
||||
});
|
||||
return aisle;
|
||||
},
|
||||
|
||||
updateAisle: (uuid, updated) =>
|
||||
updateAisle: (uuid, updated) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === uuid);
|
||||
if (aisle) {
|
||||
Object.assign(aisle, updated);
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
removeAisle: (uuid) =>
|
||||
removeAisle: (uuid) => {
|
||||
let removedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
state.aisles = state.aisles.filter((a) => a.aisleUuid !== uuid);
|
||||
}),
|
||||
const index = state.aisles.findIndex((a) => a.aisleUuid === uuid);
|
||||
if (index !== -1) {
|
||||
removedAisle = JSON.parse(JSON.stringify(state.aisles[index]));
|
||||
state.aisles.splice(index, 1);
|
||||
}
|
||||
});
|
||||
return removedAisle;
|
||||
},
|
||||
|
||||
removePoint: (uuid) => {
|
||||
const removedAisles: Aisle[] = [];
|
||||
@@ -94,9 +108,11 @@ export const createAisleStore = () => {
|
||||
},
|
||||
|
||||
clearAisles: () => {
|
||||
const clearedAisles = get().aisles;
|
||||
set((state) => {
|
||||
state.aisles = [];
|
||||
})
|
||||
});
|
||||
return clearedAisles;
|
||||
},
|
||||
|
||||
setPosition: (pointUuid, position) => {
|
||||
@@ -113,34 +129,46 @@ export const createAisleStore = () => {
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setLayer: (pointUuid, layer) =>
|
||||
setLayer: (pointUuid, layer) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
for (const aisle of state.aisles) {
|
||||
const point = aisle.points.find((p) => p.pointUuid === pointUuid);
|
||||
if (point) {
|
||||
point.layer = layer;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setColor: (aisleUuid, color) =>
|
||||
setColor: (aisleUuid, color) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle) {
|
||||
aisle.type.aisleColor = color;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
// Type-specific property setters
|
||||
setSolidAisleWidth: (aisleUuid, width) =>
|
||||
setSolidAisleWidth: (aisleUuid, width) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "solid-aisle") {
|
||||
aisle.type.aisleWidth = width;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setDashedAisleProperties: (aisleUuid, props) =>
|
||||
setDashedAisleProperties: (aisleUuid, props) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "dashed-aisle") {
|
||||
@@ -150,10 +178,14 @@ export const createAisleStore = () => {
|
||||
aisle.type.dashLength = props.dashLength;
|
||||
if (props.gapLength !== undefined)
|
||||
aisle.type.gapLength = props.gapLength;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setDottedAisleProperties: (aisleUuid, props) =>
|
||||
setDottedAisleProperties: (aisleUuid, props) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "dotted-aisle") {
|
||||
@@ -161,18 +193,26 @@ export const createAisleStore = () => {
|
||||
aisle.type.dotRadius = props.dotRadius;
|
||||
if (props.gapLength !== undefined)
|
||||
aisle.type.gapLength = props.gapLength;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setArrowAisleWidth: (aisleUuid, width) =>
|
||||
setArrowAisleWidth: (aisleUuid, width) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "arrow-aisle") {
|
||||
aisle.type.aisleWidth = width;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setArrowsAisleProperties: (aisleUuid, props) =>
|
||||
setArrowsAisleProperties: (aisleUuid, props) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "arrows-aisle") {
|
||||
@@ -182,10 +222,14 @@ export const createAisleStore = () => {
|
||||
aisle.type.aisleLength = props.aisleLength;
|
||||
if (props.gapLength !== undefined)
|
||||
aisle.type.gapLength = props.gapLength;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setArcAisleWidth: (aisleUuid, props) =>
|
||||
setArcAisleWidth: (aisleUuid, props) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "arc-aisle") {
|
||||
@@ -193,18 +237,26 @@ export const createAisleStore = () => {
|
||||
aisle.type.aisleWidth = props.aisleWidth;
|
||||
if (props.isFlipped !== undefined)
|
||||
aisle.type.isFlipped = props.isFlipped;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setCircleAisleWidth: (aisleUuid, width) =>
|
||||
setCircleAisleWidth: (aisleUuid, width) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "circle-aisle") {
|
||||
aisle.type.aisleWidth = width;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
setJunctionAisleProperties: (aisleUuid, props) =>
|
||||
setJunctionAisleProperties: (aisleUuid, props) => {
|
||||
let updatedAisle: Aisle | undefined;
|
||||
set((state) => {
|
||||
const aisle = state.aisles.find((a) => a.aisleUuid === aisleUuid);
|
||||
if (aisle && aisle.type.aisleType === "junction-aisle") {
|
||||
@@ -212,8 +264,11 @@ export const createAisleStore = () => {
|
||||
aisle.type.aisleWidth = props.aisleWidth;
|
||||
if (props.isFlipped !== undefined)
|
||||
aisle.type.isFlipped = props.isFlipped;
|
||||
updatedAisle = JSON.parse(JSON.stringify(aisle));
|
||||
}
|
||||
}),
|
||||
});
|
||||
return updatedAisle;
|
||||
},
|
||||
|
||||
getAisleById: (uuid) => {
|
||||
return get().aisles.find((a) => a.aisleUuid === uuid);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Object3D } from 'three';
|
||||
import { Object3D, Vector3 } from 'three';
|
||||
import { create } from 'zustand';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
|
||||
@@ -38,10 +38,16 @@ interface BuilderState {
|
||||
zoneColor: string;
|
||||
|
||||
// Decal Settings
|
||||
selectedDecal: Object3D | null;
|
||||
selectedDecal: { decalMesh: Object3D | null, decalData: Decal } | null;
|
||||
deletableDecal: Object3D | null;
|
||||
decalDragState: {
|
||||
isDragging: boolean,
|
||||
draggingDecalUuid: string | null,
|
||||
dragOffset: Vector3 | null,
|
||||
},
|
||||
|
||||
// Aisle General
|
||||
selectedAisle: Object3D | null;
|
||||
selectedAisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null;
|
||||
aisleType: AisleTypes;
|
||||
aisleWidth: number;
|
||||
aisleColor: AisleColors;
|
||||
@@ -86,10 +92,12 @@ interface BuilderState {
|
||||
setZoneColor: (color: string) => void;
|
||||
|
||||
// Setters - Decal
|
||||
setSelectedDecal: (decal: Object3D | null) => void;
|
||||
setSelectedDecal: (decal: { decalMesh: Object3D | null, decalData: Decal } | null) => void;
|
||||
setDeletableDecal: (decal: Object3D | null) => void;
|
||||
setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => void;
|
||||
|
||||
// Setters - Aisle General
|
||||
setSelectedAisle: (aisle: Object3D | null) => void;
|
||||
setSelectedAisle: (aisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null) => void;
|
||||
setAisleType: (type: AisleTypes) => void;
|
||||
setAisleWidth: (width: number) => void;
|
||||
setAisleColor: (color: AisleColors) => void;
|
||||
@@ -140,11 +148,17 @@ export const useBuilderStore = create<BuilderState>()(
|
||||
zoneColor: 'blue',
|
||||
|
||||
selectedDecal: null,
|
||||
deletableDecal: null,
|
||||
decalDragState: {
|
||||
isDragging: false,
|
||||
draggingDecalUuid: null,
|
||||
dragOffset: null,
|
||||
},
|
||||
|
||||
selectedAisle: null,
|
||||
aisleType: 'solid-aisle',
|
||||
aisleWidth: 0.1,
|
||||
aisleColor: 'yellow',
|
||||
aisleColor: '#FBE50E',
|
||||
|
||||
dashLength: 0.5,
|
||||
gapLength: 0.3,
|
||||
@@ -287,15 +301,31 @@ export const useBuilderStore = create<BuilderState>()(
|
||||
|
||||
// === Setters: Decal ===
|
||||
|
||||
setSelectedDecal: (decal: Object3D | null) => {
|
||||
setSelectedDecal: (decal: { decalMesh: Object3D | null, decalData: Decal } | null) => {
|
||||
set((state) => {
|
||||
state.selectedDecal = decal;
|
||||
})
|
||||
},
|
||||
|
||||
setDeletableDecal: (decal: Object3D | null) => {
|
||||
set((state) => {
|
||||
state.deletableDecal = decal;
|
||||
})
|
||||
},
|
||||
|
||||
setDecalDragState: (isDragging: boolean, draggingDecalUuid: string | null, dragOffset: Vector3 | null) => {
|
||||
set((state) => {
|
||||
state.decalDragState = {
|
||||
isDragging,
|
||||
draggingDecalUuid,
|
||||
dragOffset,
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// === Setters: Aisle General ===
|
||||
|
||||
setSelectedAisle: (aisle: Object3D | null) => {
|
||||
setSelectedAisle: (aisle: {aisleMesh: Object3D | null, aisleData: Aisle} | null) => {
|
||||
set((state) => {
|
||||
state.selectedAisle = aisle;
|
||||
});
|
||||
|
||||
@@ -19,9 +19,9 @@ interface FloorStore {
|
||||
setBevelStrength: (uuid: string, strength: number) => void;
|
||||
setDepth: (uuid: string, depth: number) => void;
|
||||
setMaterial: (uuid: string, sideMaterial: string, topMaterial: string) => void;
|
||||
addDecal: (floors: string, decal: Decal) => void;
|
||||
updateDecal: (decalUuid: string, decal: Decal) => void;
|
||||
removeDecal: (decalUuid: string) => void;
|
||||
addDecal: (floors: string, decal: Decal) => Decal | undefined;
|
||||
updateDecal: (decalUuid: string, decal: Partial<Decal>) => Floor | undefined;
|
||||
removeDecal: (decalUuid: string) => Floor | undefined;
|
||||
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void;
|
||||
updateDecalRotation: (decalUuid: string, rotation: number) => void;
|
||||
updateDecalScale: (decalUuid: string, scale: number) => void;
|
||||
@@ -196,28 +196,46 @@ export const createFloorStore = () => {
|
||||
}
|
||||
}),
|
||||
|
||||
addDecal: (floorUuid, decal) => set(state => {
|
||||
const floor = state.floors.find(f => f.floorUuid === floorUuid);
|
||||
if (floor) {
|
||||
floor.decals.push(decal);
|
||||
}
|
||||
}),
|
||||
|
||||
updateDecal: (decalUuid, updatedDecal) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
const index = floor.decals.findIndex(d => d.decalUuid === decalUuid);
|
||||
if (index !== -1) {
|
||||
floor.decals[index] = updatedDecal;
|
||||
break;
|
||||
addDecal: (floorUuid, decal) => {
|
||||
let addedDecal: Decal | undefined;
|
||||
set(state => {
|
||||
const floor = state.floors.find(f => f.floorUuid === floorUuid);
|
||||
if (floor) {
|
||||
floor.decals.push(decal);
|
||||
addedDecal = JSON.parse(JSON.stringify(decal));
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
return addedDecal;
|
||||
},
|
||||
|
||||
removeDecal: (decalUuid) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
floor.decals = floor.decals.filter(d => d.decalUuid !== decalUuid);
|
||||
}
|
||||
}),
|
||||
updateDecal: (decalUuid, updatedDecal) => {
|
||||
let affectedFloor: Floor | undefined;
|
||||
set(state => {
|
||||
for (const floor of state.floors) {
|
||||
const index = floor.decals.findIndex(d => d.decalUuid === decalUuid);
|
||||
if (index !== -1) {
|
||||
Object.assign(floor.decals[index], updatedDecal);
|
||||
affectedFloor = JSON.parse(JSON.stringify(floor));
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
return affectedFloor;
|
||||
},
|
||||
|
||||
removeDecal: (decalUuid) => {
|
||||
let affectedFloor: Floor | undefined;
|
||||
set(state => {
|
||||
for (const floor of state.floors) {
|
||||
const hasDecal = floor.decals.some(d => d.decalUuid === decalUuid);
|
||||
if (hasDecal) {
|
||||
floor.decals = floor.decals.filter(d => d.decalUuid !== decalUuid);
|
||||
affectedFloor = JSON.parse(JSON.stringify(floor));
|
||||
}
|
||||
}
|
||||
});
|
||||
return affectedFloor;
|
||||
},
|
||||
|
||||
updateDecalPosition: (decalUuid, position) => set(state => {
|
||||
for (const floor of state.floors) {
|
||||
|
||||
@@ -13,6 +13,9 @@ type UndoRedo3DStore = {
|
||||
|
||||
peekUndo3D: () => UndoRedo3DTypes | undefined;
|
||||
peekRedo3D: () => UndoRedo3DTypes | undefined;
|
||||
|
||||
subscribeUndoRedo: (cb: (action: { type: "undo" | "redo"; entry: UndoRedo3DTypes }) => void) => () => void;
|
||||
_listeners: ((action: { type: "undo" | "redo"; entry: UndoRedo3DTypes }) => void)[];
|
||||
};
|
||||
|
||||
export const createUndoRedo3DStore = () => {
|
||||
@@ -20,15 +23,14 @@ export const createUndoRedo3DStore = () => {
|
||||
immer((set, get) => ({
|
||||
undoStack: [],
|
||||
redoStack: [],
|
||||
_listeners: [],
|
||||
|
||||
push3D: (entry) => {
|
||||
set((state) => {
|
||||
state.undoStack.push(entry);
|
||||
|
||||
if (state.undoStack.length > undoRedoConfig.undoRedoCount) {
|
||||
state.undoStack.shift();
|
||||
}
|
||||
|
||||
state.redoStack = [];
|
||||
});
|
||||
},
|
||||
@@ -41,6 +43,9 @@ export const createUndoRedo3DStore = () => {
|
||||
state.redoStack.unshift(lastAction);
|
||||
}
|
||||
});
|
||||
if (lastAction) {
|
||||
get()._listeners.forEach((cb) => cb({ type: "undo", entry: lastAction! }));
|
||||
}
|
||||
return lastAction;
|
||||
},
|
||||
|
||||
@@ -52,6 +57,9 @@ export const createUndoRedo3DStore = () => {
|
||||
state.undoStack.push(redoAction);
|
||||
}
|
||||
});
|
||||
if (redoAction) {
|
||||
get()._listeners.forEach((cb) => cb({ type: "redo", entry: redoAction! }));
|
||||
}
|
||||
return redoAction;
|
||||
},
|
||||
|
||||
@@ -71,8 +79,19 @@ export const createUndoRedo3DStore = () => {
|
||||
const stack = get().redoStack;
|
||||
return stack.length > 0 ? stack[0] : undefined;
|
||||
},
|
||||
|
||||
subscribeUndoRedo: (cb) => {
|
||||
set((state) => {
|
||||
state._listeners.push(cb);
|
||||
});
|
||||
return () => {
|
||||
set((state) => {
|
||||
state._listeners = state._listeners.filter((l) => l !== cb);
|
||||
});
|
||||
};
|
||||
},
|
||||
}))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export type UndoRedo3DStoreType = ReturnType<typeof createUndoRedo3DStore>;
|
||||
@@ -9,10 +9,10 @@ interface WallStore {
|
||||
removeWall: (uuid: string) => void;
|
||||
clearWalls: () => void;
|
||||
removeWallByPoints: (Points: [Point, Point]) => Wall | undefined;
|
||||
addDecal: (wallUuid: string, decal: Decal) => void;
|
||||
updateDecal: (decalUuid: string, decal: Decal) => void;
|
||||
removeDecal: (decalUuid: string) => void;
|
||||
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => void;
|
||||
addDecal: (wallUuid: string, decal: Decal) => Decal | undefined;
|
||||
updateDecal: (decalUuid: string, decal: Partial<Decal>) => Wall | undefined;
|
||||
removeDecal: (decalUuid: string) => Wall | undefined;
|
||||
updateDecalPosition: (decalUuid: string, position: [number, number, number]) => Wall | undefined;
|
||||
updateDecalRotation: (decalUuid: string, rotation: number) => void;
|
||||
updateDecalScale: (decalUuid: string, scale: number) => void;
|
||||
|
||||
@@ -83,37 +83,60 @@ export const createWallStore = () => {
|
||||
return removedWall;
|
||||
},
|
||||
|
||||
addDecal: (wallUuid, decal) => set((state) => {
|
||||
const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid);
|
||||
if (wallToUpdate) {
|
||||
wallToUpdate.decals.push(decal);
|
||||
}
|
||||
}),
|
||||
|
||||
updateDecal: (decalUuid, decal) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid);
|
||||
if (decalToUpdate) {
|
||||
Object.assign(decalToUpdate, decal);
|
||||
addDecal: (wallUuid, decal) => {
|
||||
let addedDecal: Decal | undefined;
|
||||
set((state) => {
|
||||
const wallToUpdate = state.walls.find(w => w.wallUuid === wallUuid);
|
||||
if (wallToUpdate) {
|
||||
wallToUpdate.decals.push(decal);
|
||||
addedDecal = JSON.parse(JSON.stringify(decal));
|
||||
}
|
||||
}
|
||||
}),
|
||||
})
|
||||
return addedDecal;
|
||||
},
|
||||
|
||||
removeDecal: (decalUuid) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
wall.decals = wall.decals.filter(d => d.decalUuid !== decalUuid);
|
||||
}
|
||||
}),
|
||||
|
||||
updateDecalPosition: (decalUuid, position) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const decal = wall.decals.find(d => d.decalUuid === decalUuid);
|
||||
if (decal) {
|
||||
decal.decalPosition = position;
|
||||
break;
|
||||
updateDecal: (decalUuid, decal) => {
|
||||
let affectedWall: Wall | undefined;
|
||||
set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const decalToUpdate = wall.decals.find(d => d.decalUuid === decalUuid);
|
||||
if (decalToUpdate) {
|
||||
Object.assign(decalToUpdate, decal);
|
||||
affectedWall = JSON.parse(JSON.stringify(wall));
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
});
|
||||
return affectedWall;
|
||||
},
|
||||
|
||||
removeDecal: (decalUuid) => {
|
||||
let affectedWall: Wall | undefined;
|
||||
set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const hasDecal = wall.decals.some(d => d.decalUuid === decalUuid);
|
||||
if (hasDecal) {
|
||||
wall.decals = wall.decals.filter(d => d.decalUuid !== decalUuid);
|
||||
affectedWall = JSON.parse(JSON.stringify(wall));
|
||||
}
|
||||
}
|
||||
});
|
||||
return affectedWall;
|
||||
},
|
||||
|
||||
updateDecalPosition: (decalUuid, position) => {
|
||||
let affectedWall: Wall | undefined;
|
||||
set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
const decal = wall.decals.find(d => d.decalUuid === decalUuid);
|
||||
if (decal) {
|
||||
decal.decalPosition = position;
|
||||
affectedWall = JSON.parse(JSON.stringify(wall));
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
return affectedWall;
|
||||
},
|
||||
|
||||
updateDecalRotation: (decalUuid, rotation) => set((state) => {
|
||||
for (const wall of state.walls) {
|
||||
|
||||
@@ -1500,6 +1500,28 @@
|
||||
border-radius: #{$border-radius-large};
|
||||
margin-right: 4px;
|
||||
overflow: hidden;
|
||||
&.yellow-black {
|
||||
background-color: black;
|
||||
background-size: 10px 10px;
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
#FBE50E 0,
|
||||
#FBE50E 2px,
|
||||
black 0,
|
||||
black 50%
|
||||
);
|
||||
}
|
||||
&.white-black {
|
||||
background-color: black;
|
||||
background-size: 10px 10px;
|
||||
background-image: repeating-linear-gradient(
|
||||
45deg,
|
||||
white 0,
|
||||
white 2px,
|
||||
black 0,
|
||||
black 50%
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.aisle-color {
|
||||
@@ -1984,17 +2006,51 @@
|
||||
max-height: 50vh;
|
||||
overflow: auto;
|
||||
|
||||
h2,
|
||||
.searched-content {
|
||||
.header,
|
||||
.searched-content,
|
||||
.categories-header {
|
||||
color: var(--text-color);
|
||||
font-family: $large;
|
||||
font-weight: $bold-weight;
|
||||
padding: 8px;
|
||||
padding: 4px 8px;
|
||||
@include flex-space-between;
|
||||
|
||||
.back-button {
|
||||
cursor: pointer;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 4;
|
||||
background: var(--background-color);
|
||||
border-radius: 0 0 12px 12px;
|
||||
backdrop-filter: blur(4px);
|
||||
.search-for {
|
||||
display: inline-block;
|
||||
color: var(--accent-color);
|
||||
max-width: 238px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.back-button {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
transition: background 0.2s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.back-arrow {
|
||||
margin-right: 2px;
|
||||
transform: translateX(0) translateY(-1px) scale(1.6) rotate(90deg);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
&:hover {
|
||||
background: var(--background-color-solid);
|
||||
.back-arrow {
|
||||
transform: translateX(-2px) translateY(-1px) scale(1.6)
|
||||
rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.categories-header,
|
||||
.searched-content {
|
||||
position: relative;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.categories-container {
|
||||
@@ -2132,6 +2188,7 @@
|
||||
border: 1px solid #564b69;
|
||||
padding: 12px 10px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
.catogory-asset-filter-wrapper {
|
||||
display: flex;
|
||||
|
||||
@@ -144,7 +144,8 @@
|
||||
}
|
||||
|
||||
.cards-container {
|
||||
height: 100%;
|
||||
height: auto;
|
||||
max-height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
position: relative;
|
||||
|
||||
3
app/src/types/builderTypes.d.ts
vendored
3
app/src/types/builderTypes.d.ts
vendored
@@ -88,6 +88,7 @@ interface Decal {
|
||||
decalType: WallDecal | FloorDecal;
|
||||
decalPosition: [number, number, number];
|
||||
decalRotation: number;
|
||||
decalOpacity: number;
|
||||
decalScale: number;
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ type Zones = Zone[];
|
||||
|
||||
type AisleTypes = 'solid-aisle' | 'dashed-aisle' | 'stripped-aisle' | 'dotted-aisle' | 'arrow-aisle' | 'arrows-aisle' | 'arc-aisle' | 'circle-aisle' | 'junction-aisle';
|
||||
|
||||
type AisleColors = 'gray' | 'yellow' | 'green' | 'orange' | 'blue' | 'purple' | 'red' | '#66FF00' | 'yellow-black' | 'white-black'
|
||||
type AisleColors = '#6F6F7A' | '#FBE50E' | '#43C06D' | '#FF711B' | '#488EF6' | '#AF52DE' | '#FF3B30' | '#66FF00' | 'yellow-black' | 'white-black'
|
||||
|
||||
interface SolidAisle {
|
||||
aisleType: 'solid-aisle';
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
const DB_NAME = 'GLTFStorage';
|
||||
const STORE_NAME = 'models';
|
||||
const DB_VERSION = 1;
|
||||
|
||||
export function initializeDB(): Promise<IDBDatabase> {
|
||||
const ASSET_DB_NAME = 'GLTFStorage';
|
||||
const ASSET_STORE_NAME = 'models';
|
||||
|
||||
const IMAGE_DB_NAME = 'ImageStorage';
|
||||
const IMAGE_STORE_NAME = 'images';
|
||||
|
||||
export function initializeAssetDB(): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
const request = indexedDB.open(ASSET_DB_NAME, DB_VERSION);
|
||||
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result;
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.createObjectStore(STORE_NAME);
|
||||
if (!db.objectStoreNames.contains(ASSET_STORE_NAME)) {
|
||||
db.createObjectStore(ASSET_STORE_NAME);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,11 +23,11 @@ export function initializeDB(): Promise<IDBDatabase> {
|
||||
}
|
||||
|
||||
export async function storeGLTF(key: string, file: Blob): Promise<void> {
|
||||
const db = await initializeDB();
|
||||
const db = await initializeAssetDB();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(STORE_NAME, 'readwrite');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const transaction = db.transaction(ASSET_STORE_NAME, 'readwrite');
|
||||
const store = transaction.objectStore(ASSET_STORE_NAME);
|
||||
const request = store.put(file, key);
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
@@ -32,11 +36,53 @@ export async function storeGLTF(key: string, file: Blob): Promise<void> {
|
||||
}
|
||||
|
||||
export async function retrieveGLTF(key: string): Promise<Blob | undefined> {
|
||||
const db = await initializeDB();
|
||||
const db = await initializeAssetDB();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(STORE_NAME, 'readonly');
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const transaction = db.transaction(ASSET_STORE_NAME, 'readonly');
|
||||
const store = transaction.objectStore(ASSET_STORE_NAME);
|
||||
const request = store.get(key);
|
||||
|
||||
request.onsuccess = () => resolve(request.result as Blob | undefined);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
export function initializeImageDB(): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(IMAGE_DB_NAME, DB_VERSION);
|
||||
|
||||
request.onupgradeneeded = () => {
|
||||
const db = request.result;
|
||||
if (!db.objectStoreNames.contains(IMAGE_STORE_NAME)) {
|
||||
db.createObjectStore(IMAGE_STORE_NAME);
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function storeImage(key: string, file: Blob): Promise<void> {
|
||||
const db = await initializeImageDB();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(IMAGE_STORE_NAME, 'readwrite');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
const request = store.put(file, key);
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function retrieveImage(key: string): Promise<Blob | undefined> {
|
||||
const db = await initializeImageDB();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = db.transaction(IMAGE_STORE_NAME, 'readonly');
|
||||
const store = transaction.objectStore(IMAGE_STORE_NAME);
|
||||
const request = store.get(key);
|
||||
|
||||
request.onsuccess = () => resolve(request.result as Blob | undefined);
|
||||
|
||||
Reference in New Issue
Block a user