markuno_lib 1.1.24 → 1.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/bus_utils.js +76 -1
- package/bin/markcad.js +505 -1
- package/bin/markcad3d.js +13 -1723
- package/bin/marked.js +21 -1
- package/bin/markuno.js +267 -1
- package/bin/proto.js +184 -1
- package/package.json +5 -2
package/bin/markcad3d.js
CHANGED
|
@@ -1,186 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { PIF, clean, hash, getshape, raccordabezier } from '#cad/markcad.js';
|
|
3
|
-
export { clamp } from '#cad/markcad.js';
|
|
4
|
-
import earcut from 'earcut';
|
|
5
|
-
|
|
6
|
-
const SIDE = THREE.FrontSide; // default sides THREE.DoubleSide/THREE.FrontSide;
|
|
7
|
-
let materialline1 = new THREE.LineBasicMaterial({ color: 0x303030 });
|
|
8
|
-
let materialline2 = new THREE.LineBasicMaterial({ color: 0x505050 });
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const mwhite$1 = new THREE.MeshStandardMaterial({ color: 0xffffff, roughness: 0.5, metalness: 0.4, side: SIDE });
|
|
12
|
-
const mgray1 = new THREE.MeshStandardMaterial({ color: 0x808080, roughness: 0.5, metalness: 0.4, side: SIDE });
|
|
13
|
-
const mgray2 = new THREE.MeshStandardMaterial({ color: 0xb0b0b0, roughness: 0.5, metalness: 0.4, side: SIDE });
|
|
14
|
-
const mred = new THREE.MeshStandardMaterial({ color: 0xff0000, roughness: 0.5, metalness: 0.4, side: SIDE });
|
|
15
|
-
const mblue = new THREE.MeshStandardMaterial({ color: 0x1e40af, roughness: 0.5, metalness: 0.4, side: SIDE });
|
|
16
|
-
const mgreen = new THREE.MeshStandardMaterial({ color: 0x009000, roughness: 0.5, metalness: 0.4, side: SIDE });
|
|
17
|
-
const mblack = new THREE.MeshStandardMaterial({ color: 0x000000, roughness: 0.5, metalness: 0.4, side: SIDE });
|
|
18
|
-
const scaleunit = 1 / 1000;
|
|
19
|
-
|
|
20
|
-
function groupfromgeometry(geometry, material, x, y, z, name, layer) {
|
|
21
|
-
let child = new THREE.Group();
|
|
22
|
-
child.position.set(x, y, z);
|
|
23
|
-
child.name = name;
|
|
24
|
-
|
|
25
|
-
// Crea la mesh principale
|
|
26
|
-
const mesh = new THREE.Mesh(geometry, material);
|
|
27
|
-
mesh.name = name;
|
|
28
|
-
mesh.castShadow = true;
|
|
29
|
-
mesh.receiveShadow = true;
|
|
30
|
-
mesh.layers.set(layer);
|
|
31
|
-
|
|
32
|
-
// Aggiunge i bordi
|
|
33
|
-
const edges = new THREE.EdgesGeometry(geometry);
|
|
34
|
-
const lineSegments = new THREE.LineSegments(edges, materialline1);
|
|
35
|
-
lineSegments.layers.set(30);
|
|
36
|
-
|
|
37
|
-
child.add(mesh);
|
|
38
|
-
child.add(lineSegments);
|
|
39
|
-
return child;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function posiziona(grp, pos = {}) {
|
|
43
|
-
let tm = new THREE.Group();
|
|
44
|
-
if (grp) {
|
|
45
|
-
let g2 = grp.clone();
|
|
46
|
-
let tm2 = g2;
|
|
47
|
-
let { sl = 0, sa = 0, sp = 0, ax = 0, ay = 0, az = 0, ul = 0, ua = 0, up = 0, scale = 1, scx = scale, scy = scale, scz = scale } = pos;
|
|
48
|
-
tm.position.set(sl, sa, sp);
|
|
49
|
-
tm.rotation.set(ax * PIF, ay * PIF, az * PIF);
|
|
50
|
-
if (ul || ua || up) {
|
|
51
|
-
tm2 = new THREE.Group();
|
|
52
|
-
tm2.add(g2);
|
|
53
|
-
tm2.position.set(ul, ua, up);
|
|
54
|
-
}
|
|
55
|
-
tm.scale.set(scx, scy, scz);
|
|
56
|
-
tm.add(tm2);
|
|
57
|
-
}
|
|
58
|
-
return tm;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function edgesfromgeometry(g1, layer = 30) {
|
|
62
|
-
const edges = new THREE.EdgesGeometry(g1, 20);
|
|
63
|
-
return getlinesgeom(edges, layer);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function getlinesgeom(edges, layer = 30) {
|
|
67
|
-
const lineSegments = new THREE.LineSegments(edges, materialline1);
|
|
68
|
-
lineSegments.layers.set(layer);
|
|
69
|
-
return lineSegments;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function getmesh(geom, material, layer = 1, clone = false) {
|
|
73
|
-
let m = new THREE.Mesh(geom, clone ? material.clone() : material);
|
|
74
|
-
m.castShadow = true;
|
|
75
|
-
m.receiveShadow = true;
|
|
76
|
-
m.layers.set(layer);
|
|
77
|
-
return m;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function get3dshape(punti, material, layer) {
|
|
81
|
-
if (!punti || punti.length < 3) {
|
|
82
|
-
return new THREE.BufferGeometry();
|
|
83
|
-
}
|
|
84
|
-
const vertices = [];
|
|
85
|
-
for (let i = 0; i < punti.length; i++) {
|
|
86
|
-
const p1 = punti[i];
|
|
87
|
-
const p2 = punti[(i + 1) % punti.length];
|
|
88
|
-
vertices.push(p1.x, 0, p1.y);
|
|
89
|
-
vertices.push(p2.x, 0, p2.y);
|
|
90
|
-
}
|
|
91
|
-
const geometry = new THREE.BufferGeometry();
|
|
92
|
-
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
|
|
93
|
-
let m = new THREE.LineSegments(geometry, material);
|
|
94
|
-
m.layers.set(layer);
|
|
95
|
-
return m;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function creategroup(name) {
|
|
99
|
-
let g = new THREE.Group();
|
|
100
|
-
g.name = name || '$$';
|
|
101
|
-
return g
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function svuotanodo(n) {
|
|
105
|
-
function _dispose(node) {
|
|
106
|
-
// Gestisce ricorsivamente tutti i figli
|
|
107
|
-
if (node.children.length) {
|
|
108
|
-
const children = [...node.children];
|
|
109
|
-
children.forEach(child => {
|
|
110
|
-
_dispose(child);
|
|
111
|
-
node.remove(child); // Usa node.remove invece di children.remove
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
// Dispone delle risorse del nodo corrente
|
|
115
|
-
if (node.geometry) {
|
|
116
|
-
node.geometry.dispose();
|
|
117
|
-
}
|
|
118
|
-
if (node.material) {
|
|
119
|
-
if (Array.isArray(node.material)) {
|
|
120
|
-
node.material.forEach(m => m.dispose());
|
|
121
|
-
} else {
|
|
122
|
-
node.material.dispose();
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
_dispose(n);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// cancella uno specifico nodo in una scena THREEJS
|
|
130
|
-
function deletegroup(grpbase, name) {
|
|
131
|
-
if (!grpbase?.children) return;
|
|
132
|
-
let gr = grpbase.children.find(e => e.name == name);
|
|
133
|
-
if (gr) {
|
|
134
|
-
// Rimuovi tutti i figli in modo ricorsivo
|
|
135
|
-
while (gr.children.length > 0) {
|
|
136
|
-
const child = gr.children[0];
|
|
137
|
-
/*
|
|
138
|
-
if (child.geometry) {
|
|
139
|
-
child.geometry.dispose();
|
|
140
|
-
}
|
|
141
|
-
if (child.material) {
|
|
142
|
-
if (Array.isArray(child.material)) {
|
|
143
|
-
child.material.forEach(m => m.dispose());
|
|
144
|
-
} else {
|
|
145
|
-
child.material.dispose();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
*/
|
|
149
|
-
gr.remove(child);
|
|
150
|
-
}
|
|
151
|
-
// Rimuovi il gruppo dal parent
|
|
152
|
-
if (gr.parent) {
|
|
153
|
-
gr.parent.remove(gr);
|
|
154
|
-
}
|
|
155
|
-
// Pulisci eventuali riferimenti
|
|
156
|
-
gr = null;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function randombasemat() {
|
|
161
|
-
const material = new THREE.LineBasicMaterial();
|
|
162
|
-
const color = new THREE.Color();
|
|
163
|
-
color.setHSL(Math.random(), 0.7, 0.4);
|
|
164
|
-
material.color = color;
|
|
165
|
-
return material;
|
|
166
|
-
}
|
|
167
|
-
|
|
1
|
+
import*as THREE from"three";import{PIF,clean,hash,getshape,raccordabezier}from"#markuno_cad";export{clamp}from"#markuno_cad";import earcut from"earcut";const SIDE=THREE.FrontSide;let materialline1=new THREE.LineBasicMaterial({color:3158064}),materialline2=new THREE.LineBasicMaterial({color:5263440});const mwhite$1=new THREE.MeshStandardMaterial({color:16777215,roughness:.5,metalness:.4,side:SIDE}),mgray1=new THREE.MeshStandardMaterial({color:8421504,roughness:.5,metalness:.4,side:SIDE}),mgray2=new THREE.MeshStandardMaterial({color:11579568,roughness:.5,metalness:.4,side:SIDE}),mred=new THREE.MeshStandardMaterial({color:16711680,roughness:.5,metalness:.4,side:SIDE}),mblue=new THREE.MeshStandardMaterial({color:1982639,roughness:.5,metalness:.4,side:SIDE}),mgreen=new THREE.MeshStandardMaterial({color:36864,roughness:.5,metalness:.4,side:SIDE}),mblack=new THREE.MeshStandardMaterial({color:0,roughness:.5,metalness:.4,side:SIDE}),scaleunit=.001;function groupfromgeometry(geometry,material,x,y,z,name,layer){let child=new THREE.Group;child.position.set(x,y,z),child.name=name;const mesh=new THREE.Mesh(geometry,material);mesh.name=name,mesh.castShadow=!0,mesh.receiveShadow=!0,mesh.layers.set(layer);const edges=new THREE.EdgesGeometry(geometry),lineSegments=new THREE.LineSegments(edges,materialline1);return lineSegments.layers.set(30),child.add(mesh),child.add(lineSegments),child}function posiziona(grp,pos={}){let tm=new THREE.Group;if(grp){let g2=grp.clone(),tm2=g2,{sl:sl=0,sa:sa=0,sp:sp=0,ax:ax=0,ay:ay=0,az:az=0,ul:ul=0,ua:ua=0,up:up=0,scale:scale=1,scx:scx=scale,scy:scy=scale,scz:scz=scale}=pos;tm.position.set(sl,sa,sp),tm.rotation.set(ax*PIF,ay*PIF,az*PIF),(ul||ua||up)&&(tm2=new THREE.Group,tm2.add(g2),tm2.position.set(ul,ua,up)),tm.scale.set(scx,scy,scz),tm.add(tm2)}return tm}function edgesfromgeometry(g1,layer=30){return getlinesgeom(new THREE.EdgesGeometry(g1,20),layer)}function getlinesgeom(edges,layer=30){const lineSegments=new THREE.LineSegments(edges,materialline1);return lineSegments.layers.set(layer),lineSegments}function getmesh(geom,material,layer=1,clone=!1){let m=new THREE.Mesh(geom,clone?material.clone():material);return m.castShadow=!0,m.receiveShadow=!0,m.layers.set(layer),m}function get3dshape(punti,material,layer){if(!punti||punti.length<3)return new THREE.BufferGeometry;const vertices=[];for(let i=0;i<punti.length;i++){const p1=punti[i],p2=punti[(i+1)%punti.length];vertices.push(p1.x,0,p1.y),vertices.push(p2.x,0,p2.y)}const geometry=new THREE.BufferGeometry;geometry.setAttribute("position",new THREE.Float32BufferAttribute(vertices,3));let m=new THREE.LineSegments(geometry,material);return m.layers.set(layer),m}function creategroup(name){let g=new THREE.Group;return g.name=name||"$$",g}function svuotanodo(n){!function _dispose(node){if(node.children.length){[...node.children].forEach((child=>{_dispose(child),node.remove(child)}))}node.geometry&&node.geometry.dispose(),node.material&&(Array.isArray(node.material)?node.material.forEach((m=>m.dispose())):node.material.dispose())}(n)}function deletegroup(grpbase,name){if(!grpbase?.children)return;let gr=grpbase.children.find((e=>e.name==name));if(gr){for(;gr.children.length>0;){const child=gr.children[0];gr.remove(child)}gr.parent&&gr.parent.remove(gr),gr=null}}function randombasemat(){const material=new THREE.LineBasicMaterial,color=new THREE.Color;return color.setHSL(Math.random(),.7,.4),material.color=color,material}
|
|
168
2
|
/**
|
|
169
3
|
* Crea una linea 3D
|
|
170
4
|
* @param {Object} l - Oggetto contenente punti p1 e p2
|
|
171
5
|
* @param {string} id - Identificatore
|
|
172
6
|
* @param {THREE.Material} [mat=null] - Materiale da applicare
|
|
173
7
|
* @returns {THREE.Line} Linea 3D
|
|
174
|
-
*/
|
|
175
|
-
function getline(l, id, mat = null) {
|
|
176
|
-
const lineGeometry = new THREE.BufferGeometry().setFromPoints([
|
|
177
|
-
new THREE.Vector3(l.p1.x, 0, l.p1.y),
|
|
178
|
-
new THREE.Vector3(l.p2.x, 0, l.p2.y)
|
|
179
|
-
]);
|
|
180
|
-
let tm = new THREE.Line(lineGeometry, mat || randombasemat());
|
|
181
|
-
return tm;
|
|
182
|
-
}
|
|
183
|
-
|
|
8
|
+
*/function getline(l,id,mat=null){const lineGeometry=(new THREE.BufferGeometry).setFromPoints([new THREE.Vector3(l.p1.x,0,l.p1.y),new THREE.Vector3(l.p2.x,0,l.p2.y)]);return new THREE.Line(lineGeometry,mat||randombasemat())}
|
|
184
9
|
/**
|
|
185
10
|
* Crea un punto 3D rappresentato da una sfera
|
|
186
11
|
* @param {Object} p - Coordinate del punto
|
|
@@ -188,45 +13,7 @@ function getline(l, id, mat = null) {
|
|
|
188
13
|
* @param {THREE.Material} [mat=null] - Materiale da applicare
|
|
189
14
|
* @param {number} [size=5] - Dimensione della sfera
|
|
190
15
|
* @returns {THREE.Mesh} Punto 3D
|
|
191
|
-
*/
|
|
192
|
-
function getpoint(p, id, mat = null, size = 5) {
|
|
193
|
-
const pointgeom = new THREE.SphereGeometry(size, 8, 8);
|
|
194
|
-
let tm = new THREE.Mesh(pointgeom, mat || randombasemat());
|
|
195
|
-
tm.position.set(p.x, 0, p.y);
|
|
196
|
-
return tm;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
function meshrotate(orientamento, mesh, x = 0, y = 0, z = 0) {
|
|
201
|
-
switch (orientamento.trim().toUpperCase()) {
|
|
202
|
-
case 'LPA':
|
|
203
|
-
mesh.rotation.y = Math.PI / 2;
|
|
204
|
-
mesh.rotation.x = Math.PI;
|
|
205
|
-
mesh.position.set(0, z, 0);
|
|
206
|
-
break;
|
|
207
|
-
case 'ALP':
|
|
208
|
-
mesh.rotation.x = Math.PI / 2;
|
|
209
|
-
mesh.position.set(0, x, 0);
|
|
210
|
-
break;
|
|
211
|
-
case 'APL':
|
|
212
|
-
mesh.rotation.x = -Math.PI / 2;
|
|
213
|
-
mesh.rotation.z = -Math.PI / 2;
|
|
214
|
-
break;
|
|
215
|
-
case 'PLA':
|
|
216
|
-
break;
|
|
217
|
-
case 'PAL':
|
|
218
|
-
mesh.rotation.z = Math.PI / 2;
|
|
219
|
-
mesh.position.set(z, 0, 0);
|
|
220
|
-
break;
|
|
221
|
-
case 'LAP':
|
|
222
|
-
mesh.rotation.y = Math.PI / 2;
|
|
223
|
-
mesh.rotation.z = Math.PI / 2;
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
return mesh;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
|
|
16
|
+
*/function getpoint(p,id,mat=null,size=5){const pointgeom=new THREE.SphereGeometry(size,8,8);let tm=new THREE.Mesh(pointgeom,mat||randombasemat());return tm.position.set(p.x,0,p.y),tm}
|
|
230
17
|
/**
|
|
231
18
|
* Aggiunge un pivot a un oggetto 3D esistente, mantenendone posizione e orientamento globale.
|
|
232
19
|
* @param {THREE.Object3D} grp - Oggetto esistente già posizionato nella scena.
|
|
@@ -234,54 +21,7 @@ function meshrotate(orientamento, mesh, x = 0, y = 0, z = 0) {
|
|
|
234
21
|
* @param {Object} movimento - Istanza della classe MovimentoBase o derivata.
|
|
235
22
|
* @returns {THREE.Group} - Nuovo gruppo contenitore con pivot.
|
|
236
23
|
*/
|
|
237
|
-
|
|
238
|
-
function addmovpivot(gcad, grp, movimento, op = {}, x = 0, y = 0, z = 0) {
|
|
239
|
-
movimento = clean(movimento, true);
|
|
240
|
-
if (!gcad.movs[movimento]) return grp;
|
|
241
|
-
gcad.movs[movimento];
|
|
242
|
-
const pivotLocal = new THREE.Vector3(x, y, z);
|
|
243
|
-
const isZeroPivot = pivotLocal.lengthSq() === 0;
|
|
244
|
-
|
|
245
|
-
const pivotGroup = new THREE.Group(); // gruppopivot
|
|
246
|
-
pivotGroup.name = `pivot_${movimento}`;
|
|
247
|
-
|
|
248
|
-
const movimentoGroup = new THREE.Group(); // gruppomovimento
|
|
249
|
-
movimentoGroup.name = `mov_${movimento}`;
|
|
250
|
-
|
|
251
|
-
// Gestione posizione
|
|
252
|
-
if (isZeroPivot) {
|
|
253
|
-
pivotGroup.position.copy(grp.position);
|
|
254
|
-
pivotGroup.quaternion.copy(grp.quaternion);
|
|
255
|
-
grp.position.set(0, 0, 0);
|
|
256
|
-
grp.rotation.set(0, 0, 0);
|
|
257
|
-
} else {
|
|
258
|
-
const pivotWorld = pivotLocal.clone().applyMatrix4(grp.matrixWorld);
|
|
259
|
-
pivotGroup.position.copy(pivotWorld);
|
|
260
|
-
|
|
261
|
-
const offset = pivotLocal.clone().negate();
|
|
262
|
-
grp.position.add(offset);
|
|
263
|
-
|
|
264
|
-
pivotGroup.quaternion.copy(grp.getWorldQuaternion(new THREE.Quaternion()));
|
|
265
|
-
grp.rotation.set(0, 0, 0);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Costruisci gerarchia: pivot -> movimento -> oggetto
|
|
269
|
-
movimentoGroup.add(grp);
|
|
270
|
-
pivotGroup.add(movimentoGroup);
|
|
271
|
-
|
|
272
|
-
// Imposta userData del gruppo MOVIMENTO, non del pivot!
|
|
273
|
-
if (!op) op = {};
|
|
274
|
-
op.inmov = false;
|
|
275
|
-
op.key = movimento;
|
|
276
|
-
op.dt = 0;
|
|
277
|
-
op.dtstart = false;
|
|
278
|
-
|
|
279
|
-
movimentoGroup.userData.mov = { ...op };
|
|
280
|
-
|
|
281
|
-
return pivotGroup;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
|
|
24
|
+
function addmovpivot(gcad,grp,movimento,op={},x=0,y=0,z=0){if(movimento=clean(movimento,!0),!gcad.movs[movimento])return grp;gcad.movs[movimento];const pivotLocal=new THREE.Vector3(x,y,z),isZeroPivot=0===pivotLocal.lengthSq(),pivotGroup=new THREE.Group;pivotGroup.name=`pivot_${movimento}`;const movimentoGroup=new THREE.Group;if(movimentoGroup.name=`mov_${movimento}`,isZeroPivot)pivotGroup.position.copy(grp.position),pivotGroup.quaternion.copy(grp.quaternion),grp.position.set(0,0,0),grp.rotation.set(0,0,0);else{const pivotWorld=pivotLocal.clone().applyMatrix4(grp.matrixWorld);pivotGroup.position.copy(pivotWorld);const offset=pivotLocal.clone().negate();grp.position.add(offset),pivotGroup.quaternion.copy(grp.getWorldQuaternion(new THREE.Quaternion)),grp.rotation.set(0,0,0)}return movimentoGroup.add(grp),pivotGroup.add(movimentoGroup),op||(op={}),op.inmov=!1,op.key=movimento,op.dt=0,op.dtstart=!1,movimentoGroup.userData.mov={...op},pivotGroup}
|
|
285
25
|
/**
|
|
286
26
|
* Crea un gestore di movimento per animare oggetti 3D.
|
|
287
27
|
* @param {string} key - Chiave identificativa del movimento
|
|
@@ -305,148 +45,7 @@ function addmovpivot(gcad, grp, movimento, op = {}, x = 0, y = 0, z = 0) {
|
|
|
305
45
|
* @property {string} key - Chiave del movimento
|
|
306
46
|
* @property {function} step - Esegue un passo dell'animazione
|
|
307
47
|
* @property {function} reset - Resetta l'oggetto alla posizione iniziale
|
|
308
|
-
*/
|
|
309
|
-
function getmovimento(key, gtimeline = []) {
|
|
310
|
-
let totale = 0;
|
|
311
|
-
let timeline = [];
|
|
312
|
-
const _calcolatotale = () => {
|
|
313
|
-
totale = timeline.reduce((t, e) => t + (e.time || 0), 0);
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
const _cleartimeline = () => {
|
|
317
|
-
timeline = [];
|
|
318
|
-
totale = 0;
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
const _add = (t, op = {}) => {
|
|
322
|
-
if (t) {
|
|
323
|
-
timeline.push({ ...op, time: t });
|
|
324
|
-
}
|
|
325
|
-
_calcolatotale();
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
_cleartimeline();
|
|
329
|
-
if (gtimeline && gtimeline.length) {
|
|
330
|
-
gtimeline.forEach(e => _add(e.time, e));
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const _resetmov = (grp) => {
|
|
334
|
-
const { mov } = grp?.userData || {};
|
|
335
|
-
if (mov) {
|
|
336
|
-
mov.inmov = false;
|
|
337
|
-
grp.position.set(0, 0, 0);
|
|
338
|
-
grp.scale.set(1, 1, 1);
|
|
339
|
-
grp.rotation.set(0, 0, 0);
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
|
|
343
|
-
const _step = (grp, callback) => {
|
|
344
|
-
if (!grp || !grp.userData?.mov || !totale) return;
|
|
345
|
-
const { mov } = grp.userData;
|
|
346
|
-
if (!mov.inmov) return;
|
|
347
|
-
let dt = mov.dt - mov.dtstart;
|
|
348
|
-
if (mov.ripeti) {
|
|
349
|
-
dt = dt % totale;
|
|
350
|
-
} else if (dt > totale) {
|
|
351
|
-
_resetmov(grp);
|
|
352
|
-
return;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
let x = 0, y = 0, z = 0;
|
|
356
|
-
let ax = 0, ay = 0, az = 0;
|
|
357
|
-
let sx = 1, sy = 1, sz = 1;
|
|
358
|
-
let t = null;
|
|
359
|
-
|
|
360
|
-
let accumTime = 0;
|
|
361
|
-
|
|
362
|
-
for (let step of timeline) {
|
|
363
|
-
accumTime += step.time;
|
|
364
|
-
if (dt < accumTime) {
|
|
365
|
-
const c = (step.time > 0) ? (dt - (accumTime - step.time)) / step.time : 1;
|
|
366
|
-
const _calc = (f, def = 0) => typeof f === 'function' ? f(mov) * c : (f || def) * c;
|
|
367
|
-
|
|
368
|
-
x += _calc(step.x);
|
|
369
|
-
y += _calc(step.y);
|
|
370
|
-
z += _calc(step.z);
|
|
371
|
-
|
|
372
|
-
sx *= 1 + _calc(step.sx ?? step.s, 0);
|
|
373
|
-
sy *= 1 + _calc(step.sy ?? step.s, 0);
|
|
374
|
-
sz *= 1 + _calc(step.sz ?? step.s, 0);
|
|
375
|
-
|
|
376
|
-
ax += _calc(step.ax) * PIF;
|
|
377
|
-
ay += _calc(step.ay) * PIF;
|
|
378
|
-
az += _calc(step.az) * PIF;
|
|
379
|
-
|
|
380
|
-
if (typeof step.t !== 'undefined') {
|
|
381
|
-
t = typeof step.t === 'function' ? step.t(mov) * c : step.t * c;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
break; // consider only the current step
|
|
385
|
-
} else {
|
|
386
|
-
// Accumula completamente il valore intero
|
|
387
|
-
const _calc = (f, def = 0) => typeof f === 'function' ? f(mov) : f || def;
|
|
388
|
-
|
|
389
|
-
x += _calc(step.x);
|
|
390
|
-
y += _calc(step.y);
|
|
391
|
-
z += _calc(step.z);
|
|
392
|
-
|
|
393
|
-
sx *= 1 + _calc(step.sx ?? step.s, 0);
|
|
394
|
-
sy *= 1 + _calc(step.sy ?? step.s, 0);
|
|
395
|
-
sz *= 1 + _calc(step.sz ?? step.s, 0);
|
|
396
|
-
|
|
397
|
-
ax += _calc(step.ax) * PIF;
|
|
398
|
-
ay += _calc(step.ay) * PIF;
|
|
399
|
-
az += _calc(step.az) * PIF;
|
|
400
|
-
|
|
401
|
-
if (typeof step.t !== 'undefined') {
|
|
402
|
-
t = typeof step.t === 'function' ? step.t(mov) : step.t;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
grp.position.set(x, y, z);
|
|
408
|
-
grp.scale.set(sx, sy, sz);
|
|
409
|
-
grp.rotation.set(ax, ay, az);
|
|
410
|
-
|
|
411
|
-
if (t !== null) {
|
|
412
|
-
// Applica la trasparenza se esistono materiali con `transparent: true`
|
|
413
|
-
grp.traverse(obj => {
|
|
414
|
-
if (obj.material) {
|
|
415
|
-
const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
|
|
416
|
-
materials.forEach(mat => {
|
|
417
|
-
if (mat.transparent) {
|
|
418
|
-
mat.opacity = 1 - t;
|
|
419
|
-
}
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
if (callback) callback(grp, dt);
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
return {
|
|
429
|
-
get tline() { return totale },
|
|
430
|
-
clear: _cleartimeline,
|
|
431
|
-
add: _add,
|
|
432
|
-
key,
|
|
433
|
-
step: _step,
|
|
434
|
-
reset: _resetmov,
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// Canvas globale per il rendering delle targhette
|
|
439
|
-
let globalLabelCanvas = null;
|
|
440
|
-
let globalLabelContext = null;
|
|
441
|
-
function getGlobalCanvas() {
|
|
442
|
-
if (!globalLabelCanvas) {
|
|
443
|
-
globalLabelCanvas = document.createElement('canvas');
|
|
444
|
-
globalLabelContext = globalLabelCanvas.getContext('2d', { willReadFrequently: true });
|
|
445
|
-
}
|
|
446
|
-
return { canvas: globalLabelCanvas, context: globalLabelContext };
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
|
|
48
|
+
*/function getmovimento(key,gtimeline=[]){let totale=0,timeline=[];const _cleartimeline=()=>{timeline=[],totale=0},_add=(t,op={})=>{t&&timeline.push({...op,time:t}),totale=timeline.reduce(((t,e)=>t+(e.time||0)),0)};_cleartimeline(),gtimeline&>imeline.length&>imeline.forEach((e=>_add(e.time,e)));const _resetmov=grp=>{const{mov:mov}=grp?.userData||{};mov&&(mov.inmov=!1,grp.position.set(0,0,0),grp.scale.set(1,1,1),grp.rotation.set(0,0,0))};return{get tline(){return totale},clear:_cleartimeline,add:_add,key:key,step:(grp,callback)=>{if(!grp||!grp.userData?.mov||!totale)return;const{mov:mov}=grp.userData;if(!mov.inmov)return;let dt=mov.dt-mov.dtstart;if(mov.ripeti)dt%=totale;else if(dt>totale)return void _resetmov(grp);let x=0,y=0,z=0,ax=0,ay=0,az=0,sx=1,sy=1,sz=1,t=null,accumTime=0;for(let step of timeline){if(accumTime+=step.time,dt<accumTime){const c=step.time>0?(dt-(accumTime-step.time))/step.time:1,_calc=(f,def=0)=>"function"==typeof f?f(mov)*c:(f||def)*c;x+=_calc(step.x),y+=_calc(step.y),z+=_calc(step.z),sx*=1+_calc(step.sx??step.s,0),sy*=1+_calc(step.sy??step.s,0),sz*=1+_calc(step.sz??step.s,0),ax+=_calc(step.ax)*PIF,ay+=_calc(step.ay)*PIF,az+=_calc(step.az)*PIF,void 0!==step.t&&(t="function"==typeof step.t?step.t(mov)*c:step.t*c);break}{const _calc=(f,def=0)=>"function"==typeof f?f(mov):f||def;x+=_calc(step.x),y+=_calc(step.y),z+=_calc(step.z),sx*=1+_calc(step.sx??step.s,0),sy*=1+_calc(step.sy??step.s,0),sz*=1+_calc(step.sz??step.s,0),ax+=_calc(step.ax)*PIF,ay+=_calc(step.ay)*PIF,az+=_calc(step.az)*PIF,void 0!==step.t&&(t="function"==typeof step.t?step.t(mov):step.t)}}grp.position.set(x,y,z),grp.scale.set(sx,sy,sz),grp.rotation.set(ax,ay,az),null!==t&&grp.traverse((obj=>{if(obj.material){(Array.isArray(obj.material)?obj.material:[obj.material]).forEach((mat=>{mat.transparent&&(mat.opacity=1-t)}))}})),callback&&callback(grp,dt)},reset:_resetmov}}let globalLabelCanvas=null,globalLabelContext=null;
|
|
450
49
|
/**
|
|
451
50
|
* Crea un punto di riferimento invisibile nell'albero 3D
|
|
452
51
|
* @param {number} x - Coordinata X
|
|
@@ -456,216 +55,14 @@ function getGlobalCanvas() {
|
|
|
456
55
|
* @param {string} [id=null] - Identificatore opzionale
|
|
457
56
|
* @returns {THREE.Object3D} Oggetto di riferimento invisibile
|
|
458
57
|
*/
|
|
459
|
-
function getriferimento(dati,
|
|
460
|
-
if (typeof dati == 'string') dati = { testo: dati };
|
|
461
|
-
let riferimento = new THREE.Object3D();
|
|
462
|
-
riferimento.position.set(x, y, z);
|
|
463
|
-
|
|
464
|
-
// Imposta il nome se fornito
|
|
465
|
-
if (id) riferimento.name = id;
|
|
466
|
-
riferimento.userData = { tipo: 'rif', ...dati };
|
|
467
|
-
return riferimento;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
|
|
58
|
+
function getriferimento(dati,x=0,y=0,z=0,id="rif"){"string"==typeof dati&&(dati={testo:dati});let riferimento=new THREE.Object3D;return riferimento.position.set(x,y,z),id&&(riferimento.name=id),riferimento.userData={tipo:"rif",...dati},riferimento}
|
|
471
59
|
/**
|
|
472
60
|
* Crea una targhetta rettangolare con testo
|
|
473
61
|
* @param {Array|string} testo - Array di oggetti {testo, size, colore} o stringa
|
|
474
62
|
* @param {number} dim - Larghezza/altezza della targhetta
|
|
475
63
|
* @param {Object} options - Opzioni aggiuntive
|
|
476
64
|
* @returns {THREE.Mesh} Mesh con la targhetta
|
|
477
|
-
*/
|
|
478
|
-
function gettarghetta(gcad, testo, dim = 100, options = {}) {
|
|
479
|
-
// Converti stringa in array di oggetti testo
|
|
480
|
-
const {
|
|
481
|
-
noSfondo = false,
|
|
482
|
-
forcey = false,
|
|
483
|
-
coloreSfondo = 'white',
|
|
484
|
-
coloreBordo = 'darkgray',
|
|
485
|
-
spessoreBordo = 3,
|
|
486
|
-
padding = 8,
|
|
487
|
-
layer = 21,
|
|
488
|
-
fontFamily = 'Arial',
|
|
489
|
-
raggioAngoli = 20, // Raggio degli angoli arrotondati
|
|
490
|
-
} = options;
|
|
491
|
-
let tt = hash(`T:${coloreSfondo}|${coloreBordo}|${padding}|${spessoreBordo}|${raggioAngoli}|${noSfondo}|${testo}`);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
testo = testo.split('\n').map(e => {
|
|
496
|
-
let size = 12, color = 'black';
|
|
497
|
-
e = e.trim();
|
|
498
|
-
let rr = /^\s*\#(\w)(\d*)\,(.*)$/.exec(e);
|
|
499
|
-
if (rr) {
|
|
500
|
-
size = parseInt(rr[2] || 12);
|
|
501
|
-
e = rr[3];
|
|
502
|
-
switch (rr[1].toLowerCase()) {
|
|
503
|
-
case 'r': color = 'red'; break;
|
|
504
|
-
case 'g': color = 'green'; break;
|
|
505
|
-
case 'b': color = 'blue'; break;
|
|
506
|
-
case 'c': color = 'cyan'; break;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
return { testo: e, size, color }
|
|
510
|
-
}).filter(e => e.testo);
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
// Ottieni il canvas globale
|
|
515
|
-
const { canvas, context } = getGlobalCanvas();
|
|
516
|
-
|
|
517
|
-
// Calcola le dimensioni necessarie per il testo
|
|
518
|
-
let maxWidth = 0;
|
|
519
|
-
let totalHeight = padding * 2; // Inizia con il padding superiore e inferiore
|
|
520
|
-
const scale = 2;
|
|
521
|
-
testo.forEach((riga, i) => {
|
|
522
|
-
const { testo: testoRiga, size = 12 } = riga;
|
|
523
|
-
const fontSize = size * scale;
|
|
524
|
-
context.font = `${fontSize}px ${fontFamily}`;
|
|
525
|
-
|
|
526
|
-
const metrics = context.measureText(testoRiga);
|
|
527
|
-
const textWidth = metrics.width;
|
|
528
|
-
const textHeight = fontSize * (i == testo.length - 1 ? 1 : 1.2); // Approssimazione dell'altezza del testo
|
|
529
|
-
maxWidth = Math.max(maxWidth, textWidth);
|
|
530
|
-
totalHeight += textHeight;
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
// Aggiungi padding laterale alla larghezza
|
|
534
|
-
maxWidth += padding * 2;
|
|
535
|
-
|
|
536
|
-
// Calcola il rapporto di aspetto del testo
|
|
537
|
-
const aspectRatio = totalHeight / maxWidth || 1;
|
|
538
|
-
|
|
539
|
-
// Calcola dimx e dimy in base all'opzione forcey
|
|
540
|
-
let dimx, dimy;
|
|
541
|
-
if (forcey) {
|
|
542
|
-
dimy = dim;
|
|
543
|
-
dimx = Math.ceil(dimy / aspectRatio);
|
|
544
|
-
} else {
|
|
545
|
-
dimx = dim;
|
|
546
|
-
dimy = Math.ceil(dimx * aspectRatio);
|
|
547
|
-
}
|
|
548
|
-
if (!gcad.textures[tt]) {
|
|
549
|
-
|
|
550
|
-
// Ridimensiona il canvas per adattarlo alle dimensioni specificate
|
|
551
|
-
// Aggiungi spazio extra per il bordo
|
|
552
|
-
const canvasWidth = maxWidth + spessoreBordo * 2;
|
|
553
|
-
const canvasHeight = totalHeight + spessoreBordo * 2;
|
|
554
|
-
|
|
555
|
-
// Ridimensiona il canvas globale se necessario
|
|
556
|
-
if (canvas.width < canvasWidth || canvas.height < canvasHeight) {
|
|
557
|
-
canvas.width = Math.max(canvas.width, canvasWidth);
|
|
558
|
-
canvas.height = Math.max(canvas.height, canvasHeight);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// Pulisci l'area del canvas che useremo
|
|
562
|
-
context.clearRect(0, 0, canvasWidth, canvasHeight);
|
|
563
|
-
|
|
564
|
-
// Converti il colore di sfondo da esadecimale a RGB
|
|
565
|
-
const bgColor = new THREE.Color(coloreSfondo);
|
|
566
|
-
const bgColorStyle = `rgb(${Math.floor(bgColor.r * 255)}, ${Math.floor(bgColor.g * 255)}, ${Math.floor(bgColor.b * 255)})`;
|
|
567
|
-
|
|
568
|
-
// Converti il colore del bordo da esadecimale a RGB
|
|
569
|
-
const borderColor = new THREE.Color(coloreBordo);
|
|
570
|
-
const borderColorStyle = `rgb(${Math.floor(borderColor.r * 255)}, ${Math.floor(borderColor.g * 255)}, ${Math.floor(borderColor.b * 255)})`;
|
|
571
|
-
|
|
572
|
-
// Calcola il raggio degli angoli
|
|
573
|
-
const radius = raggioAngoli;
|
|
574
|
-
|
|
575
|
-
// Disegna prima il bordo (rettangolo più grande)
|
|
576
|
-
context.beginPath();
|
|
577
|
-
if (!noSfondo) {
|
|
578
|
-
drawRoundedRect(context, 0, 0, canvasWidth, canvasHeight, radius + spessoreBordo / 2);
|
|
579
|
-
context.fillStyle = borderColorStyle;
|
|
580
|
-
context.fill();
|
|
581
|
-
|
|
582
|
-
// Poi disegna lo sfondo (rettangolo più piccolo)
|
|
583
|
-
context.beginPath();
|
|
584
|
-
drawRoundedRect(context, spessoreBordo, spessoreBordo,
|
|
585
|
-
canvasWidth - spessoreBordo * 2,
|
|
586
|
-
canvasHeight - spessoreBordo * 2,
|
|
587
|
-
radius - spessoreBordo / 2);
|
|
588
|
-
context.fillStyle = bgColorStyle;
|
|
589
|
-
context.fill();
|
|
590
|
-
}
|
|
591
|
-
// Disegna ogni riga di testo
|
|
592
|
-
let yPosition = padding + spessoreBordo;
|
|
593
|
-
|
|
594
|
-
// Disegna ogni riga di testo
|
|
595
|
-
testo.forEach((riga) => {
|
|
596
|
-
const { testo: testoRiga, size = 12, color = 'black' } = riga;
|
|
597
|
-
|
|
598
|
-
// Imposta lo stile del testo
|
|
599
|
-
const fontSize = size * scale;
|
|
600
|
-
context.font = `${fontSize}px ${fontFamily}`;
|
|
601
|
-
context.fillStyle = color;
|
|
602
|
-
context.textAlign = 'center';
|
|
603
|
-
context.textBaseline = 'top';
|
|
604
|
-
|
|
605
|
-
// Disegna il testo
|
|
606
|
-
context.fillText(testoRiga, canvasWidth / 2, yPosition);
|
|
607
|
-
yPosition += fontSize * 1.2; // Spazio tra le righe
|
|
608
|
-
});
|
|
609
|
-
|
|
610
|
-
// Ottieni i dati dell'immagine direttamente dal canvas globale
|
|
611
|
-
const imageData = context.getImageData(0, 0, canvasWidth, canvasHeight);
|
|
612
|
-
|
|
613
|
-
// Crea una texture direttamente dai dati dell'immagine
|
|
614
|
-
const texture = new THREE.DataTexture(
|
|
615
|
-
imageData.data,
|
|
616
|
-
imageData.width,
|
|
617
|
-
imageData.height,
|
|
618
|
-
THREE.RGBAFormat
|
|
619
|
-
);
|
|
620
|
-
|
|
621
|
-
// Imposta le proprietà della texture
|
|
622
|
-
texture.needsUpdate = true;
|
|
623
|
-
texture.flipY = true;
|
|
624
|
-
texture.minFilter = THREE.LinearFilter;
|
|
625
|
-
texture.magFilter = THREE.LinearFilter;
|
|
626
|
-
|
|
627
|
-
// Imposta l'area del canvas da utilizzare come texture
|
|
628
|
-
// Memorizza la texture nella cache
|
|
629
|
-
gcad.textures[tt] = texture;
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
// Crea un materiale con la texture
|
|
635
|
-
const materialeTesto = new THREE.MeshBasicMaterial({
|
|
636
|
-
map: gcad.textures[tt],
|
|
637
|
-
transparent: true,
|
|
638
|
-
side: SIDE
|
|
639
|
-
});
|
|
640
|
-
|
|
641
|
-
// Crea un piano per il testo con le dimensioni specificate
|
|
642
|
-
const geometriaTesto = new THREE.PlaneGeometry(dimx, dimy);
|
|
643
|
-
const meshTesto = new THREE.Mesh(geometriaTesto, materialeTesto);
|
|
644
|
-
meshTesto.layers.set(layer);
|
|
645
|
-
meshTesto.userData = {
|
|
646
|
-
dimx, dimy
|
|
647
|
-
};
|
|
648
|
-
|
|
649
|
-
return meshTesto;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
// Funzione helper per disegnare un rettangolo con angoli arrotondati
|
|
653
|
-
function drawRoundedRect(ctx, x, y, width, height, radius) {
|
|
654
|
-
// Assicurati che il raggio non sia troppo grande
|
|
655
|
-
radius = Math.min(radius, Math.min(width / 2, height / 2));
|
|
656
|
-
|
|
657
|
-
ctx.moveTo(x + radius, y);
|
|
658
|
-
ctx.lineTo(x + width - radius, y);
|
|
659
|
-
ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
|
|
660
|
-
ctx.lineTo(x + width, y + height - radius);
|
|
661
|
-
ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
|
|
662
|
-
ctx.lineTo(x + radius, y + height);
|
|
663
|
-
ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
|
|
664
|
-
ctx.lineTo(x, y + radius);
|
|
665
|
-
ctx.quadraticCurveTo(x, y, x + radius, y);
|
|
666
|
-
ctx.closePath();
|
|
667
|
-
}
|
|
668
|
-
|
|
65
|
+
*/function gettarghetta(gcad,testo,dim=100,options={}){const{noSfondo:noSfondo=!1,forcey:forcey=!1,coloreSfondo:coloreSfondo="white",coloreBordo:coloreBordo="darkgray",spessoreBordo:spessoreBordo=3,padding:padding=8,layer:layer=21,fontFamily:fontFamily="Arial",raggioAngoli:raggioAngoli=20}=options;let tt=hash(`T:${coloreSfondo}|${coloreBordo}|${padding}|${spessoreBordo}|${raggioAngoli}|${noSfondo}|${testo}`);testo=testo.split("\n").map((e=>{let size=12,color="black";e=e.trim();let rr=/^\s*\#(\w)(\d*)\,(.*)$/.exec(e);if(rr)switch(size=parseInt(rr[2]||12),e=rr[3],rr[1].toLowerCase()){case"r":color="red";break;case"g":color="green";break;case"b":color="blue";break;case"c":color="cyan"}return{testo:e,size:size,color:color}})).filter((e=>e.testo));const{canvas:canvas,context:context}=function getGlobalCanvas(){return globalLabelCanvas||(globalLabelCanvas=document.createElement("canvas"),globalLabelContext=globalLabelCanvas.getContext("2d",{willReadFrequently:!0})),{canvas:globalLabelCanvas,context:globalLabelContext}}();let maxWidth=0,totalHeight=2*padding;testo.forEach(((riga,i)=>{const{testo:testoRiga,size:size=12}=riga,fontSize=2*size;context.font=`${fontSize}px ${fontFamily}`;const textWidth=context.measureText(testoRiga).width,textHeight=fontSize*(i==testo.length-1?1:1.2);maxWidth=Math.max(maxWidth,textWidth),totalHeight+=textHeight})),maxWidth+=2*padding;const aspectRatio=totalHeight/maxWidth||1;let dimx,dimy;if(forcey?(dimy=dim,dimx=Math.ceil(dimy/aspectRatio)):(dimx=dim,dimy=Math.ceil(dimx*aspectRatio)),!gcad.textures[tt]){const canvasWidth=maxWidth+2*spessoreBordo,canvasHeight=totalHeight+2*spessoreBordo;(canvas.width<canvasWidth||canvas.height<canvasHeight)&&(canvas.width=Math.max(canvas.width,canvasWidth),canvas.height=Math.max(canvas.height,canvasHeight)),context.clearRect(0,0,canvasWidth,canvasHeight);const bgColor=new THREE.Color(coloreSfondo),bgColorStyle=`rgb(${Math.floor(255*bgColor.r)}, ${Math.floor(255*bgColor.g)}, ${Math.floor(255*bgColor.b)})`,borderColor=new THREE.Color(coloreBordo),borderColorStyle=`rgb(${Math.floor(255*borderColor.r)}, ${Math.floor(255*borderColor.g)}, ${Math.floor(255*borderColor.b)})`,radius=raggioAngoli;context.beginPath(),noSfondo||(drawRoundedRect(context,0,0,canvasWidth,canvasHeight,radius+spessoreBordo/2),context.fillStyle=borderColorStyle,context.fill(),context.beginPath(),drawRoundedRect(context,spessoreBordo,spessoreBordo,canvasWidth-2*spessoreBordo,canvasHeight-2*spessoreBordo,radius-spessoreBordo/2),context.fillStyle=bgColorStyle,context.fill());let yPosition=padding+spessoreBordo;testo.forEach((riga=>{const{testo:testoRiga,size:size=12,color:color="black"}=riga,fontSize=2*size;context.font=`${fontSize}px ${fontFamily}`,context.fillStyle=color,context.textAlign="center",context.textBaseline="top",context.fillText(testoRiga,canvasWidth/2,yPosition),yPosition+=1.2*fontSize}));const imageData=context.getImageData(0,0,canvasWidth,canvasHeight),texture=new THREE.DataTexture(imageData.data,imageData.width,imageData.height,THREE.RGBAFormat);texture.needsUpdate=!0,texture.flipY=!0,texture.minFilter=THREE.LinearFilter,texture.magFilter=THREE.LinearFilter,gcad.textures[tt]=texture}const materialeTesto=new THREE.MeshBasicMaterial({map:gcad.textures[tt],transparent:!0,side:SIDE}),geometriaTesto=new THREE.PlaneGeometry(dimx,dimy),meshTesto=new THREE.Mesh(geometriaTesto,materialeTesto);return meshTesto.layers.set(layer),meshTesto.userData={dimx:dimx,dimy:dimy},meshTesto}function drawRoundedRect(ctx,x,y,width,height,radius){radius=Math.min(radius,Math.min(width/2,height/2)),ctx.moveTo(x+radius,y),ctx.lineTo(x+width-radius,y),ctx.quadraticCurveTo(x+width,y,x+width,y+radius),ctx.lineTo(x+width,y+height-radius),ctx.quadraticCurveTo(x+width,y+height,x+width-radius,y+height),ctx.lineTo(x+radius,y+height),ctx.quadraticCurveTo(x,y+height,x,y+height-radius),ctx.lineTo(x,y+radius),ctx.quadraticCurveTo(x,y,x+radius,y),ctx.closePath()}
|
|
669
66
|
/**
|
|
670
67
|
* Crea una quota tra due punti in 3D sul piano XY
|
|
671
68
|
* @param {string} testo - Testo da visualizzare nella quota
|
|
@@ -676,178 +73,7 @@ function drawRoundedRect(ctx, x, y, width, height, radius) {
|
|
|
676
73
|
* @param {number} sizetesto - Dimensione del testo
|
|
677
74
|
* @param {Object} options - Opzioni aggiuntive
|
|
678
75
|
* @returns {THREE.Group} Gruppo contenente la quota
|
|
679
|
-
*/
|
|
680
|
-
function getquota(gcad, testo, x1, y1, x2, y2, altezza = 30, offset = 0, options = {}) {
|
|
681
|
-
// Opzioni di default
|
|
682
|
-
const {
|
|
683
|
-
piano = 'xy',
|
|
684
|
-
layer = 22,
|
|
685
|
-
delta = 5, // Margine per decidere se la targhetta è troppo grande
|
|
686
|
-
spessoreLinea = 3, // Spessore della linea (diametro del cilindro)
|
|
687
|
-
} = options;
|
|
688
|
-
// pp point on plane. usare questa funzione per convertire i punti 2D rispetto al piano scelto
|
|
689
|
-
const pp = (x, y) => {
|
|
690
|
-
if (piano === 'xz') return [x, 0, y];
|
|
691
|
-
if (piano === 'yz') return [0, x, y]; // Corretto: x diventa y, y diventa z
|
|
692
|
-
return [x, y, 0]; // Default: piano xy
|
|
693
|
-
};
|
|
694
|
-
|
|
695
|
-
// Creo un gruppo per contenere tutti gli elementi della quota
|
|
696
|
-
const quotaGroup = new THREE.Group();
|
|
697
|
-
quotaGroup.name = 'quota';
|
|
698
|
-
|
|
699
|
-
// Calcolo la distanza tra i due punti
|
|
700
|
-
const dx = x2 - x1;
|
|
701
|
-
const dy = y2 - y1;
|
|
702
|
-
const distanza = Math.sqrt(dx * dx + dy * dy);
|
|
703
|
-
|
|
704
|
-
// Calcolo l'angolo di rotazione
|
|
705
|
-
const angolo = Math.atan2(dy, dx);
|
|
706
|
-
|
|
707
|
-
// Calcolo l'offset perpendicolare alla linea
|
|
708
|
-
const offsetX = -Math.sin(angolo) * offset;
|
|
709
|
-
const offsetY = Math.cos(angolo) * offset;
|
|
710
|
-
// Creo il testo della quota
|
|
711
|
-
const targhetta = gettarghetta(gcad,
|
|
712
|
-
testo || `${distanza.toFixed(1)}`, altezza * 2,
|
|
713
|
-
{
|
|
714
|
-
noSfondo: true,
|
|
715
|
-
layer,
|
|
716
|
-
forcey: true
|
|
717
|
-
}
|
|
718
|
-
);
|
|
719
|
-
const { dimx, dimy } = targhetta.userData;
|
|
720
|
-
|
|
721
|
-
// Determino se la targhetta è più grande della distanza disponibile
|
|
722
|
-
const targhettaTroppoGrande = dimx > (distanza - delta);
|
|
723
|
-
|
|
724
|
-
// Funzione per creare un cilindro tra due punti
|
|
725
|
-
function getCilindro(puntoInizio, puntoFine, spessore) {
|
|
726
|
-
const direzione = new THREE.Vector3().subVectors(puntoFine, puntoInizio);
|
|
727
|
-
const lunghezza = direzione.length();
|
|
728
|
-
|
|
729
|
-
// Creo la geometria del cilindro senza top e bottom
|
|
730
|
-
let ky = hash(`c:${spessore}|${lunghezza}`);
|
|
731
|
-
let geometria;
|
|
732
|
-
if (!gcad.geo[ky]) {
|
|
733
|
-
geometria = new THREE.CylinderGeometry(
|
|
734
|
-
spessore / 2, // raggio superiore
|
|
735
|
-
spessore / 2, // raggio inferiore
|
|
736
|
-
lunghezza, // altezza
|
|
737
|
-
3, // numero di segmenti
|
|
738
|
-
1, // segmenti di altezza
|
|
739
|
-
true // openEnded = true per rimuovere top e bottom
|
|
740
|
-
);
|
|
741
|
-
gcad.geo[ky] = geometria;
|
|
742
|
-
} else {
|
|
743
|
-
geometria = gcad.geo[ky];
|
|
744
|
-
}
|
|
745
|
-
|
|
746
|
-
// Creo la mesh
|
|
747
|
-
const cilindro = new THREE.Mesh(geometria, mblack);
|
|
748
|
-
|
|
749
|
-
// Posiziono il cilindro
|
|
750
|
-
cilindro.position.copy(puntoInizio).add(direzione.multiplyScalar(0.5));
|
|
751
|
-
|
|
752
|
-
// Calcolo la rotazione per allineare il cilindro con la direzione
|
|
753
|
-
const punto = new THREE.Vector3().copy(puntoFine);
|
|
754
|
-
cilindro.lookAt(punto);
|
|
755
|
-
|
|
756
|
-
// Ruoto di 90 gradi per allineare correttamente il cilindro
|
|
757
|
-
cilindro.rotateX(Math.PI / 2);
|
|
758
|
-
|
|
759
|
-
return cilindro;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// Gestisco il posizionamento della targhetta e delle linee
|
|
763
|
-
if (targhettaTroppoGrande) {
|
|
764
|
-
// Posiziono la targhetta sopra la linea
|
|
765
|
-
targhetta.position.set(distanza / 2, altezza, 0);
|
|
766
|
-
|
|
767
|
-
// Creo una singola linea orizzontale come cilindro
|
|
768
|
-
const puntoInizio = new THREE.Vector3(...pp(0, 0));
|
|
769
|
-
const puntoFine = new THREE.Vector3(...pp(distanza, 0));
|
|
770
|
-
const lineaOrizzontale = getCilindro(puntoInizio, puntoFine, spessoreLinea);
|
|
771
|
-
quotaGroup.add(lineaOrizzontale);
|
|
772
|
-
} else {
|
|
773
|
-
// Calcolo lo spazio necessario per la targhetta
|
|
774
|
-
const spazioTarghetta = dimx + delta;
|
|
775
|
-
const inizioTarghetta = (distanza - spazioTarghetta) / 2;
|
|
776
|
-
const fineTarghetta = inizioTarghetta + spazioTarghetta;
|
|
777
|
-
|
|
778
|
-
// Creo due linee orizzontali separate come cilindri
|
|
779
|
-
const puntoInizio1 = new THREE.Vector3(...pp(0, 0));
|
|
780
|
-
const puntoFine1 = new THREE.Vector3(...pp(inizioTarghetta, 0));
|
|
781
|
-
const lineaOrizzontale1 = getCilindro(puntoInizio1, puntoFine1, spessoreLinea);
|
|
782
|
-
quotaGroup.add(lineaOrizzontale1);
|
|
783
|
-
|
|
784
|
-
const puntoInizio2 = new THREE.Vector3(...pp(fineTarghetta, 0));
|
|
785
|
-
const puntoFine2 = new THREE.Vector3(...pp(distanza, 0));
|
|
786
|
-
const lineaOrizzontale2 = getCilindro(puntoInizio2, puntoFine2, spessoreLinea);
|
|
787
|
-
quotaGroup.add(lineaOrizzontale2);
|
|
788
|
-
|
|
789
|
-
// Posiziono la targhetta al centro
|
|
790
|
-
targhetta.position.set(...pp(distanza / 2, 0));
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Creo le linee verticali alle estremità come cilindri
|
|
794
|
-
// Calcolo l'altezza delle linee verticali in base all'offset
|
|
795
|
-
const h1 = offset > altezza / 2 ? -offset : -altezza / 2;
|
|
796
|
-
const h2 = -offset > altezza / 2 ? -offset : altezza / 2;
|
|
797
|
-
|
|
798
|
-
// Creo le linee verticali - semplificato per tutti i piani usando pp
|
|
799
|
-
const puntoInizioV1 = new THREE.Vector3(...pp(0, h1));
|
|
800
|
-
const puntoFineV1 = new THREE.Vector3(...pp(0, h2));
|
|
801
|
-
const puntoInizioV2 = new THREE.Vector3(...pp(distanza, h1));
|
|
802
|
-
const puntoFineV2 = new THREE.Vector3(...pp(distanza, h2));
|
|
803
|
-
|
|
804
|
-
const lineaVerticale1 = getCilindro(puntoInizioV1, puntoFineV1, spessoreLinea);
|
|
805
|
-
quotaGroup.add(lineaVerticale1);
|
|
806
|
-
|
|
807
|
-
const lineaVerticale2 = getCilindro(puntoInizioV2, puntoFineV2, spessoreLinea);
|
|
808
|
-
quotaGroup.add(lineaVerticale2);
|
|
809
|
-
|
|
810
|
-
// Creo una funzione di aggiornamento per far sì che la targhetta sia sempre rivolta verso la camera
|
|
811
|
-
targhetta.userData.updateOrientation = function (camera) {
|
|
812
|
-
if (camera) {
|
|
813
|
-
this.quaternion.copy(camera.quaternion);
|
|
814
|
-
}
|
|
815
|
-
};
|
|
816
|
-
|
|
817
|
-
// Aggiungo la targhetta al gruppo
|
|
818
|
-
quotaGroup.add(targhetta);
|
|
819
|
-
|
|
820
|
-
// Posiziono e ruoto il gruppo in base al piano scelto
|
|
821
|
-
if (piano === 'xy') {
|
|
822
|
-
quotaGroup.position.set(x1 + offsetX, y1 + offsetY, 0);
|
|
823
|
-
quotaGroup.rotation.z = angolo;
|
|
824
|
-
} else if (piano === 'xz') {
|
|
825
|
-
quotaGroup.position.set(x1 + offsetX, 0, y1 + offsetY);
|
|
826
|
-
quotaGroup.rotation.y = -angolo; // Rotazione attorno all'asse Y per il piano XZ
|
|
827
|
-
} else if (piano === 'yz') {
|
|
828
|
-
quotaGroup.position.set(0, x1 + offsetX, y1 + offsetY);
|
|
829
|
-
quotaGroup.rotation.x = angolo; // Rotazione attorno all'asse X per il piano YZ
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
quotaGroup.traverse(function (object) {
|
|
834
|
-
object.layers.set(layer);
|
|
835
|
-
});
|
|
836
|
-
|
|
837
|
-
return quotaGroup;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
function cylgeometry(gcad, h, r, rtop, segments = 16) {
|
|
841
|
-
if (!r || !h) return null;
|
|
842
|
-
let ky = hash(`cy${h}|${r}|${rtop || r}|${segments}`);
|
|
843
|
-
if (!gcad.geo[ky]) {
|
|
844
|
-
const geometry = new THREE.CylinderGeometry(rtop || r, r, h, segments);
|
|
845
|
-
gcad.geo[ky] = geometry;
|
|
846
|
-
}
|
|
847
|
-
return gcad.geo[ky];
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
|
|
76
|
+
*/function getquota(gcad,testo,x1,y1,x2,y2,altezza=30,offset=0,options={}){const{piano:piano="xy",layer:layer=22,delta:delta=5,spessoreLinea:spessoreLinea=3}=options,pp=(x,y)=>"xz"===piano?[x,0,y]:"yz"===piano?[0,x,y]:[x,y,0],quotaGroup=new THREE.Group;quotaGroup.name="quota";const dx=x2-x1,dy=y2-y1,distanza=Math.sqrt(dx*dx+dy*dy),angolo=Math.atan2(dy,dx),offsetX=-Math.sin(angolo)*offset,offsetY=Math.cos(angolo)*offset,targhetta=gettarghetta(gcad,testo||`${distanza.toFixed(1)}`,2*altezza,{noSfondo:!0,layer:layer,forcey:!0}),{dimx:dimx,dimy:dimy}=targhetta.userData;function getCilindro(puntoInizio,puntoFine,spessore){const direzione=(new THREE.Vector3).subVectors(puntoFine,puntoInizio),lunghezza=direzione.length();let geometria,ky=hash(`c:${spessore}|${lunghezza}`);gcad.geo[ky]?geometria=gcad.geo[ky]:(geometria=new THREE.CylinderGeometry(spessore/2,spessore/2,lunghezza,3,1,!0),gcad.geo[ky]=geometria);const cilindro=new THREE.Mesh(geometria,mblack);cilindro.position.copy(puntoInizio).add(direzione.multiplyScalar(.5));const punto=(new THREE.Vector3).copy(puntoFine);return cilindro.lookAt(punto),cilindro.rotateX(Math.PI/2),cilindro}if(dimx>distanza-delta){targhetta.position.set(distanza/2,altezza,0);const lineaOrizzontale=getCilindro(new THREE.Vector3(...pp(0,0)),new THREE.Vector3(...pp(distanza,0)),spessoreLinea);quotaGroup.add(lineaOrizzontale)}else{const spazioTarghetta=dimx+delta,inizioTarghetta=(distanza-spazioTarghetta)/2,fineTarghetta=inizioTarghetta+spazioTarghetta,lineaOrizzontale1=getCilindro(new THREE.Vector3(...pp(0,0)),new THREE.Vector3(...pp(inizioTarghetta,0)),spessoreLinea);quotaGroup.add(lineaOrizzontale1);const lineaOrizzontale2=getCilindro(new THREE.Vector3(...pp(fineTarghetta,0)),new THREE.Vector3(...pp(distanza,0)),spessoreLinea);quotaGroup.add(lineaOrizzontale2),targhetta.position.set(...pp(distanza/2,0))}const h1=offset>altezza/2?-offset:-altezza/2,h2=-offset>altezza/2?-offset:altezza/2,puntoInizioV1=new THREE.Vector3(...pp(0,h1)),puntoFineV1=new THREE.Vector3(...pp(0,h2)),puntoInizioV2=new THREE.Vector3(...pp(distanza,h1)),puntoFineV2=new THREE.Vector3(...pp(distanza,h2)),lineaVerticale1=getCilindro(puntoInizioV1,puntoFineV1,spessoreLinea);quotaGroup.add(lineaVerticale1);const lineaVerticale2=getCilindro(puntoInizioV2,puntoFineV2,spessoreLinea);return quotaGroup.add(lineaVerticale2),targhetta.userData.updateOrientation=function(camera){camera&&this.quaternion.copy(camera.quaternion)},quotaGroup.add(targhetta),"xy"===piano?(quotaGroup.position.set(x1+offsetX,y1+offsetY,0),quotaGroup.rotation.z=angolo):"xz"===piano?(quotaGroup.position.set(x1+offsetX,0,y1+offsetY),quotaGroup.rotation.y=-angolo):"yz"===piano&&(quotaGroup.position.set(0,x1+offsetX,y1+offsetY),quotaGroup.rotation.x=angolo),quotaGroup.traverse((function(object){object.layers.set(layer)})),quotaGroup}
|
|
851
77
|
/**
|
|
852
78
|
* Crea un cilindro 3D con orientamento personalizzabile
|
|
853
79
|
* @param {string} ori - Orientamento (X/L, Y/A, Z/P)
|
|
@@ -858,44 +84,7 @@ function cylgeometry(gcad, h, r, rtop, segments = 16) {
|
|
|
858
84
|
* @param {Object} options - Opzioni di configurazione
|
|
859
85
|
* @returns {Promise<THREE.Group>} Gruppo contenente il cilindro
|
|
860
86
|
*/
|
|
861
|
-
async function getcilindro(gcad,
|
|
862
|
-
if (!options) options = {};
|
|
863
|
-
if (!Array.isArray(mats)) mats = [mats];
|
|
864
|
-
|
|
865
|
-
const cyl = cylgeometry(gcad, h, r1, r2, options.sides || 16);
|
|
866
|
-
|
|
867
|
-
let grp = new THREE.Group();
|
|
868
|
-
|
|
869
|
-
// Gestione orientamento
|
|
870
|
-
switch ((ori || '').trim().toUpperCase()) {
|
|
871
|
-
case 'X':
|
|
872
|
-
case 'L':
|
|
873
|
-
grp.position.set(h / 2, 0, 0);
|
|
874
|
-
grp.rotation.z = Math.PI / 2;
|
|
875
|
-
break;
|
|
876
|
-
case 'Z':
|
|
877
|
-
case 'P':
|
|
878
|
-
grp.position.set(0, 0, h / 2);
|
|
879
|
-
grp.rotation.x = Math.PI / 2;
|
|
880
|
-
break;
|
|
881
|
-
default:
|
|
882
|
-
grp.position.set(0, h / 2, 0);
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
if (cyl) {
|
|
886
|
-
if (!options.nolines) {
|
|
887
|
-
let segments = edgesfromgeometry(cyl);
|
|
888
|
-
grp.add(segments);
|
|
889
|
-
}
|
|
890
|
-
|
|
891
|
-
const matTop = mats[0] || mwhite$1;
|
|
892
|
-
const matBottom = mats[1] || matTop;
|
|
893
|
-
const matSides = mats[2] || matTop;
|
|
894
|
-
grp.add(getmesh(cyl, [matTop, matBottom, matSides]));
|
|
895
|
-
}
|
|
896
|
-
return grp;
|
|
897
|
-
}
|
|
898
|
-
|
|
87
|
+
async function getcilindro(gcad,ori,h,r1,r2,mats=mwhite$1,options){options||(options={}),Array.isArray(mats)||(mats=[mats]);const cyl=function cylgeometry(gcad,h,r,rtop,segments=16){if(!r||!h)return null;let ky=hash(`cy${h}|${r}|${rtop||r}|${segments}`);if(!gcad.geo[ky]){const geometry=new THREE.CylinderGeometry(rtop||r,r,h,segments);gcad.geo[ky]=geometry}return gcad.geo[ky]}(gcad,h,r1,r2,options.sides||16);let grp=new THREE.Group;switch((ori||"").trim().toUpperCase()){case"X":case"L":grp.position.set(h/2,0,0),grp.rotation.z=Math.PI/2;break;case"Z":case"P":grp.position.set(0,0,h/2),grp.rotation.x=Math.PI/2;break;default:grp.position.set(0,h/2,0)}if(cyl){if(!options.nolines){let segments=edgesfromgeometry(cyl);grp.add(segments)}const matTop=mats[0]||mwhite$1,matBottom=mats[1]||matTop,matSides=mats[2]||matTop;grp.add(getmesh(cyl,[matTop,matBottom,matSides]))}return grp}
|
|
899
88
|
/**
|
|
900
89
|
* Crea un box 3D con linee di bordo opzionali
|
|
901
90
|
* @param {number} x - Larghezza
|
|
@@ -904,663 +93,12 @@ async function getcilindro(gcad, ori, h, r1, r2, mats = mwhite$1, options) {
|
|
|
904
93
|
* @param {THREE.Material} mat - Materiale da applicare
|
|
905
94
|
* @param {Object} options - Opzioni di configurazione
|
|
906
95
|
* @returns {Promise<THREE.Group>} Gruppo contenente il box
|
|
907
|
-
*/
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
function boxgeometry(gcad, x, y, z) {
|
|
911
|
-
let ky = hash(`b:${x}|${y}|${x}`);
|
|
912
|
-
if (!gcad.geo[ky]) {
|
|
913
|
-
const bx = new THREE.BoxGeometry(x, y, z);
|
|
914
|
-
gcad.geo[ky] = bx;
|
|
915
|
-
}
|
|
916
|
-
return gcad.geo[ky];
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
function getface(gcad, x, y, mat, scaled = false) { // rimosso async
|
|
920
|
-
let ky = hash(`f:${x}|${y}|${scaled ? 1 : 0}`);
|
|
921
|
-
if (!gcad.geo[ky]) {
|
|
922
|
-
const geometry = new THREE.PlaneGeometry(x, y);
|
|
923
|
-
const uvs = geometry.attributes.uv;
|
|
924
|
-
const positions = geometry.attributes.position;
|
|
925
|
-
for (let i = 0; i < positions.count; i++) {
|
|
926
|
-
uvs.setXY(i,
|
|
927
|
-
scaled ? positions.getX(i) : positions.getX(i) / (x * scaleunit),
|
|
928
|
-
scaled ? positions.getY(i) : positions.getY(i) / (y * scaleunit)
|
|
929
|
-
);
|
|
930
|
-
}
|
|
931
|
-
gcad.geo[ky] = geometry;
|
|
932
|
-
}
|
|
933
|
-
const plane = new THREE.Mesh(gcad.geo[ky], mat);
|
|
934
|
-
plane.position.set(x / 2, y / 2, 0);
|
|
935
|
-
if (mat?.transparent) {
|
|
936
|
-
plane.layers.set(2);
|
|
937
|
-
}
|
|
938
|
-
return plane;
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
async function getbox(gcad, x, y, z, mat, options) {
|
|
942
|
-
if (!options) options = {};
|
|
943
|
-
let grp = new THREE.Group();
|
|
944
|
-
const box = boxgeometry(gcad, x, y, z);
|
|
945
|
-
grp.position.set(x / 2, y / 2, z / 2);
|
|
946
|
-
if (!options.nolines) {
|
|
947
|
-
let segments = edgesfromgeometry(box);
|
|
948
|
-
grp.add(segments);
|
|
949
|
-
}
|
|
950
|
-
grp.add(getmesh(box, mat || mwhite$1));
|
|
951
|
-
return grp;
|
|
952
|
-
}
|
|
953
|
-
|
|
96
|
+
*/function getface(gcad,x,y,mat,scaled=!1){let ky=hash(`f:${x}|${y}|${scaled?1:0}`);if(!gcad.geo[ky]){const geometry=new THREE.PlaneGeometry(x,y),uvs=geometry.attributes.uv,positions=geometry.attributes.position;for(let i=0;i<positions.count;i++)uvs.setXY(i,scaled?positions.getX(i):positions.getX(i)/(.001*x),scaled?positions.getY(i):positions.getY(i)/(.001*y));gcad.geo[ky]=geometry}const plane=new THREE.Mesh(gcad.geo[ky],mat);return plane.position.set(x/2,y/2,0),mat?.transparent&&plane.layers.set(2),plane}async function getbox(gcad,x,y,z,mat,options){options||(options={});let grp=new THREE.Group;const box=function boxgeometry(gcad,x,y,z){let ky=hash(`b:${x}|${y}|${x}`);if(!gcad.geo[ky]){const bx=new THREE.BoxGeometry(x,y,z);gcad.geo[ky]=bx}return gcad.geo[ky]}(gcad,x,y,z);if(grp.position.set(x/2,y/2,z/2),!options.nolines){let segments=edgesfromgeometry(box);grp.add(segments)}return grp.add(getmesh(box,mat||mwhite$1)),grp}
|
|
954
97
|
/**
|
|
955
98
|
* @param {BufferGeometry} geometry
|
|
956
99
|
* @param {number} tolerance
|
|
957
100
|
* @return {BufferGeometry}
|
|
958
|
-
*/
|
|
959
|
-
function mergeVertices( geometry, tolerance = 1e-4 ) {
|
|
960
|
-
|
|
961
|
-
tolerance = Math.max( tolerance, Number.EPSILON );
|
|
962
|
-
|
|
963
|
-
// Generate an index buffer if the geometry doesn't have one, or optimize it
|
|
964
|
-
// if it's already available.
|
|
965
|
-
const hashToIndex = {};
|
|
966
|
-
const indices = geometry.getIndex();
|
|
967
|
-
const positions = geometry.getAttribute( 'position' );
|
|
968
|
-
const vertexCount = indices ? indices.count : positions.count;
|
|
969
|
-
|
|
970
|
-
// next value for triangle indices
|
|
971
|
-
let nextIndex = 0;
|
|
972
|
-
|
|
973
|
-
// attributes and new attribute arrays
|
|
974
|
-
const attributeNames = Object.keys( geometry.attributes );
|
|
975
|
-
const tmpAttributes = {};
|
|
976
|
-
const tmpMorphAttributes = {};
|
|
977
|
-
const newIndices = [];
|
|
978
|
-
const getters = [ 'getX', 'getY', 'getZ', 'getW' ];
|
|
979
|
-
const setters = [ 'setX', 'setY', 'setZ', 'setW' ];
|
|
980
|
-
|
|
981
|
-
// Initialize the arrays, allocating space conservatively. Extra
|
|
982
|
-
// space will be trimmed in the last step.
|
|
983
|
-
for ( let i = 0, l = attributeNames.length; i < l; i ++ ) {
|
|
984
|
-
|
|
985
|
-
const name = attributeNames[ i ];
|
|
986
|
-
const attr = geometry.attributes[ name ];
|
|
987
|
-
|
|
988
|
-
tmpAttributes[ name ] = new attr.constructor(
|
|
989
|
-
new attr.array.constructor( attr.count * attr.itemSize ),
|
|
990
|
-
attr.itemSize,
|
|
991
|
-
attr.normalized
|
|
992
|
-
);
|
|
993
|
-
|
|
994
|
-
const morphAttributes = geometry.morphAttributes[ name ];
|
|
995
|
-
if ( morphAttributes ) {
|
|
996
|
-
|
|
997
|
-
if ( ! tmpMorphAttributes[ name ] ) tmpMorphAttributes[ name ] = [];
|
|
998
|
-
morphAttributes.forEach( ( morphAttr, i ) => {
|
|
999
|
-
|
|
1000
|
-
const array = new morphAttr.array.constructor( morphAttr.count * morphAttr.itemSize );
|
|
1001
|
-
tmpMorphAttributes[ name ][ i ] = new morphAttr.constructor( array, morphAttr.itemSize, morphAttr.normalized );
|
|
1002
|
-
|
|
1003
|
-
} );
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
// convert the error tolerance to an amount of decimal places to truncate to
|
|
1010
|
-
const halfTolerance = tolerance * 0.5;
|
|
1011
|
-
const exponent = Math.log10( 1 / tolerance );
|
|
1012
|
-
const hashMultiplier = Math.pow( 10, exponent );
|
|
1013
|
-
const hashAdditive = halfTolerance * hashMultiplier;
|
|
1014
|
-
for ( let i = 0; i < vertexCount; i ++ ) {
|
|
1015
|
-
|
|
1016
|
-
const index = indices ? indices.getX( i ) : i;
|
|
1017
|
-
|
|
1018
|
-
// Generate a hash for the vertex attributes at the current index 'i'
|
|
1019
|
-
let hash = '';
|
|
1020
|
-
for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
|
|
1021
|
-
|
|
1022
|
-
const name = attributeNames[ j ];
|
|
1023
|
-
const attribute = geometry.getAttribute( name );
|
|
1024
|
-
const itemSize = attribute.itemSize;
|
|
1025
|
-
|
|
1026
|
-
for ( let k = 0; k < itemSize; k ++ ) {
|
|
1027
|
-
|
|
1028
|
-
// double tilde truncates the decimal value
|
|
1029
|
-
hash += `${ ~ ~ ( attribute[ getters[ k ] ]( index ) * hashMultiplier + hashAdditive ) },`;
|
|
1030
|
-
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
// Add another reference to the vertex if it's already
|
|
1036
|
-
// used by another index
|
|
1037
|
-
if ( hash in hashToIndex ) {
|
|
1038
|
-
|
|
1039
|
-
newIndices.push( hashToIndex[ hash ] );
|
|
1040
|
-
|
|
1041
|
-
} else {
|
|
1042
|
-
|
|
1043
|
-
// copy data to the new index in the temporary attributes
|
|
1044
|
-
for ( let j = 0, l = attributeNames.length; j < l; j ++ ) {
|
|
1045
|
-
|
|
1046
|
-
const name = attributeNames[ j ];
|
|
1047
|
-
const attribute = geometry.getAttribute( name );
|
|
1048
|
-
const morphAttributes = geometry.morphAttributes[ name ];
|
|
1049
|
-
const itemSize = attribute.itemSize;
|
|
1050
|
-
const newArray = tmpAttributes[ name ];
|
|
1051
|
-
const newMorphArrays = tmpMorphAttributes[ name ];
|
|
1052
|
-
|
|
1053
|
-
for ( let k = 0; k < itemSize; k ++ ) {
|
|
1054
|
-
|
|
1055
|
-
const getterFunc = getters[ k ];
|
|
1056
|
-
const setterFunc = setters[ k ];
|
|
1057
|
-
newArray[ setterFunc ]( nextIndex, attribute[ getterFunc ]( index ) );
|
|
1058
|
-
|
|
1059
|
-
if ( morphAttributes ) {
|
|
1060
|
-
|
|
1061
|
-
for ( let m = 0, ml = morphAttributes.length; m < ml; m ++ ) {
|
|
1062
|
-
|
|
1063
|
-
newMorphArrays[ m ][ setterFunc ]( nextIndex, morphAttributes[ m ][ getterFunc ]( index ) );
|
|
1064
|
-
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
hashToIndex[ hash ] = nextIndex;
|
|
1074
|
-
newIndices.push( nextIndex );
|
|
1075
|
-
nextIndex ++;
|
|
1076
|
-
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
// generate result BufferGeometry
|
|
1082
|
-
const result = geometry.clone();
|
|
1083
|
-
for ( const name in geometry.attributes ) {
|
|
1084
|
-
|
|
1085
|
-
const tmpAttribute = tmpAttributes[ name ];
|
|
1086
|
-
|
|
1087
|
-
result.setAttribute( name, new tmpAttribute.constructor(
|
|
1088
|
-
tmpAttribute.array.slice( 0, nextIndex * tmpAttribute.itemSize ),
|
|
1089
|
-
tmpAttribute.itemSize,
|
|
1090
|
-
tmpAttribute.normalized,
|
|
1091
|
-
) );
|
|
1092
|
-
|
|
1093
|
-
if ( ! ( name in tmpMorphAttributes ) ) continue;
|
|
1094
|
-
|
|
1095
|
-
for ( let j = 0; j < tmpMorphAttributes[ name ].length; j ++ ) {
|
|
1096
|
-
|
|
1097
|
-
const tmpMorphAttribute = tmpMorphAttributes[ name ][ j ];
|
|
1098
|
-
|
|
1099
|
-
result.morphAttributes[ name ][ j ] = new tmpMorphAttribute.constructor(
|
|
1100
|
-
tmpMorphAttribute.array.slice( 0, nextIndex * tmpMorphAttribute.itemSize ),
|
|
1101
|
-
tmpMorphAttribute.itemSize,
|
|
1102
|
-
tmpMorphAttribute.normalized,
|
|
1103
|
-
);
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
}
|
|
1108
|
-
|
|
1109
|
-
// indices
|
|
1110
|
-
|
|
1111
|
-
result.setIndex( newIndices );
|
|
1112
|
-
|
|
1113
|
-
return result;
|
|
1114
|
-
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
function pannellogeometry(gcad, l, a, p, r1 = 0, r2 = 0, r3 = 0, r4 = 0, b = 0, npt = 2) {
|
|
1118
|
-
let ky = hash(`pg--${l}|${a}|${p}|${r1}|${r2}|${r3}|${r4}|${b}|${npt}`);
|
|
1119
|
-
l -= 0.01;
|
|
1120
|
-
a -= 0.01;
|
|
1121
|
-
p -= 0.01;
|
|
1122
|
-
|
|
1123
|
-
if (!gcad.geo[ky]) {
|
|
1124
|
-
l -= b * 2;
|
|
1125
|
-
a -= b * 2;
|
|
1126
|
-
p -= b * 2;
|
|
1127
|
-
let tm = getshape();
|
|
1128
|
-
|
|
1129
|
-
const vv = [
|
|
1130
|
-
{ x: r4, y: 0 }, // 0
|
|
1131
|
-
{ x: a - r1, y: 0 }, //1
|
|
1132
|
-
{ x: a, y: r1 }, //2
|
|
1133
|
-
{ x: a, y: p - r2 }, //3
|
|
1134
|
-
{ x: a - r2, y: p }, //4
|
|
1135
|
-
{ x: r3, y: p }, //5
|
|
1136
|
-
{ x: 0, y: p - r3 }, //6
|
|
1137
|
-
{ x: 0, y: r4 }, //7
|
|
1138
|
-
];
|
|
1139
|
-
tm.addpt(vv[0]);
|
|
1140
|
-
tm.addpt(vv[1]);
|
|
1141
|
-
if (r1) {
|
|
1142
|
-
tm.addpt(raccordabezier(vv[0], vv[1], vv[2], vv[3], npt));
|
|
1143
|
-
}
|
|
1144
|
-
tm.addpt(vv[2]);
|
|
1145
|
-
tm.addpt(vv[3]);
|
|
1146
|
-
if (r2) {
|
|
1147
|
-
tm.addpt(raccordabezier(vv[2], vv[3], vv[4], vv[5], npt));
|
|
1148
|
-
}
|
|
1149
|
-
tm.addpt(vv[4]);
|
|
1150
|
-
tm.addpt(vv[5]);
|
|
1151
|
-
if (r3) {
|
|
1152
|
-
tm.addpt(raccordabezier(vv[4], vv[5], vv[6], vv[7], npt));
|
|
1153
|
-
}
|
|
1154
|
-
tm.addpt(vv[6]);
|
|
1155
|
-
tm.addpt(vv[7]);
|
|
1156
|
-
if (r2) {
|
|
1157
|
-
tm.addpt(raccordabezier(vv[6], vv[7], vv[0], vv[1], npt));
|
|
1158
|
-
}
|
|
1159
|
-
tm.removeduplicate(0.01);
|
|
1160
|
-
let pts = tm.pt;
|
|
1161
|
-
//pts = tm.computenormals(0.01);
|
|
1162
|
-
//const pts = tm.computenormals(0);
|
|
1163
|
-
const shape = new THREE.Shape();
|
|
1164
|
-
shape.moveTo(pts[0].x, pts[0].y);
|
|
1165
|
-
for (let i = 1; i < pts.length; i++) {
|
|
1166
|
-
shape.lineTo(pts[i].x, pts[i].y);
|
|
1167
|
-
}
|
|
1168
|
-
shape.lineTo(pts[0].x, pts[0].y);
|
|
1169
|
-
const extrudeSettings = {
|
|
1170
|
-
depth: l, // Estrusione lungo l'asse Z
|
|
1171
|
-
bevelEnabled: b > 0, // Attiva il bevel se r è valido
|
|
1172
|
-
bevelThickness: b,
|
|
1173
|
-
bevelSize: b,
|
|
1174
|
-
bevelSegments: 1,
|
|
1175
|
-
};
|
|
1176
|
-
let xgeo = new THREE.ExtrudeGeometry(shape, extrudeSettings);
|
|
1177
|
-
xgeo = mergeVertices(xgeo);
|
|
1178
|
-
xgeo.computeVertexNormals();
|
|
1179
|
-
const uv = xgeo.attributes.uv;
|
|
1180
|
-
let x1 = Math.random() * l;
|
|
1181
|
-
let y1 = Math.random() * (a + p);
|
|
1182
|
-
for (let i = 0; i < uv.count; i++) {
|
|
1183
|
-
uv.setXY(i,
|
|
1184
|
-
uv.getX(i) + x1,
|
|
1185
|
-
uv.getY(i) + y1
|
|
1186
|
-
);
|
|
1187
|
-
}
|
|
1188
|
-
uv.needsUpdate = true;
|
|
1189
|
-
gcad.geo[ky] = xgeo;
|
|
1190
|
-
|
|
1191
|
-
}
|
|
1192
|
-
return gcad.geo[ky];
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
async function getpannello(gcad, orientamento, x, y, z, mat1, mat2, options) {
|
|
1198
|
-
if (!options) options = {};
|
|
1199
|
-
let { r, r1, r2, r3, r4, b, npt } = options;
|
|
1200
|
-
|
|
1201
|
-
// Normalizza i raggi degli angoli
|
|
1202
|
-
r1 = r1 || r || 0;
|
|
1203
|
-
r2 = r2 || r || 0;
|
|
1204
|
-
r3 = r3 || r || 0;
|
|
1205
|
-
r4 = r4 || r || 0;
|
|
1206
|
-
npt = npt || 2;
|
|
1207
|
-
b = b || 0;
|
|
1208
|
-
if (b >= x / 2 || b >= y / 2 || b >= z / 2) b = 0;
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
// Calcola dimensioni in base all'orientamento
|
|
1212
|
-
let l, a, p;
|
|
1213
|
-
orientamento = orientamento.trim().toUpperCase();
|
|
1214
|
-
l = orientamento[0] == 'L' ? x : orientamento[0] == 'A' ? y : z;
|
|
1215
|
-
a = orientamento[1] == 'L' ? x : orientamento[1] == 'A' ? y : z;
|
|
1216
|
-
p = orientamento[2] == 'L' ? x : orientamento[2] == 'A' ? y : z;
|
|
1217
|
-
|
|
1218
|
-
let grp = new THREE.Group();
|
|
1219
|
-
|
|
1220
|
-
// Aggiunge linee di bordo se richiesto
|
|
1221
|
-
if (!options.nolines) {
|
|
1222
|
-
let segments = edgesfromgeometry(new THREE.BoxGeometry(x, y, z));
|
|
1223
|
-
segments.position.set(x / 2, y / 2, z / 2);
|
|
1224
|
-
grp.add(segments);
|
|
1225
|
-
}
|
|
1226
|
-
let geometry = pannellogeometry(gcad, l, a, p, r1, r2, r3, r4, b, npt);
|
|
1227
|
-
|
|
1228
|
-
let mesh = getmesh(geometry, [mat2 || mgreen, mat1 || mwhite$1]);
|
|
1229
|
-
mesh.name = 'pannello';
|
|
1230
|
-
mesh.layers.set(1);
|
|
1231
|
-
let m2 = mesh;
|
|
1232
|
-
if (b) {
|
|
1233
|
-
|
|
1234
|
-
mesh.position.set(b, b, b);
|
|
1235
|
-
m2 = new THREE.Group();
|
|
1236
|
-
m2.add(mesh);
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
mesh = meshrotate(orientamento, m2, l, a, p);
|
|
1240
|
-
grp.add(mesh);
|
|
1241
|
-
return grp;
|
|
1242
|
-
}
|
|
1243
|
-
|
|
1244
|
-
function infoestrudi(shape, hshape, pts, options) {
|
|
1245
|
-
if (!options) options = {};
|
|
1246
|
-
if (!pts) pts = [];
|
|
1247
|
-
// calcola le posizioni;
|
|
1248
|
-
let p0 = options.p0 || 0;
|
|
1249
|
-
let coeffbase1 = Math.tan(options.coeffbase1 * PIF || 0);
|
|
1250
|
-
let coeffbase2 = Math.tan(options.coeffbase2 * PIF || 0);
|
|
1251
|
-
let p1 = hshape || options.p1 || 20;
|
|
1252
|
-
let coefftop1 = Math.tan(options.coefftop1 * PIF || 0);
|
|
1253
|
-
let coefftop2 = Math.tan(options.coefftop2 * PIF || 0);
|
|
1254
|
-
// calcola x,y minimo e massimo della shape
|
|
1255
|
-
let { mi, ma } = shape.pt.reduce((t, e) => {
|
|
1256
|
-
// e.x,e.y punto
|
|
1257
|
-
t.mi.x = Math.min(t.mi.x, e.x);
|
|
1258
|
-
t.mi.y = Math.min(t.mi.y, e.y);
|
|
1259
|
-
t.ma.x = Math.max(t.ma.x, e.x);
|
|
1260
|
-
t.ma.y = Math.max(t.ma.y, e.y);
|
|
1261
|
-
return t;
|
|
1262
|
-
}, { mi: { x: 1e9, y: 1e9 }, ma: { x: -1e9, y: -1e9 } });
|
|
1263
|
-
// calcola lunghezza minima e massima, e calcola la z per ogni punto
|
|
1264
|
-
let lmax = 0;
|
|
1265
|
-
let lmin = 1e9;
|
|
1266
|
-
function getpars(p) {
|
|
1267
|
-
p.z1 = getzeta(p.x, p.y, p0, coeffbase1, coeffbase2);
|
|
1268
|
-
p.z2 = getzeta(p.x, p.y, p1, coefftop1, coefftop2);
|
|
1269
|
-
p.l = Math.abs(p.z2 - p.z1);
|
|
1270
|
-
}
|
|
1271
|
-
for (let p of pts) {
|
|
1272
|
-
getpars(p);
|
|
1273
|
-
}
|
|
1274
|
-
getpars(mi);
|
|
1275
|
-
getpars(ma);
|
|
1276
|
-
let rect = [{ x: mi.x, y: mi.y },
|
|
1277
|
-
{ x: ma.x, y: mi.y },
|
|
1278
|
-
{ x: ma.x, y: ma.y },
|
|
1279
|
-
{ x: mi.x, y: ma.y }];
|
|
1280
|
-
for (let r of rect) {
|
|
1281
|
-
getpars(r);
|
|
1282
|
-
if (r.l > lmax) lmax = r.l;
|
|
1283
|
-
if (r.l < lmin) lmin = r.l;
|
|
1284
|
-
}
|
|
1285
|
-
return {
|
|
1286
|
-
aini: options.coeffbase1 || 0,
|
|
1287
|
-
aini2: options.coeffbase2 || 0,
|
|
1288
|
-
afin: options.coefftop1 || 0,
|
|
1289
|
-
afin2: options.coefftop2 || 0,
|
|
1290
|
-
|
|
1291
|
-
pts,
|
|
1292
|
-
mi,
|
|
1293
|
-
ma,
|
|
1294
|
-
rect,
|
|
1295
|
-
dimx: ma.x - mi.x,
|
|
1296
|
-
dimy: ma.y - mi.y,
|
|
1297
|
-
lnom: p1 - p0,
|
|
1298
|
-
lmax,
|
|
1299
|
-
lmin,
|
|
1300
|
-
lmed: (lmax + lmin) / 2
|
|
1301
|
-
}
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
function getzeta(x, y, zbase, cx, cy) {
|
|
1306
|
-
return x * cx + y * cy + zbase;
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
function sidegeomfromshapes(gcad, ky, s1, s2, invert = false) {
|
|
1310
|
-
// la chiave per questa funzione è complessa e va ricavata prima!
|
|
1311
|
-
|
|
1312
|
-
if (!s1 || !s2) return;
|
|
1313
|
-
if (s1.length !== s2.length) throw new Error('shapes with different length');
|
|
1314
|
-
if (s1.length < 2) throw new Error('I percorsi devono contenere almeno due punti ciascuno.');
|
|
1315
|
-
if (!gcad.geo[ky]) {
|
|
1316
|
-
const positions = [];
|
|
1317
|
-
// const normals = [];
|
|
1318
|
-
const uvs = [];
|
|
1319
|
-
const indices = [];
|
|
1320
|
-
const np = s1.length;
|
|
1321
|
-
const addpts = (ss) => {
|
|
1322
|
-
for (const s of ss) {
|
|
1323
|
-
positions.push(s.x, s.y, s.z);
|
|
1324
|
-
// normals.push(s.nx, s.ny, s.nz);
|
|
1325
|
-
uvs.push(s.u, s.v);
|
|
1326
|
-
}
|
|
1327
|
-
};
|
|
1328
|
-
|
|
1329
|
-
// Funzione per verificare se due punti sono uguali
|
|
1330
|
-
const equalpos = (p1, p2) => p1.x === p2.x && p1.y === p2.y && p1.z === p2.z;
|
|
1331
|
-
|
|
1332
|
-
const addindexquad = (i1, i2, i3, i4) => {
|
|
1333
|
-
if (invert) {
|
|
1334
|
-
indices.push(i1, i3, i2, i3, i4, i2);
|
|
1335
|
-
|
|
1336
|
-
} else {
|
|
1337
|
-
indices.push(i1, i2, i3, i3, i2, i4);
|
|
1338
|
-
}
|
|
1339
|
-
};
|
|
1340
|
-
|
|
1341
|
-
// Aggiungi tutti i punti di s1 e s2
|
|
1342
|
-
addpts(s1);
|
|
1343
|
-
addpts(s2);
|
|
1344
|
-
|
|
1345
|
-
// Creazione degli indici per i triangoli
|
|
1346
|
-
for (let i = 0; i < np - 1; i++) {
|
|
1347
|
-
const j = (i + 1); // raccorda sempre 'ultimo punto con il primo!
|
|
1348
|
-
|
|
1349
|
-
// Verifica se i punti consecutivi di s1 sono duplicati
|
|
1350
|
-
if (equalpos(s1[i], s1[j])) {
|
|
1351
|
-
continue;
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
// Aggiungi i triangoli del quadrilatero
|
|
1355
|
-
addindexquad(i, j, i + np, j + np);
|
|
1356
|
-
}
|
|
1357
|
-
|
|
1358
|
-
// Creazione della BufferGeometry
|
|
1359
|
-
const geometry = new THREE.BufferGeometry();
|
|
1360
|
-
|
|
1361
|
-
// Impostazione degli attributi
|
|
1362
|
-
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
1363
|
-
// geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
|
|
1364
|
-
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
|
1365
|
-
geometry.setIndex(indices);
|
|
1366
|
-
|
|
1367
|
-
//Computazione delle normali (opzionale, se non già calcolate)
|
|
1368
|
-
geometry.computeVertexNormals();
|
|
1369
|
-
gcad.geo[ky] = geometry;
|
|
1370
|
-
}
|
|
1371
|
-
return gcad.geo[ky];
|
|
1372
|
-
|
|
1373
|
-
}
|
|
1374
|
-
function bottomgeomfromshape(gcad, inverti, outer, holes, c = 0, a = 0, b = 0) {
|
|
1375
|
-
// determina chiave
|
|
1376
|
-
let ky = `bg:${c}|${a}${b}|${outer.key}|${inverti}`;
|
|
1377
|
-
for (var h of holes) ky = `${ky}|${h.key}`;
|
|
1378
|
-
ky = hash(ky);
|
|
1379
|
-
if (!gcad.geo[ky]) {
|
|
1380
|
-
let pos = [],
|
|
1381
|
-
hl = [];
|
|
1382
|
-
let triangles;
|
|
1383
|
-
let cc = outer.pt.length;
|
|
1384
|
-
pos = outer.vec;
|
|
1385
|
-
|
|
1386
|
-
if (Array.isArray(holes)) {
|
|
1387
|
-
for (var h of holes) {
|
|
1388
|
-
hl.push(cc);
|
|
1389
|
-
cc += h.pt.length;
|
|
1390
|
-
pos = [...pos, ...h.vec];
|
|
1391
|
-
}
|
|
1392
|
-
triangles = earcut(pos, hl);
|
|
1393
|
-
} else {
|
|
1394
|
-
triangles = earcut(pos);
|
|
1395
|
-
}
|
|
1396
|
-
const geometry = new THREE.BufferGeometry();
|
|
1397
|
-
const vertices = [];
|
|
1398
|
-
const uvs = [];
|
|
1399
|
-
const indices = [];
|
|
1400
|
-
// Aggiungi i vertici per ogni triangolo
|
|
1401
|
-
for (let i = 0; i < triangles.length; i++) {
|
|
1402
|
-
const index = triangles[i];
|
|
1403
|
-
const x = pos[index * 2];
|
|
1404
|
-
const y = -pos[index * 2 + 1];
|
|
1405
|
-
const z = getzeta(x, y, c, a, b);// a * x + b * y + c; // Calcolo della coordinata z
|
|
1406
|
-
|
|
1407
|
-
// Aggiungi il vertice 3D
|
|
1408
|
-
vertices.push(x, y, z);
|
|
1409
|
-
|
|
1410
|
-
// Imposta le coordinate UV (x, y)
|
|
1411
|
-
uvs.push(y, x);
|
|
1412
|
-
}
|
|
1413
|
-
for (let i = 0; i < triangles.length; i += 3) {
|
|
1414
|
-
if (inverti) {
|
|
1415
|
-
indices.push(i, i + 2, i + 1);
|
|
1416
|
-
} else {
|
|
1417
|
-
indices.push(i, i + 1, i + 2);
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
// Calcola la normale una sola volta
|
|
1424
|
-
//const v0 = new THREE.Vector3(pos[triangles[0] * 2], pos[triangles[0] * 2 + 1], a * pos[triangles[0] * 2] + b * pos[triangles[0] * 2 + 1] + c);
|
|
1425
|
-
//const v1 = new THREE.Vector3(pos[triangles[1] * 2], pos[triangles[1] * 2 + 1], a * pos[triangles[1] * 2] + b * pos[triangles[1] * 2 + 1] + c);
|
|
1426
|
-
//const v2 = new THREE.Vector3(pos[triangles[2] * 2], pos[triangles[2] * 2 + 1], a * pos[triangles[2] * 2] + b * pos[triangles[2] * 2 + 1] + c);
|
|
1427
|
-
|
|
1428
|
-
// Calcola i vettori del triangolo
|
|
1429
|
-
//const edge1 = new THREE.Vector3().subVectors(v1, v0);
|
|
1430
|
-
//const edge2 = new THREE.Vector3().subVectors(v2, v0);
|
|
1431
|
-
|
|
1432
|
-
// Calcola il prodotto vettoriale per ottenere la normale
|
|
1433
|
-
//const normal = new THREE.Vector3().crossVectors(edge1, edge2).normalize();
|
|
1434
|
-
|
|
1435
|
-
// Aggiungi la normale per ogni vertice del triangolo
|
|
1436
|
-
//const normals = [];
|
|
1437
|
-
//for (let i = 0; i < triangles.length; i++) {
|
|
1438
|
-
// normals.push(normal.x, normal.y, normal.z);
|
|
1439
|
-
//}
|
|
1440
|
-
|
|
1441
|
-
// Imposta i vertici nella geometria
|
|
1442
|
-
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
|
|
1443
|
-
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
|
1444
|
-
geometry.setIndex(indices);
|
|
1445
|
-
|
|
1446
|
-
//geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
|
|
1447
|
-
geometry.computeVertexNormals();
|
|
1448
|
-
gcad.geo[ky] = geometry;
|
|
1449
|
-
}
|
|
1450
|
-
return gcad.geo[ky];
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
function sidegeomfrompat(gcad, pbase, pat, invert) {
|
|
1455
|
-
let ky = `bsg:${pbase.key}|${pat.key}|${invert}`;
|
|
1456
|
-
ky = hash(ky);
|
|
1457
|
-
if (!gcad.geo[ky]) {
|
|
1458
|
-
const positions = [];
|
|
1459
|
-
const uvs = [];
|
|
1460
|
-
const indices = [];
|
|
1461
|
-
const np = pbase.length;
|
|
1462
|
-
const lp = pat.pt.length;
|
|
1463
|
-
// Funzione per verificare se due punti sono uguali
|
|
1464
|
-
const equalpos = (p1, p2) => p1.x === p2.x && p1.y === p2.y && p1.z === p2.z;
|
|
1465
|
-
|
|
1466
|
-
let l0 = 0;
|
|
1467
|
-
for (let ii = 0; ii < lp; ii++) {
|
|
1468
|
-
const addpts = (ss, mat, deltav) => {
|
|
1469
|
-
for (const s of ss) {
|
|
1470
|
-
let p = new THREE.Vector3(s.x, s.y, s.z);
|
|
1471
|
-
p.applyMatrix4(mat);
|
|
1472
|
-
positions.push(p.x, p.y, p.z);
|
|
1473
|
-
uvs.push(s.u, s.v + deltav);
|
|
1474
|
-
}
|
|
1475
|
-
};
|
|
1476
|
-
let seg1 = pat.infosegmento(ii, true);
|
|
1477
|
-
let obj = new THREE.Object3D();
|
|
1478
|
-
obj.position.set(seg1.x, 0, seg1.y);
|
|
1479
|
-
obj.rotation.set(0, (90 - seg1.ang) * PIF, 0);
|
|
1480
|
-
|
|
1481
|
-
//obj.position.set(seg1.y, -seg1.x, 0);
|
|
1482
|
-
|
|
1483
|
-
obj.updateMatrix();
|
|
1484
|
-
addpts(pbase, obj.matrix, l0);
|
|
1485
|
-
l0 += seg1.l;
|
|
1486
|
-
}
|
|
1487
|
-
|
|
1488
|
-
// Creazione degli indici per i triangoli
|
|
1489
|
-
for (let li = 0; li < lp - 1; li++) {
|
|
1490
|
-
|
|
1491
|
-
for (let i = 0; i < np - 1; i++) {
|
|
1492
|
-
const addindexquad = (i1, i2, i3, i4) => {
|
|
1493
|
-
if (invert) {
|
|
1494
|
-
indices.push(i1, i3, i2, i3, i4, i2);
|
|
1495
|
-
} else {
|
|
1496
|
-
indices.push(i1, i2, i3, i3, i2, i4);
|
|
1497
|
-
}
|
|
1498
|
-
};
|
|
1499
|
-
const j = (i + 1); // raccorda sempre 'ultimo punto con il primo!
|
|
1500
|
-
// Verifica se i punti consecutivi di s1 sono duplicati
|
|
1501
|
-
if (equalpos(pbase[i], pbase[j])) {
|
|
1502
|
-
continue;
|
|
1503
|
-
}
|
|
1504
|
-
addindexquad(
|
|
1505
|
-
i + li * np,
|
|
1506
|
-
j + li * np,
|
|
1507
|
-
i + (li + 1) * np,
|
|
1508
|
-
j + (li + 1) * np
|
|
1509
|
-
);
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
const geometry = new THREE.BufferGeometry();
|
|
1513
|
-
geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
|
|
1514
|
-
// geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
|
|
1515
|
-
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
|
1516
|
-
geometry.setIndex(indices);
|
|
1517
|
-
geometry.computeVertexNormals();
|
|
1518
|
-
gcad[ky] = geometry;
|
|
1519
|
-
}
|
|
1520
|
-
return gcad[ky];
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
function estrusorotate(orientamento, mesh, z = 0) {
|
|
1524
|
-
switch (orientamento.trim().toUpperCase().slice(0, 1)) {
|
|
1525
|
-
case 'A':
|
|
1526
|
-
mesh.rotation.x = -Math.PI / 2;
|
|
1527
|
-
break;
|
|
1528
|
-
case 'L':
|
|
1529
|
-
mesh.rotation.y = Math.PI / 2;
|
|
1530
|
-
mesh.rotation.z = Math.PI;
|
|
1531
|
-
break;
|
|
1532
|
-
// mesh.position.set(0, z, 0);
|
|
1533
|
-
// break;
|
|
1534
|
-
// case 'ALP':
|
|
1535
|
-
// mesh.rotation.x = Math.PI / 2;
|
|
1536
|
-
// mesh.rotation.y = Math.PI / 2;
|
|
1537
|
-
// break;
|
|
1538
|
-
// case 'APL':
|
|
1539
|
-
// mesh.rotation.z = Math.PI / 2;
|
|
1540
|
-
// mesh.rotation.y = Math.PI;
|
|
1541
|
-
// mesh.position.set(0, 0, z);
|
|
1542
|
-
// break;
|
|
1543
|
-
// case 'PLA':
|
|
1544
|
-
// //mesh.rotation.y = -Math.PI / 2;
|
|
1545
|
-
// mesh.rotation.x = -Math.PI / 2;
|
|
1546
|
-
// mesh.rotation.z = - Math.PI / 2;
|
|
1547
|
-
|
|
1548
|
-
// break;
|
|
1549
|
-
// case 'PAL':
|
|
1550
|
-
// mesh.rotation.y = -Math.PI / 2;
|
|
1551
|
-
// mesh.position.set(z, 0, 0);
|
|
1552
|
-
// break;
|
|
1553
|
-
// case 'LAP':
|
|
1554
|
-
// break;
|
|
1555
|
-
case 'P':
|
|
1556
|
-
mesh.rotation.z = Math.PI / 2;
|
|
1557
|
-
|
|
1558
|
-
break;
|
|
1559
|
-
}
|
|
1560
|
-
return mesh;
|
|
1561
|
-
}
|
|
1562
|
-
|
|
1563
|
-
|
|
101
|
+
*/function pannellogeometry(gcad,l,a,p,r1=0,r2=0,r3=0,r4=0,b=0,npt=2){let ky=hash(`pg--${l}|${a}|${p}|${r1}|${r2}|${r3}|${r4}|${b}|${npt}`);if(l-=.01,a-=.01,p-=.01,!gcad.geo[ky]){l-=2*b,a-=2*b,p-=2*b;let tm=getshape();const vv=[{x:r4,y:0},{x:a-r1,y:0},{x:a,y:r1},{x:a,y:p-r2},{x:a-r2,y:p},{x:r3,y:p},{x:0,y:p-r3},{x:0,y:r4}];tm.addpt(vv[0]),tm.addpt(vv[1]),r1&&tm.addpt(raccordabezier(vv[0],vv[1],vv[2],vv[3],npt)),tm.addpt(vv[2]),tm.addpt(vv[3]),r2&&tm.addpt(raccordabezier(vv[2],vv[3],vv[4],vv[5],npt)),tm.addpt(vv[4]),tm.addpt(vv[5]),r3&&tm.addpt(raccordabezier(vv[4],vv[5],vv[6],vv[7],npt)),tm.addpt(vv[6]),tm.addpt(vv[7]),r2&&tm.addpt(raccordabezier(vv[6],vv[7],vv[0],vv[1],npt)),tm.removeduplicate(.01);let pts=tm.pt;const shape=new THREE.Shape;shape.moveTo(pts[0].x,pts[0].y);for(let i=1;i<pts.length;i++)shape.lineTo(pts[i].x,pts[i].y);shape.lineTo(pts[0].x,pts[0].y);const extrudeSettings={depth:l,bevelEnabled:b>0,bevelThickness:b,bevelSize:b,bevelSegments:1};let xgeo=new THREE.ExtrudeGeometry(shape,extrudeSettings);xgeo=function mergeVertices(geometry,tolerance=1e-4){tolerance=Math.max(tolerance,Number.EPSILON);const hashToIndex={},indices=geometry.getIndex(),positions=geometry.getAttribute("position"),vertexCount=indices?indices.count:positions.count;let nextIndex=0;const attributeNames=Object.keys(geometry.attributes),tmpAttributes={},tmpMorphAttributes={},newIndices=[],getters=["getX","getY","getZ","getW"],setters=["setX","setY","setZ","setW"];for(let i=0,l=attributeNames.length;i<l;i++){const name=attributeNames[i],attr=geometry.attributes[name];tmpAttributes[name]=new attr.constructor(new attr.array.constructor(attr.count*attr.itemSize),attr.itemSize,attr.normalized);const morphAttributes=geometry.morphAttributes[name];morphAttributes&&(tmpMorphAttributes[name]||(tmpMorphAttributes[name]=[]),morphAttributes.forEach(((morphAttr,i)=>{const array=new morphAttr.array.constructor(morphAttr.count*morphAttr.itemSize);tmpMorphAttributes[name][i]=new morphAttr.constructor(array,morphAttr.itemSize,morphAttr.normalized)})))}const halfTolerance=.5*tolerance,exponent=Math.log10(1/tolerance),hashMultiplier=Math.pow(10,exponent),hashAdditive=halfTolerance*hashMultiplier;for(let i=0;i<vertexCount;i++){const index=indices?indices.getX(i):i;let hash="";for(let j=0,l=attributeNames.length;j<l;j++){const name=attributeNames[j],attribute=geometry.getAttribute(name),itemSize=attribute.itemSize;for(let k=0;k<itemSize;k++)hash+=~~(attribute[getters[k]](index)*hashMultiplier+hashAdditive)+","}if(hash in hashToIndex)newIndices.push(hashToIndex[hash]);else{for(let j=0,l=attributeNames.length;j<l;j++){const name=attributeNames[j],attribute=geometry.getAttribute(name),morphAttributes=geometry.morphAttributes[name],itemSize=attribute.itemSize,newArray=tmpAttributes[name],newMorphArrays=tmpMorphAttributes[name];for(let k=0;k<itemSize;k++){const getterFunc=getters[k],setterFunc=setters[k];if(newArray[setterFunc](nextIndex,attribute[getterFunc](index)),morphAttributes)for(let m=0,ml=morphAttributes.length;m<ml;m++)newMorphArrays[m][setterFunc](nextIndex,morphAttributes[m][getterFunc](index))}}hashToIndex[hash]=nextIndex,newIndices.push(nextIndex),nextIndex++}}const result=geometry.clone();for(const name in geometry.attributes){const tmpAttribute=tmpAttributes[name];if(result.setAttribute(name,new tmpAttribute.constructor(tmpAttribute.array.slice(0,nextIndex*tmpAttribute.itemSize),tmpAttribute.itemSize,tmpAttribute.normalized)),name in tmpMorphAttributes)for(let j=0;j<tmpMorphAttributes[name].length;j++){const tmpMorphAttribute=tmpMorphAttributes[name][j];result.morphAttributes[name][j]=new tmpMorphAttribute.constructor(tmpMorphAttribute.array.slice(0,nextIndex*tmpMorphAttribute.itemSize),tmpMorphAttribute.itemSize,tmpMorphAttribute.normalized)}}return result.setIndex(newIndices),result}(xgeo),xgeo.computeVertexNormals();const uv=xgeo.attributes.uv;let x1=Math.random()*l,y1=Math.random()*(a+p);for(let i=0;i<uv.count;i++)uv.setXY(i,uv.getX(i)+x1,uv.getY(i)+y1);uv.needsUpdate=!0,gcad.geo[ky]=xgeo}return gcad.geo[ky]}async function getpannello(gcad,orientamento,x,y,z,mat1,mat2,options){options||(options={});let l,a,p,{r:r,r1:r1,r2:r2,r3:r3,r4:r4,b:b,npt:npt}=options;r1=r1||r||0,r2=r2||r||0,r3=r3||r||0,r4=r4||r||0,npt=npt||2,b=b||0,(b>=x/2||b>=y/2||b>=z/2)&&(b=0),l="L"==(orientamento=orientamento.trim().toUpperCase())[0]?x:"A"==orientamento[0]?y:z,a="L"==orientamento[1]?x:"A"==orientamento[1]?y:z,p="L"==orientamento[2]?x:"A"==orientamento[2]?y:z;let grp=new THREE.Group;if(!options.nolines){let segments=edgesfromgeometry(new THREE.BoxGeometry(x,y,z));segments.position.set(x/2,y/2,z/2),grp.add(segments)}let mesh=getmesh(pannellogeometry(gcad,l,a,p,r1,r2,r3,r4,b,npt),[mat2||mgreen,mat1||mwhite$1]);mesh.name="pannello",mesh.layers.set(1);let m2=mesh;return b&&(mesh.position.set(b,b,b),m2=new THREE.Group,m2.add(mesh)),mesh=function meshrotate(orientamento,mesh,x=0,y=0,z=0){switch(orientamento.trim().toUpperCase()){case"LPA":mesh.rotation.y=Math.PI/2,mesh.rotation.x=Math.PI,mesh.position.set(0,z,0);break;case"ALP":mesh.rotation.x=Math.PI/2,mesh.position.set(0,x,0);break;case"APL":mesh.rotation.x=-Math.PI/2,mesh.rotation.z=-Math.PI/2;break;case"PLA":break;case"PAL":mesh.rotation.z=Math.PI/2,mesh.position.set(z,0,0);break;case"LAP":mesh.rotation.y=Math.PI/2,mesh.rotation.z=Math.PI/2}return mesh}(orientamento,m2,l,a,p),grp.add(mesh),grp}function infoestrudi(shape,hshape,pts,options){options||(options={}),pts||(pts=[]);let p0=options.p0||0,coeffbase1=Math.tan(options.coeffbase1*PIF||0),coeffbase2=Math.tan(options.coeffbase2*PIF||0),p1=hshape||options.p1||20,coefftop1=Math.tan(options.coefftop1*PIF||0),coefftop2=Math.tan(options.coefftop2*PIF||0),{mi:mi,ma:ma}=shape.pt.reduce(((t,e)=>(t.mi.x=Math.min(t.mi.x,e.x),t.mi.y=Math.min(t.mi.y,e.y),t.ma.x=Math.max(t.ma.x,e.x),t.ma.y=Math.max(t.ma.y,e.y),t)),{mi:{x:1e9,y:1e9},ma:{x:-1e9,y:-1e9}}),lmax=0,lmin=1e9;function getpars(p){p.z1=getzeta(p.x,p.y,p0,coeffbase1,coeffbase2),p.z2=getzeta(p.x,p.y,p1,coefftop1,coefftop2),p.l=Math.abs(p.z2-p.z1)}for(let p of pts)getpars(p);getpars(mi),getpars(ma);let rect=[{x:mi.x,y:mi.y},{x:ma.x,y:mi.y},{x:ma.x,y:ma.y},{x:mi.x,y:ma.y}];for(let r of rect)getpars(r),r.l>lmax&&(lmax=r.l),r.l<lmin&&(lmin=r.l);return{aini:options.coeffbase1||0,aini2:options.coeffbase2||0,afin:options.coefftop1||0,afin2:options.coefftop2||0,pts:pts,mi:mi,ma:ma,rect:rect,dimx:ma.x-mi.x,dimy:ma.y-mi.y,lnom:p1-p0,lmax:lmax,lmin:lmin,lmed:(lmax+lmin)/2}}function getzeta(x,y,zbase,cx,cy){return x*cx+y*cy+zbase}function sidegeomfromshapes(gcad,ky,s1,s2,invert=!1){if(s1&&s2){if(s1.length!==s2.length)throw new Error("shapes with different length");if(s1.length<2)throw new Error("I percorsi devono contenere almeno due punti ciascuno.");if(!gcad.geo[ky]){const positions=[],uvs=[],indices=[],np=s1.length,addpts=ss=>{for(const s of ss)positions.push(s.x,s.y,s.z),uvs.push(s.u,s.v)},equalpos=(p1,p2)=>p1.x===p2.x&&p1.y===p2.y&&p1.z===p2.z,addindexquad=(i1,i2,i3,i4)=>{invert?indices.push(i1,i3,i2,i3,i4,i2):indices.push(i1,i2,i3,i3,i2,i4)};addpts(s1),addpts(s2);for(let i=0;i<np-1;i++){const j=i+1;equalpos(s1[i],s1[j])||addindexquad(i,j,i+np,j+np)}const geometry=new THREE.BufferGeometry;geometry.setAttribute("position",new THREE.Float32BufferAttribute(positions,3)),geometry.setAttribute("uv",new THREE.Float32BufferAttribute(uvs,2)),geometry.setIndex(indices),geometry.computeVertexNormals(),gcad.geo[ky]=geometry}return gcad.geo[ky]}}function bottomgeomfromshape(gcad,inverti,outer,holes,c=0,a=0,b=0){let ky=`bg:${c}|${a}${b}|${outer.key}|${inverti}`;for(var h of holes)ky=`${ky}|${h.key}`;if(ky=hash(ky),!gcad.geo[ky]){let triangles,pos=[],hl=[],cc=outer.pt.length;if(pos=outer.vec,Array.isArray(holes)){for(var h of holes)hl.push(cc),cc+=h.pt.length,pos=[...pos,...h.vec];triangles=earcut(pos,hl)}else triangles=earcut(pos);const geometry=new THREE.BufferGeometry,vertices=[],uvs=[],indices=[];for(let i=0;i<triangles.length;i++){const index=triangles[i],x=pos[2*index],y=-pos[2*index+1],z=getzeta(x,y,c,a,b);vertices.push(x,y,z),uvs.push(y,x)}for(let i=0;i<triangles.length;i+=3)inverti?indices.push(i,i+2,i+1):indices.push(i,i+1,i+2);geometry.setAttribute("position",new THREE.Float32BufferAttribute(vertices,3)),geometry.setAttribute("uv",new THREE.Float32BufferAttribute(uvs,2)),geometry.setIndex(indices),geometry.computeVertexNormals(),gcad.geo[ky]=geometry}return gcad.geo[ky]}function estrusorotate(orientamento,mesh,z=0){switch(orientamento.trim().toUpperCase().slice(0,1)){case"A":mesh.rotation.x=-Math.PI/2;break;case"L":mesh.rotation.y=Math.PI/2,mesh.rotation.z=Math.PI;break;case"P":mesh.rotation.z=Math.PI/2}return mesh}
|
|
1564
102
|
/**
|
|
1565
103
|
* Crea una geometria estrusa con opzioni avanzate
|
|
1566
104
|
* @param {string} orient - Orientamento dell'estrusione
|
|
@@ -1570,102 +108,7 @@ function estrusorotate(orientamento, mesh, z = 0) {
|
|
|
1570
108
|
* @param {Array} mats - Array di materiali
|
|
1571
109
|
* @param {Object} options - Opzioni di configurazione
|
|
1572
110
|
* @returns {THREE.Group} Gruppo contenente la geometria estrusa
|
|
1573
|
-
*/
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
function estruso(gcad, orient, hshape, shape, holes, mats, options) {
|
|
1577
|
-
if (!options) {
|
|
1578
|
-
options = {};
|
|
1579
|
-
}
|
|
1580
|
-
if (!mats) mats = [];
|
|
1581
|
-
// Estrae i parametri dalle opzioni
|
|
1582
|
-
let p0 = options.p0 || 0;
|
|
1583
|
-
|
|
1584
|
-
let coeffbase1 = Math.tan(options.coeffbase1 * PIF || 0);
|
|
1585
|
-
let coeffbase2 = Math.tan(options.coeffbase2 * PIF || 0);
|
|
1586
|
-
let p1 = hshape || options.p1 || 20;
|
|
1587
|
-
let coefftop1 = Math.tan(options.coefftop1 * PIF || 0);
|
|
1588
|
-
let coefftop2 = Math.tan(options.coefftop2 * PIF || 0);
|
|
1589
|
-
let open = options.open || false;
|
|
1590
|
-
let grp = new THREE.Group();
|
|
1591
|
-
|
|
1592
|
-
// Crea le geometrie di base
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
// Aggiunge facce base e top se richiesto
|
|
1597
|
-
if (!options.nobase) {
|
|
1598
|
-
let g1 = bottomgeomfromshape(gcad, false, shape, open ? [] : holes, p0, coeffbase1, coeffbase2);
|
|
1599
|
-
if (!options.nolines) grp.add(edgesfromgeometry(g1));
|
|
1600
|
-
grp.add(getmesh(g1, mats[0] || mwhite$1));
|
|
1601
|
-
}
|
|
1602
|
-
if (!options.notop) {
|
|
1603
|
-
let g3 = bottomgeomfromshape(gcad, true, shape, open ? [] : holes, p1, coefftop1, coefftop2);
|
|
1604
|
-
grp.add(getmesh(g3, mats[1] || mats[0] || mwhite$1));
|
|
1605
|
-
if (!options.nolines) grp.add(edgesfromgeometry(g3));
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
// Gestione dei lati e dei fori
|
|
1609
|
-
if (!options.nosides) {
|
|
1610
|
-
let pat1 = shape.to3d(0, p0, coeffbase1, coeffbase2, open);
|
|
1611
|
-
let pat2 = shape.to3d(0, p1, coefftop1, coefftop2, open);
|
|
1612
|
-
let ky = `${shape.key}|${p0}|${p1}|${coeffbase1}|${coeffbase2}|${coefftop1}|${coefftop2}|${open}`;
|
|
1613
|
-
let geo1 = sidegeomfromshapes(gcad, ky, pat1, pat2, false);
|
|
1614
|
-
|
|
1615
|
-
if (!options.nolines && !options.nosidelines) {
|
|
1616
|
-
grp.add(edgesfromgeometry(geo1));
|
|
1617
|
-
}
|
|
1618
|
-
grp.add(getmesh(geo1, mats[2] || mats[0] || mwhite$1));
|
|
1619
|
-
|
|
1620
|
-
// Gestione dei fori
|
|
1621
|
-
if (holes && !open) {
|
|
1622
|
-
for (var h of holes) {
|
|
1623
|
-
pat1 = h.to3d(0, p0, coeffbase1, coeffbase2);
|
|
1624
|
-
pat2 = h.to3d(0, p1, coefftop1, coefftop2);
|
|
1625
|
-
ky = `${h.key}|${p0}|${p1}|${coeffbase1}|${coeffbase2}|${coefftop1}|${coefftop2}`;
|
|
1626
|
-
let geo2 = sidegeomfromshapes(gcad, ky, pat1, pat2, true);
|
|
1627
|
-
if (!options.nolines && !options.nosidelines) {
|
|
1628
|
-
grp.add(edgesfromgeometry(geo2));
|
|
1629
|
-
}
|
|
1630
|
-
grp.add(getmesh(geo2, mats[3] || mats[0] || mwhite$1));
|
|
1631
|
-
}
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1634
|
-
return estrusorotate(orient, grp, hshape);
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
function revolve(gcad, shape, orient, mat, options) {
|
|
1638
|
-
if (!options) {
|
|
1639
|
-
options = {};
|
|
1640
|
-
}
|
|
1641
|
-
let segmenti = options.segmenti ?? 12;
|
|
1642
|
-
let ky = hash(`rev|${shape.key}|${orient}|${segmenti}`);
|
|
1643
|
-
let geometria = gcad.geo[ky];
|
|
1644
|
-
let grp = new THREE.Group();
|
|
1645
|
-
if (!geometria) {
|
|
1646
|
-
const punti3D = shape.pt.map(p => new THREE.Vector3(p.x, p.y, 0));
|
|
1647
|
-
// Creiamo la geometria di rivoluzione
|
|
1648
|
-
geometria = new THREE.LatheGeometry(
|
|
1649
|
-
punti3D,
|
|
1650
|
-
segmenti,
|
|
1651
|
-
0,
|
|
1652
|
-
Math.PI * 2
|
|
1653
|
-
);
|
|
1654
|
-
gcad.geo[ky] = geometria;
|
|
1655
|
-
}
|
|
1656
|
-
const mesh = new THREE.Mesh(geometria, mat);
|
|
1657
|
-
grp.add(mesh);
|
|
1658
|
-
if (!options.nolines && !options.nosidelines) {
|
|
1659
|
-
grp.add(edgesfromgeometry(geometria));
|
|
1660
|
-
}
|
|
1661
|
-
return grp;
|
|
1662
|
-
// todo: orienta il gruppo
|
|
1663
|
-
//return estrusorotate(orient, grp);
|
|
1664
|
-
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
111
|
+
*/function estruso(gcad,orient,hshape,shape,holes,mats,options){options||(options={}),mats||(mats=[]);let p0=options.p0||0,coeffbase1=Math.tan(options.coeffbase1*PIF||0),coeffbase2=Math.tan(options.coeffbase2*PIF||0),p1=hshape||options.p1||20,coefftop1=Math.tan(options.coefftop1*PIF||0),coefftop2=Math.tan(options.coefftop2*PIF||0),open=options.open||!1,grp=new THREE.Group;if(!options.nobase){let g1=bottomgeomfromshape(gcad,!1,shape,open?[]:holes,p0,coeffbase1,coeffbase2);options.nolines||grp.add(edgesfromgeometry(g1)),grp.add(getmesh(g1,mats[0]||mwhite$1))}if(!options.notop){let g3=bottomgeomfromshape(gcad,!0,shape,open?[]:holes,p1,coefftop1,coefftop2);grp.add(getmesh(g3,mats[1]||mats[0]||mwhite$1)),options.nolines||grp.add(edgesfromgeometry(g3))}if(!options.nosides){let pat1=shape.to3d(0,p0,coeffbase1,coeffbase2,open),pat2=shape.to3d(0,p1,coefftop1,coefftop2,open),ky=`${shape.key}|${p0}|${p1}|${coeffbase1}|${coeffbase2}|${coefftop1}|${coefftop2}|${open}`,geo1=sidegeomfromshapes(gcad,ky,pat1,pat2,!1);if(options.nolines||options.nosidelines||grp.add(edgesfromgeometry(geo1)),grp.add(getmesh(geo1,mats[2]||mats[0]||mwhite$1)),holes&&!open)for(var h of holes){pat1=h.to3d(0,p0,coeffbase1,coeffbase2),pat2=h.to3d(0,p1,coefftop1,coefftop2),ky=`${h.key}|${p0}|${p1}|${coeffbase1}|${coeffbase2}|${coefftop1}|${coefftop2}`;let geo2=sidegeomfromshapes(gcad,ky,pat1,pat2,!0);options.nolines||options.nosidelines||grp.add(edgesfromgeometry(geo2)),grp.add(getmesh(geo2,mats[3]||mats[0]||mwhite$1))}}return estrusorotate(orient,grp,hshape)}function revolve(gcad,shape,orient,mat,options){options||(options={});let segmenti=options.segmenti??12,ky=hash(`rev|${shape.key}|${orient}|${segmenti}`),geometria=gcad.geo[ky],grp=new THREE.Group;if(!geometria){const punti3D=shape.pt.map((p=>new THREE.Vector3(p.x,p.y,0)));geometria=new THREE.LatheGeometry(punti3D,segmenti,0,2*Math.PI),gcad.geo[ky]=geometria}const mesh=new THREE.Mesh(geometria,mat);return grp.add(mesh),options.nolines||options.nosidelines||grp.add(edgesfromgeometry(geometria)),grp}
|
|
1669
112
|
/**
|
|
1670
113
|
* Crea una geometria estrusa con opzioni avanzate
|
|
1671
114
|
* @param {string} orient - Orientamento dell'estrusione
|
|
@@ -1675,157 +118,4 @@ function revolve(gcad, shape, orient, mat, options) {
|
|
|
1675
118
|
* @param {Array} mats - Array di materiali
|
|
1676
119
|
* @param {Object} options - Opzioni di configurazione
|
|
1677
120
|
* @returns {THREE.Group} Gruppo contenente la geometria estrusa
|
|
1678
|
-
*/
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
function estrusopat(gcad, orient, pat, shape, mats, options) {
|
|
1682
|
-
if (!options) {
|
|
1683
|
-
options = {};
|
|
1684
|
-
}
|
|
1685
|
-
let invert = options.invert;
|
|
1686
|
-
//
|
|
1687
|
-
if (!mats) mats = [];
|
|
1688
|
-
let open = options.open;
|
|
1689
|
-
// Estrae i parametri dalle opzioni
|
|
1690
|
-
if (!pat.pt?.length) return;
|
|
1691
|
-
let pbase = shape.to3d(0, 0, 0, 0, open);
|
|
1692
|
-
let grp = new THREE.Group();
|
|
1693
|
-
|
|
1694
|
-
if (!options.nobase) {
|
|
1695
|
-
let gb = new THREE.Group();
|
|
1696
|
-
let g1 = bottomgeomfromshape(gcad, false, shape, [], 0, 0, 0);
|
|
1697
|
-
gb.add(getmesh(g1, mats[0] || mwhite$1));
|
|
1698
|
-
gb.add(edgesfromgeometry(g1));
|
|
1699
|
-
let seg1 = pat.infosegmento(0, true);
|
|
1700
|
-
grp.add(posiziona(gb, { sl: seg1.x, sp: seg1.y, sa: 0, ay: (90 - seg1.ang) }));
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
if (!options.notop) {
|
|
1704
|
-
let gt = new THREE.Group();
|
|
1705
|
-
let g2 = bottomgeomfromshape(gcad, true, shape, [], 0, 0, 0);
|
|
1706
|
-
gt.add(getmesh(g2, mats[1] || mats[0] || mwhite$1));
|
|
1707
|
-
gt.add(edgesfromgeometry(g2));
|
|
1708
|
-
let seg1 = pat.infosegmento(pat.pt.length - 1, true);
|
|
1709
|
-
grp.add(posiziona(gt, { sl: seg1.x, sp: seg1.y, sa: 0, ay: (90 - seg1.ang) }));
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
const geo1 = sidegeomfrompat(gcad, pbase, pat, invert);
|
|
1715
|
-
if (!options.nolines && !options.nosidelines) {
|
|
1716
|
-
grp.add(edgesfromgeometry(geo1));
|
|
1717
|
-
}
|
|
1718
|
-
grp.add(getmesh(geo1, mats[2] || mats[0] || mwhite$1));
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
// for (let ii = 1; ii < pat.pt.length - 1; ii++) {
|
|
1722
|
-
|
|
1723
|
-
// let seg1 = pat.infosegmento(ii, true);
|
|
1724
|
-
|
|
1725
|
-
// grp.add(posiziona(gb, { sp: seg1.y, sa: - seg1.x, ax: 90 - seg1.ang }));
|
|
1726
|
-
// }
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
return estrusorotate(orient, grp, 0);
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
async function createCircleTexture(gcad, size = 64, color = 'rgba(255,0,0,1)') {
|
|
1733
|
-
const ky = `ct|${size}|${color}`;
|
|
1734
|
-
if (!gcad.textures[ky]) {
|
|
1735
|
-
const canvas = document.createElement('canvas');
|
|
1736
|
-
canvas.width = canvas.height = size;
|
|
1737
|
-
|
|
1738
|
-
const ctx = canvas.getContext('2d');
|
|
1739
|
-
const radius = size / 2;
|
|
1740
|
-
|
|
1741
|
-
// Sfondo trasparente
|
|
1742
|
-
ctx.clearRect(0, 0, size, size);
|
|
1743
|
-
|
|
1744
|
-
// Cerchio pieno al centro
|
|
1745
|
-
ctx.fillStyle = color;
|
|
1746
|
-
ctx.beginPath();
|
|
1747
|
-
ctx.arc(radius, radius, radius, 0, Math.PI * 2);
|
|
1748
|
-
ctx.fill();
|
|
1749
|
-
|
|
1750
|
-
const texture = new THREE.CanvasTexture(canvas);
|
|
1751
|
-
texture.needsUpdate = true;
|
|
1752
|
-
gcad.textures[ky] = texture;
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
return gcad.textures[ky];
|
|
1756
|
-
}
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
async function spritemat(gcad, file) {
|
|
1760
|
-
// le varie opzioni per smat sono sx,sy,rot,rough,metal,env
|
|
1761
|
-
try {
|
|
1762
|
-
let tm = {
|
|
1763
|
-
transparent: true,
|
|
1764
|
-
depthTest: false,
|
|
1765
|
-
depthWrite: false,
|
|
1766
|
-
fog: false,
|
|
1767
|
-
toneMapped: false, // ❗ importante con HDR
|
|
1768
|
-
};
|
|
1769
|
-
if (file.startsWith("#")) {
|
|
1770
|
-
tm.color = file;
|
|
1771
|
-
} else {
|
|
1772
|
-
let tx = await gcad.tex(file, 1000, 1000);
|
|
1773
|
-
if (tx) {
|
|
1774
|
-
tm.map = tx;
|
|
1775
|
-
} else {
|
|
1776
|
-
tm.color = "black";
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
return new THREE.SpriteMaterial(tm);
|
|
1780
|
-
} catch (error) {
|
|
1781
|
-
return mwhite;
|
|
1782
|
-
}
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
function getsprite0(ispunto, x, y, z, mat, options = {}) {
|
|
1786
|
-
if (!options) options = {};
|
|
1787
|
-
let { size = 50, pick = false, screen = false, sizex, sizey } = options;
|
|
1788
|
-
|
|
1789
|
-
const sprite = new THREE.Sprite(mat);
|
|
1790
|
-
sprite.position.set(0, 0, 0);
|
|
1791
|
-
sprite.userData.issprite = true;
|
|
1792
|
-
sprite.userData.ispunto = ispunto;
|
|
1793
|
-
sprite.userData.ispick = pick;
|
|
1794
|
-
sprite.userData.isScreen = screen;
|
|
1795
|
-
sprite.renderOrder = 999;
|
|
1796
|
-
sprite.layers.set(29);
|
|
1797
|
-
sprite.name = ispunto ? '_punto' : '_sprite';
|
|
1798
|
-
let gr = new THREE.Group();
|
|
1799
|
-
gr.position.set(x, y, z);
|
|
1800
|
-
gr.scale.set(sizex || size, sizey || sizex || size, 1); // dimensione apparente (adatta al tuo uso)
|
|
1801
|
-
gr.add(sprite);
|
|
1802
|
-
gr.traverse(obj => {
|
|
1803
|
-
if (obj.material) {
|
|
1804
|
-
obj.material.depthTest = false;
|
|
1805
|
-
obj.material.depthWrite = false;
|
|
1806
|
-
}
|
|
1807
|
-
});
|
|
1808
|
-
gr.renderOrder = 999;
|
|
1809
|
-
return gr;
|
|
1810
|
-
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
async function getpunto(gcad, x, y, z, color = 'yellow', options) {
|
|
1814
|
-
if (!options) options = { size: 20 };
|
|
1815
|
-
options.screen = true;
|
|
1816
|
-
const texture = await createCircleTexture(gcad, 64, color); // rosso sfumato
|
|
1817
|
-
const mat = new THREE.SpriteMaterial({
|
|
1818
|
-
map: texture,
|
|
1819
|
-
transparent: true,
|
|
1820
|
-
|
|
1821
|
-
fog: false,
|
|
1822
|
-
toneMapped: false, // ❗ importante con HDR
|
|
1823
|
-
});
|
|
1824
|
-
return getsprite0(true, x, y, z, mat, options)
|
|
1825
|
-
}
|
|
1826
|
-
|
|
1827
|
-
async function getsprite(gcad, x, y, z, mat, options = {}) {
|
|
1828
|
-
return getsprite0(false, x, y, z, mat, options);
|
|
1829
|
-
}
|
|
1830
|
-
|
|
1831
|
-
export { SIDE, addmovpivot, creategroup, deletegroup, edgesfromgeometry, estruso, estrusopat, get3dshape, getbox, getcilindro, getface, getline, getlinesgeom, getmesh, getmovimento, getpannello, getpoint, getpunto, getquota, getriferimento, getsprite, gettarghetta, groupfromgeometry, infoestrudi, materialline1, materialline2, mblack, mblue, mgray1, mgray2, mgreen, mred, mwhite$1 as mwhite, posiziona, randombasemat, revolve, scaleunit, spritemat, svuotanodo };
|
|
121
|
+
*/function estrusopat(gcad,orient,pat,shape,mats,options){options||(options={});let invert=options.invert;mats||(mats=[]);let open=options.open;if(!pat.pt?.length)return;let pbase=shape.to3d(0,0,0,0,open),grp=new THREE.Group;if(!options.nobase){let gb=new THREE.Group,g1=bottomgeomfromshape(gcad,!1,shape,[],0,0,0);gb.add(getmesh(g1,mats[0]||mwhite$1)),gb.add(edgesfromgeometry(g1));let seg1=pat.infosegmento(0,!0);grp.add(posiziona(gb,{sl:seg1.x,sp:seg1.y,sa:0,ay:90-seg1.ang}))}if(!options.notop){let gt=new THREE.Group,g2=bottomgeomfromshape(gcad,!0,shape,[],0,0,0);gt.add(getmesh(g2,mats[1]||mats[0]||mwhite$1)),gt.add(edgesfromgeometry(g2));let seg1=pat.infosegmento(pat.pt.length-1,!0);grp.add(posiziona(gt,{sl:seg1.x,sp:seg1.y,sa:0,ay:90-seg1.ang}))}const geo1=function sidegeomfrompat(gcad,pbase,pat,invert){let ky=`bsg:${pbase.key}|${pat.key}|${invert}`;if(ky=hash(ky),!gcad.geo[ky]){const positions=[],uvs=[],indices=[],np=pbase.length,lp=pat.pt.length,equalpos=(p1,p2)=>p1.x===p2.x&&p1.y===p2.y&&p1.z===p2.z;let l0=0;for(let ii=0;ii<lp;ii++){const addpts=(ss,mat,deltav)=>{for(const s of ss){let p=new THREE.Vector3(s.x,s.y,s.z);p.applyMatrix4(mat),positions.push(p.x,p.y,p.z),uvs.push(s.u,s.v+deltav)}};let seg1=pat.infosegmento(ii,!0),obj=new THREE.Object3D;obj.position.set(seg1.x,0,seg1.y),obj.rotation.set(0,(90-seg1.ang)*PIF,0),obj.updateMatrix(),addpts(pbase,obj.matrix,l0),l0+=seg1.l}for(let li=0;li<lp-1;li++)for(let i=0;i<np-1;i++){const addindexquad=(i1,i2,i3,i4)=>{invert?indices.push(i1,i3,i2,i3,i4,i2):indices.push(i1,i2,i3,i3,i2,i4)},j=i+1;equalpos(pbase[i],pbase[j])||addindexquad(i+li*np,j+li*np,i+(li+1)*np,j+(li+1)*np)}const geometry=new THREE.BufferGeometry;geometry.setAttribute("position",new THREE.Float32BufferAttribute(positions,3)),geometry.setAttribute("uv",new THREE.Float32BufferAttribute(uvs,2)),geometry.setIndex(indices),geometry.computeVertexNormals(),gcad[ky]=geometry}return gcad[ky]}(gcad,pbase,pat,invert);return options.nolines||options.nosidelines||grp.add(edgesfromgeometry(geo1)),grp.add(getmesh(geo1,mats[2]||mats[0]||mwhite$1)),estrusorotate(orient,grp,0)}async function spritemat(gcad,file){try{let tm={transparent:!0,depthTest:!1,depthWrite:!1,fog:!1,toneMapped:!1};if(file.startsWith("#"))tm.color=file;else{let tx=await gcad.tex(file,1e3,1e3);tx?tm.map=tx:tm.color="black"}return new THREE.SpriteMaterial(tm)}catch(error){return mwhite}}function getsprite0(ispunto,x,y,z,mat,options={}){options||(options={});let{size:size=50,pick:pick=!1,screen:screen=!1,sizex:sizex,sizey:sizey}=options;const sprite=new THREE.Sprite(mat);sprite.position.set(0,0,0),sprite.userData.issprite=!0,sprite.userData.ispunto=ispunto,sprite.userData.ispick=pick,sprite.userData.isScreen=screen,sprite.renderOrder=999,sprite.layers.set(29),sprite.name=ispunto?"_punto":"_sprite";let gr=new THREE.Group;return gr.position.set(x,y,z),gr.scale.set(sizex||size,sizey||sizex||size,1),gr.add(sprite),gr.traverse((obj=>{obj.material&&(obj.material.depthTest=!1,obj.material.depthWrite=!1)})),gr.renderOrder=999,gr}async function getpunto(gcad,x,y,z,color="yellow",options){options||(options={size:20}),options.screen=!0;const texture=await async function createCircleTexture(gcad,size=64,color="rgba(255,0,0,1)"){const ky=`ct|${size}|${color}`;if(!gcad.textures[ky]){const canvas=document.createElement("canvas");canvas.width=canvas.height=size;const ctx=canvas.getContext("2d"),radius=size/2;ctx.clearRect(0,0,size,size),ctx.fillStyle=color,ctx.beginPath(),ctx.arc(radius,radius,radius,0,2*Math.PI),ctx.fill();const texture=new THREE.CanvasTexture(canvas);texture.needsUpdate=!0,gcad.textures[ky]=texture}return gcad.textures[ky]}(gcad,64,color);return getsprite0(!0,x,y,z,new THREE.SpriteMaterial({map:texture,transparent:!0,fog:!1,toneMapped:!1}),options)}async function getsprite(gcad,x,y,z,mat,options={}){return getsprite0(!1,x,y,z,mat,options)}export{SIDE,addmovpivot,creategroup,deletegroup,edgesfromgeometry,estruso,estrusopat,get3dshape,getbox,getcilindro,getface,getline,getlinesgeom,getmesh,getmovimento,getpannello,getpoint,getpunto,getquota,getriferimento,getsprite,gettarghetta,groupfromgeometry,infoestrudi,materialline1,materialline2,mblack,mblue,mgray1,mgray2,mgreen,mred,mwhite$1 as mwhite,posiziona,randombasemat,revolve,scaleunit,spritemat,svuotanodo};
|