├── .gitignore
├── .npmignore
├── README.md
├── index.html
├── package-lock.json
├── package.json
├── screenshot.png
├── server.js
└── sketch.js
/.gitignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | *.log
4 | .DS_Store
5 | bundle.js
6 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | bower_components
2 | node_modules
3 | *.log
4 | .DS_Store
5 | bundle.js
6 | test
7 | test.js
8 | demo/
9 | .npmignore
10 | LICENSE.md
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # euclid-space
2 |
3 | A collaborative shape drawing app with colours inspired by [Oliver Byrne's edition of Euclid](https://www.math.ubc.ca/~cass/Euclid/byrne.html) and the [Nicholas Rougeax's interactive Euclid website](https://www.c82.net/euclid/).
4 |
5 | When a user joins, they are assigned a random shape and colour which they can place around the document.
6 |
7 | Play it here:
8 |
9 | https://gentle-depths-69960.herokuapp.com/
10 |
11 |
12 |
13 | # tools
14 |
15 | - [p5.js](https://p5js.org) for drawing
16 | - [socket.io](https://socket.io/) for networking
17 |
18 | # running locally
19 |
20 | To run it locally, first clone and install dependencies:
21 |
22 | ```sh
23 | git clone https://github.com/mattdesl/euclid-space.git
24 |
25 | cd euclid-space
26 |
27 | npm install
28 | ```
29 |
30 | Then you can run the server to start the app:
31 |
32 | ```sh
33 | npm run start
34 | ```
35 |
36 | Then open [http://localhost:3000/](http://localhost:3000/) in a few different browser tabs and click around to see the different shapes.
37 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | sketch
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "euclid-space",
3 | "version": "0.0.1",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "accepts": {
8 | "version": "1.3.7",
9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
10 | "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
11 | "requires": {
12 | "mime-types": "~2.1.24",
13 | "negotiator": "0.6.2"
14 | }
15 | },
16 | "after": {
17 | "version": "0.8.2",
18 | "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
19 | "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8="
20 | },
21 | "array-flatten": {
22 | "version": "1.1.1",
23 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
24 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
25 | },
26 | "arraybuffer.slice": {
27 | "version": "0.0.7",
28 | "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
29 | "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog=="
30 | },
31 | "async-limiter": {
32 | "version": "1.0.1",
33 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
34 | "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
35 | },
36 | "backo2": {
37 | "version": "1.0.2",
38 | "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
39 | "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc="
40 | },
41 | "base64-arraybuffer": {
42 | "version": "0.1.5",
43 | "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
44 | "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
45 | },
46 | "base64id": {
47 | "version": "2.0.0",
48 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
49 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog=="
50 | },
51 | "better-assert": {
52 | "version": "1.0.2",
53 | "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
54 | "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
55 | "requires": {
56 | "callsite": "1.0.0"
57 | }
58 | },
59 | "blob": {
60 | "version": "0.0.5",
61 | "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
62 | "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig=="
63 | },
64 | "body-parser": {
65 | "version": "1.19.0",
66 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
67 | "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
68 | "requires": {
69 | "bytes": "3.1.0",
70 | "content-type": "~1.0.4",
71 | "debug": "2.6.9",
72 | "depd": "~1.1.2",
73 | "http-errors": "1.7.2",
74 | "iconv-lite": "0.4.24",
75 | "on-finished": "~2.3.0",
76 | "qs": "6.7.0",
77 | "raw-body": "2.4.0",
78 | "type-is": "~1.6.17"
79 | }
80 | },
81 | "bytes": {
82 | "version": "3.1.0",
83 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
84 | "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg=="
85 | },
86 | "callsite": {
87 | "version": "1.0.0",
88 | "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
89 | "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
90 | },
91 | "component-bind": {
92 | "version": "1.0.0",
93 | "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
94 | "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E="
95 | },
96 | "component-emitter": {
97 | "version": "1.2.1",
98 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
99 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY="
100 | },
101 | "component-inherit": {
102 | "version": "0.0.3",
103 | "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
104 | "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM="
105 | },
106 | "content-disposition": {
107 | "version": "0.5.3",
108 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
109 | "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
110 | "requires": {
111 | "safe-buffer": "5.1.2"
112 | }
113 | },
114 | "content-type": {
115 | "version": "1.0.4",
116 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
117 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
118 | },
119 | "cookie": {
120 | "version": "0.4.0",
121 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
122 | "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
123 | },
124 | "cookie-signature": {
125 | "version": "1.0.6",
126 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
127 | "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw="
128 | },
129 | "debug": {
130 | "version": "2.6.9",
131 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
132 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
133 | "requires": {
134 | "ms": "2.0.0"
135 | }
136 | },
137 | "depd": {
138 | "version": "1.1.2",
139 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
140 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
141 | },
142 | "destroy": {
143 | "version": "1.0.4",
144 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
145 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
146 | },
147 | "ee-first": {
148 | "version": "1.1.1",
149 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
150 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
151 | },
152 | "encodeurl": {
153 | "version": "1.0.2",
154 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
155 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
156 | },
157 | "engine.io": {
158 | "version": "3.4.0",
159 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.0.tgz",
160 | "integrity": "sha512-XCyYVWzcHnK5cMz7G4VTu2W7zJS7SM1QkcelghyIk/FmobWBtXE7fwhBusEKvCSqc3bMh8fNFMlUkCKTFRxH2w==",
161 | "requires": {
162 | "accepts": "~1.3.4",
163 | "base64id": "2.0.0",
164 | "cookie": "0.3.1",
165 | "debug": "~4.1.0",
166 | "engine.io-parser": "~2.2.0",
167 | "ws": "^7.1.2"
168 | },
169 | "dependencies": {
170 | "cookie": {
171 | "version": "0.3.1",
172 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
173 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
174 | },
175 | "debug": {
176 | "version": "4.1.1",
177 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
178 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
179 | "requires": {
180 | "ms": "^2.1.1"
181 | }
182 | },
183 | "ms": {
184 | "version": "2.1.2",
185 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
186 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
187 | }
188 | }
189 | },
190 | "engine.io-client": {
191 | "version": "3.4.0",
192 | "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.0.tgz",
193 | "integrity": "sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA==",
194 | "requires": {
195 | "component-emitter": "1.2.1",
196 | "component-inherit": "0.0.3",
197 | "debug": "~4.1.0",
198 | "engine.io-parser": "~2.2.0",
199 | "has-cors": "1.1.0",
200 | "indexof": "0.0.1",
201 | "parseqs": "0.0.5",
202 | "parseuri": "0.0.5",
203 | "ws": "~6.1.0",
204 | "xmlhttprequest-ssl": "~1.5.4",
205 | "yeast": "0.1.2"
206 | },
207 | "dependencies": {
208 | "debug": {
209 | "version": "4.1.1",
210 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
211 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
212 | "requires": {
213 | "ms": "^2.1.1"
214 | }
215 | },
216 | "ms": {
217 | "version": "2.1.2",
218 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
219 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
220 | },
221 | "ws": {
222 | "version": "6.1.4",
223 | "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
224 | "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
225 | "requires": {
226 | "async-limiter": "~1.0.0"
227 | }
228 | }
229 | }
230 | },
231 | "engine.io-parser": {
232 | "version": "2.2.0",
233 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz",
234 | "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==",
235 | "requires": {
236 | "after": "0.8.2",
237 | "arraybuffer.slice": "~0.0.7",
238 | "base64-arraybuffer": "0.1.5",
239 | "blob": "0.0.5",
240 | "has-binary2": "~1.0.2"
241 | }
242 | },
243 | "escape-html": {
244 | "version": "1.0.3",
245 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
246 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg="
247 | },
248 | "etag": {
249 | "version": "1.8.1",
250 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
251 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
252 | },
253 | "express": {
254 | "version": "4.17.1",
255 | "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
256 | "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
257 | "requires": {
258 | "accepts": "~1.3.7",
259 | "array-flatten": "1.1.1",
260 | "body-parser": "1.19.0",
261 | "content-disposition": "0.5.3",
262 | "content-type": "~1.0.4",
263 | "cookie": "0.4.0",
264 | "cookie-signature": "1.0.6",
265 | "debug": "2.6.9",
266 | "depd": "~1.1.2",
267 | "encodeurl": "~1.0.2",
268 | "escape-html": "~1.0.3",
269 | "etag": "~1.8.1",
270 | "finalhandler": "~1.1.2",
271 | "fresh": "0.5.2",
272 | "merge-descriptors": "1.0.1",
273 | "methods": "~1.1.2",
274 | "on-finished": "~2.3.0",
275 | "parseurl": "~1.3.3",
276 | "path-to-regexp": "0.1.7",
277 | "proxy-addr": "~2.0.5",
278 | "qs": "6.7.0",
279 | "range-parser": "~1.2.1",
280 | "safe-buffer": "5.1.2",
281 | "send": "0.17.1",
282 | "serve-static": "1.14.1",
283 | "setprototypeof": "1.1.1",
284 | "statuses": "~1.5.0",
285 | "type-is": "~1.6.18",
286 | "utils-merge": "1.0.1",
287 | "vary": "~1.1.2"
288 | }
289 | },
290 | "finalhandler": {
291 | "version": "1.1.2",
292 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
293 | "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
294 | "requires": {
295 | "debug": "2.6.9",
296 | "encodeurl": "~1.0.2",
297 | "escape-html": "~1.0.3",
298 | "on-finished": "~2.3.0",
299 | "parseurl": "~1.3.3",
300 | "statuses": "~1.5.0",
301 | "unpipe": "~1.0.0"
302 | }
303 | },
304 | "forwarded": {
305 | "version": "0.1.2",
306 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
307 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ="
308 | },
309 | "fresh": {
310 | "version": "0.5.2",
311 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
312 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
313 | },
314 | "has-binary2": {
315 | "version": "1.0.3",
316 | "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
317 | "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
318 | "requires": {
319 | "isarray": "2.0.1"
320 | }
321 | },
322 | "has-cors": {
323 | "version": "1.1.0",
324 | "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
325 | "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk="
326 | },
327 | "http-errors": {
328 | "version": "1.7.2",
329 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
330 | "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
331 | "requires": {
332 | "depd": "~1.1.2",
333 | "inherits": "2.0.3",
334 | "setprototypeof": "1.1.1",
335 | "statuses": ">= 1.5.0 < 2",
336 | "toidentifier": "1.0.0"
337 | }
338 | },
339 | "iconv-lite": {
340 | "version": "0.4.24",
341 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
342 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
343 | "requires": {
344 | "safer-buffer": ">= 2.1.2 < 3"
345 | }
346 | },
347 | "indexof": {
348 | "version": "0.0.1",
349 | "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
350 | "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10="
351 | },
352 | "inherits": {
353 | "version": "2.0.3",
354 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
355 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
356 | },
357 | "ipaddr.js": {
358 | "version": "1.9.0",
359 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
360 | "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA=="
361 | },
362 | "isarray": {
363 | "version": "2.0.1",
364 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
365 | "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
366 | },
367 | "media-typer": {
368 | "version": "0.3.0",
369 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
370 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
371 | },
372 | "merge-descriptors": {
373 | "version": "1.0.1",
374 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
375 | "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E="
376 | },
377 | "methods": {
378 | "version": "1.1.2",
379 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
380 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4="
381 | },
382 | "mime": {
383 | "version": "1.6.0",
384 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
385 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
386 | },
387 | "mime-db": {
388 | "version": "1.40.0",
389 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
390 | "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
391 | },
392 | "mime-types": {
393 | "version": "2.1.24",
394 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
395 | "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
396 | "requires": {
397 | "mime-db": "1.40.0"
398 | }
399 | },
400 | "ms": {
401 | "version": "2.0.0",
402 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
403 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
404 | },
405 | "negotiator": {
406 | "version": "0.6.2",
407 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
408 | "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
409 | },
410 | "object-component": {
411 | "version": "0.0.3",
412 | "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
413 | "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE="
414 | },
415 | "on-finished": {
416 | "version": "2.3.0",
417 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
418 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
419 | "requires": {
420 | "ee-first": "1.1.1"
421 | }
422 | },
423 | "parseqs": {
424 | "version": "0.0.5",
425 | "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
426 | "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
427 | "requires": {
428 | "better-assert": "~1.0.0"
429 | }
430 | },
431 | "parseuri": {
432 | "version": "0.0.5",
433 | "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
434 | "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
435 | "requires": {
436 | "better-assert": "~1.0.0"
437 | }
438 | },
439 | "parseurl": {
440 | "version": "1.3.3",
441 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
442 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
443 | },
444 | "path-to-regexp": {
445 | "version": "0.1.7",
446 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
447 | "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
448 | },
449 | "proxy-addr": {
450 | "version": "2.0.5",
451 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
452 | "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
453 | "requires": {
454 | "forwarded": "~0.1.2",
455 | "ipaddr.js": "1.9.0"
456 | }
457 | },
458 | "qs": {
459 | "version": "6.7.0",
460 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
461 | "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
462 | },
463 | "range-parser": {
464 | "version": "1.2.1",
465 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
466 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
467 | },
468 | "raw-body": {
469 | "version": "2.4.0",
470 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
471 | "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
472 | "requires": {
473 | "bytes": "3.1.0",
474 | "http-errors": "1.7.2",
475 | "iconv-lite": "0.4.24",
476 | "unpipe": "1.0.0"
477 | }
478 | },
479 | "safe-buffer": {
480 | "version": "5.1.2",
481 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
482 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
483 | },
484 | "safer-buffer": {
485 | "version": "2.1.2",
486 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
487 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
488 | },
489 | "send": {
490 | "version": "0.17.1",
491 | "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
492 | "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
493 | "requires": {
494 | "debug": "2.6.9",
495 | "depd": "~1.1.2",
496 | "destroy": "~1.0.4",
497 | "encodeurl": "~1.0.2",
498 | "escape-html": "~1.0.3",
499 | "etag": "~1.8.1",
500 | "fresh": "0.5.2",
501 | "http-errors": "~1.7.2",
502 | "mime": "1.6.0",
503 | "ms": "2.1.1",
504 | "on-finished": "~2.3.0",
505 | "range-parser": "~1.2.1",
506 | "statuses": "~1.5.0"
507 | },
508 | "dependencies": {
509 | "ms": {
510 | "version": "2.1.1",
511 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
512 | "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
513 | }
514 | }
515 | },
516 | "serve-static": {
517 | "version": "1.14.1",
518 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
519 | "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
520 | "requires": {
521 | "encodeurl": "~1.0.2",
522 | "escape-html": "~1.0.3",
523 | "parseurl": "~1.3.3",
524 | "send": "0.17.1"
525 | }
526 | },
527 | "setprototypeof": {
528 | "version": "1.1.1",
529 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
530 | "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw=="
531 | },
532 | "socket.io": {
533 | "version": "2.3.0",
534 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
535 | "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
536 | "requires": {
537 | "debug": "~4.1.0",
538 | "engine.io": "~3.4.0",
539 | "has-binary2": "~1.0.2",
540 | "socket.io-adapter": "~1.1.0",
541 | "socket.io-client": "2.3.0",
542 | "socket.io-parser": "~3.4.0"
543 | },
544 | "dependencies": {
545 | "debug": {
546 | "version": "4.1.1",
547 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
548 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
549 | "requires": {
550 | "ms": "^2.1.1"
551 | }
552 | },
553 | "ms": {
554 | "version": "2.1.2",
555 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
556 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
557 | }
558 | }
559 | },
560 | "socket.io-adapter": {
561 | "version": "1.1.1",
562 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.1.tgz",
563 | "integrity": "sha1-KoBeihTWNyEk3ZFZrUUC+MsH8Gs="
564 | },
565 | "socket.io-client": {
566 | "version": "2.3.0",
567 | "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
568 | "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
569 | "requires": {
570 | "backo2": "1.0.2",
571 | "base64-arraybuffer": "0.1.5",
572 | "component-bind": "1.0.0",
573 | "component-emitter": "1.2.1",
574 | "debug": "~4.1.0",
575 | "engine.io-client": "~3.4.0",
576 | "has-binary2": "~1.0.2",
577 | "has-cors": "1.1.0",
578 | "indexof": "0.0.1",
579 | "object-component": "0.0.3",
580 | "parseqs": "0.0.5",
581 | "parseuri": "0.0.5",
582 | "socket.io-parser": "~3.3.0",
583 | "to-array": "0.1.4"
584 | },
585 | "dependencies": {
586 | "debug": {
587 | "version": "4.1.1",
588 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
589 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
590 | "requires": {
591 | "ms": "^2.1.1"
592 | }
593 | },
594 | "ms": {
595 | "version": "2.1.2",
596 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
597 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
598 | },
599 | "socket.io-parser": {
600 | "version": "3.3.0",
601 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz",
602 | "integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==",
603 | "requires": {
604 | "component-emitter": "1.2.1",
605 | "debug": "~3.1.0",
606 | "isarray": "2.0.1"
607 | },
608 | "dependencies": {
609 | "debug": {
610 | "version": "3.1.0",
611 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
612 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
613 | "requires": {
614 | "ms": "2.0.0"
615 | }
616 | },
617 | "ms": {
618 | "version": "2.0.0",
619 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
620 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
621 | }
622 | }
623 | }
624 | }
625 | },
626 | "socket.io-parser": {
627 | "version": "3.4.0",
628 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.0.tgz",
629 | "integrity": "sha512-/G/VOI+3DBp0+DJKW4KesGnQkQPFmUCbA/oO2QGT6CWxU7hLGWqU3tyuzeSK/dqcyeHsQg1vTe9jiZI8GU9SCQ==",
630 | "requires": {
631 | "component-emitter": "1.2.1",
632 | "debug": "~4.1.0",
633 | "isarray": "2.0.1"
634 | },
635 | "dependencies": {
636 | "debug": {
637 | "version": "4.1.1",
638 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
639 | "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
640 | "requires": {
641 | "ms": "^2.1.1"
642 | }
643 | },
644 | "ms": {
645 | "version": "2.1.2",
646 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
647 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
648 | }
649 | }
650 | },
651 | "statuses": {
652 | "version": "1.5.0",
653 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
654 | "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
655 | },
656 | "to-array": {
657 | "version": "0.1.4",
658 | "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
659 | "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA="
660 | },
661 | "toidentifier": {
662 | "version": "1.0.0",
663 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
664 | "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
665 | },
666 | "type-is": {
667 | "version": "1.6.18",
668 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
669 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
670 | "requires": {
671 | "media-typer": "0.3.0",
672 | "mime-types": "~2.1.24"
673 | }
674 | },
675 | "unpipe": {
676 | "version": "1.0.0",
677 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
678 | "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
679 | },
680 | "utils-merge": {
681 | "version": "1.0.1",
682 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
683 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
684 | },
685 | "vary": {
686 | "version": "1.1.2",
687 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
688 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw="
689 | },
690 | "ws": {
691 | "version": "7.1.2",
692 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.1.2.tgz",
693 | "integrity": "sha512-gftXq3XI81cJCgkUiAVixA0raD9IVmXqsylCrjRygw4+UOOGzPoxnQ6r/CnVL9i+mDncJo94tSkyrtuuQVBmrg==",
694 | "requires": {
695 | "async-limiter": "^1.0.0"
696 | }
697 | },
698 | "xmlhttprequest-ssl": {
699 | "version": "1.5.5",
700 | "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
701 | "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4="
702 | },
703 | "yeast": {
704 | "version": "0.1.2",
705 | "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
706 | "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk="
707 | }
708 | }
709 | }
710 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "euclid-space",
3 | "version": "0.0.1",
4 | "description": "A simple Node app built on Express, instantly up and running.",
5 | "main": "server.js",
6 | "scripts": {
7 | "start": "node server.js"
8 | },
9 | "dependencies": {
10 | "express": "^4.16.4",
11 | "socket.io": "^2.3.0"
12 | },
13 | "engines": {
14 | "node": "8.x"
15 | },
16 | "repository": {},
17 | "license": "MIT",
18 | "keywords": [
19 | "node",
20 | "glitch",
21 | "express"
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mattdesl/euclid-space/cdde956ca6cec7cf8fcc253bb6268d0f89c9687c/screenshot.png
--------------------------------------------------------------------------------
/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express');
2 |
3 | // create an express app
4 | const app = express();
5 |
6 | // create a server
7 | const server = require('http').createServer(app);
8 |
9 | // attach socket.io to the server
10 | const io = require('socket.io').listen(server);
11 |
12 | const port = process.env.PORT || 3000;
13 |
14 | // Serve the current directory as a static website
15 | // This allows us to use HTML, JavaScript, etc
16 | app.use(express.static(__dirname));
17 |
18 | // Start listening on port 3000
19 | server.listen(port, () => {
20 | // Print to console just so we know its ready to go...
21 | console.log(`Server listening on http://localhost:${port}/`);
22 | });
23 |
24 | // ----------------------------------
25 | // Below we setup our networking code
26 | // ----------------------------------
27 |
28 | // Max # of shapes in the system at any point
29 | const MAX_SHAPES = 500;
30 |
31 | // each user is assigned a random colour
32 | const colors = [ '#0a0607', '#ffbe00', '#006697', '#fa0001' ];
33 | const types = [ 'circle', 'semicircle', 'arc', 'rect', 'triangle' ];
34 | const rotations = [ 0, Math.PI / 2, Math.PI, Math.PI * 1.5, Math.PI * 2 ];
35 |
36 | // The list of all shapes contained in our app
37 | let shapes = [];
38 |
39 | // Create a socket.io connection
40 | io.on('connection', socket => {
41 | // For the user, select some random properties
42 | const color = colors[Math.floor(Math.random() * colors.length)];
43 | const type = types[Math.floor(Math.random() * types.length)];
44 | const fill = Math.random() > 0.5;
45 | const rotation = rotations[Math.floor(Math.random() * rotations.length)];
46 |
47 | // When the user first connects we will show them everything on screen
48 | socket.emit('shapes', shapes);
49 |
50 | // When the user adds a shape, add it to a list
51 | socket.on('add', position => {
52 | // If we've hit the limit, remove the oldest shape
53 | if (shapes.length > MAX_SHAPES) {
54 | shapes.shift();
55 | }
56 |
57 | // Make a new shape object
58 | const shape = {
59 | color,
60 | fill,
61 | type,
62 | rotation,
63 | x: position.x,
64 | y: position.y
65 | };
66 |
67 | // Push the shape into our list of all shapes
68 | shapes.push(shape);
69 |
70 | // Now we tell ALL users that the shapes have updated
71 | io.emit('shapes', shapes);
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/sketch.js:
--------------------------------------------------------------------------------
1 | const socket = io();
2 |
3 | let shapes = [];
4 |
5 | function setup () {
6 | createCanvas(windowWidth, windowHeight);
7 |
8 | // Received a new shape list from the server
9 | socket.on('shapes', updateShapes);
10 | }
11 |
12 | function updateShapes (newShapes) {
13 | console.log(newShapes);
14 | shapes = newShapes;
15 | }
16 |
17 | function windowResized () {
18 | resizeCanvas(windowWidth, windowHeight);
19 | }
20 |
21 | function draw () {
22 | background('#fff2d5');
23 | strokeWeight(1);
24 |
25 | for (let i = 0; i < shapes.length; i++) {
26 | // disable both fill and stroke
27 | noStroke();
28 | noFill();
29 |
30 | // current shape
31 | const shape = shapes[i];
32 |
33 | // get color and set it
34 | const color = shape.color;
35 |
36 | // user can be filled or stroked
37 | if (shape.fill) fill(color);
38 | else stroke(color);
39 |
40 | const minDim = min(width, height);
41 | const x = shape.x * width;
42 | const y = shape.y * height;
43 | const size = minDim * 0.05;
44 |
45 | const rotation = shape.rotation;
46 |
47 | drawShape(shape.type, x, y, size, rotation);
48 | }
49 | }
50 |
51 | function mousePressed () {
52 | // provide the mouseX and mouseY positions between 0..1
53 | socket.emit('add', {
54 | x: mouseX / width,
55 | y: mouseY / height
56 | });
57 | }
58 |
59 | function drawShape (type, x, y, size, rotation = 0) {
60 | push();
61 |
62 | translate(x, y);
63 | rotate(rotation);
64 | translate(-x, -y);
65 |
66 | // draw shapes
67 | if (type === 'circle') {
68 | circle(x, y, size);
69 | } else if (type === 'rect') {
70 | rectMode(CENTER);
71 | rect(x, y, size, size);
72 | } else if (type === 'triangle') {
73 | const halfSize = size / 2;
74 | triangle(
75 | x - halfSize, y - halfSize,
76 | x + halfSize, y + halfSize,
77 | x - halfSize, y + halfSize
78 | );
79 | } else if (type === 'arc') {
80 | arc(x, y, size, size, 0, PI / 2, PIE);
81 | } else if (type === 'semicircle') {
82 | arc(x, y, size, size, 0, PI, PIE);
83 | }
84 |
85 | pop();
86 | }
--------------------------------------------------------------------------------