├── A4.png
├── Greenlab Data.xlsx
├── LICENSE
├── README.md
├── background.A4.jpg
├── package-lock.json
├── package.json
├── public
├── assets
│ ├── clear.png
│ ├── dark.png
│ ├── day-view.png
│ ├── delete.png
│ ├── download--v1.png
│ ├── download.png
│ ├── email.png
│ ├── engineering.png
│ ├── help.png
│ ├── icon.png
│ ├── light.png
│ ├── logo.png
│ ├── logoBlack.png
│ ├── menu.png
│ ├── refresh.png
│ ├── settings.png
│ ├── xls-import.png
│ ├── ziko.PNG
│ └── ziko.png
├── index.html
├── index.js
├── js
│ ├── db.js
│ ├── footer.js
│ ├── form.js
│ ├── header.js
│ ├── preview.js
│ ├── scene.js
│ └── theme.js
└── lib
│ ├── 0
│ ├── CSS3DRenderer.js
│ ├── OrbitControls.js
│ ├── TransformControls.js
│ ├── htmltoimage.js
│ ├── jspdf.js
│ ├── socket.io.js
│ ├── three.js
│ ├── three.min.js
│ ├── three.module.js
│ └── xlsx.js
├── server.js
└── video.mp4
/A4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/A4.png
--------------------------------------------------------------------------------
/Greenlab Data.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/Greenlab Data.xlsx
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 ZAKARIA ELALAOUI
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Greenlab-Challenge
2 | ## Preview
3 | [Demo ](https://drive.google.com/file/d/11SMlCCuraowYi7QtERldPtLFl3TP0B9Y/view?usp=sharing)
4 |
5 | [Want To Try!](https://greenlab-challenge.vercel.app/)
6 | ## Libraries
7 | - [zikojs](https://github.com/zakarialaoui10/ziko.js)
8 | - [threejs](https://threejs.org/)
9 | - [xlsxjs](https://www.npmjs.com/package/xlsx)
10 | - [htmltoimage](https://www.npmjs.com/package/html-to-image)
11 | - [jsPdf](https://www.npmjs.com/package/jspdf)
12 | - [express](https://expressjs.com/fr/)
13 | - [socket.io](https://socket.io/fr/)
14 | - [nodemailer](https://nodemailer.com/about/)
15 |
16 |
17 | ## Features
18 | - [x] Change font family & size and color
19 | - [X] Toggle Dark/Light Mode
20 | - [x] store settings in Local storage (styles & positions)
21 | - [x] Upload Background
22 | - [x] Export Data from Excel File
23 | - [x] Orbit and transform control
24 | - [x] Preview of generated Attestations
25 | - [x] Download Attestations as pdf file
26 | - [x] Send Attestations via Email
27 |
28 | ## Index
29 |
30 |
31 |
32 | Refresh
33 |
34 |
35 |
36 | Reset settings
37 |
38 |
39 |
40 | help
41 |
42 |
43 |
44 | Toggle Dark/Light Mode
45 |
46 |
47 |
48 | ## Limitations
49 | To send attestaions via Email we need real-time communication between the client and the server,I use socket.io library to reach this communication ,on the other hand Serverless Functions on Vercel are stateless and have a maximum execution duration.
50 | As a result, it is not possible to maintain a WebSocket connection to a Serverless Function.
51 |
52 | ## Want to try localy
53 |
54 |
55 |
56 | Install any IDE (I Recommand Vs Code)
57 | Install Node.js
58 | Clone this repository
59 |
60 |
61 |
62 |
63 | ```
64 | gh repo clone zakarialaoui10/Greenlab-Challenge
65 | ```
66 |
67 |
68 | ## License
69 | [MIT](https://choosealicense.com/licenses/mit/)
70 |
71 |
72 |
75 |
--------------------------------------------------------------------------------
/background.A4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/background.A4.jpg
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "greenlab-app",
3 | "version": "1.0.0",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@types/component-emitter": {
8 | "version": "1.2.11",
9 | "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.11.tgz",
10 | "integrity": "sha512-SRXjM+tfsSlA9VuG8hGO2nft2p8zjXCK1VcC6N4NXbBbYbSia9kzCChYQajIjzIqOOOuh5Ock6MmV2oux4jDZQ=="
11 | },
12 | "@types/cookie": {
13 | "version": "0.4.1",
14 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
15 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
16 | },
17 | "@types/cors": {
18 | "version": "2.8.12",
19 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
20 | "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw=="
21 | },
22 | "@types/node": {
23 | "version": "17.0.33",
24 | "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.33.tgz",
25 | "integrity": "sha512-miWq2m2FiQZmaHfdZNcbpp9PuXg34W5JZ5CrJ/BaS70VuhoJENBEQybeiYSaPBRNq6KQGnjfEnc/F3PN++D+XQ=="
26 | },
27 | "accepts": {
28 | "version": "1.3.8",
29 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
30 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
31 | "requires": {
32 | "mime-types": "~2.1.34",
33 | "negotiator": "0.6.3"
34 | }
35 | },
36 | "array-flatten": {
37 | "version": "1.1.1",
38 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
39 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
40 | },
41 | "base64id": {
42 | "version": "2.0.0",
43 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
44 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
45 | },
46 | "body-parser": {
47 | "version": "1.20.0",
48 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz",
49 | "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==",
50 | "requires": {
51 | "bytes": "3.1.2",
52 | "content-type": "~1.0.4",
53 | "debug": "2.6.9",
54 | "depd": "2.0.0",
55 | "destroy": "1.2.0",
56 | "http-errors": "2.0.0",
57 | "iconv-lite": "0.4.24",
58 | "on-finished": "2.4.1",
59 | "qs": "6.10.3",
60 | "raw-body": "2.5.1",
61 | "type-is": "~1.6.18",
62 | "unpipe": "1.0.0"
63 | }
64 | },
65 | "bytes": {
66 | "version": "3.1.2",
67 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
68 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="
69 | },
70 | "call-bind": {
71 | "version": "1.0.2",
72 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
73 | "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
74 | "requires": {
75 | "function-bind": "^1.1.1",
76 | "get-intrinsic": "^1.0.2"
77 | }
78 | },
79 | "component-emitter": {
80 | "version": "1.3.0",
81 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
82 | "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
83 | },
84 | "content-disposition": {
85 | "version": "0.5.4",
86 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
87 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
88 | "requires": {
89 | "safe-buffer": "5.2.1"
90 | }
91 | },
92 | "content-type": {
93 | "version": "1.0.4",
94 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
95 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
96 | },
97 | "cookie": {
98 | "version": "0.5.0",
99 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
100 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
101 | },
102 | "cookie-signature": {
103 | "version": "1.0.6",
104 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
105 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
106 | },
107 | "cors": {
108 | "version": "2.8.5",
109 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
110 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
111 | "requires": {
112 | "object-assign": "^4",
113 | "vary": "^1"
114 | }
115 | },
116 | "debug": {
117 | "version": "2.6.9",
118 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
119 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
120 | "requires": {
121 | "ms": "2.0.0"
122 | }
123 | },
124 | "depd": {
125 | "version": "2.0.0",
126 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
127 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
128 | },
129 | "destroy": {
130 | "version": "1.2.0",
131 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
132 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="
133 | },
134 | "dotenv": {
135 | "version": "16.0.1",
136 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.1.tgz",
137 | "integrity": "sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ=="
138 | },
139 | "ee-first": {
140 | "version": "1.1.1",
141 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
142 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
143 | },
144 | "encodeurl": {
145 | "version": "1.0.2",
146 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
147 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
148 | },
149 | "engine.io": {
150 | "version": "6.2.0",
151 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.2.0.tgz",
152 | "integrity": "sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg==",
153 | "requires": {
154 | "@types/cookie": "^0.4.1",
155 | "@types/cors": "^2.8.12",
156 | "@types/node": ">=10.0.0",
157 | "accepts": "~1.3.4",
158 | "base64id": "2.0.0",
159 | "cookie": "~0.4.1",
160 | "cors": "~2.8.5",
161 | "debug": "~4.3.1",
162 | "engine.io-parser": "~5.0.3",
163 | "ws": "~8.2.3"
164 | },
165 | "dependencies": {
166 | "cookie": {
167 | "version": "0.4.2",
168 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
169 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA=="
170 | },
171 | "debug": {
172 | "version": "4.3.4",
173 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
174 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
175 | "requires": {
176 | "ms": "2.1.2"
177 | }
178 | },
179 | "ms": {
180 | "version": "2.1.2",
181 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
182 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
183 | }
184 | }
185 | },
186 | "engine.io-parser": {
187 | "version": "5.0.4",
188 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz",
189 | "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg=="
190 | },
191 | "escape-html": {
192 | "version": "1.0.3",
193 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
194 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
195 | },
196 | "etag": {
197 | "version": "1.8.1",
198 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
199 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
200 | },
201 | "express": {
202 | "version": "4.18.1",
203 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz",
204 | "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==",
205 | "requires": {
206 | "accepts": "~1.3.8",
207 | "array-flatten": "1.1.1",
208 | "body-parser": "1.20.0",
209 | "content-disposition": "0.5.4",
210 | "content-type": "~1.0.4",
211 | "cookie": "0.5.0",
212 | "cookie-signature": "1.0.6",
213 | "debug": "2.6.9",
214 | "depd": "2.0.0",
215 | "encodeurl": "~1.0.2",
216 | "escape-html": "~1.0.3",
217 | "etag": "~1.8.1",
218 | "finalhandler": "1.2.0",
219 | "fresh": "0.5.2",
220 | "http-errors": "2.0.0",
221 | "merge-descriptors": "1.0.1",
222 | "methods": "~1.1.2",
223 | "on-finished": "2.4.1",
224 | "parseurl": "~1.3.3",
225 | "path-to-regexp": "0.1.7",
226 | "proxy-addr": "~2.0.7",
227 | "qs": "6.10.3",
228 | "range-parser": "~1.2.1",
229 | "safe-buffer": "5.2.1",
230 | "send": "0.18.0",
231 | "serve-static": "1.15.0",
232 | "setprototypeof": "1.2.0",
233 | "statuses": "2.0.1",
234 | "type-is": "~1.6.18",
235 | "utils-merge": "1.0.1",
236 | "vary": "~1.1.2"
237 | }
238 | },
239 | "finalhandler": {
240 | "version": "1.2.0",
241 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
242 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
243 | "requires": {
244 | "debug": "2.6.9",
245 | "encodeurl": "~1.0.2",
246 | "escape-html": "~1.0.3",
247 | "on-finished": "2.4.1",
248 | "parseurl": "~1.3.3",
249 | "statuses": "2.0.1",
250 | "unpipe": "~1.0.0"
251 | }
252 | },
253 | "forwarded": {
254 | "version": "0.2.0",
255 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
256 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
257 | },
258 | "fresh": {
259 | "version": "0.5.2",
260 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
261 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
262 | },
263 | "function-bind": {
264 | "version": "1.1.1",
265 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
266 | "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
267 | },
268 | "get-intrinsic": {
269 | "version": "1.1.1",
270 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
271 | "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
272 | "requires": {
273 | "function-bind": "^1.1.1",
274 | "has": "^1.0.3",
275 | "has-symbols": "^1.0.1"
276 | }
277 | },
278 | "has": {
279 | "version": "1.0.3",
280 | "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
281 | "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
282 | "requires": {
283 | "function-bind": "^1.1.1"
284 | }
285 | },
286 | "has-symbols": {
287 | "version": "1.0.3",
288 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
289 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
290 | },
291 | "http-errors": {
292 | "version": "2.0.0",
293 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
294 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
295 | "requires": {
296 | "depd": "2.0.0",
297 | "inherits": "2.0.4",
298 | "setprototypeof": "1.2.0",
299 | "statuses": "2.0.1",
300 | "toidentifier": "1.0.1"
301 | }
302 | },
303 | "iconv-lite": {
304 | "version": "0.4.24",
305 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
306 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
307 | "requires": {
308 | "safer-buffer": ">= 2.1.2 < 3"
309 | }
310 | },
311 | "inherits": {
312 | "version": "2.0.4",
313 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
314 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
315 | },
316 | "ipaddr.js": {
317 | "version": "1.9.1",
318 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
319 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="
320 | },
321 | "media-typer": {
322 | "version": "0.3.0",
323 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
324 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
325 | },
326 | "merge-descriptors": {
327 | "version": "1.0.1",
328 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
329 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
330 | },
331 | "methods": {
332 | "version": "1.1.2",
333 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
334 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
335 | },
336 | "mime": {
337 | "version": "1.6.0",
338 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
339 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
340 | },
341 | "mime-db": {
342 | "version": "1.52.0",
343 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
344 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
345 | },
346 | "mime-types": {
347 | "version": "2.1.35",
348 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
349 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
350 | "requires": {
351 | "mime-db": "1.52.0"
352 | }
353 | },
354 | "ms": {
355 | "version": "2.0.0",
356 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
357 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
358 | },
359 | "negotiator": {
360 | "version": "0.6.3",
361 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
362 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
363 | },
364 | "nodemailer": {
365 | "version": "6.7.5",
366 | "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.7.5.tgz",
367 | "integrity": "sha512-6VtMpwhsrixq1HDYSBBHvW0GwiWawE75dS3oal48VqRhUvKJNnKnJo2RI/bCVQubj1vgrgscMNW4DHaD6xtMCg=="
368 | },
369 | "object-assign": {
370 | "version": "4.1.1",
371 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
372 | "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
373 | },
374 | "object-inspect": {
375 | "version": "1.12.0",
376 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz",
377 | "integrity": "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="
378 | },
379 | "on-finished": {
380 | "version": "2.4.1",
381 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
382 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
383 | "requires": {
384 | "ee-first": "1.1.1"
385 | }
386 | },
387 | "parseurl": {
388 | "version": "1.3.3",
389 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
390 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
391 | },
392 | "path-to-regexp": {
393 | "version": "0.1.7",
394 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
395 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
396 | },
397 | "proxy-addr": {
398 | "version": "2.0.7",
399 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
400 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
401 | "requires": {
402 | "forwarded": "0.2.0",
403 | "ipaddr.js": "1.9.1"
404 | }
405 | },
406 | "qs": {
407 | "version": "6.10.3",
408 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz",
409 | "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==",
410 | "requires": {
411 | "side-channel": "^1.0.4"
412 | }
413 | },
414 | "range-parser": {
415 | "version": "1.2.1",
416 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
417 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
418 | },
419 | "raw-body": {
420 | "version": "2.5.1",
421 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
422 | "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
423 | "requires": {
424 | "bytes": "3.1.2",
425 | "http-errors": "2.0.0",
426 | "iconv-lite": "0.4.24",
427 | "unpipe": "1.0.0"
428 | }
429 | },
430 | "safe-buffer": {
431 | "version": "5.2.1",
432 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
433 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
434 | },
435 | "safer-buffer": {
436 | "version": "2.1.2",
437 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
438 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
439 | },
440 | "send": {
441 | "version": "0.18.0",
442 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
443 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
444 | "requires": {
445 | "debug": "2.6.9",
446 | "depd": "2.0.0",
447 | "destroy": "1.2.0",
448 | "encodeurl": "~1.0.2",
449 | "escape-html": "~1.0.3",
450 | "etag": "~1.8.1",
451 | "fresh": "0.5.2",
452 | "http-errors": "2.0.0",
453 | "mime": "1.6.0",
454 | "ms": "2.1.3",
455 | "on-finished": "2.4.1",
456 | "range-parser": "~1.2.1",
457 | "statuses": "2.0.1"
458 | },
459 | "dependencies": {
460 | "ms": {
461 | "version": "2.1.3",
462 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
463 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
464 | }
465 | }
466 | },
467 | "serve-static": {
468 | "version": "1.15.0",
469 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
470 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
471 | "requires": {
472 | "encodeurl": "~1.0.2",
473 | "escape-html": "~1.0.3",
474 | "parseurl": "~1.3.3",
475 | "send": "0.18.0"
476 | }
477 | },
478 | "setprototypeof": {
479 | "version": "1.2.0",
480 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
481 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
482 | },
483 | "side-channel": {
484 | "version": "1.0.4",
485 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
486 | "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
487 | "requires": {
488 | "call-bind": "^1.0.0",
489 | "get-intrinsic": "^1.0.2",
490 | "object-inspect": "^1.9.0"
491 | }
492 | },
493 | "socket.io": {
494 | "version": "4.5.0",
495 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.5.0.tgz",
496 | "integrity": "sha512-slTYqU2jCgMjXwresG8grhUi/cC6GjzmcfqArzaH3BN/9I/42eZk9yamNvZJdBfTubkjEdKAKs12NEztId+bUA==",
497 | "requires": {
498 | "accepts": "~1.3.4",
499 | "base64id": "~2.0.0",
500 | "debug": "~4.3.2",
501 | "engine.io": "~6.2.0",
502 | "socket.io-adapter": "~2.4.0",
503 | "socket.io-parser": "~4.0.4"
504 | },
505 | "dependencies": {
506 | "debug": {
507 | "version": "4.3.4",
508 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
509 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
510 | "requires": {
511 | "ms": "2.1.2"
512 | }
513 | },
514 | "ms": {
515 | "version": "2.1.2",
516 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
517 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
518 | }
519 | }
520 | },
521 | "socket.io-adapter": {
522 | "version": "2.4.0",
523 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz",
524 | "integrity": "sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg=="
525 | },
526 | "socket.io-parser": {
527 | "version": "4.0.4",
528 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.0.4.tgz",
529 | "integrity": "sha512-t+b0SS+IxG7Rxzda2EVvyBZbvFPBCjJoyHuE0P//7OAsN23GItzDRdWa6ALxZI/8R5ygK7jAR6t028/z+7295g==",
530 | "requires": {
531 | "@types/component-emitter": "^1.2.10",
532 | "component-emitter": "~1.3.0",
533 | "debug": "~4.3.1"
534 | },
535 | "dependencies": {
536 | "debug": {
537 | "version": "4.3.4",
538 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
539 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
540 | "requires": {
541 | "ms": "2.1.2"
542 | }
543 | },
544 | "ms": {
545 | "version": "2.1.2",
546 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
547 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
548 | }
549 | }
550 | },
551 | "statuses": {
552 | "version": "2.0.1",
553 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
554 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="
555 | },
556 | "toidentifier": {
557 | "version": "1.0.1",
558 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
559 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="
560 | },
561 | "type-is": {
562 | "version": "1.6.18",
563 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
564 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
565 | "requires": {
566 | "media-typer": "0.3.0",
567 | "mime-types": "~2.1.24"
568 | }
569 | },
570 | "unpipe": {
571 | "version": "1.0.0",
572 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
573 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
574 | },
575 | "utils-merge": {
576 | "version": "1.0.1",
577 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
578 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
579 | },
580 | "vary": {
581 | "version": "1.1.2",
582 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
583 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
584 | },
585 | "ws": {
586 | "version": "8.2.3",
587 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz",
588 | "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA=="
589 | }
590 | }
591 | }
592 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "greenlab-app",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "server.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "start": "node server.js"
9 | },
10 | "author": "zakaria elalaoui",
11 | "license": "MIT",
12 | "dependencies": {
13 | "dotenv": "^16.0.1",
14 | "express": "^4.18.1",
15 | "nodemailer": "^6.7.5",
16 | "socket.io": "^4.5.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/public/assets/clear.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/clear.png
--------------------------------------------------------------------------------
/public/assets/dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/dark.png
--------------------------------------------------------------------------------
/public/assets/day-view.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/day-view.png
--------------------------------------------------------------------------------
/public/assets/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/delete.png
--------------------------------------------------------------------------------
/public/assets/download--v1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/download--v1.png
--------------------------------------------------------------------------------
/public/assets/download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/download.png
--------------------------------------------------------------------------------
/public/assets/email.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/email.png
--------------------------------------------------------------------------------
/public/assets/engineering.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/engineering.png
--------------------------------------------------------------------------------
/public/assets/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/help.png
--------------------------------------------------------------------------------
/public/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/icon.png
--------------------------------------------------------------------------------
/public/assets/light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/light.png
--------------------------------------------------------------------------------
/public/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/logo.png
--------------------------------------------------------------------------------
/public/assets/logoBlack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/logoBlack.png
--------------------------------------------------------------------------------
/public/assets/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/menu.png
--------------------------------------------------------------------------------
/public/assets/refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/refresh.png
--------------------------------------------------------------------------------
/public/assets/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/settings.png
--------------------------------------------------------------------------------
/public/assets/xls-import.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/xls-import.png
--------------------------------------------------------------------------------
/public/assets/ziko.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/ziko.PNG
--------------------------------------------------------------------------------
/public/assets/ziko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/public/assets/ziko.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | GreenLab
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/public/index.js:
--------------------------------------------------------------------------------
1 | Ziko.UI.ExtractAll()
2 | Ziko.Math.ExtractAll()
3 | Ziko.THREE.ExtractAll()
4 | import {header, logo, modeImage} from "./js/header.js";
5 | import {form} from "./js/form.js";
6 | import {scene,resetCamera,saveTransform} from "./js/scene.js";
7 | import {Galerie,GridPreview} from "./js/preview.js"
8 | import {theme,setTheme,getTheme} from "./js/theme.js"
9 | import { footer } from "./js/footer.js";
10 | header.render()
11 | form.render()
12 | scene.render()
13 | Galerie.render().hide()
14 | GridPreview.render().hide()
15 |
16 | var App=Flex(
17 | header,
18 | form,
19 | scene,
20 | Galerie,
21 | GridPreview,
22 | footer,
23 | ).size("100%","auto").border("none").vertical(0,0)
24 | App.style(theme[getTheme()])
25 | modeImage.updateSrc(theme[getTheme()].src)
26 | logo.updateSrc(theme[getTheme()].logosrc)
27 | modeImage.click(()=>{
28 | if(getTheme()==="dark")setTheme("light");
29 | else setTheme("dark")
30 | modeImage.updateSrc(theme[getTheme()].src);
31 | logo.updateSrc(theme[getTheme()].logosrc);
32 | App.style(theme[getTheme()])
33 | })
34 | function resize(){
35 | if(window.innerWidth<700){
36 | scene.hide();
37 | footer.margin("30px auto")
38 | }
39 | else{
40 | scene.show();
41 | footer.margin("10px auto")
42 | }
43 | }
44 | App.resizeObserver(resize)
45 | resize()
46 |
--------------------------------------------------------------------------------
/public/js/db.js:
--------------------------------------------------------------------------------
1 | export var styles=new Array(5).fill(null).map(n=>Object.assign({},
2 | {
3 | color:"black",
4 | fontSize:"14",
5 | fontFamily:"Arial"
6 | }
7 | ))
8 | export var position=[
9 | [0,150,0],
10 | [0,100,0],
11 | [0,-50,0],
12 | [100,-180,0],
13 | [0,0,0],
14 | [0,200,0],
15 | [0,-120,0],
16 | ]
17 | if(!localStorage.getItem("styles"))localStorage.setItem("styles",JSON.stringify(styles));
18 | else styles=JSON.parse(localStorage.getItem("styles"))
19 | if(!localStorage.getItem("position"))localStorage.setItem("position",JSON.stringify(position));
20 | else position=JSON.parse(localStorage.getItem("position"))
21 | export var setStyle=(i,newConfig)=>{
22 | styles[i]=Object.assign(getStyle(i),newConfig)
23 | localStorage.setItem("styles",JSON.stringify(styles))
24 | }
25 | export var getStyle=(i)=>{
26 | return JSON.parse(localStorage.getItem("styles"))[i]
27 | }
28 | export var setPosition=(i,newPosition)=>{
29 | position[i]=newPosition;
30 | localStorage.setItem("position",JSON.stringify(position))
31 | }
32 | export var getPosition=(i)=>{
33 | return JSON.parse(localStorage.getItem("position"))[i]
34 | }
35 | export var resetAll=()=>{
36 | localStorage.removeItem("position");
37 | localStorage.removeItem("styles");
38 | location.reload()
39 | }
--------------------------------------------------------------------------------
/public/js/footer.js:
--------------------------------------------------------------------------------
1 | Ziko.UI.ExtractAll()
2 | export var footer=Footer(
3 | Flex(
4 | h2("Powered by "),
5 | image("./assets/ziko.png","150px","auto").link("https://github.com/zakarialaoui10/ziko.js","_blank")
6 | ).size("100%","auto").horizontal("space-around",0).border("none")
7 | ).size("70%","auto").margin("10px auto")
--------------------------------------------------------------------------------
/public/js/form.js:
--------------------------------------------------------------------------------
1 | import {getStyle,setStyle,styles} from "./db.js"
2 | var dataForm = {
3 | "Type de Certificat": "",
4 | "Titre de formation": "",
5 | "Description de formation": "",
6 | "Date": "",
7 | "Nom et Prénoms":null,
8 | "Image d'arriére plan": null
9 | }
10 | var labels = Object.keys(dataForm).map(n => Flex(text(n).margin("3px 0")).border("none").margin("10px 0").size("300px","auto").vertical(0,0))
11 | var inputs = [];
12 | inputs[0] = input().size("200px","15px")
13 | inputs[0].oninput(() => {Object.assign(dataForm, {
14 | "Type de Certificat": inputs[0].value
15 | })
16 | txt[0].setValue(inputs[0].value)
17 | })
18 | inputs[1] = input().size("200px","15px")
19 | inputs[1].oninput(() => {Object.assign(dataForm, {
20 | "Titre de formation": inputs[1].value
21 | })
22 | txt[1].setValue(inputs[1].value)
23 | })
24 | inputs[2] = input().size("200px","15px")
25 | inputs[2].oninput(() => { Object.assign(dataForm, {
26 | "Description de formation": inputs[2].value
27 | })
28 | txt[2].setValue(inputs[2].value)
29 | })
30 | inputs[3] = inputDate().size("200px","15px")
31 | inputs[3].oninput(() => {Object.assign(dataForm, {
32 | "Date": inputs[3].value
33 | })
34 | txt[3].setValue(inputs[3].value)
35 | })
36 | inputs[4] = input().setType("file").accept(".xls,.xlsx").size("200px","20px").hide();
37 | var importExcel=Grid(text("import excel file").center,image("./assets/xls-import.png","50px","50px").border("none")).columns(2).size("200px","50px").margin("auto 0").cursor("pointer")
38 | importExcel.click(()=>inputs[4].element.click()).border("none")
39 | var ExcelHandler=inputs[4]
40 | inputs[5] = input().setType("file").cursor("pointer").size("200px","20px")
41 | var backgroundInput=inputs[5];
42 | inputs.map(n=>n.padding("5px"))
43 | var stylesConfig=new Array(5).fill(null).map((n,i)=>
44 | Grid(
45 | text("color"),
46 | text("font size"),
47 | text("font family"),
48 | inputColor().width("40px").setValue(getStyle(i).color),
49 | inputNumber().width("80px").setValue(getStyle(i).fontSize),
50 | select("Arial","Verdana","Helvetica","Tahoma","Trebuchet MS","Times New Roman","Georgia","Garamond","Courier New","Brush Swcript MT").setSelectedValue(getStyle(i).fontFamily).width("80px")
51 |
52 | ).columns(3).size("250px","auto").spaceAround(1,1).padding("5px").fontSize("1rem").border("none"))
53 | stylesConfig.map((n,i)=>{
54 | n.items[3].oninput(()=>{
55 | setStyle(i,{color:n.items[3].value});
56 | txt[i].color(n.items[3].value)
57 | });
58 | n.items[4].oninput(()=>{
59 | setStyle(i,{fontSize:n.items[4].value+""})
60 | txt[i].style({fontSize:n.items[4].value+"px"})
61 | });
62 | n.items[5].onchange(()=>{
63 | setStyle(i,{fontFamily:n.items[5].value});
64 | txt[i].style({fontFamily:n.items[5].value});
65 | });
66 | })
67 | labels.map((n,i)=>n.append(inputs[i]))
68 | labels.slice(0,5).map((n,i)=>n.append(stylesConfig[i]))
69 | var form=Flex(...labels).resp("300px").style({
70 | width:"95vw",
71 | height:"auto",
72 | margin:"5px auto",
73 | fontFamily:"verdana",
74 | justifyContent:"space-around",
75 | alignItems:"stretch",
76 | border:"none"
77 | })
78 | form.append(importExcel)
79 | var txt=["Type de Certificat","Titre de formation","Description de formation","Date","Nom Et Prénom"].map((n,i)=>text(n).style({maxWidth:"200px",fontFamily:"arial"}).style({
80 | color:getStyle(i).color,
81 | fontSize:getStyle(i).fontSize+"px",
82 | fontFamily:getStyle(i),
83 | }))
84 | var nameText=txt[4]
85 | export{form,txt,backgroundInput,ExcelHandler,nameText}
--------------------------------------------------------------------------------
/public/js/header.js:
--------------------------------------------------------------------------------
1 | import { resetAll } from "./db.js";
2 |
3 | Ziko.UI.ExtractAll()
4 | Ziko.Math.ExtractAll()
5 | Ziko.THREE.ExtractAll()
6 | var modeImage=image("./assets/dark.png","30px","auto").border("none").cursor("pointer");
7 | var logo=image("./assets/logo.png","auto","100%").border("none")
8 | var refresh=image("./assets/refresh.png","30px","auto").border("none").cursor("pointer").click(()=>location.reload())
9 | var refreshSetting=image("./assets/clear.png","30px","auto").border("none").cursor("pointer").click(()=>resetAll())
10 | var help=image("./assets/help.png","30px","auto").border("none").link("https://github.com/zakarialaoui10/Greenlab-Challenge","_blank")
11 | var header=Header(
12 | Flex(
13 | logo,
14 | refresh,
15 | refreshSetting,
16 | help,
17 | modeImage
18 | ).size("100%","100%").horizontal("space-around",0).border("none")
19 | ).style({
20 | width:"90vw",
21 | height:"50px",
22 | margin:"10px auto",
23 | padding:"10px auto",
24 | top:0,
25 | zIndex:3
26 | })
27 | if(window.innerWidth<700){
28 | help.hide()
29 | refresh.hide()
30 | refreshSetting.hide()
31 | }
32 | export{header,modeImage,logo}
--------------------------------------------------------------------------------
/public/js/preview.js:
--------------------------------------------------------------------------------
1 | export var Galerie=Carousel().style({
2 | width:"90%",
3 | height:"500px",
4 | margin:"5px auto",
5 | background:"#eee"
6 | }).cursor("move").style({padding:"10px",width:"90%"})
7 | export var GridPreview=Grid().columns(4).spaceAround(1,0).style({
8 | width:"80%",
9 | height:"auto",
10 | margin:"5px auto",
11 | padding:"10px",
12 | fontSize:"1.5rem",
13 | fontFamily:"Cursive"
14 | })
15 |
--------------------------------------------------------------------------------
/public/js/scene.js:
--------------------------------------------------------------------------------
1 | Ziko.UI.ExtractAll()
2 | Ziko.Math.ExtractAll()
3 | Ziko.THREE.ExtractAll()
4 | var socket = io();
5 | import {backgroundInput, ExcelHandler, nameText, txt} from "./form.js"
6 | import {setStyle,getStyle,setPosition,getPosition} from "./db.js"
7 | import {Galerie,GridPreview} from "./preview.js"
8 | txt[5]=image("./assets/icon.png","50px").border("none");
9 | txt[6]=image("./assets/logoBlack.png","100px").border("none")
10 | var scene=Css3D("1170px","827px").style({minHeight:"70vh",zIndex:2}).margin("10px auto").orbitOn().background("#ddd").border("1px solid black").style({
11 | boxShadow:"1px 1px 5px white,-1px -1px 10px black"
12 | })
13 | scene.camera.translateZ(200);
14 | var backgroundImage=image("");
15 | scene.addCss(backgroundImage).position(0,0,-2);
16 | var t=new Array(5).fill(null);
17 | t=t.map((n,i)=>scene.addCss(txt[i].style(setStyle(i))).position(...getPosition(i)))
18 | t[5]=scene.addCss(txt[5]).position(...getPosition(5))
19 | t[6]=scene.addCss(txt[6]).position(...getPosition(6))
20 |
21 | txt.map((n,i)=>n.mousedown(()=>{
22 | scene.setTransform(t[i]);
23 | }))
24 | txt.map((n,i)=>n.mouseup(()=>{
25 | saveTransform()
26 | }))
27 | scene.addGl(gridH(1000,100).rotX(PI/2))
28 | var saveTransform=()=>t.map((n,i)=>setPosition(i,Object.values(n.mesh.position)))
29 | var resetCamera=()=>{
30 | saveTransform()
31 | scene.camera.rotation.x=-6.123233995736766e-17;
32 | scene.camera.rotation.y=0;
33 | scene.camera.rotation.z=0;
34 | scene.camera.position.x=0;
35 | scene.camera.position.y=1.8369701987210297e-14;
36 | scene.camera.position.z=300
37 | scene.renderGl().renderCss()
38 | }
39 | async function handleImageAsync(e) {
40 | const reader = new FileReader();
41 | const img = new Image();
42 | reader.onload = function (event) {
43 | img.src = event.target.result;
44 | backgroundImage.updateSrc(img.src)
45 | };
46 | reader.readAsDataURL(e.target.files[0]);
47 | this.img = img;
48 | }
49 |
50 | var images=[],pdfs=[]
51 | var toImage=(i=0)=>{
52 | resetCamera()
53 | var node = scene.element.children[1];
54 | Galerie.show()
55 | htmlToImage.toPng(node)
56 | .then(function (dataUrl) {
57 | var im=new Image()
58 | im.src=dataUrl;
59 | images[i]=image(im.src).render(true).background("white");
60 | Galerie.addTrack(images[i].size("500px","auto"));
61 | var doc = new jspdf.jsPDF('l', 'mm', 'a4');
62 | doc.addImage(im.src, 'JPEG', 0, 0);
63 | pdfs[i]=doc;
64 | url[i] = "data:application/pdf;base64," + btoa(pdfs[i].output());
65 | //console.log(url[i])
66 | const pdfo = new File([doc.output("blob")],names[i]+".pdf", {
67 | type: "application/pdf",
68 | });
69 | })
70 | .catch(function (error) {
71 | console.error('oops, something went wrong!', error);
72 | });
73 | }
74 | Galerie.pointermove(()=>{
75 | images.map(n=>{
76 | n.intersectRatio(e=>n.scale(e).opacity(e))
77 | })
78 | })
79 | var names=[],id=[],save=[],emails=[],url=[]
80 | async function handleFileAsync(e) {
81 | const file = e.target.files[0];
82 | const data = await file.arrayBuffer();
83 | GridPreview.show()
84 | const workbook = XLSX.read(data);
85 | workbook.SheetNames.forEach(sheet => {
86 | let rowObject = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheet]);
87 | var arr = rowObject.map(n => Object.values(n));
88 | id=arange(1,arr.length+1,1);
89 | save=new Array(arr.length).fill("download");
90 | arr.map((n,i)=>{
91 | names[i]=arr[i][1];
92 | emails[i]=arr[i][3]
93 | });
94 | var GridData=matrix([id,names,save,emails]).T.arr;
95 | var Emails=emails
96 | for(let i=0;ipdfs[i].save(names[i]))
100 | GridData[i][3]=image("./assets/email.png","50px","50px").cursor("pointer").border("none").click(()=>{
101 | var data=Object.assign({},{
102 | data:url[i],
103 | email:Emails[i],
104 | name:names[i]
105 | })
106 | socket.emit('email',JSON.stringify(data));
107 | })
108 | }
109 | GridPreview.append(...GridData.flat(1))
110 | })
111 | names.map((n,i)=>{
112 | setTimeout(() => {
113 | nameText.setValue(names[i]);
114 | toImage(i)
115 | }, 100);
116 | });
117 | }
118 |
119 | ExcelHandler.onchange((e)=>handleFileAsync(e).then(()=>{
120 | saveTransform()
121 | id=[];
122 | save=[];
123 | pdfs=[];
124 | images=[];
125 | emails=[];
126 | }));
127 | backgroundInput.onchange(handleImageAsync)
128 | socket.on('succed',(data)=>alert(data))
129 | socket.on('fail',(data)=>alert(data))
130 | export{scene,backgroundImage,resetCamera,saveTransform,toImage}
131 |
--------------------------------------------------------------------------------
/public/js/theme.js:
--------------------------------------------------------------------------------
1 | var colors = ["#e7f5fe", "#88bdff", "#4680c2", "#1778f2", "#1778f2", "#0d66c2"];
2 | var theme={
3 | dark:{
4 | background:"#4680c2",
5 | color:"#eee",
6 | src:"./assets/light.png",
7 | logosrc:"./assets/logoBlack.png"
8 | },
9 | light:{
10 | background:"#e7f5fe",
11 | color:"#333",
12 | src:"./assets/dark.png",
13 | logosrc:"./assets/logo.png"
14 | }
15 | }
16 | if(!localStorage.getItem("theme"))localStorage.setItem("theme","dark");
17 | var setTheme=(mode)=>localStorage.setItem("theme",mode);
18 | var getTheme=()=>localStorage.getItem("theme");
19 | export{setTheme,theme,getTheme}
--------------------------------------------------------------------------------
/public/lib/0:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/lib/CSS3DRenderer.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Based on http://www.emagix.net/academic/mscs-project/item/camera-sync-with-css3-and-webgl-threejs
3 | * @author mrdoob / http://mrdoob.com/
4 | */
5 |
6 | THREE.CSS3DObject = function ( element ) {
7 |
8 | THREE.Object3D.call( this );
9 |
10 | this.element = element;
11 | this.element.style.position = "absolute";
12 | this.element.style.WebkitTransformStyle = 'preserve-3d';
13 | this.element.style.MozTransformStyle = 'preserve-3d';
14 | this.element.style.oTransformStyle = 'preserve-3d';
15 | this.element.style.transformStyle = 'preserve-3d';
16 |
17 | };
18 |
19 | THREE.CSS3DObject.prototype = Object.create( THREE.Object3D.prototype );
20 |
21 | THREE.CSS3DSprite = function ( element ) {
22 |
23 | THREE.CSS3DObject.call( this, element );
24 |
25 | };
26 |
27 | THREE.CSS3DSprite.prototype = Object.create( THREE.CSS3DObject.prototype );
28 |
29 | //
30 |
31 | THREE.CSS3DRenderer = function () {
32 |
33 | console.log( 'THREE.CSS3DRenderer', THREE.REVISION );
34 |
35 | var _width, _height;
36 | var _widthHalf, _heightHalf;
37 |
38 | var matrix = new THREE.Matrix4();
39 |
40 | var domElement = document.createElement( 'div' );
41 | domElement.style.overflow = 'hidden';
42 |
43 | domElement.style.WebkitTransformStyle = 'preserve-3d';
44 | domElement.style.WebkitPerspectiveOrigin = '50% 50%';
45 |
46 | domElement.style.MozTransformStyle = 'preserve-3d';
47 | domElement.style.MozPerspectiveOrigin = '50% 50%';
48 |
49 | domElement.style.oTransformStyle = 'preserve-3d';
50 | domElement.style.oPerspectiveOrigin = '50% 50%';
51 |
52 | domElement.style.transformStyle = 'preserve-3d';
53 | domElement.style.perspectiveOrigin = '50% 50%';
54 |
55 | this.domElement = domElement;
56 |
57 | var cameraElement = document.createElement( 'div' );
58 |
59 | cameraElement.style.WebkitTransformStyle = 'preserve-3d';
60 | cameraElement.style.MozTransformStyle = 'preserve-3d';
61 | cameraElement.style.oTransformStyle = 'preserve-3d';
62 | cameraElement.style.transformStyle = 'preserve-3d';
63 |
64 | domElement.appendChild( cameraElement );
65 |
66 | this.cameraElement = cameraElement;
67 |
68 | this.setSize = function ( width, height ) {
69 |
70 | _width = width;
71 | _height = height;
72 |
73 | _widthHalf = _width / 2;
74 | _heightHalf = _height / 2;
75 |
76 | domElement.style.width = width + 'px';
77 | domElement.style.height = height + 'px';
78 |
79 | cameraElement.style.width = width + 'px';
80 | cameraElement.style.height = height + 'px';
81 |
82 | };
83 |
84 | var epsilon = function ( value ) {
85 |
86 | return Math.abs( value ) < 0.000001 ? 0 : value;
87 |
88 | };
89 |
90 | var getCameraCSSMatrix = function ( matrix ) {
91 |
92 | var elements = matrix.elements;
93 |
94 | return 'matrix3d(' +
95 | epsilon( elements[ 0 ] ) + ',' +
96 | epsilon( - elements[ 1 ] ) + ',' +
97 | epsilon( elements[ 2 ] ) + ',' +
98 | epsilon( elements[ 3 ] ) + ',' +
99 | epsilon( elements[ 4 ] ) + ',' +
100 | epsilon( - elements[ 5 ] ) + ',' +
101 | epsilon( elements[ 6 ] ) + ',' +
102 | epsilon( elements[ 7 ] ) + ',' +
103 | epsilon( elements[ 8 ] ) + ',' +
104 | epsilon( - elements[ 9 ] ) + ',' +
105 | epsilon( elements[ 10 ] ) + ',' +
106 | epsilon( elements[ 11 ] ) + ',' +
107 | epsilon( elements[ 12 ] ) + ',' +
108 | epsilon( - elements[ 13 ] ) + ',' +
109 | epsilon( elements[ 14 ] ) + ',' +
110 | epsilon( elements[ 15 ] ) +
111 | ')';
112 |
113 | };
114 |
115 | var getObjectCSSMatrix = function ( matrix ) {
116 |
117 | var elements = matrix.elements;
118 |
119 | return 'translate3d(-50%,-50%,0) matrix3d(' +
120 | epsilon( elements[ 0 ] ) + ',' +
121 | epsilon( elements[ 1 ] ) + ',' +
122 | epsilon( elements[ 2 ] ) + ',' +
123 | epsilon( elements[ 3 ] ) + ',' +
124 | epsilon( - elements[ 4 ] ) + ',' +
125 | epsilon( - elements[ 5 ] ) + ',' +
126 | epsilon( - elements[ 6 ] ) + ',' +
127 | epsilon( - elements[ 7 ] ) + ',' +
128 | epsilon( elements[ 8 ] ) + ',' +
129 | epsilon( elements[ 9 ] ) + ',' +
130 | epsilon( elements[ 10 ] ) + ',' +
131 | epsilon( elements[ 11 ] ) + ',' +
132 | epsilon( elements[ 12 ] ) + ',' +
133 | epsilon( elements[ 13 ] ) + ',' +
134 | epsilon( elements[ 14 ] ) + ',' +
135 | epsilon( elements[ 15 ] ) +
136 | ')';
137 |
138 | };
139 |
140 | var renderObject = function ( object, camera ) {
141 |
142 | if ( object instanceof THREE.CSS3DObject ) {
143 |
144 | var style;
145 |
146 | if ( object instanceof THREE.CSS3DSprite ) {
147 |
148 | // http://swiftcoder.wordpress.com/2008/11/25/constructing-a-billboard-matrix/
149 |
150 | matrix.copy( camera.matrixWorldInverse );
151 | matrix.transpose();
152 | matrix.copyPosition( object.matrixWorld );
153 | matrix.scale( object.scale );
154 |
155 | matrix.elements[ 3 ] = 0;
156 | matrix.elements[ 7 ] = 0;
157 | matrix.elements[ 11 ] = 0;
158 | matrix.elements[ 15 ] = 1;
159 |
160 | style = getObjectCSSMatrix( matrix );
161 |
162 | } else {
163 |
164 | style = getObjectCSSMatrix( object.matrixWorld );
165 |
166 | }
167 |
168 | var element = object.element;
169 |
170 | element.style.WebkitTransform = style;
171 | element.style.MozTransform = style;
172 | element.style.oTransform = style;
173 | element.style.transform = style;
174 |
175 | if ( element.parentNode !== cameraElement ) {
176 |
177 | cameraElement.appendChild( element );
178 |
179 | }
180 |
181 | }
182 |
183 | for ( var i = 0, l = object.children.length; i < l; i ++ ) {
184 |
185 | renderObject( object.children[ i ], camera );
186 |
187 | }
188 |
189 | };
190 |
191 | this.render = function ( scene, camera ) {
192 |
193 | var fov = 0.5 / Math.tan( THREE.Math.degToRad( camera.fov * 0.5 ) ) * _height;
194 |
195 | domElement.style.WebkitPerspective = fov + "px";
196 | domElement.style.MozPerspective = fov + "px";
197 | domElement.style.oPerspective = fov + "px";
198 | domElement.style.perspective = fov + "px";
199 |
200 | scene.updateMatrixWorld();
201 |
202 | if ( camera.parent === undefined ) camera.updateMatrixWorld();
203 |
204 | camera.matrixWorldInverse.getInverse( camera.matrixWorld );
205 |
206 | var style = "translate3d(0,0," + fov + "px)" + getCameraCSSMatrix( camera.matrixWorldInverse ) +
207 | " translate3d(" + _widthHalf + "px," + _heightHalf + "px, 0)";
208 |
209 | cameraElement.style.WebkitTransform = style;
210 | cameraElement.style.MozTransform = style;
211 | cameraElement.style.oTransform = style;
212 | cameraElement.style.transform = style;
213 |
214 | renderObject( scene, camera );
215 |
216 | };
217 |
218 | };
219 |
--------------------------------------------------------------------------------
/public/lib/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | * @author ScieCode / http://github.com/sciecode
8 | */
9 |
10 | // This set of controls performs orbiting, dollying (zooming), and panning.
11 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
12 | //
13 | // Orbit - left mouse / touch: one-finger move
14 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
15 | // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
16 |
17 | THREE.OrbitControls = function ( object, domElement ) {
18 |
19 | this.object = object;
20 |
21 | this.domElement = ( domElement !== undefined ) ? domElement : document;
22 |
23 | // Set to false to disable this control
24 | this.enabled = true;
25 |
26 | // "target" sets the location of focus, where the object orbits around
27 | this.target = new THREE.Vector3();
28 |
29 | // How far you can dolly in and out ( PerspectiveCamera only )
30 | this.minDistance = 0;
31 | this.maxDistance = Infinity;
32 |
33 | // How far you can zoom in and out ( OrthographicCamera only )
34 | this.minZoom = 0;
35 | this.maxZoom = Infinity;
36 |
37 | // How far you can orbit vertically, upper and lower limits.
38 | // Range is 0 to Math.PI radians.
39 | this.minPolarAngle = 0; // radians
40 | this.maxPolarAngle = Math.PI; // radians
41 |
42 | // How far you can orbit horizontally, upper and lower limits.
43 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
44 | this.minAzimuthAngle = - Infinity; // radians
45 | this.maxAzimuthAngle = Infinity; // radians
46 |
47 | // Set to true to enable damping (inertia)
48 | // If damping is enabled, you must call controls.update() in your animation loop
49 | this.enableDamping = false;
50 | this.dampingFactor = 0.05;
51 |
52 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
53 | // Set to false to disable zooming
54 | this.enableZoom = true;
55 | this.zoomSpeed = 1.0;
56 |
57 | // Set to false to disable rotating
58 | this.enableRotate = true;
59 | this.rotateSpeed = 1.0;
60 |
61 | // Set to false to disable panning
62 | this.enablePan = true;
63 | this.panSpeed = 1.0;
64 | this.screenSpacePanning = false; // if true, pan in screen-space
65 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
66 |
67 | // Set to true to automatically rotate around the target
68 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
69 | this.autoRotate = false;
70 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
71 |
72 | // Set to false to disable use of the keys
73 | this.enableKeys = true;
74 |
75 | // The four arrow keys
76 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
77 |
78 | // Mouse buttons
79 | this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN };
80 |
81 | // Touch fingers
82 | this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };
83 |
84 | // for reset
85 | this.target0 = this.target.clone();
86 | this.position0 = this.object.position.clone();
87 | this.zoom0 = this.object.zoom;
88 |
89 | //
90 | // public methods
91 | //
92 |
93 | this.getPolarAngle = function () {
94 |
95 | return spherical.phi;
96 |
97 | };
98 |
99 | this.getAzimuthalAngle = function () {
100 |
101 | return spherical.theta;
102 |
103 | };
104 |
105 | this.saveState = function () {
106 |
107 | scope.target0.copy( scope.target );
108 | scope.position0.copy( scope.object.position );
109 | scope.zoom0 = scope.object.zoom;
110 |
111 | };
112 |
113 | this.reset = function () {
114 |
115 | scope.target.copy( scope.target0 );
116 | scope.object.position.copy( scope.position0 );
117 | scope.object.zoom = scope.zoom0;
118 |
119 | scope.object.updateProjectionMatrix();
120 | scope.dispatchEvent( changeEvent );
121 |
122 | scope.update();
123 |
124 | state = STATE.NONE;
125 |
126 | };
127 |
128 | // this method is exposed, but perhaps it would be better if we can make it private...
129 | this.update = function () {
130 |
131 | var offset = new THREE.Vector3();
132 |
133 | // so camera.up is the orbit axis
134 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
135 | var quatInverse = quat.clone().inverse();
136 |
137 | var lastPosition = new THREE.Vector3();
138 | var lastQuaternion = new THREE.Quaternion();
139 |
140 | return function update() {
141 |
142 | var position = scope.object.position;
143 |
144 | offset.copy( position ).sub( scope.target );
145 |
146 | // rotate offset to "y-axis-is-up" space
147 | offset.applyQuaternion( quat );
148 |
149 | // angle from z-axis around y-axis
150 | spherical.setFromVector3( offset );
151 |
152 | if ( scope.autoRotate && state === STATE.NONE ) {
153 |
154 | rotateLeft( getAutoRotationAngle() );
155 |
156 | }
157 |
158 | if ( scope.enableDamping ) {
159 |
160 | spherical.theta += sphericalDelta.theta * scope.dampingFactor;
161 | spherical.phi += sphericalDelta.phi * scope.dampingFactor;
162 |
163 | } else {
164 |
165 | spherical.theta += sphericalDelta.theta;
166 | spherical.phi += sphericalDelta.phi;
167 |
168 | }
169 |
170 | // restrict theta to be between desired limits
171 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
172 |
173 | // restrict phi to be between desired limits
174 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
175 |
176 | spherical.makeSafe();
177 |
178 |
179 | spherical.radius *= scale;
180 |
181 | // restrict radius to be between desired limits
182 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
183 |
184 | // move target to panned location
185 |
186 | if ( scope.enableDamping === true ) {
187 |
188 | scope.target.addScaledVector( panOffset, scope.dampingFactor );
189 |
190 | } else {
191 |
192 | scope.target.add( panOffset );
193 |
194 | }
195 |
196 | offset.setFromSpherical( spherical );
197 |
198 | // rotate offset back to "camera-up-vector-is-up" space
199 | offset.applyQuaternion( quatInverse );
200 |
201 | position.copy( scope.target ).add( offset );
202 |
203 | scope.object.lookAt( scope.target );
204 |
205 | if ( scope.enableDamping === true ) {
206 |
207 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
208 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
209 |
210 | panOffset.multiplyScalar( 1 - scope.dampingFactor );
211 |
212 | } else {
213 |
214 | sphericalDelta.set( 0, 0, 0 );
215 |
216 | panOffset.set( 0, 0, 0 );
217 |
218 | }
219 |
220 | scale = 1;
221 |
222 | // update condition is:
223 | // min(camera displacement, camera rotation in radians)^2 > EPS
224 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
225 |
226 | if ( zoomChanged ||
227 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
228 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
229 |
230 | scope.dispatchEvent( changeEvent );
231 |
232 | lastPosition.copy( scope.object.position );
233 | lastQuaternion.copy( scope.object.quaternion );
234 | zoomChanged = false;
235 |
236 | return true;
237 |
238 | }
239 |
240 | return false;
241 |
242 | };
243 |
244 | }();
245 |
246 | this.dispose = function () {
247 |
248 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
249 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
250 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
251 |
252 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
253 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
254 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
255 |
256 | document.removeEventListener( 'mousemove', onMouseMove, false );
257 | document.removeEventListener( 'mouseup', onMouseUp, false );
258 |
259 | window.removeEventListener( 'keydown', onKeyDown, false );
260 |
261 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
262 |
263 | };
264 |
265 | //
266 | // internals
267 | //
268 |
269 | var scope = this;
270 |
271 | var changeEvent = { type: 'change' };
272 | var startEvent = { type: 'start' };
273 | var endEvent = { type: 'end' };
274 |
275 | var STATE = {
276 | NONE: - 1,
277 | ROTATE: 0,
278 | DOLLY: 1,
279 | PAN: 2,
280 | TOUCH_ROTATE: 3,
281 | TOUCH_PAN: 4,
282 | TOUCH_DOLLY_PAN: 5,
283 | TOUCH_DOLLY_ROTATE: 6
284 | };
285 |
286 | var state = STATE.NONE;
287 |
288 | var EPS = 0.000001;
289 |
290 | // current position in spherical coordinates
291 | var spherical = new THREE.Spherical();
292 | var sphericalDelta = new THREE.Spherical();
293 |
294 | var scale = 1;
295 | var panOffset = new THREE.Vector3();
296 | var zoomChanged = false;
297 |
298 | var rotateStart = new THREE.Vector2();
299 | var rotateEnd = new THREE.Vector2();
300 | var rotateDelta = new THREE.Vector2();
301 |
302 | var panStart = new THREE.Vector2();
303 | var panEnd = new THREE.Vector2();
304 | var panDelta = new THREE.Vector2();
305 |
306 | var dollyStart = new THREE.Vector2();
307 | var dollyEnd = new THREE.Vector2();
308 | var dollyDelta = new THREE.Vector2();
309 |
310 | function getAutoRotationAngle() {
311 |
312 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
313 |
314 | }
315 |
316 | function getZoomScale() {
317 |
318 | return Math.pow( 0.95, scope.zoomSpeed );
319 |
320 | }
321 |
322 | function rotateLeft( angle ) {
323 |
324 | sphericalDelta.theta -= angle;
325 |
326 | }
327 |
328 | function rotateUp( angle ) {
329 |
330 | sphericalDelta.phi -= angle;
331 |
332 | }
333 |
334 | var panLeft = function () {
335 |
336 | var v = new THREE.Vector3();
337 |
338 | return function panLeft( distance, objectMatrix ) {
339 |
340 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
341 | v.multiplyScalar( - distance );
342 |
343 | panOffset.add( v );
344 |
345 | };
346 |
347 | }();
348 |
349 | var panUp = function () {
350 |
351 | var v = new THREE.Vector3();
352 |
353 | return function panUp( distance, objectMatrix ) {
354 |
355 | if ( scope.screenSpacePanning === true ) {
356 |
357 | v.setFromMatrixColumn( objectMatrix, 1 );
358 |
359 | } else {
360 |
361 | v.setFromMatrixColumn( objectMatrix, 0 );
362 | v.crossVectors( scope.object.up, v );
363 |
364 | }
365 |
366 | v.multiplyScalar( distance );
367 |
368 | panOffset.add( v );
369 |
370 | };
371 |
372 | }();
373 |
374 | // deltaX and deltaY are in pixels; right and down are positive
375 | var pan = function () {
376 |
377 | var offset = new THREE.Vector3();
378 |
379 | return function pan( deltaX, deltaY ) {
380 |
381 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
382 |
383 | if ( scope.object.isPerspectiveCamera ) {
384 |
385 | // perspective
386 | var position = scope.object.position;
387 | offset.copy( position ).sub( scope.target );
388 | var targetDistance = offset.length();
389 |
390 | // half of the fov is center to top of screen
391 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
392 |
393 | // we use only clientHeight here so aspect ratio does not distort speed
394 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
395 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
396 |
397 | } else if ( scope.object.isOrthographicCamera ) {
398 |
399 | // orthographic
400 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
401 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
402 |
403 | } else {
404 |
405 | // camera neither orthographic nor perspective
406 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
407 | scope.enablePan = false;
408 |
409 | }
410 |
411 | };
412 |
413 | }();
414 |
415 | function dollyIn( dollyScale ) {
416 |
417 | if ( scope.object.isPerspectiveCamera ) {
418 |
419 | scale /= dollyScale;
420 |
421 | } else if ( scope.object.isOrthographicCamera ) {
422 |
423 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
424 | scope.object.updateProjectionMatrix();
425 | zoomChanged = true;
426 |
427 | } else {
428 |
429 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
430 | scope.enableZoom = false;
431 |
432 | }
433 |
434 | }
435 |
436 | function dollyOut( dollyScale ) {
437 |
438 | if ( scope.object.isPerspectiveCamera ) {
439 |
440 | scale *= dollyScale;
441 |
442 | } else if ( scope.object.isOrthographicCamera ) {
443 |
444 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
445 | scope.object.updateProjectionMatrix();
446 | zoomChanged = true;
447 |
448 | } else {
449 |
450 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
451 | scope.enableZoom = false;
452 |
453 | }
454 |
455 | }
456 |
457 | //
458 | // event callbacks - update the object state
459 | //
460 |
461 | function handleMouseDownRotate( event ) {
462 |
463 | rotateStart.set( event.clientX, event.clientY );
464 |
465 | }
466 |
467 | function handleMouseDownDolly( event ) {
468 |
469 | dollyStart.set( event.clientX, event.clientY );
470 |
471 | }
472 |
473 | function handleMouseDownPan( event ) {
474 |
475 | panStart.set( event.clientX, event.clientY );
476 |
477 | }
478 |
479 | function handleMouseMoveRotate( event ) {
480 |
481 | rotateEnd.set( event.clientX, event.clientY );
482 |
483 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
484 |
485 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
486 |
487 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
488 |
489 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
490 |
491 | rotateStart.copy( rotateEnd );
492 |
493 | scope.update();
494 |
495 | }
496 |
497 | function handleMouseMoveDolly( event ) {
498 |
499 | dollyEnd.set( event.clientX, event.clientY );
500 |
501 | dollyDelta.subVectors( dollyEnd, dollyStart );
502 |
503 | if ( dollyDelta.y > 0 ) {
504 |
505 | dollyIn( getZoomScale() );
506 |
507 | } else if ( dollyDelta.y < 0 ) {
508 |
509 | dollyOut( getZoomScale() );
510 |
511 | }
512 |
513 | dollyStart.copy( dollyEnd );
514 |
515 | scope.update();
516 |
517 | }
518 |
519 | function handleMouseMovePan( event ) {
520 |
521 | panEnd.set( event.clientX, event.clientY );
522 |
523 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
524 |
525 | pan( panDelta.x, panDelta.y );
526 |
527 | panStart.copy( panEnd );
528 |
529 | scope.update();
530 |
531 | }
532 |
533 | function handleMouseUp( /*event*/ ) {
534 |
535 | // no-op
536 |
537 | }
538 |
539 | function handleMouseWheel( event ) {
540 |
541 | if ( event.deltaY < 0 ) {
542 |
543 | dollyOut( getZoomScale() );
544 |
545 | } else if ( event.deltaY > 0 ) {
546 |
547 | dollyIn( getZoomScale() );
548 |
549 | }
550 |
551 | scope.update();
552 |
553 | }
554 |
555 | function handleKeyDown( event ) {
556 |
557 | var needsUpdate = false;
558 |
559 | switch ( event.keyCode ) {
560 |
561 | case scope.keys.UP:
562 | pan( 0, scope.keyPanSpeed );
563 | needsUpdate = true;
564 | break;
565 |
566 | case scope.keys.BOTTOM:
567 | pan( 0, - scope.keyPanSpeed );
568 | needsUpdate = true;
569 | break;
570 |
571 | case scope.keys.LEFT:
572 | pan( scope.keyPanSpeed, 0 );
573 | needsUpdate = true;
574 | break;
575 |
576 | case scope.keys.RIGHT:
577 | pan( - scope.keyPanSpeed, 0 );
578 | needsUpdate = true;
579 | break;
580 |
581 | }
582 |
583 | if ( needsUpdate ) {
584 |
585 | // prevent the browser from scrolling on cursor keys
586 | event.preventDefault();
587 |
588 | scope.update();
589 |
590 | }
591 |
592 |
593 | }
594 |
595 | function handleTouchStartRotate( event ) {
596 |
597 | if ( event.touches.length == 1 ) {
598 |
599 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
600 |
601 | } else {
602 |
603 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
604 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
605 |
606 | rotateStart.set( x, y );
607 |
608 | }
609 |
610 | }
611 |
612 | function handleTouchStartPan( event ) {
613 |
614 | if ( event.touches.length == 1 ) {
615 |
616 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
617 |
618 | } else {
619 |
620 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
621 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
622 |
623 | panStart.set( x, y );
624 |
625 | }
626 |
627 | }
628 |
629 | function handleTouchStartDolly( event ) {
630 |
631 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
632 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
633 |
634 | var distance = Math.sqrt( dx * dx + dy * dy );
635 |
636 | dollyStart.set( 0, distance );
637 |
638 | }
639 |
640 | function handleTouchStartDollyPan( event ) {
641 |
642 | if ( scope.enableZoom ) handleTouchStartDolly( event );
643 |
644 | if ( scope.enablePan ) handleTouchStartPan( event );
645 |
646 | }
647 |
648 | function handleTouchStartDollyRotate( event ) {
649 |
650 | if ( scope.enableZoom ) handleTouchStartDolly( event );
651 |
652 | if ( scope.enableRotate ) handleTouchStartRotate( event );
653 |
654 | }
655 |
656 | function handleTouchMoveRotate( event ) {
657 |
658 | if ( event.touches.length == 1 ) {
659 |
660 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
661 |
662 | } else {
663 |
664 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
665 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
666 |
667 | rotateEnd.set( x, y );
668 |
669 | }
670 |
671 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
672 |
673 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
674 |
675 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
676 |
677 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
678 |
679 | rotateStart.copy( rotateEnd );
680 |
681 | }
682 |
683 | function handleTouchMovePan( event ) {
684 |
685 | if ( event.touches.length == 1 ) {
686 |
687 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
688 |
689 | } else {
690 |
691 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
692 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
693 |
694 | panEnd.set( x, y );
695 |
696 | }
697 |
698 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
699 |
700 | pan( panDelta.x, panDelta.y );
701 |
702 | panStart.copy( panEnd );
703 |
704 | }
705 |
706 | function handleTouchMoveDolly( event ) {
707 |
708 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
709 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
710 |
711 | var distance = Math.sqrt( dx * dx + dy * dy );
712 |
713 | dollyEnd.set( 0, distance );
714 |
715 | dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
716 |
717 | dollyIn( dollyDelta.y );
718 |
719 | dollyStart.copy( dollyEnd );
720 |
721 | }
722 |
723 | function handleTouchMoveDollyPan( event ) {
724 |
725 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
726 |
727 | if ( scope.enablePan ) handleTouchMovePan( event );
728 |
729 | }
730 |
731 | function handleTouchMoveDollyRotate( event ) {
732 |
733 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
734 |
735 | if ( scope.enableRotate ) handleTouchMoveRotate( event );
736 |
737 | }
738 |
739 | function handleTouchEnd( /*event*/ ) {
740 |
741 | // no-op
742 |
743 | }
744 |
745 | //
746 | // event handlers - FSM: listen for events and reset state
747 | //
748 |
749 | function onMouseDown( event ) {
750 |
751 | if ( scope.enabled === false ) return;
752 |
753 | // Prevent the browser from scrolling.
754 |
755 | event.preventDefault();
756 |
757 | // Manually set the focus since calling preventDefault above
758 | // prevents the browser from setting it automatically.
759 |
760 | scope.domElement.focus ? scope.domElement.focus() : window.focus();
761 |
762 | switch ( event.button ) {
763 |
764 | case 0:
765 |
766 | switch ( scope.mouseButtons.LEFT ) {
767 |
768 | case THREE.MOUSE.ROTATE:
769 |
770 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
771 |
772 | if ( scope.enablePan === false ) return;
773 |
774 | handleMouseDownPan( event );
775 |
776 | state = STATE.PAN;
777 |
778 | } else {
779 |
780 | if ( scope.enableRotate === false ) return;
781 |
782 | handleMouseDownRotate( event );
783 |
784 | state = STATE.ROTATE;
785 |
786 | }
787 |
788 | break;
789 |
790 | case THREE.MOUSE.PAN:
791 |
792 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
793 |
794 | if ( scope.enableRotate === false ) return;
795 |
796 | handleMouseDownRotate( event );
797 |
798 | state = STATE.ROTATE;
799 |
800 | } else {
801 |
802 | if ( scope.enablePan === false ) return;
803 |
804 | handleMouseDownPan( event );
805 |
806 | state = STATE.PAN;
807 |
808 | }
809 |
810 | break;
811 |
812 | default:
813 |
814 | state = STATE.NONE;
815 |
816 | }
817 |
818 | break;
819 |
820 |
821 | case 1:
822 |
823 | switch ( scope.mouseButtons.MIDDLE ) {
824 |
825 | case THREE.MOUSE.DOLLY:
826 |
827 | if ( scope.enableZoom === false ) return;
828 |
829 | handleMouseDownDolly( event );
830 |
831 | state = STATE.DOLLY;
832 |
833 | break;
834 |
835 |
836 | default:
837 |
838 | state = STATE.NONE;
839 |
840 | }
841 |
842 | break;
843 |
844 | case 2:
845 |
846 | switch ( scope.mouseButtons.RIGHT ) {
847 |
848 | case THREE.MOUSE.ROTATE:
849 |
850 | if ( scope.enableRotate === false ) return;
851 |
852 | handleMouseDownRotate( event );
853 |
854 | state = STATE.ROTATE;
855 |
856 | break;
857 |
858 | case THREE.MOUSE.PAN:
859 |
860 | if ( scope.enablePan === false ) return;
861 |
862 | handleMouseDownPan( event );
863 |
864 | state = STATE.PAN;
865 |
866 | break;
867 |
868 | default:
869 |
870 | state = STATE.NONE;
871 |
872 | }
873 |
874 | break;
875 |
876 | }
877 |
878 | if ( state !== STATE.NONE ) {
879 |
880 | document.addEventListener( 'mousemove', onMouseMove, false );
881 | document.addEventListener( 'mouseup', onMouseUp, false );
882 |
883 | scope.dispatchEvent( startEvent );
884 |
885 | }
886 |
887 | }
888 |
889 | function onMouseMove( event ) {
890 |
891 | if ( scope.enabled === false ) return;
892 |
893 | event.preventDefault();
894 |
895 | switch ( state ) {
896 |
897 | case STATE.ROTATE:
898 |
899 | if ( scope.enableRotate === false ) return;
900 |
901 | handleMouseMoveRotate( event );
902 |
903 | break;
904 |
905 | case STATE.DOLLY:
906 |
907 | if ( scope.enableZoom === false ) return;
908 |
909 | handleMouseMoveDolly( event );
910 |
911 | break;
912 |
913 | case STATE.PAN:
914 |
915 | if ( scope.enablePan === false ) return;
916 |
917 | handleMouseMovePan( event );
918 |
919 | break;
920 |
921 | }
922 |
923 | }
924 |
925 | function onMouseUp( event ) {
926 |
927 | if ( scope.enabled === false ) return;
928 |
929 | handleMouseUp( event );
930 |
931 | document.removeEventListener( 'mousemove', onMouseMove, false );
932 | document.removeEventListener( 'mouseup', onMouseUp, false );
933 |
934 | scope.dispatchEvent( endEvent );
935 |
936 | state = STATE.NONE;
937 |
938 | }
939 |
940 | function onMouseWheel( event ) {
941 |
942 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
943 |
944 | event.preventDefault();
945 | event.stopPropagation();
946 |
947 | scope.dispatchEvent( startEvent );
948 |
949 | handleMouseWheel( event );
950 |
951 | scope.dispatchEvent( endEvent );
952 |
953 | }
954 |
955 | function onKeyDown( event ) {
956 |
957 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
958 |
959 | handleKeyDown( event );
960 |
961 | }
962 |
963 | function onTouchStart( event ) {
964 |
965 | if ( scope.enabled === false ) return;
966 |
967 | event.preventDefault();
968 |
969 | switch ( event.touches.length ) {
970 |
971 | case 1:
972 |
973 | switch ( scope.touches.ONE ) {
974 |
975 | case THREE.TOUCH.ROTATE:
976 |
977 | if ( scope.enableRotate === false ) return;
978 |
979 | handleTouchStartRotate( event );
980 |
981 | state = STATE.TOUCH_ROTATE;
982 |
983 | break;
984 |
985 | case THREE.TOUCH.PAN:
986 |
987 | if ( scope.enablePan === false ) return;
988 |
989 | handleTouchStartPan( event );
990 |
991 | state = STATE.TOUCH_PAN;
992 |
993 | break;
994 |
995 | default:
996 |
997 | state = STATE.NONE;
998 |
999 | }
1000 |
1001 | break;
1002 |
1003 | case 2:
1004 |
1005 | switch ( scope.touches.TWO ) {
1006 |
1007 | case THREE.TOUCH.DOLLY_PAN:
1008 |
1009 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1010 |
1011 | handleTouchStartDollyPan( event );
1012 |
1013 | state = STATE.TOUCH_DOLLY_PAN;
1014 |
1015 | break;
1016 |
1017 | case THREE.TOUCH.DOLLY_ROTATE:
1018 |
1019 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1020 |
1021 | handleTouchStartDollyRotate( event );
1022 |
1023 | state = STATE.TOUCH_DOLLY_ROTATE;
1024 |
1025 | break;
1026 |
1027 | default:
1028 |
1029 | state = STATE.NONE;
1030 |
1031 | }
1032 |
1033 | break;
1034 |
1035 | default:
1036 |
1037 | state = STATE.NONE;
1038 |
1039 | }
1040 |
1041 | if ( state !== STATE.NONE ) {
1042 |
1043 | scope.dispatchEvent( startEvent );
1044 |
1045 | }
1046 |
1047 | }
1048 |
1049 | function onTouchMove( event ) {
1050 |
1051 | if ( scope.enabled === false ) return;
1052 |
1053 | event.preventDefault();
1054 | event.stopPropagation();
1055 |
1056 | switch ( state ) {
1057 |
1058 | case STATE.TOUCH_ROTATE:
1059 |
1060 | if ( scope.enableRotate === false ) return;
1061 |
1062 | handleTouchMoveRotate( event );
1063 |
1064 | scope.update();
1065 |
1066 | break;
1067 |
1068 | case STATE.TOUCH_PAN:
1069 |
1070 | if ( scope.enablePan === false ) return;
1071 |
1072 | handleTouchMovePan( event );
1073 |
1074 | scope.update();
1075 |
1076 | break;
1077 |
1078 | case STATE.TOUCH_DOLLY_PAN:
1079 |
1080 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1081 |
1082 | handleTouchMoveDollyPan( event );
1083 |
1084 | scope.update();
1085 |
1086 | break;
1087 |
1088 | case STATE.TOUCH_DOLLY_ROTATE:
1089 |
1090 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1091 |
1092 | handleTouchMoveDollyRotate( event );
1093 |
1094 | scope.update();
1095 |
1096 | break;
1097 |
1098 | default:
1099 |
1100 | state = STATE.NONE;
1101 |
1102 | }
1103 |
1104 | }
1105 |
1106 | function onTouchEnd( event ) {
1107 |
1108 | if ( scope.enabled === false ) return;
1109 |
1110 | handleTouchEnd( event );
1111 |
1112 | scope.dispatchEvent( endEvent );
1113 |
1114 | state = STATE.NONE;
1115 |
1116 | }
1117 |
1118 | function onContextMenu( event ) {
1119 |
1120 | if ( scope.enabled === false ) return;
1121 |
1122 | event.preventDefault();
1123 |
1124 | }
1125 |
1126 | //
1127 |
1128 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
1129 |
1130 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
1131 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
1132 |
1133 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
1134 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
1135 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
1136 |
1137 | window.addEventListener( 'keydown', onKeyDown, false );
1138 |
1139 | // force an update at start
1140 |
1141 | this.update();
1142 |
1143 | };
1144 |
1145 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1146 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
1147 |
1148 |
1149 | // This set of controls performs orbiting, dollying (zooming), and panning.
1150 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
1151 | // This is very similar to OrbitControls, another set of touch behavior
1152 | //
1153 | // Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
1154 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
1155 | // Pan - left mouse, or arrow keys / touch: one-finger move
1156 |
1157 | THREE.MapControls = function ( object, domElement ) {
1158 |
1159 | THREE.OrbitControls.call( this, object, domElement );
1160 |
1161 | this.mouseButtons.LEFT = THREE.MOUSE.PAN;
1162 | this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
1163 |
1164 | this.touches.ONE = THREE.TOUCH.PAN;
1165 | this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE;
1166 |
1167 | };
1168 |
1169 | THREE.MapControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1170 | THREE.MapControls.prototype.constructor = THREE.MapControls;
--------------------------------------------------------------------------------
/public/lib/TransformControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author arodic / https://github.com/arodic
3 | */
4 |
5 | var TransformControls = function ( camera, domElement ) {
6 |
7 | if ( domElement === undefined ) {
8 |
9 | console.warn( 'THREE.TransformControls: The second parameter "domElement" is now mandatory.' );
10 | domElement = document;
11 |
12 | }
13 |
14 | Object3D.call( this );
15 |
16 | this.visible = false;
17 | this.domElement = domElement;
18 |
19 | var _gizmo = new TransformControlsGizmo();
20 | this.add( _gizmo );
21 |
22 | var _plane = new TransformControlsPlane();
23 | this.add( _plane );
24 |
25 | var scope = this;
26 |
27 | // Define properties with getters/setter
28 | // Setting the defined property will automatically trigger change event
29 | // Defined properties are passed down to gizmo and plane
30 |
31 | defineProperty( "camera", camera );
32 | defineProperty( "object", undefined );
33 | defineProperty( "enabled", true );
34 | defineProperty( "axis", null );
35 | defineProperty( "mode", "translate" );
36 | defineProperty( "translationSnap", null );
37 | defineProperty( "rotationSnap", null );
38 | defineProperty( "scaleSnap", null );
39 | defineProperty( "space", "world" );
40 | defineProperty( "size", 1 );
41 | defineProperty( "dragging", false );
42 | defineProperty( "showX", true );
43 | defineProperty( "showY", true );
44 | defineProperty( "showZ", true );
45 |
46 | var changeEvent = { type: "change" };
47 | var mouseDownEvent = { type: "mouseDown" };
48 | var mouseUpEvent = { type: "mouseUp", mode: scope.mode };
49 | var objectChangeEvent = { type: "objectChange" };
50 |
51 | // Reusable utility variables
52 |
53 | var raycaster = new Raycaster();
54 |
55 | function intersectObjectWithRay( object, raycaster, includeInvisible ) {
56 |
57 | var allIntersections = raycaster.intersectObject( object, true );
58 |
59 | for ( var i = 0; i < allIntersections.length; i ++ ) {
60 |
61 | if ( allIntersections[ i ].object.visible || includeInvisible ) {
62 |
63 | return allIntersections[ i ];
64 |
65 | }
66 |
67 | }
68 |
69 | return false;
70 |
71 | }
72 |
73 | var _tempVector = new Vector3();
74 | var _tempVector2 = new Vector3();
75 | var _tempQuaternion = new Quaternion();
76 | var _unit = {
77 | X: new Vector3( 1, 0, 0 ),
78 | Y: new Vector3( 0, 1, 0 ),
79 | Z: new Vector3( 0, 0, 1 )
80 | };
81 |
82 | var pointStart = new Vector3();
83 | var pointEnd = new Vector3();
84 | var offset = new Vector3();
85 | var rotationAxis = new Vector3();
86 | var startNorm = new Vector3();
87 | var endNorm = new Vector3();
88 | var rotationAngle = 0;
89 |
90 | var cameraPosition = new Vector3();
91 | var cameraQuaternion = new Quaternion();
92 | var cameraScale = new Vector3();
93 |
94 | var parentPosition = new Vector3();
95 | var parentQuaternion = new Quaternion();
96 | var parentQuaternionInv = new Quaternion();
97 | var parentScale = new Vector3();
98 |
99 | var worldPositionStart = new Vector3();
100 | var worldQuaternionStart = new Quaternion();
101 | var worldScaleStart = new Vector3();
102 |
103 | var worldPosition = new Vector3();
104 | var worldQuaternion = new Quaternion();
105 | var worldQuaternionInv = new Quaternion();
106 | var worldScale = new Vector3();
107 |
108 | var eye = new Vector3();
109 |
110 | var positionStart = new Vector3();
111 | var quaternionStart = new Quaternion();
112 | var scaleStart = new Vector3();
113 |
114 | // TODO: remove properties unused in plane and gizmo
115 |
116 | defineProperty( "worldPosition", worldPosition );
117 | defineProperty( "worldPositionStart", worldPositionStart );
118 | defineProperty( "worldQuaternion", worldQuaternion );
119 | defineProperty( "worldQuaternionStart", worldQuaternionStart );
120 | defineProperty( "cameraPosition", cameraPosition );
121 | defineProperty( "cameraQuaternion", cameraQuaternion );
122 | defineProperty( "pointStart", pointStart );
123 | defineProperty( "pointEnd", pointEnd );
124 | defineProperty( "rotationAxis", rotationAxis );
125 | defineProperty( "rotationAngle", rotationAngle );
126 | defineProperty( "eye", eye );
127 |
128 | {
129 |
130 | domElement.addEventListener( "mousedown", onPointerDown, false );
131 | domElement.addEventListener( "touchstart", onPointerDown, false );
132 | domElement.addEventListener( "mousemove", onPointerHover, false );
133 | domElement.addEventListener( "touchmove", onPointerHover, false );
134 | domElement.addEventListener( "touchmove", onPointerMove, false );
135 | document.addEventListener( "mouseup", onPointerUp, false );
136 | domElement.addEventListener( "touchend", onPointerUp, false );
137 | domElement.addEventListener( "touchcancel", onPointerUp, false );
138 | domElement.addEventListener( "touchleave", onPointerUp, false );
139 |
140 | }
141 |
142 | this.dispose = function () {
143 |
144 | domElement.removeEventListener( "mousedown", onPointerDown );
145 | domElement.removeEventListener( "touchstart", onPointerDown );
146 | domElement.removeEventListener( "mousemove", onPointerHover );
147 | document.removeEventListener( "mousemove", onPointerMove );
148 | domElement.removeEventListener( "touchmove", onPointerHover );
149 | domElement.removeEventListener( "touchmove", onPointerMove );
150 | document.removeEventListener( "mouseup", onPointerUp );
151 | domElement.removeEventListener( "touchend", onPointerUp );
152 | domElement.removeEventListener( "touchcancel", onPointerUp );
153 | domElement.removeEventListener( "touchleave", onPointerUp );
154 |
155 | this.traverse( function ( child ) {
156 |
157 | if ( child.geometry ) child.geometry.dispose();
158 | if ( child.material ) child.material.dispose();
159 |
160 | } );
161 |
162 | };
163 |
164 | // Set current object
165 | this.attach = function ( object ) {
166 |
167 | this.object = object;
168 | this.visible = true;
169 |
170 | return this;
171 |
172 | };
173 |
174 | // Detatch from object
175 | this.detach = function () {
176 |
177 | this.object = undefined;
178 | this.visible = false;
179 | this.axis = null;
180 |
181 | return this;
182 |
183 | };
184 |
185 | // Defined getter, setter and store for a property
186 | function defineProperty( propName, defaultValue ) {
187 |
188 | var propValue = defaultValue;
189 |
190 | Object.defineProperty( scope, propName, {
191 |
192 | get: function () {
193 |
194 | return propValue !== undefined ? propValue : defaultValue;
195 |
196 | },
197 |
198 | set: function ( value ) {
199 |
200 | if ( propValue !== value ) {
201 |
202 | propValue = value;
203 | _plane[ propName ] = value;
204 | _gizmo[ propName ] = value;
205 |
206 | scope.dispatchEvent( { type: propName + "-changed", value: value } );
207 | scope.dispatchEvent( changeEvent );
208 |
209 | }
210 |
211 | }
212 |
213 | } );
214 |
215 | scope[ propName ] = defaultValue;
216 | _plane[ propName ] = defaultValue;
217 | _gizmo[ propName ] = defaultValue;
218 |
219 | }
220 |
221 | // updateMatrixWorld updates key transformation variables
222 | this.updateMatrixWorld = function () {
223 |
224 | if ( this.object !== undefined ) {
225 |
226 | this.object.updateMatrixWorld();
227 |
228 | if ( this.object.parent === null ) {
229 |
230 | console.error( 'TransformControls: The attached 3D object must be a part of the scene graph.' );
231 |
232 | } else {
233 |
234 | this.object.parent.matrixWorld.decompose( parentPosition, parentQuaternion, parentScale );
235 |
236 | }
237 |
238 | this.object.matrixWorld.decompose( worldPosition, worldQuaternion, worldScale );
239 |
240 | parentQuaternionInv.copy( parentQuaternion ).inverse();
241 | worldQuaternionInv.copy( worldQuaternion ).inverse();
242 |
243 | }
244 |
245 | this.camera.updateMatrixWorld();
246 | this.camera.matrixWorld.decompose( cameraPosition, cameraQuaternion, cameraScale );
247 |
248 | eye.copy( cameraPosition ).sub( worldPosition ).normalize();
249 |
250 | Object3D.prototype.updateMatrixWorld.call( this );
251 |
252 | };
253 |
254 | this.pointerHover = function ( pointer ) {
255 |
256 | if ( this.object === undefined || this.dragging === true || ( pointer.button !== undefined && pointer.button !== 0 ) ) return;
257 |
258 | raycaster.setFromCamera( pointer, this.camera );
259 |
260 | var intersect = intersectObjectWithRay( _gizmo.picker[ this.mode ], raycaster );
261 |
262 | if ( intersect ) {
263 |
264 | this.axis = intersect.object.name;
265 |
266 | } else {
267 |
268 | this.axis = null;
269 |
270 | }
271 |
272 | };
273 |
274 | this.pointerDown = function ( pointer ) {
275 |
276 | if ( this.object === undefined || this.dragging === true || ( pointer.button !== undefined && pointer.button !== 0 ) ) return;
277 |
278 | if ( ( pointer.button === 0 || pointer.button === undefined ) && this.axis !== null ) {
279 |
280 | raycaster.setFromCamera( pointer, this.camera );
281 |
282 | var planeIntersect = intersectObjectWithRay( _plane, raycaster, true );
283 |
284 | if ( planeIntersect ) {
285 |
286 | var space = this.space;
287 |
288 | if ( this.mode === 'scale' ) {
289 |
290 | space = 'local';
291 |
292 | } else if ( this.axis === 'E' || this.axis === 'XYZE' || this.axis === 'XYZ' ) {
293 |
294 | space = 'world';
295 |
296 | }
297 |
298 | if ( space === 'local' && this.mode === 'rotate' ) {
299 |
300 | var snap = this.rotationSnap;
301 |
302 | if ( this.axis === 'X' && snap ) this.object.rotation.x = Math.round( this.object.rotation.x / snap ) * snap;
303 | if ( this.axis === 'Y' && snap ) this.object.rotation.y = Math.round( this.object.rotation.y / snap ) * snap;
304 | if ( this.axis === 'Z' && snap ) this.object.rotation.z = Math.round( this.object.rotation.z / snap ) * snap;
305 |
306 | }
307 |
308 | this.object.updateMatrixWorld();
309 | this.object.parent.updateMatrixWorld();
310 |
311 | positionStart.copy( this.object.position );
312 | quaternionStart.copy( this.object.quaternion );
313 | scaleStart.copy( this.object.scale );
314 |
315 | this.object.matrixWorld.decompose( worldPositionStart, worldQuaternionStart, worldScaleStart );
316 |
317 | pointStart.copy( planeIntersect.point ).sub( worldPositionStart );
318 |
319 | }
320 |
321 | this.dragging = true;
322 | mouseDownEvent.mode = this.mode;
323 | this.dispatchEvent( mouseDownEvent );
324 |
325 | }
326 |
327 | };
328 |
329 | this.pointerMove = function ( pointer ) {
330 |
331 | var axis = this.axis;
332 | var mode = this.mode;
333 | var object = this.object;
334 | var space = this.space;
335 |
336 | if ( mode === 'scale' ) {
337 |
338 | space = 'local';
339 |
340 | } else if ( axis === 'E' || axis === 'XYZE' || axis === 'XYZ' ) {
341 |
342 | space = 'world';
343 |
344 | }
345 |
346 | if ( object === undefined || axis === null || this.dragging === false || ( pointer.button !== undefined && pointer.button !== 0 ) ) return;
347 |
348 | raycaster.setFromCamera( pointer, this.camera );
349 |
350 | var planeIntersect = intersectObjectWithRay( _plane, raycaster, true );
351 |
352 | if ( ! planeIntersect ) return;
353 |
354 | pointEnd.copy( planeIntersect.point ).sub( worldPositionStart );
355 |
356 | if ( mode === 'translate' ) {
357 |
358 | // Apply translate
359 |
360 | offset.copy( pointEnd ).sub( pointStart );
361 |
362 | if ( space === 'local' && axis !== 'XYZ' ) {
363 |
364 | offset.applyQuaternion( worldQuaternionInv );
365 |
366 | }
367 |
368 | if ( axis.indexOf( 'X' ) === - 1 ) offset.x = 0;
369 | if ( axis.indexOf( 'Y' ) === - 1 ) offset.y = 0;
370 | if ( axis.indexOf( 'Z' ) === - 1 ) offset.z = 0;
371 |
372 | if ( space === 'local' && axis !== 'XYZ' ) {
373 |
374 | offset.applyQuaternion( quaternionStart ).divide( parentScale );
375 |
376 | } else {
377 |
378 | offset.applyQuaternion( parentQuaternionInv ).divide( parentScale );
379 |
380 | }
381 |
382 | object.position.copy( offset ).add( positionStart );
383 |
384 | // Apply translation snap
385 |
386 | if ( this.translationSnap ) {
387 |
388 | if ( space === 'local' ) {
389 |
390 | object.position.applyQuaternion( _tempQuaternion.copy( quaternionStart ).inverse() );
391 |
392 | if ( axis.search( 'X' ) !== - 1 ) {
393 |
394 | object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
395 |
396 | }
397 |
398 | if ( axis.search( 'Y' ) !== - 1 ) {
399 |
400 | object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
401 |
402 | }
403 |
404 | if ( axis.search( 'Z' ) !== - 1 ) {
405 |
406 | object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
407 |
408 | }
409 |
410 | object.position.applyQuaternion( quaternionStart );
411 |
412 | }
413 |
414 | if ( space === 'world' ) {
415 |
416 | if ( object.parent ) {
417 |
418 | object.position.add( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
419 |
420 | }
421 |
422 | if ( axis.search( 'X' ) !== - 1 ) {
423 |
424 | object.position.x = Math.round( object.position.x / this.translationSnap ) * this.translationSnap;
425 |
426 | }
427 |
428 | if ( axis.search( 'Y' ) !== - 1 ) {
429 |
430 | object.position.y = Math.round( object.position.y / this.translationSnap ) * this.translationSnap;
431 |
432 | }
433 |
434 | if ( axis.search( 'Z' ) !== - 1 ) {
435 |
436 | object.position.z = Math.round( object.position.z / this.translationSnap ) * this.translationSnap;
437 |
438 | }
439 |
440 | if ( object.parent ) {
441 |
442 | object.position.sub( _tempVector.setFromMatrixPosition( object.parent.matrixWorld ) );
443 |
444 | }
445 |
446 | }
447 |
448 | }
449 |
450 | } else if ( mode === 'scale' ) {
451 |
452 | if ( axis.search( 'XYZ' ) !== - 1 ) {
453 |
454 | var d = pointEnd.length() / pointStart.length();
455 |
456 | if ( pointEnd.dot( pointStart ) < 0 ) d *= - 1;
457 |
458 | _tempVector2.set( d, d, d );
459 |
460 | } else {
461 |
462 | _tempVector.copy( pointStart );
463 | _tempVector2.copy( pointEnd );
464 |
465 | _tempVector.applyQuaternion( worldQuaternionInv );
466 | _tempVector2.applyQuaternion( worldQuaternionInv );
467 |
468 | _tempVector2.divide( _tempVector );
469 |
470 | if ( axis.search( 'X' ) === - 1 ) {
471 |
472 | _tempVector2.x = 1;
473 |
474 | }
475 |
476 | if ( axis.search( 'Y' ) === - 1 ) {
477 |
478 | _tempVector2.y = 1;
479 |
480 | }
481 |
482 | if ( axis.search( 'Z' ) === - 1 ) {
483 |
484 | _tempVector2.z = 1;
485 |
486 | }
487 |
488 | }
489 |
490 | // Apply scale
491 |
492 | object.scale.copy( scaleStart ).multiply( _tempVector2 );
493 |
494 | if ( this.scaleSnap ) {
495 |
496 | if ( axis.search( 'X' ) !== - 1 ) {
497 |
498 | object.scale.x = Math.round( object.scale.x / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
499 |
500 | }
501 |
502 | if ( axis.search( 'Y' ) !== - 1 ) {
503 |
504 | object.scale.y = Math.round( object.scale.y / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
505 |
506 | }
507 |
508 | if ( axis.search( 'Z' ) !== - 1 ) {
509 |
510 | object.scale.z = Math.round( object.scale.z / this.scaleSnap ) * this.scaleSnap || this.scaleSnap;
511 |
512 | }
513 |
514 | }
515 |
516 | } else if ( mode === 'rotate' ) {
517 |
518 | offset.copy( pointEnd ).sub( pointStart );
519 |
520 | var ROTATION_SPEED = 20 / worldPosition.distanceTo( _tempVector.setFromMatrixPosition( this.camera.matrixWorld ) );
521 |
522 | if ( axis === 'E' ) {
523 |
524 | rotationAxis.copy( eye );
525 | rotationAngle = pointEnd.angleTo( pointStart );
526 |
527 | startNorm.copy( pointStart ).normalize();
528 | endNorm.copy( pointEnd ).normalize();
529 |
530 | rotationAngle *= ( endNorm.cross( startNorm ).dot( eye ) < 0 ? 1 : - 1 );
531 |
532 | } else if ( axis === 'XYZE' ) {
533 |
534 | rotationAxis.copy( offset ).cross( eye ).normalize();
535 | rotationAngle = offset.dot( _tempVector.copy( rotationAxis ).cross( this.eye ) ) * ROTATION_SPEED;
536 |
537 | } else if ( axis === 'X' || axis === 'Y' || axis === 'Z' ) {
538 |
539 | rotationAxis.copy( _unit[ axis ] );
540 |
541 | _tempVector.copy( _unit[ axis ] );
542 |
543 | if ( space === 'local' ) {
544 |
545 | _tempVector.applyQuaternion( worldQuaternion );
546 |
547 | }
548 |
549 | rotationAngle = offset.dot( _tempVector.cross( eye ).normalize() ) * ROTATION_SPEED;
550 |
551 | }
552 |
553 | // Apply rotation snap
554 |
555 | if ( this.rotationSnap ) rotationAngle = Math.round( rotationAngle / this.rotationSnap ) * this.rotationSnap;
556 |
557 | this.rotationAngle = rotationAngle;
558 |
559 | // Apply rotate
560 | if ( space === 'local' && axis !== 'E' && axis !== 'XYZE' ) {
561 |
562 | object.quaternion.copy( quaternionStart );
563 | object.quaternion.multiply( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) ).normalize();
564 |
565 | } else {
566 |
567 | rotationAxis.applyQuaternion( parentQuaternionInv );
568 | object.quaternion.copy( _tempQuaternion.setFromAxisAngle( rotationAxis, rotationAngle ) );
569 | object.quaternion.multiply( quaternionStart ).normalize();
570 |
571 | }
572 |
573 | }
574 |
575 | this.dispatchEvent( changeEvent );
576 | this.dispatchEvent( objectChangeEvent );
577 |
578 | };
579 |
580 | this.pointerUp = function ( pointer ) {
581 |
582 | if ( pointer.button !== undefined && pointer.button !== 0 ) return;
583 |
584 | if ( this.dragging && ( this.axis !== null ) ) {
585 |
586 | mouseUpEvent.mode = this.mode;
587 | this.dispatchEvent( mouseUpEvent );
588 |
589 | }
590 |
591 | this.dragging = false;
592 |
593 | if ( pointer.button === undefined ) this.axis = null;
594 |
595 | };
596 |
597 | // normalize mouse / touch pointer and remap {x,y} to view space.
598 |
599 | function getPointer( event ) {
600 |
601 | if ( document.pointerLockElement ) {
602 |
603 | return {
604 | x: 0,
605 | y: 0,
606 | button: event.button
607 | };
608 |
609 | } else {
610 |
611 | var pointer = event.changedTouches ? event.changedTouches[ 0 ] : event;
612 |
613 | var rect = domElement.getBoundingClientRect();
614 |
615 | return {
616 | x: ( pointer.clientX - rect.left ) / rect.width * 2 - 1,
617 | y: - ( pointer.clientY - rect.top ) / rect.height * 2 + 1,
618 | button: event.button
619 | };
620 |
621 | }
622 |
623 | }
624 |
625 | // mouse / touch event handlers
626 |
627 | function onPointerHover( event ) {
628 |
629 | if ( ! scope.enabled ) return;
630 |
631 | scope.pointerHover( getPointer( event ) );
632 |
633 | }
634 |
635 | function onPointerDown( event ) {
636 |
637 | if ( ! scope.enabled ) return;
638 |
639 | document.addEventListener( "mousemove", onPointerMove, false );
640 |
641 | scope.pointerHover( getPointer( event ) );
642 | scope.pointerDown( getPointer( event ) );
643 |
644 | }
645 |
646 | function onPointerMove( event ) {
647 |
648 | if ( ! scope.enabled ) return;
649 |
650 | scope.pointerMove( getPointer( event ) );
651 |
652 | }
653 |
654 | function onPointerUp( event ) {
655 |
656 | if ( ! scope.enabled ) return;
657 |
658 | document.removeEventListener( "mousemove", onPointerMove, false );
659 |
660 | scope.pointerUp( getPointer( event ) );
661 |
662 | }
663 |
664 | // TODO: deprecate
665 |
666 | this.getMode = function () {
667 |
668 | return scope.mode;
669 |
670 | };
671 |
672 | this.setMode = function ( mode ) {
673 |
674 | scope.mode = mode;
675 |
676 | };
677 |
678 | this.setTranslationSnap = function ( translationSnap ) {
679 |
680 | scope.translationSnap = translationSnap;
681 |
682 | };
683 |
684 | this.setRotationSnap = function ( rotationSnap ) {
685 |
686 | scope.rotationSnap = rotationSnap;
687 |
688 | };
689 |
690 | this.setScaleSnap = function ( scaleSnap ) {
691 |
692 | scope.scaleSnap = scaleSnap;
693 |
694 | };
695 |
696 | this.setSize = function ( size ) {
697 |
698 | scope.size = size;
699 |
700 | };
701 |
702 | this.setSpace = function ( space ) {
703 |
704 | scope.space = space;
705 |
706 | };
707 |
708 | this.update = function () {
709 |
710 | console.warn( 'THREE.TransformControls: update function has no more functionality and therefore has been deprecated.' );
711 |
712 | };
713 |
714 | };
715 |
716 | TransformControls.prototype = Object.assign( Object.create( Object3D.prototype ), {
717 |
718 | constructor: TransformControls,
719 |
720 | isTransformControls: true
721 |
722 | } );
723 |
724 |
725 | var TransformControlsGizmo = function () {
726 |
727 | 'use strict';
728 |
729 | Object3D.call( this );
730 |
731 | this.type = 'TransformControlsGizmo';
732 |
733 | // shared materials
734 |
735 | var gizmoMaterial = new MeshBasicMaterial( {
736 | depthTest: false,
737 | depthWrite: false,
738 | transparent: true,
739 | side: DoubleSide,
740 | fog: false
741 | } );
742 |
743 | var gizmoLineMaterial = new LineBasicMaterial( {
744 | depthTest: false,
745 | depthWrite: false,
746 | transparent: true,
747 | linewidth: 1,
748 | fog: false
749 | } );
750 |
751 | // Make unique material for each axis/color
752 |
753 | var matInvisible = gizmoMaterial.clone();
754 | matInvisible.opacity = 0.15;
755 |
756 | var matHelper = gizmoMaterial.clone();
757 | matHelper.opacity = 0.33;
758 |
759 | var matRed = gizmoMaterial.clone();
760 | matRed.color.set( 0xff0000 );
761 |
762 | var matGreen = gizmoMaterial.clone();
763 | matGreen.color.set( 0x00ff00 );
764 |
765 | var matBlue = gizmoMaterial.clone();
766 | matBlue.color.set( 0x0000ff );
767 |
768 | var matWhiteTransparent = gizmoMaterial.clone();
769 | matWhiteTransparent.opacity = 0.25;
770 |
771 | var matYellowTransparent = matWhiteTransparent.clone();
772 | matYellowTransparent.color.set( 0xffff00 );
773 |
774 | var matCyanTransparent = matWhiteTransparent.clone();
775 | matCyanTransparent.color.set( 0x00ffff );
776 |
777 | var matMagentaTransparent = matWhiteTransparent.clone();
778 | matMagentaTransparent.color.set( 0xff00ff );
779 |
780 | var matYellow = gizmoMaterial.clone();
781 | matYellow.color.set( 0xffff00 );
782 |
783 | var matLineRed = gizmoLineMaterial.clone();
784 | matLineRed.color.set( 0xff0000 );
785 |
786 | var matLineGreen = gizmoLineMaterial.clone();
787 | matLineGreen.color.set( 0x00ff00 );
788 |
789 | var matLineBlue = gizmoLineMaterial.clone();
790 | matLineBlue.color.set( 0x0000ff );
791 |
792 | var matLineCyan = gizmoLineMaterial.clone();
793 | matLineCyan.color.set( 0x00ffff );
794 |
795 | var matLineMagenta = gizmoLineMaterial.clone();
796 | matLineMagenta.color.set( 0xff00ff );
797 |
798 | var matLineYellow = gizmoLineMaterial.clone();
799 | matLineYellow.color.set( 0xffff00 );
800 |
801 | var matLineGray = gizmoLineMaterial.clone();
802 | matLineGray.color.set( 0x787878 );
803 |
804 | var matLineYellowTransparent = matLineYellow.clone();
805 | matLineYellowTransparent.opacity = 0.25;
806 |
807 | // reusable geometry
808 |
809 | var arrowGeometry = new CylinderBufferGeometry( 0, 0.05, 0.2, 12, 1, false );
810 |
811 | var scaleHandleGeometry = new BoxBufferGeometry( 0.125, 0.125, 0.125 );
812 |
813 | var lineGeometry = new BufferGeometry( );
814 | lineGeometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 1, 0, 0 ], 3 ) );
815 |
816 | var CircleGeometry = function ( radius, arc ) {
817 |
818 | var geometry = new BufferGeometry( );
819 | var vertices = [];
820 |
821 | for ( var i = 0; i <= 64 * arc; ++ i ) {
822 |
823 | vertices.push( 0, Math.cos( i / 32 * Math.PI ) * radius, Math.sin( i / 32 * Math.PI ) * radius );
824 |
825 | }
826 |
827 | geometry.setAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );
828 |
829 | return geometry;
830 |
831 | };
832 |
833 | // Special geometry for transform helper. If scaled with position vector it spans from [0,0,0] to position
834 |
835 | var TranslateHelperGeometry = function () {
836 |
837 | var geometry = new BufferGeometry();
838 |
839 | geometry.setAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 1, 1, 1 ], 3 ) );
840 |
841 | return geometry;
842 |
843 | };
844 |
845 | // Gizmo definitions - custom hierarchy definitions for setupGizmo() function
846 |
847 | var gizmoTranslate = {
848 | X: [
849 | [ new Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, - Math.PI / 2 ], null, 'fwd' ],
850 | [ new Mesh( arrowGeometry, matRed ), [ 1, 0, 0 ], [ 0, 0, Math.PI / 2 ], null, 'bwd' ],
851 | [ new Line( lineGeometry, matLineRed ) ]
852 | ],
853 | Y: [
854 | [ new Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], null, null, 'fwd' ],
855 | [ new Mesh( arrowGeometry, matGreen ), [ 0, 1, 0 ], [ Math.PI, 0, 0 ], null, 'bwd' ],
856 | [ new Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ]]
857 | ],
858 | Z: [
859 | [ new Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ Math.PI / 2, 0, 0 ], null, 'fwd' ],
860 | [ new Mesh( arrowGeometry, matBlue ), [ 0, 0, 1 ], [ - Math.PI / 2, 0, 0 ], null, 'bwd' ],
861 | [ new Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ]]
862 | ],
863 | XYZ: [
864 | [ new Mesh( new OctahedronBufferGeometry( 0.1, 0 ), matWhiteTransparent.clone() ), [ 0, 0, 0 ], [ 0, 0, 0 ]]
865 | ],
866 | XY: [
867 | [ new Mesh( new PlaneBufferGeometry( 0.295, 0.295 ), matYellowTransparent.clone() ), [ 0.15, 0.15, 0 ]],
868 | [ new Line( lineGeometry, matLineYellow ), [ 0.18, 0.3, 0 ], null, [ 0.125, 1, 1 ]],
869 | [ new Line( lineGeometry, matLineYellow ), [ 0.3, 0.18, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]]
870 | ],
871 | YZ: [
872 | [ new Mesh( new PlaneBufferGeometry( 0.295, 0.295 ), matCyanTransparent.clone() ), [ 0, 0.15, 0.15 ], [ 0, Math.PI / 2, 0 ]],
873 | [ new Line( lineGeometry, matLineCyan ), [ 0, 0.18, 0.3 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]],
874 | [ new Line( lineGeometry, matLineCyan ), [ 0, 0.3, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]
875 | ],
876 | XZ: [
877 | [ new Mesh( new PlaneBufferGeometry( 0.295, 0.295 ), matMagentaTransparent.clone() ), [ 0.15, 0, 0.15 ], [ - Math.PI / 2, 0, 0 ]],
878 | [ new Line( lineGeometry, matLineMagenta ), [ 0.18, 0, 0.3 ], null, [ 0.125, 1, 1 ]],
879 | [ new Line( lineGeometry, matLineMagenta ), [ 0.3, 0, 0.18 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]
880 | ]
881 | };
882 |
883 | var pickerTranslate = {
884 | X: [
885 | [ new Mesh( new CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0.6, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]
886 | ],
887 | Y: [
888 | [ new Mesh( new CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0.6, 0 ]]
889 | ],
890 | Z: [
891 | [ new Mesh( new CylinderBufferGeometry( 0.2, 0, 1, 4, 1, false ), matInvisible ), [ 0, 0, 0.6 ], [ Math.PI / 2, 0, 0 ]]
892 | ],
893 | XYZ: [
894 | [ new Mesh( new OctahedronBufferGeometry( 0.2, 0 ), matInvisible ) ]
895 | ],
896 | XY: [
897 | [ new Mesh( new PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0.2, 0 ]]
898 | ],
899 | YZ: [
900 | [ new Mesh( new PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0, 0.2, 0.2 ], [ 0, Math.PI / 2, 0 ]]
901 | ],
902 | XZ: [
903 | [ new Mesh( new PlaneBufferGeometry( 0.4, 0.4 ), matInvisible ), [ 0.2, 0, 0.2 ], [ - Math.PI / 2, 0, 0 ]]
904 | ]
905 | };
906 |
907 | var helperTranslate = {
908 | START: [
909 | [ new Mesh( new OctahedronBufferGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]
910 | ],
911 | END: [
912 | [ new Mesh( new OctahedronBufferGeometry( 0.01, 2 ), matHelper ), null, null, null, 'helper' ]
913 | ],
914 | DELTA: [
915 | [ new Line( TranslateHelperGeometry(), matHelper ), null, null, null, 'helper' ]
916 | ],
917 | X: [
918 | [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
919 | ],
920 | Y: [
921 | [ new Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]
922 | ],
923 | Z: [
924 | [ new Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]
925 | ]
926 | };
927 |
928 | var gizmoRotate = {
929 | X: [
930 | [ new Line( CircleGeometry( 1, 0.5 ), matLineRed ) ],
931 | [ new Mesh( new OctahedronBufferGeometry( 0.04, 0 ), matRed ), [ 0, 0, 0.99 ], null, [ 1, 3, 1 ]],
932 | ],
933 | Y: [
934 | [ new Line( CircleGeometry( 1, 0.5 ), matLineGreen ), null, [ 0, 0, - Math.PI / 2 ]],
935 | [ new Mesh( new OctahedronBufferGeometry( 0.04, 0 ), matGreen ), [ 0, 0, 0.99 ], null, [ 3, 1, 1 ]],
936 | ],
937 | Z: [
938 | [ new Line( CircleGeometry( 1, 0.5 ), matLineBlue ), null, [ 0, Math.PI / 2, 0 ]],
939 | [ new Mesh( new OctahedronBufferGeometry( 0.04, 0 ), matBlue ), [ 0.99, 0, 0 ], null, [ 1, 3, 1 ]],
940 | ],
941 | E: [
942 | [ new Line( CircleGeometry( 1.25, 1 ), matLineYellowTransparent ), null, [ 0, Math.PI / 2, 0 ]],
943 | [ new Mesh( new CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 1.17, 0, 0 ], [ 0, 0, - Math.PI / 2 ], [ 1, 1, 0.001 ]],
944 | [ new Mesh( new CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ - 1.17, 0, 0 ], [ 0, 0, Math.PI / 2 ], [ 1, 1, 0.001 ]],
945 | [ new Mesh( new CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, - 1.17, 0 ], [ Math.PI, 0, 0 ], [ 1, 1, 0.001 ]],
946 | [ new Mesh( new CylinderBufferGeometry( 0.03, 0, 0.15, 4, 1, false ), matLineYellowTransparent ), [ 0, 1.17, 0 ], [ 0, 0, 0 ], [ 1, 1, 0.001 ]],
947 | ],
948 | XYZE: [
949 | [ new Line( CircleGeometry( 1, 1 ), matLineGray ), null, [ 0, Math.PI / 2, 0 ]]
950 | ]
951 | };
952 |
953 | var helperRotate = {
954 | AXIS: [
955 | [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
956 | ]
957 | };
958 |
959 | var pickerRotate = {
960 | X: [
961 | [ new Mesh( new TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, - Math.PI / 2, - Math.PI / 2 ]],
962 | ],
963 | Y: [
964 | [ new Mesh( new TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ Math.PI / 2, 0, 0 ]],
965 | ],
966 | Z: [
967 | [ new Mesh( new TorusBufferGeometry( 1, 0.1, 4, 24 ), matInvisible ), [ 0, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
968 | ],
969 | E: [
970 | [ new Mesh( new TorusBufferGeometry( 1.25, 0.1, 2, 24 ), matInvisible ) ]
971 | ],
972 | XYZE: [
973 | [ new Mesh( new SphereBufferGeometry( 0.7, 10, 8 ), matInvisible ) ]
974 | ]
975 | };
976 |
977 | var gizmoScale = {
978 | X: [
979 | [ new Mesh( scaleHandleGeometry, matRed ), [ 0.8, 0, 0 ], [ 0, 0, - Math.PI / 2 ]],
980 | [ new Line( lineGeometry, matLineRed ), null, null, [ 0.8, 1, 1 ]]
981 | ],
982 | Y: [
983 | [ new Mesh( scaleHandleGeometry, matGreen ), [ 0, 0.8, 0 ]],
984 | [ new Line( lineGeometry, matLineGreen ), null, [ 0, 0, Math.PI / 2 ], [ 0.8, 1, 1 ]]
985 | ],
986 | Z: [
987 | [ new Mesh( scaleHandleGeometry, matBlue ), [ 0, 0, 0.8 ], [ Math.PI / 2, 0, 0 ]],
988 | [ new Line( lineGeometry, matLineBlue ), null, [ 0, - Math.PI / 2, 0 ], [ 0.8, 1, 1 ]]
989 | ],
990 | XY: [
991 | [ new Mesh( scaleHandleGeometry, matYellowTransparent ), [ 0.85, 0.85, 0 ], null, [ 2, 2, 0.2 ]],
992 | [ new Line( lineGeometry, matLineYellow ), [ 0.855, 0.98, 0 ], null, [ 0.125, 1, 1 ]],
993 | [ new Line( lineGeometry, matLineYellow ), [ 0.98, 0.855, 0 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]]
994 | ],
995 | YZ: [
996 | [ new Mesh( scaleHandleGeometry, matCyanTransparent ), [ 0, 0.85, 0.85 ], null, [ 0.2, 2, 2 ]],
997 | [ new Line( lineGeometry, matLineCyan ), [ 0, 0.855, 0.98 ], [ 0, 0, Math.PI / 2 ], [ 0.125, 1, 1 ]],
998 | [ new Line( lineGeometry, matLineCyan ), [ 0, 0.98, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]
999 | ],
1000 | XZ: [
1001 | [ new Mesh( scaleHandleGeometry, matMagentaTransparent ), [ 0.85, 0, 0.85 ], null, [ 2, 0.2, 2 ]],
1002 | [ new Line( lineGeometry, matLineMagenta ), [ 0.855, 0, 0.98 ], null, [ 0.125, 1, 1 ]],
1003 | [ new Line( lineGeometry, matLineMagenta ), [ 0.98, 0, 0.855 ], [ 0, - Math.PI / 2, 0 ], [ 0.125, 1, 1 ]]
1004 | ],
1005 | XYZX: [
1006 | [ new Mesh( new BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 1.1, 0, 0 ]],
1007 | ],
1008 | XYZY: [
1009 | [ new Mesh( new BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 1.1, 0 ]],
1010 | ],
1011 | XYZZ: [
1012 | [ new Mesh( new BoxBufferGeometry( 0.125, 0.125, 0.125 ), matWhiteTransparent.clone() ), [ 0, 0, 1.1 ]],
1013 | ]
1014 | };
1015 |
1016 | var pickerScale = {
1017 | X: [
1018 | [ new Mesh( new CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0.5, 0, 0 ], [ 0, 0, - Math.PI / 2 ]]
1019 | ],
1020 | Y: [
1021 | [ new Mesh( new CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0.5, 0 ]]
1022 | ],
1023 | Z: [
1024 | [ new Mesh( new CylinderBufferGeometry( 0.2, 0, 0.8, 4, 1, false ), matInvisible ), [ 0, 0, 0.5 ], [ Math.PI / 2, 0, 0 ]]
1025 | ],
1026 | XY: [
1027 | [ new Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0.85, 0 ], null, [ 3, 3, 0.2 ]],
1028 | ],
1029 | YZ: [
1030 | [ new Mesh( scaleHandleGeometry, matInvisible ), [ 0, 0.85, 0.85 ], null, [ 0.2, 3, 3 ]],
1031 | ],
1032 | XZ: [
1033 | [ new Mesh( scaleHandleGeometry, matInvisible ), [ 0.85, 0, 0.85 ], null, [ 3, 0.2, 3 ]],
1034 | ],
1035 | XYZX: [
1036 | [ new Mesh( new BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 1.1, 0, 0 ]],
1037 | ],
1038 | XYZY: [
1039 | [ new Mesh( new BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 1.1, 0 ]],
1040 | ],
1041 | XYZZ: [
1042 | [ new Mesh( new BoxBufferGeometry( 0.2, 0.2, 0.2 ), matInvisible ), [ 0, 0, 1.1 ]],
1043 | ]
1044 | };
1045 |
1046 | var helperScale = {
1047 | X: [
1048 | [ new Line( lineGeometry, matHelper.clone() ), [ - 1e3, 0, 0 ], null, [ 1e6, 1, 1 ], 'helper' ]
1049 | ],
1050 | Y: [
1051 | [ new Line( lineGeometry, matHelper.clone() ), [ 0, - 1e3, 0 ], [ 0, 0, Math.PI / 2 ], [ 1e6, 1, 1 ], 'helper' ]
1052 | ],
1053 | Z: [
1054 | [ new Line( lineGeometry, matHelper.clone() ), [ 0, 0, - 1e3 ], [ 0, - Math.PI / 2, 0 ], [ 1e6, 1, 1 ], 'helper' ]
1055 | ]
1056 | };
1057 |
1058 | // Creates an Object3D with gizmos described in custom hierarchy definition.
1059 |
1060 | var setupGizmo = function ( gizmoMap ) {
1061 |
1062 | var gizmo = new Object3D();
1063 |
1064 | for ( var name in gizmoMap ) {
1065 |
1066 | for ( var i = gizmoMap[ name ].length; i --; ) {
1067 |
1068 | var object = gizmoMap[ name ][ i ][ 0 ].clone();
1069 | var position = gizmoMap[ name ][ i ][ 1 ];
1070 | var rotation = gizmoMap[ name ][ i ][ 2 ];
1071 | var scale = gizmoMap[ name ][ i ][ 3 ];
1072 | var tag = gizmoMap[ name ][ i ][ 4 ];
1073 |
1074 | // name and tag properties are essential for picking and updating logic.
1075 | object.name = name;
1076 | object.tag = tag;
1077 |
1078 | if ( position ) {
1079 |
1080 | object.position.set( position[ 0 ], position[ 1 ], position[ 2 ] );
1081 |
1082 | }
1083 |
1084 | if ( rotation ) {
1085 |
1086 | object.rotation.set( rotation[ 0 ], rotation[ 1 ], rotation[ 2 ] );
1087 |
1088 | }
1089 |
1090 | if ( scale ) {
1091 |
1092 | object.scale.set( scale[ 0 ], scale[ 1 ], scale[ 2 ] );
1093 |
1094 | }
1095 |
1096 | object.updateMatrix();
1097 |
1098 | var tempGeometry = object.geometry.clone();
1099 | tempGeometry.applyMatrix4( object.matrix );
1100 | object.geometry = tempGeometry;
1101 | object.renderOrder = Infinity;
1102 |
1103 | object.position.set( 0, 0, 0 );
1104 | object.rotation.set( 0, 0, 0 );
1105 | object.scale.set( 1, 1, 1 );
1106 |
1107 | gizmo.add( object );
1108 |
1109 | }
1110 |
1111 | }
1112 |
1113 | return gizmo;
1114 |
1115 | };
1116 |
1117 | // Reusable utility variables
1118 |
1119 | var tempVector = new Vector3( 0, 0, 0 );
1120 | var tempEuler = new Euler();
1121 | var alignVector = new Vector3( 0, 1, 0 );
1122 | var zeroVector = new Vector3( 0, 0, 0 );
1123 | var lookAtMatrix = new Matrix4();
1124 | var tempQuaternion = new Quaternion();
1125 | var tempQuaternion2 = new Quaternion();
1126 | var identityQuaternion = new Quaternion();
1127 |
1128 | var unitX = new Vector3( 1, 0, 0 );
1129 | var unitY = new Vector3( 0, 1, 0 );
1130 | var unitZ = new Vector3( 0, 0, 1 );
1131 |
1132 | // Gizmo creation
1133 |
1134 | this.gizmo = {};
1135 | this.picker = {};
1136 | this.helper = {};
1137 |
1138 | this.add( this.gizmo[ "translate" ] = setupGizmo( gizmoTranslate ) );
1139 | this.add( this.gizmo[ "rotate" ] = setupGizmo( gizmoRotate ) );
1140 | this.add( this.gizmo[ "scale" ] = setupGizmo( gizmoScale ) );
1141 | this.add( this.picker[ "translate" ] = setupGizmo( pickerTranslate ) );
1142 | this.add( this.picker[ "rotate" ] = setupGizmo( pickerRotate ) );
1143 | this.add( this.picker[ "scale" ] = setupGizmo( pickerScale ) );
1144 | this.add( this.helper[ "translate" ] = setupGizmo( helperTranslate ) );
1145 | this.add( this.helper[ "rotate" ] = setupGizmo( helperRotate ) );
1146 | this.add( this.helper[ "scale" ] = setupGizmo( helperScale ) );
1147 |
1148 | // Pickers should be hidden always
1149 |
1150 | this.picker[ "translate" ].visible = false;
1151 | this.picker[ "rotate" ].visible = false;
1152 | this.picker[ "scale" ].visible = false;
1153 |
1154 | // updateMatrixWorld will update transformations and appearance of individual handles
1155 |
1156 | this.updateMatrixWorld = function () {
1157 |
1158 | var space = this.space;
1159 |
1160 | if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
1161 |
1162 | var quaternion = space === "local" ? this.worldQuaternion : identityQuaternion;
1163 |
1164 | // Show only gizmos for current transform mode
1165 |
1166 | this.gizmo[ "translate" ].visible = this.mode === "translate";
1167 | this.gizmo[ "rotate" ].visible = this.mode === "rotate";
1168 | this.gizmo[ "scale" ].visible = this.mode === "scale";
1169 |
1170 | this.helper[ "translate" ].visible = this.mode === "translate";
1171 | this.helper[ "rotate" ].visible = this.mode === "rotate";
1172 | this.helper[ "scale" ].visible = this.mode === "scale";
1173 |
1174 |
1175 | var handles = [];
1176 | handles = handles.concat( this.picker[ this.mode ].children );
1177 | handles = handles.concat( this.gizmo[ this.mode ].children );
1178 | handles = handles.concat( this.helper[ this.mode ].children );
1179 |
1180 | for ( var i = 0; i < handles.length; i ++ ) {
1181 |
1182 | var handle = handles[ i ];
1183 |
1184 | // hide aligned to camera
1185 |
1186 | handle.visible = true;
1187 | handle.rotation.set( 0, 0, 0 );
1188 | handle.position.copy( this.worldPosition );
1189 |
1190 | var factor;
1191 |
1192 | if ( this.camera.isOrthographicCamera ) {
1193 |
1194 | factor = ( this.camera.top - this.camera.bottom ) / this.camera.zoom;
1195 |
1196 | } else {
1197 |
1198 | factor = this.worldPosition.distanceTo( this.cameraPosition ) * Math.min( 1.9 * Math.tan( Math.PI * this.camera.fov / 360 ) / this.camera.zoom, 7 );
1199 |
1200 | }
1201 |
1202 | handle.scale.set( 1, 1, 1 ).multiplyScalar( factor * this.size / 7 );
1203 |
1204 | // TODO: simplify helpers and consider decoupling from gizmo
1205 |
1206 | if ( handle.tag === 'helper' ) {
1207 |
1208 | handle.visible = false;
1209 |
1210 | if ( handle.name === 'AXIS' ) {
1211 |
1212 | handle.position.copy( this.worldPositionStart );
1213 | handle.visible = !! this.axis;
1214 |
1215 | if ( this.axis === 'X' ) {
1216 |
1217 | tempQuaternion.setFromEuler( tempEuler.set( 0, 0, 0 ) );
1218 | handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
1219 |
1220 | if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
1221 |
1222 | handle.visible = false;
1223 |
1224 | }
1225 |
1226 | }
1227 |
1228 | if ( this.axis === 'Y' ) {
1229 |
1230 | tempQuaternion.setFromEuler( tempEuler.set( 0, 0, Math.PI / 2 ) );
1231 | handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
1232 |
1233 | if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
1234 |
1235 | handle.visible = false;
1236 |
1237 | }
1238 |
1239 | }
1240 |
1241 | if ( this.axis === 'Z' ) {
1242 |
1243 | tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) );
1244 | handle.quaternion.copy( quaternion ).multiply( tempQuaternion );
1245 |
1246 | if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > 0.9 ) {
1247 |
1248 | handle.visible = false;
1249 |
1250 | }
1251 |
1252 | }
1253 |
1254 | if ( this.axis === 'XYZE' ) {
1255 |
1256 | tempQuaternion.setFromEuler( tempEuler.set( 0, Math.PI / 2, 0 ) );
1257 | alignVector.copy( this.rotationAxis );
1258 | handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( zeroVector, alignVector, unitY ) );
1259 | handle.quaternion.multiply( tempQuaternion );
1260 | handle.visible = this.dragging;
1261 |
1262 | }
1263 |
1264 | if ( this.axis === 'E' ) {
1265 |
1266 | handle.visible = false;
1267 |
1268 | }
1269 |
1270 |
1271 | } else if ( handle.name === 'START' ) {
1272 |
1273 | handle.position.copy( this.worldPositionStart );
1274 | handle.visible = this.dragging;
1275 |
1276 | } else if ( handle.name === 'END' ) {
1277 |
1278 | handle.position.copy( this.worldPosition );
1279 | handle.visible = this.dragging;
1280 |
1281 | } else if ( handle.name === 'DELTA' ) {
1282 |
1283 | handle.position.copy( this.worldPositionStart );
1284 | handle.quaternion.copy( this.worldQuaternionStart );
1285 | tempVector.set( 1e-10, 1e-10, 1e-10 ).add( this.worldPositionStart ).sub( this.worldPosition ).multiplyScalar( - 1 );
1286 | tempVector.applyQuaternion( this.worldQuaternionStart.clone().inverse() );
1287 | handle.scale.copy( tempVector );
1288 | handle.visible = this.dragging;
1289 |
1290 | } else {
1291 |
1292 | handle.quaternion.copy( quaternion );
1293 |
1294 | if ( this.dragging ) {
1295 |
1296 | handle.position.copy( this.worldPositionStart );
1297 |
1298 | } else {
1299 |
1300 | handle.position.copy( this.worldPosition );
1301 |
1302 | }
1303 |
1304 | if ( this.axis ) {
1305 |
1306 | handle.visible = this.axis.search( handle.name ) !== - 1;
1307 |
1308 | }
1309 |
1310 | }
1311 |
1312 | // If updating helper, skip rest of the loop
1313 | continue;
1314 |
1315 | }
1316 |
1317 | // Align handles to current local or world rotation
1318 |
1319 | handle.quaternion.copy( quaternion );
1320 |
1321 | if ( this.mode === 'translate' || this.mode === 'scale' ) {
1322 |
1323 | // Hide translate and scale axis facing the camera
1324 |
1325 | var AXIS_HIDE_TRESHOLD = 0.99;
1326 | var PLANE_HIDE_TRESHOLD = 0.2;
1327 | var AXIS_FLIP_TRESHOLD = 0.0;
1328 |
1329 |
1330 | if ( handle.name === 'X' || handle.name === 'XYZX' ) {
1331 |
1332 | if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
1333 |
1334 | handle.scale.set( 1e-10, 1e-10, 1e-10 );
1335 | handle.visible = false;
1336 |
1337 | }
1338 |
1339 | }
1340 |
1341 | if ( handle.name === 'Y' || handle.name === 'XYZY' ) {
1342 |
1343 | if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
1344 |
1345 | handle.scale.set( 1e-10, 1e-10, 1e-10 );
1346 | handle.visible = false;
1347 |
1348 | }
1349 |
1350 | }
1351 |
1352 | if ( handle.name === 'Z' || handle.name === 'XYZZ' ) {
1353 |
1354 | if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) > AXIS_HIDE_TRESHOLD ) {
1355 |
1356 | handle.scale.set( 1e-10, 1e-10, 1e-10 );
1357 | handle.visible = false;
1358 |
1359 | }
1360 |
1361 | }
1362 |
1363 | if ( handle.name === 'XY' ) {
1364 |
1365 | if ( Math.abs( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
1366 |
1367 | handle.scale.set( 1e-10, 1e-10, 1e-10 );
1368 | handle.visible = false;
1369 |
1370 | }
1371 |
1372 | }
1373 |
1374 | if ( handle.name === 'YZ' ) {
1375 |
1376 | if ( Math.abs( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
1377 |
1378 | handle.scale.set( 1e-10, 1e-10, 1e-10 );
1379 | handle.visible = false;
1380 |
1381 | }
1382 |
1383 | }
1384 |
1385 | if ( handle.name === 'XZ' ) {
1386 |
1387 | if ( Math.abs( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) ) < PLANE_HIDE_TRESHOLD ) {
1388 |
1389 | handle.scale.set( 1e-10, 1e-10, 1e-10 );
1390 | handle.visible = false;
1391 |
1392 | }
1393 |
1394 | }
1395 |
1396 | // Flip translate and scale axis ocluded behind another axis
1397 |
1398 | if ( handle.name.search( 'X' ) !== - 1 ) {
1399 |
1400 | if ( alignVector.copy( unitX ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
1401 |
1402 | if ( handle.tag === 'fwd' ) {
1403 |
1404 | handle.visible = false;
1405 |
1406 | } else {
1407 |
1408 | handle.scale.x *= - 1;
1409 |
1410 | }
1411 |
1412 | } else if ( handle.tag === 'bwd' ) {
1413 |
1414 | handle.visible = false;
1415 |
1416 | }
1417 |
1418 | }
1419 |
1420 | if ( handle.name.search( 'Y' ) !== - 1 ) {
1421 |
1422 | if ( alignVector.copy( unitY ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
1423 |
1424 | if ( handle.tag === 'fwd' ) {
1425 |
1426 | handle.visible = false;
1427 |
1428 | } else {
1429 |
1430 | handle.scale.y *= - 1;
1431 |
1432 | }
1433 |
1434 | } else if ( handle.tag === 'bwd' ) {
1435 |
1436 | handle.visible = false;
1437 |
1438 | }
1439 |
1440 | }
1441 |
1442 | if ( handle.name.search( 'Z' ) !== - 1 ) {
1443 |
1444 | if ( alignVector.copy( unitZ ).applyQuaternion( quaternion ).dot( this.eye ) < AXIS_FLIP_TRESHOLD ) {
1445 |
1446 | if ( handle.tag === 'fwd' ) {
1447 |
1448 | handle.visible = false;
1449 |
1450 | } else {
1451 |
1452 | handle.scale.z *= - 1;
1453 |
1454 | }
1455 |
1456 | } else if ( handle.tag === 'bwd' ) {
1457 |
1458 | handle.visible = false;
1459 |
1460 | }
1461 |
1462 | }
1463 |
1464 | } else if ( this.mode === 'rotate' ) {
1465 |
1466 | // Align handles to current local or world rotation
1467 |
1468 | tempQuaternion2.copy( quaternion );
1469 | alignVector.copy( this.eye ).applyQuaternion( tempQuaternion.copy( quaternion ).inverse() );
1470 |
1471 | if ( handle.name.search( "E" ) !== - 1 ) {
1472 |
1473 | handle.quaternion.setFromRotationMatrix( lookAtMatrix.lookAt( this.eye, zeroVector, unitY ) );
1474 |
1475 | }
1476 |
1477 | if ( handle.name === 'X' ) {
1478 |
1479 | tempQuaternion.setFromAxisAngle( unitX, Math.atan2( - alignVector.y, alignVector.z ) );
1480 | tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
1481 | handle.quaternion.copy( tempQuaternion );
1482 |
1483 | }
1484 |
1485 | if ( handle.name === 'Y' ) {
1486 |
1487 | tempQuaternion.setFromAxisAngle( unitY, Math.atan2( alignVector.x, alignVector.z ) );
1488 | tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
1489 | handle.quaternion.copy( tempQuaternion );
1490 |
1491 | }
1492 |
1493 | if ( handle.name === 'Z' ) {
1494 |
1495 | tempQuaternion.setFromAxisAngle( unitZ, Math.atan2( alignVector.y, alignVector.x ) );
1496 | tempQuaternion.multiplyQuaternions( tempQuaternion2, tempQuaternion );
1497 | handle.quaternion.copy( tempQuaternion );
1498 |
1499 | }
1500 |
1501 | }
1502 |
1503 | // Hide disabled axes
1504 | handle.visible = handle.visible && ( handle.name.indexOf( "X" ) === - 1 || this.showX );
1505 | handle.visible = handle.visible && ( handle.name.indexOf( "Y" ) === - 1 || this.showY );
1506 | handle.visible = handle.visible && ( handle.name.indexOf( "Z" ) === - 1 || this.showZ );
1507 | handle.visible = handle.visible && ( handle.name.indexOf( "E" ) === - 1 || ( this.showX && this.showY && this.showZ ) );
1508 |
1509 | // highlight selected axis
1510 |
1511 | handle.material._opacity = handle.material._opacity || handle.material.opacity;
1512 | handle.material._color = handle.material._color || handle.material.color.clone();
1513 |
1514 | handle.material.color.copy( handle.material._color );
1515 | handle.material.opacity = handle.material._opacity;
1516 |
1517 | if ( ! this.enabled ) {
1518 |
1519 | handle.material.opacity *= 0.5;
1520 | handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 );
1521 |
1522 | } else if ( this.axis ) {
1523 |
1524 | if ( handle.name === this.axis ) {
1525 |
1526 | handle.material.opacity = 1.0;
1527 | handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 );
1528 |
1529 | } else if ( this.axis.split( '' ).some( function ( a ) {
1530 |
1531 | return handle.name === a;
1532 |
1533 | } ) ) {
1534 |
1535 | handle.material.opacity = 1.0;
1536 | handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 );
1537 |
1538 | } else {
1539 |
1540 | handle.material.opacity *= 0.25;
1541 | handle.material.color.lerp( new Color( 1, 1, 1 ), 0.5 );
1542 |
1543 | }
1544 |
1545 | }
1546 |
1547 | }
1548 |
1549 | Object3D.prototype.updateMatrixWorld.call( this );
1550 |
1551 | };
1552 |
1553 | };
1554 |
1555 | TransformControlsGizmo.prototype = Object.assign( Object.create( Object3D.prototype ), {
1556 |
1557 | constructor: TransformControlsGizmo,
1558 |
1559 | isTransformControlsGizmo: true
1560 |
1561 | } );
1562 |
1563 |
1564 | var TransformControlsPlane = function () {
1565 |
1566 | 'use strict';
1567 |
1568 | Mesh.call( this,
1569 | new PlaneBufferGeometry( 100000, 100000, 2, 2 ),
1570 | new MeshBasicMaterial( { visible: false, wireframe: true, side: DoubleSide, transparent: true, opacity: 0.1 } )
1571 | );
1572 |
1573 | this.type = 'TransformControlsPlane';
1574 |
1575 | var unitX = new Vector3( 1, 0, 0 );
1576 | var unitY = new Vector3( 0, 1, 0 );
1577 | var unitZ = new Vector3( 0, 0, 1 );
1578 |
1579 | var tempVector = new Vector3();
1580 | var dirVector = new Vector3();
1581 | var alignVector = new Vector3();
1582 | var tempMatrix = new Matrix4();
1583 | var identityQuaternion = new Quaternion();
1584 |
1585 | this.updateMatrixWorld = function () {
1586 |
1587 | var space = this.space;
1588 |
1589 | this.position.copy( this.worldPosition );
1590 |
1591 | if ( this.mode === 'scale' ) space = 'local'; // scale always oriented to local rotation
1592 |
1593 | unitX.set( 1, 0, 0 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion );
1594 | unitY.set( 0, 1, 0 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion );
1595 | unitZ.set( 0, 0, 1 ).applyQuaternion( space === "local" ? this.worldQuaternion : identityQuaternion );
1596 |
1597 | // Align the plane for current transform mode, axis and space.
1598 |
1599 | alignVector.copy( unitY );
1600 |
1601 | switch ( this.mode ) {
1602 |
1603 | case 'translate':
1604 | case 'scale':
1605 | switch ( this.axis ) {
1606 |
1607 | case 'X':
1608 | alignVector.copy( this.eye ).cross( unitX );
1609 | dirVector.copy( unitX ).cross( alignVector );
1610 | break;
1611 | case 'Y':
1612 | alignVector.copy( this.eye ).cross( unitY );
1613 | dirVector.copy( unitY ).cross( alignVector );
1614 | break;
1615 | case 'Z':
1616 | alignVector.copy( this.eye ).cross( unitZ );
1617 | dirVector.copy( unitZ ).cross( alignVector );
1618 | break;
1619 | case 'XY':
1620 | dirVector.copy( unitZ );
1621 | break;
1622 | case 'YZ':
1623 | dirVector.copy( unitX );
1624 | break;
1625 | case 'XZ':
1626 | alignVector.copy( unitZ );
1627 | dirVector.copy( unitY );
1628 | break;
1629 | case 'XYZ':
1630 | case 'E':
1631 | dirVector.set( 0, 0, 0 );
1632 | break;
1633 |
1634 | }
1635 |
1636 | break;
1637 | case 'rotate':
1638 | default:
1639 | // special case for rotate
1640 | dirVector.set( 0, 0, 0 );
1641 |
1642 | }
1643 |
1644 | if ( dirVector.length() === 0 ) {
1645 |
1646 | // If in rotate mode, make the plane parallel to camera
1647 | this.quaternion.copy( this.cameraQuaternion );
1648 |
1649 | } else {
1650 |
1651 | tempMatrix.lookAt( tempVector.set( 0, 0, 0 ), dirVector, alignVector );
1652 |
1653 | this.quaternion.setFromRotationMatrix( tempMatrix );
1654 |
1655 | }
1656 |
1657 | Object3D.prototype.updateMatrixWorld.call( this );
1658 |
1659 | };
1660 |
1661 | };
1662 |
1663 | TransformControlsPlane.prototype = Object.assign( Object.create( Mesh.prototype ), {
1664 |
1665 | constructor: TransformControlsPlane,
1666 |
1667 | isTransformControlsPlane: true
1668 |
1669 | } );
1670 |
1671 |
--------------------------------------------------------------------------------
/public/lib/htmltoimage.js:
--------------------------------------------------------------------------------
1 | !function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).htmlToImage={})}(this,(function(t){"use strict";
2 | /*! *****************************************************************************
3 | Copyright (c) Microsoft Corporation.
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted.
7 |
8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 | PERFORMANCE OF THIS SOFTWARE.
15 | ***************************************************************************** */function n(t,n,e,r){return new(e||(e=Promise))((function(o,i){function u(t){try{f(r.next(t))}catch(t){i(t)}}function c(t){try{f(r.throw(t))}catch(t){i(t)}}function f(t){var n;t.done?o(t.value):(n=t.value,n instanceof e?n:new e((function(t){t(n)}))).then(u,c)}f((r=r.apply(t,n||[])).next())}))}function e(t,n){var e,r,o,i,u={label:0,sent:function(){if(1&o[0])throw o[1];return o[1]},trys:[],ops:[]};return i={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function c(i){return function(c){return function(i){if(e)throw new TypeError("Generator is already executing.");for(;u;)try{if(e=1,r&&(o=2&i[0]?r.return:i[0]?r.throw||((o=r.return)&&o.call(r),0):r.next)&&!(o=o.call(r,i[1])).done)return o;switch(r=0,o&&(i=[2&i[0],o.value]),i[0]){case 0:case 1:o=i;break;case 4:return u.label++,{value:i[1],done:!1};case 5:u.label++,r=i[1],i=[0];continue;case 7:i=u.ops.pop(),u.trys.pop();continue;default:if(!(o=u.trys,(o=o.length>0&&o[o.length-1])||6!==i[0]&&2!==i[0])){u=0;continue}if(3===i[0]&&(!o||i[1]>o[0]&&i[1]O||t.height>O)&&(t.width>O&&t.height>O?t.width>t.height?(t.height*=O/t.width,t.width=O):(t.width*=O/t.height,t.height=O):t.width>O?(t.height*=O/t.width,t.width=O):(t.width*=O/t.height,t.height=O))}(e),e.style.width=""+a,e.style.height=""+s,r.backgroundColor&&(o.fillStyle=r.backgroundColor,o.fillRect(0,0,e.width,e.height)),o.drawImage(n,0,0,e.width,e.height),e}))]}))}))}t.getFontEmbedCSS=function(t,r){return void 0===r&&(r={}),n(this,void 0,void 0,(function(){return e(this,(function(n){return[2,j(t,r)]}))}))},t.toBlob=function(t,r){return void 0===r&&(r={}),n(this,void 0,void 0,(function(){return e(this,(function(n){return[2,$(t,r).then(d)]}))}))},t.toCanvas=$,t.toJpeg=function(t,r){return void 0===r&&(r={}),n(this,void 0,void 0,(function(){return e(this,(function(n){return[2,$(t,r).then((function(t){return t.toDataURL("image/jpeg",r.quality||1)}))]}))}))},t.toPixelData=function(t,r){return void 0===r&&(r={}),n(this,void 0,void 0,(function(){var n,o,i;return e(this,(function(e){return n=F(t,r),o=n.width,i=n.height,[2,$(t,r).then((function(t){return t.getContext("2d").getImageData(0,0,o,i).data}))]}))}))},t.toPng=function(t,r){return void 0===r&&(r={}),n(this,void 0,void 0,(function(){return e(this,(function(n){return[2,$(t,r).then((function(t){return t.toDataURL()}))]}))}))},t.toSvg=B,Object.defineProperty(t,"__esModule",{value:!0})}));
16 | //# sourceMappingURL=html-to-image.js.map
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config()
2 | const express = require('express');
3 | const app = express();
4 | const http = require('http');
5 | const server = http.createServer(app);
6 | var nodemailer = require('nodemailer');
7 | var transporter = nodemailer.createTransport({
8 | service: 'gmail',
9 | auth: {
10 | user: process.env.EMAIL,
11 | pass: process.env.PASSWORD
12 | }
13 | });
14 | var email;
15 | app.use(express.static('public'));
16 | var io = require('socket.io')(server);
17 | io.sockets.on('connection',
18 | function (socket) {
19 | console.log("We have a new client: " + socket.id);
20 | socket.on('email',
21 | function(data) {
22 | var data=JSON.parse(data)
23 | email=data.email;
24 | var name=data.name
25 | var content=JSON.stringify(data.data).split(",")[1]
26 | var mailOptions = {
27 | from: 'greenlab.attestation.ziko.js@gmail.com',
28 | to: email,
29 | subject: 'Greenlab Certificate',
30 | html: 'Hello Congratulations You have successfully completed the course
',
31 | attachments: [{
32 | filename: name+".pdf",
33 | contentType: 'application/pdf',
34 | content:content,
35 | encoding: 'base64',
36 | }]
37 | };
38 | transporter.sendMail(mailOptions, function(error, info){
39 | if (error) {
40 | console.log(error);
41 | } else {
42 | console.log('Email sent to : ' + email);
43 | socket.emit("succed",'Email sent to : ' + email +" on "+new Date().toDateString()+" at "+new Date().toTimeString())
44 | }
45 | });
46 | }
47 | );
48 | })
49 |
50 |
51 | server.listen(3000, () => {
52 | console.log('listening on *:3000');
53 | });
54 |
--------------------------------------------------------------------------------
/video.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zakarialaoui10/greenlab-challenge/83eedc50b5bf7940087867d894efa91c1d249b26/video.mp4
--------------------------------------------------------------------------------