three-stdlib 2.19.1 → 2.20.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("three");class t extends e.Mesh{constructor(t){const i=new o(t),s=new e.PlaneGeometry(.001*i.image.width,.001*i.image.height),r=new e.MeshBasicMaterial({map:i,toneMapped:!1,transparent:!0});function l(e){r.map.dispatchDOMEvent(e)}super(s,r),this.addEventListener("mousedown",l),this.addEventListener("mousemove",l),this.addEventListener("mouseup",l),this.addEventListener("click",l),this.dispose=function(){s.dispose(),r.dispose(),r.map.dispose(),n.delete(t),this.removeEventListener("mousedown",l),this.removeEventListener("mousemove",l),this.removeEventListener("mouseup",l),this.removeEventListener("click",l)}}}class o extends e.CanvasTexture{constructor(t){super(i(t)),this.dom=t,this.anisotropy=16,this.encoding=e.sRGBEncoding,this.minFilter=e.LinearFilter,this.magFilter=e.LinearFilter;const o=new MutationObserver((()=>{this.scheduleUpdate||(this.scheduleUpdate=setTimeout((()=>this.update()),16))}));o.observe(t,{attributes:!0,childList:!0,subtree:!0,characterData:!0}),this.observer=o}dispatchDOMEvent(e){e.data&&function(e,t,o,n){const i={clientX:o*e.offsetWidth+e.offsetLeft,clientY:n*e.offsetHeight+e.offsetTop,view:e.ownerDocument.defaultView};window.dispatchEvent(new MouseEvent(t,i));const s=e.getBoundingClientRect();function r(e){if(e.nodeType!==Node.TEXT_NODE&&e.nodeType!==Node.COMMENT_NODE){const s=e.getBoundingClientRect();if(o>s.left&&o<s.right&&n>s.top&&n<s.bottom&&(e.dispatchEvent(new MouseEvent(t,i)),e instanceof HTMLInputElement&&"range"===e.type&&("mousedown"===t||"click"===t))){const[t,n]=["min","max"].map((t=>parseFloat(e[t]))),i=s.width,r=(o-s.x)/i;e.value=t+(n-t)*r,e.dispatchEvent(new InputEvent("input",{bubbles:!0}))}for(let t=0;t<e.childNodes.length;t++)r(e.childNodes[t])}}o=o*s.width+s.left,n=n*s.height+s.top,r(e)}(this.dom,e.type,e.data.x,e.data.y)}update(){this.image=i(this.dom),this.needsUpdate=!0,this.scheduleUpdate=null}dispose(){this.observer&&this.observer.disconnect(),this.scheduleUpdate=clearTimeout(this.scheduleUpdate),super.dispose()}}const n=new WeakMap;function i(t){const o=document.createRange(),i=new e.Color;function s(e,t,o,n){""!==n&&("uppercase"===e.textTransform&&(n=n.toUpperCase()),c.font=e.fontWeight+" "+e.fontSize+" "+e.fontFamily,c.textBaseline="top",c.fillStyle=e.color,c.fillText(n,t,o+.1*parseFloat(e.fontSize)))}function r(e,t,o,n,i){o<2*i&&(i=o/2),n<2*i&&(i=n/2),c.beginPath(),c.moveTo(e+i,t),c.arcTo(e+o,t,e+o,t+n,i),c.arcTo(e+o,t+n,e,t+n,i),c.arcTo(e,t+n,e,t,i),c.arcTo(e,t,e+o,t,i),c.closePath()}function l(e,t,o,n,i,s){const r=e[t+"Width"],l=e[t+"Style"],d=e[t+"Color"];"0px"!==r&&"none"!==l&&"transparent"!==d&&"rgba(0, 0, 0, 0)"!==d&&(c.strokeStyle=d,c.lineWidth=parseFloat(r),c.beginPath(),c.moveTo(o,n),c.lineTo(o+i,n+s),c.stroke())}const d=t.getBoundingClientRect();let a=n.get(t);void 0===a&&(a=document.createElement("canvas"),a.width=d.width,a.height=d.height,n.set(t,a));const c=a.getContext("2d"),h=new function(e){const t=[];let o=!1;function n(){if(o&&(o=!1,e.restore()),0===t.length)return;let n=-1/0,i=-1/0,s=1/0,r=1/0;for(let e=0;e<t.length;e++){const o=t[e];n=Math.max(n,o.x),i=Math.max(i,o.y),s=Math.min(s,o.x+o.width),r=Math.min(r,o.y+o.height)}e.save(),e.beginPath(),e.rect(n,i,s-n,r-i),e.clip(),o=!0}return{add:function(e){t.push(e),n()},remove:function(){t.pop(),n()}}}(c);return function e(t,n){let a=0,p=0,f=0,u=0;if(t.nodeType===Node.TEXT_NODE){o.selectNode(t);const e=o.getBoundingClientRect();a=e.left-d.left-.5,p=e.top-d.top-.5,f=e.width,u=e.height,s(n,a,p,t.nodeValue.trim())}else{if(t.nodeType===Node.COMMENT_NODE)return;if(t instanceof HTMLCanvasElement){if("none"===t.style.display)return;c.save();const e=window.devicePixelRatio;c.scale(1/e,1/e),c.drawImage(t,0,0),c.restore()}else{if("none"===t.style.display)return;const e=t.getBoundingClientRect();a=e.left-d.left-.5,p=e.top-d.top-.5,f=e.width,u=e.height,n=window.getComputedStyle(t),r(a,p,f,u,parseFloat(n.borderRadius));const o=n.backgroundColor;"transparent"!==o&&"rgba(0, 0, 0, 0)"!==o&&(c.fillStyle=o,c.fill());const m=["borderTop","borderLeft","borderBottom","borderRight"];let g=!0,v=null;for(const e of m){if(null!==v&&(g=n[e+"Width"]===n[v+"Width"]&&n[e+"Color"]===n[v+"Color"]&&n[e+"Style"]===n[v+"Style"]),!1===g)break;v=e}if(!0===g){const e=parseFloat(n.borderTopWidth);"0px"!==n.borderTopWidth&&"none"!==n.borderTopStyle&&"transparent"!==n.borderTopColor&&"rgba(0, 0, 0, 0)"!==n.borderTopColor&&(c.strokeStyle=n.borderTopColor,c.lineWidth=e,c.stroke())}else l(n,"borderTop",a,p,f,0),l(n,"borderLeft",a,p,0,u),l(n,"borderBottom",a,p+u,f,0),l(n,"borderRight",a+f,p,0,u);if(t instanceof HTMLInputElement){let e=n.accentColor;void 0!==e&&"auto"!==e||(e=n.color),i.set(e);const o=Math.sqrt(.299*i.r**2+.587*i.g**2+.114*i.b**2)<.5?"white":"#111111";if("radio"===t.type&&(r(a,p,f,u,u),c.fillStyle="white",c.strokeStyle=e,c.lineWidth=1,c.fill(),c.stroke(),t.checked&&(r(a+2,p+2,f-4,u-4,u),c.fillStyle=e,c.strokeStyle=o,c.lineWidth=2,c.fill(),c.stroke())),"checkbox"===t.type&&(r(a,p,f,u,2),c.fillStyle=t.checked?e:"white",c.strokeStyle=t.checked?o:e,c.lineWidth=1,c.stroke(),c.fill(),t.checked)){const e=c.textAlign;c.textAlign="center";s({color:o,fontFamily:n.fontFamily,fontSize:u+"px",fontWeight:"bold"},a+f/2,p,"✔"),c.textAlign=e}if("range"===t.type){const[n,i,s]=["min","max","value"].map((e=>parseFloat(t[e]))),l=(s-n)/(i-n)*(f-u);r(a,p+u/4,f,u/2,u/4),c.fillStyle=o,c.strokeStyle=e,c.lineWidth=1,c.fill(),c.stroke(),r(a,p+u/4,l+u/2,u/2,u/4),c.fillStyle=e,c.fill(),r(a+l,p,u,u,u/2),c.fillStyle=e,c.fill()}"color"!==t.type&&"text"!==t.type&&"number"!==t.type||(h.add({x:a,y:p,width:f,height:u}),s(n,a+parseInt(n.paddingLeft),p+parseInt(n.paddingTop),t.value),h.remove())}}}const m="auto"===n.overflow||"hidden"===n.overflow;m&&h.add({x:a,y:p,width:f,height:u});for(let o=0;o<t.childNodes.length;o++)e(t.childNodes[o],n);m&&h.remove()}(t),a}exports.HTMLMesh=t;
@@ -0,0 +1,6 @@
1
+ import { Mesh } from 'three';
2
+
3
+ export class HTMLMesh extends Mesh {
4
+ constructor(dom: HTMLElement);
5
+ dispose(): void;
6
+ }
@@ -0,0 +1,405 @@
1
+ import { Mesh, PlaneGeometry, MeshBasicMaterial, CanvasTexture, sRGBEncoding, LinearFilter, Color } from 'three';
2
+
3
+ class HTMLMesh extends Mesh {
4
+ constructor(dom) {
5
+ const texture = new HTMLTexture(dom);
6
+ const geometry = new PlaneGeometry(texture.image.width * 0.001, texture.image.height * 0.001);
7
+ const material = new MeshBasicMaterial({
8
+ map: texture,
9
+ toneMapped: false,
10
+ transparent: true
11
+ });
12
+ super(geometry, material);
13
+
14
+ function onEvent(event) {
15
+ material.map.dispatchDOMEvent(event);
16
+ }
17
+
18
+ this.addEventListener('mousedown', onEvent);
19
+ this.addEventListener('mousemove', onEvent);
20
+ this.addEventListener('mouseup', onEvent);
21
+ this.addEventListener('click', onEvent);
22
+
23
+ this.dispose = function () {
24
+ geometry.dispose();
25
+ material.dispose();
26
+ material.map.dispose();
27
+ canvases.delete(dom);
28
+ this.removeEventListener('mousedown', onEvent);
29
+ this.removeEventListener('mousemove', onEvent);
30
+ this.removeEventListener('mouseup', onEvent);
31
+ this.removeEventListener('click', onEvent);
32
+ };
33
+ }
34
+
35
+ }
36
+
37
+ class HTMLTexture extends CanvasTexture {
38
+ constructor(dom) {
39
+ super(html2canvas(dom));
40
+ this.dom = dom;
41
+ this.anisotropy = 16;
42
+ this.encoding = sRGBEncoding;
43
+ this.minFilter = LinearFilter;
44
+ this.magFilter = LinearFilter; // Create an observer on the DOM, and run html2canvas update in the next loop
45
+
46
+ const observer = new MutationObserver(() => {
47
+ if (!this.scheduleUpdate) {
48
+ // ideally should use xr.requestAnimationFrame, here setTimeout to avoid passing the renderer
49
+ this.scheduleUpdate = setTimeout(() => this.update(), 16);
50
+ }
51
+ });
52
+ const config = {
53
+ attributes: true,
54
+ childList: true,
55
+ subtree: true,
56
+ characterData: true
57
+ };
58
+ observer.observe(dom, config);
59
+ this.observer = observer;
60
+ }
61
+
62
+ dispatchDOMEvent(event) {
63
+ if (event.data) {
64
+ htmlevent(this.dom, event.type, event.data.x, event.data.y);
65
+ }
66
+ }
67
+
68
+ update() {
69
+ this.image = html2canvas(this.dom);
70
+ this.needsUpdate = true;
71
+ this.scheduleUpdate = null;
72
+ }
73
+
74
+ dispose() {
75
+ if (this.observer) {
76
+ this.observer.disconnect();
77
+ }
78
+
79
+ this.scheduleUpdate = clearTimeout(this.scheduleUpdate);
80
+ super.dispose();
81
+ }
82
+
83
+ } //
84
+
85
+
86
+ const canvases = new WeakMap();
87
+
88
+ function html2canvas(element) {
89
+ const range = document.createRange();
90
+ const color = new Color();
91
+
92
+ function Clipper(context) {
93
+ const clips = [];
94
+ let isClipping = false;
95
+
96
+ function doClip() {
97
+ if (isClipping) {
98
+ isClipping = false;
99
+ context.restore();
100
+ }
101
+
102
+ if (clips.length === 0) return;
103
+ let minX = -Infinity,
104
+ minY = -Infinity;
105
+ let maxX = Infinity,
106
+ maxY = Infinity;
107
+
108
+ for (let i = 0; i < clips.length; i++) {
109
+ const clip = clips[i];
110
+ minX = Math.max(minX, clip.x);
111
+ minY = Math.max(minY, clip.y);
112
+ maxX = Math.min(maxX, clip.x + clip.width);
113
+ maxY = Math.min(maxY, clip.y + clip.height);
114
+ }
115
+
116
+ context.save();
117
+ context.beginPath();
118
+ context.rect(minX, minY, maxX - minX, maxY - minY);
119
+ context.clip();
120
+ isClipping = true;
121
+ }
122
+
123
+ return {
124
+ add: function (clip) {
125
+ clips.push(clip);
126
+ doClip();
127
+ },
128
+ remove: function () {
129
+ clips.pop();
130
+ doClip();
131
+ }
132
+ };
133
+ }
134
+
135
+ function drawText(style, x, y, string) {
136
+ if (string !== '') {
137
+ if (style.textTransform === 'uppercase') {
138
+ string = string.toUpperCase();
139
+ }
140
+
141
+ context.font = style.fontWeight + ' ' + style.fontSize + ' ' + style.fontFamily;
142
+ context.textBaseline = 'top';
143
+ context.fillStyle = style.color;
144
+ context.fillText(string, x, y + parseFloat(style.fontSize) * 0.1);
145
+ }
146
+ }
147
+
148
+ function buildRectPath(x, y, w, h, r) {
149
+ if (w < 2 * r) r = w / 2;
150
+ if (h < 2 * r) r = h / 2;
151
+ context.beginPath();
152
+ context.moveTo(x + r, y);
153
+ context.arcTo(x + w, y, x + w, y + h, r);
154
+ context.arcTo(x + w, y + h, x, y + h, r);
155
+ context.arcTo(x, y + h, x, y, r);
156
+ context.arcTo(x, y, x + w, y, r);
157
+ context.closePath();
158
+ }
159
+
160
+ function drawBorder(style, which, x, y, width, height) {
161
+ const borderWidth = style[which + 'Width'];
162
+ const borderStyle = style[which + 'Style'];
163
+ const borderColor = style[which + 'Color'];
164
+
165
+ if (borderWidth !== '0px' && borderStyle !== 'none' && borderColor !== 'transparent' && borderColor !== 'rgba(0, 0, 0, 0)') {
166
+ context.strokeStyle = borderColor;
167
+ context.lineWidth = parseFloat(borderWidth);
168
+ context.beginPath();
169
+ context.moveTo(x, y);
170
+ context.lineTo(x + width, y + height);
171
+ context.stroke();
172
+ }
173
+ }
174
+
175
+ function drawElement(element, style) {
176
+ let x = 0,
177
+ y = 0,
178
+ width = 0,
179
+ height = 0;
180
+
181
+ if (element.nodeType === Node.TEXT_NODE) {
182
+ // text
183
+ range.selectNode(element);
184
+ const rect = range.getBoundingClientRect();
185
+ x = rect.left - offset.left - 0.5;
186
+ y = rect.top - offset.top - 0.5;
187
+ width = rect.width;
188
+ height = rect.height;
189
+ drawText(style, x, y, element.nodeValue.trim());
190
+ } else if (element.nodeType === Node.COMMENT_NODE) {
191
+ return;
192
+ } else if (element instanceof HTMLCanvasElement) {
193
+ // Canvas element
194
+ if (element.style.display === 'none') return;
195
+ context.save();
196
+ const dpr = window.devicePixelRatio;
197
+ context.scale(1 / dpr, 1 / dpr);
198
+ context.drawImage(element, 0, 0);
199
+ context.restore();
200
+ } else {
201
+ if (element.style.display === 'none') return;
202
+ const rect = element.getBoundingClientRect();
203
+ x = rect.left - offset.left - 0.5;
204
+ y = rect.top - offset.top - 0.5;
205
+ width = rect.width;
206
+ height = rect.height;
207
+ style = window.getComputedStyle(element); // Get the border of the element used for fill and border
208
+
209
+ buildRectPath(x, y, width, height, parseFloat(style.borderRadius));
210
+ const backgroundColor = style.backgroundColor;
211
+
212
+ if (backgroundColor !== 'transparent' && backgroundColor !== 'rgba(0, 0, 0, 0)') {
213
+ context.fillStyle = backgroundColor;
214
+ context.fill();
215
+ } // If all the borders match then stroke the round rectangle
216
+
217
+
218
+ const borders = ['borderTop', 'borderLeft', 'borderBottom', 'borderRight'];
219
+ let match = true;
220
+ let prevBorder = null;
221
+
222
+ for (const border of borders) {
223
+ if (prevBorder !== null) {
224
+ match = style[border + 'Width'] === style[prevBorder + 'Width'] && style[border + 'Color'] === style[prevBorder + 'Color'] && style[border + 'Style'] === style[prevBorder + 'Style'];
225
+ }
226
+
227
+ if (match === false) break;
228
+ prevBorder = border;
229
+ }
230
+
231
+ if (match === true) {
232
+ // They all match so stroke the rectangle from before allows for border-radius
233
+ const width = parseFloat(style.borderTopWidth);
234
+
235
+ if (style.borderTopWidth !== '0px' && style.borderTopStyle !== 'none' && style.borderTopColor !== 'transparent' && style.borderTopColor !== 'rgba(0, 0, 0, 0)') {
236
+ context.strokeStyle = style.borderTopColor;
237
+ context.lineWidth = width;
238
+ context.stroke();
239
+ }
240
+ } else {
241
+ // Otherwise draw individual borders
242
+ drawBorder(style, 'borderTop', x, y, width, 0);
243
+ drawBorder(style, 'borderLeft', x, y, 0, height);
244
+ drawBorder(style, 'borderBottom', x, y + height, width, 0);
245
+ drawBorder(style, 'borderRight', x + width, y, 0, height);
246
+ }
247
+
248
+ if (element instanceof HTMLInputElement) {
249
+ let accentColor = style.accentColor;
250
+ if (accentColor === undefined || accentColor === 'auto') accentColor = style.color;
251
+ color.set(accentColor);
252
+ const luminance = Math.sqrt(0.299 * color.r ** 2 + 0.587 * color.g ** 2 + 0.114 * color.b ** 2);
253
+ const accentTextColor = luminance < 0.5 ? 'white' : '#111111';
254
+
255
+ if (element.type === 'radio') {
256
+ buildRectPath(x, y, width, height, height);
257
+ context.fillStyle = 'white';
258
+ context.strokeStyle = accentColor;
259
+ context.lineWidth = 1;
260
+ context.fill();
261
+ context.stroke();
262
+
263
+ if (element.checked) {
264
+ buildRectPath(x + 2, y + 2, width - 4, height - 4, height);
265
+ context.fillStyle = accentColor;
266
+ context.strokeStyle = accentTextColor;
267
+ context.lineWidth = 2;
268
+ context.fill();
269
+ context.stroke();
270
+ }
271
+ }
272
+
273
+ if (element.type === 'checkbox') {
274
+ buildRectPath(x, y, width, height, 2);
275
+ context.fillStyle = element.checked ? accentColor : 'white';
276
+ context.strokeStyle = element.checked ? accentTextColor : accentColor;
277
+ context.lineWidth = 1;
278
+ context.stroke();
279
+ context.fill();
280
+
281
+ if (element.checked) {
282
+ const currentTextAlign = context.textAlign;
283
+ context.textAlign = 'center';
284
+ const properties = {
285
+ color: accentTextColor,
286
+ fontFamily: style.fontFamily,
287
+ fontSize: height + 'px',
288
+ fontWeight: 'bold'
289
+ };
290
+ drawText(properties, x + width / 2, y, '✔');
291
+ context.textAlign = currentTextAlign;
292
+ }
293
+ }
294
+
295
+ if (element.type === 'range') {
296
+ const [min, max, value] = ['min', 'max', 'value'].map(property => parseFloat(element[property]));
297
+ const position = (value - min) / (max - min) * (width - height);
298
+ buildRectPath(x, y + height / 4, width, height / 2, height / 4);
299
+ context.fillStyle = accentTextColor;
300
+ context.strokeStyle = accentColor;
301
+ context.lineWidth = 1;
302
+ context.fill();
303
+ context.stroke();
304
+ buildRectPath(x, y + height / 4, position + height / 2, height / 2, height / 4);
305
+ context.fillStyle = accentColor;
306
+ context.fill();
307
+ buildRectPath(x + position, y, height, height, height / 2);
308
+ context.fillStyle = accentColor;
309
+ context.fill();
310
+ }
311
+
312
+ if (element.type === 'color' || element.type === 'text' || element.type === 'number') {
313
+ clipper.add({
314
+ x: x,
315
+ y: y,
316
+ width: width,
317
+ height: height
318
+ });
319
+ drawText(style, x + parseInt(style.paddingLeft), y + parseInt(style.paddingTop), element.value);
320
+ clipper.remove();
321
+ }
322
+ }
323
+ }
324
+ /*
325
+ // debug
326
+ context.strokeStyle = '#' + Math.random().toString( 16 ).slice( - 3 );
327
+ context.strokeRect( x - 0.5, y - 0.5, width + 1, height + 1 );
328
+ */
329
+
330
+
331
+ const isClipping = style.overflow === 'auto' || style.overflow === 'hidden';
332
+ if (isClipping) clipper.add({
333
+ x: x,
334
+ y: y,
335
+ width: width,
336
+ height: height
337
+ });
338
+
339
+ for (let i = 0; i < element.childNodes.length; i++) {
340
+ drawElement(element.childNodes[i], style);
341
+ }
342
+
343
+ if (isClipping) clipper.remove();
344
+ }
345
+
346
+ const offset = element.getBoundingClientRect();
347
+ let canvas = canvases.get(element);
348
+
349
+ if (canvas === undefined) {
350
+ canvas = document.createElement('canvas');
351
+ canvas.width = offset.width;
352
+ canvas.height = offset.height;
353
+ canvases.set(element, canvas);
354
+ }
355
+
356
+ const context = canvas.getContext('2d'
357
+ /*, { alpha: false }*/
358
+ );
359
+ const clipper = new Clipper(context); // console.time( 'drawElement' );
360
+
361
+ drawElement(element); // console.timeEnd( 'drawElement' );
362
+
363
+ return canvas;
364
+ }
365
+
366
+ function htmlevent(element, event, x, y) {
367
+ const mouseEventInit = {
368
+ clientX: x * element.offsetWidth + element.offsetLeft,
369
+ clientY: y * element.offsetHeight + element.offsetTop,
370
+ view: element.ownerDocument.defaultView
371
+ };
372
+ window.dispatchEvent(new MouseEvent(event, mouseEventInit));
373
+ const rect = element.getBoundingClientRect();
374
+ x = x * rect.width + rect.left;
375
+ y = y * rect.height + rect.top;
376
+
377
+ function traverse(element) {
378
+ if (element.nodeType !== Node.TEXT_NODE && element.nodeType !== Node.COMMENT_NODE) {
379
+ const rect = element.getBoundingClientRect();
380
+
381
+ if (x > rect.left && x < rect.right && y > rect.top && y < rect.bottom) {
382
+ element.dispatchEvent(new MouseEvent(event, mouseEventInit));
383
+
384
+ if (element instanceof HTMLInputElement && element.type === 'range' && (event === 'mousedown' || event === 'click')) {
385
+ const [min, max] = ['min', 'max'].map(property => parseFloat(element[property]));
386
+ const width = rect.width;
387
+ const offsetX = x - rect.x;
388
+ const proportion = offsetX / width;
389
+ element.value = min + (max - min) * proportion;
390
+ element.dispatchEvent(new InputEvent('input', {
391
+ bubbles: true
392
+ }));
393
+ }
394
+ }
395
+
396
+ for (let i = 0; i < element.childNodes.length; i++) {
397
+ traverse(element.childNodes[i]);
398
+ }
399
+ }
400
+ }
401
+
402
+ traverse(element);
403
+ }
404
+
405
+ export { HTMLMesh };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "three-stdlib",
3
- "version": "2.19.1",
3
+ "version": "2.20.0",
4
4
  "private": false,
5
5
  "description": "stand-alone library of threejs examples",
6
6
  "main": "index.cjs.js",