5 if ( typeof define ===
"function" && define.amd ) {
6 define( [
'JSRootCore',
'threejs',
'ThreeCSG' ], factory );
7 }
else if (typeof exports ===
'object' && typeof module !==
'undefined') {
8 factory(require(
"./JSRootCore.js"), require(
"three"), require(
"./ThreeCSG.js"));
10 if (typeof JSROOT ==
'undefined')
11 throw new Error(
'JSROOT is not defined',
'JSRootGeoBase.js');
12 if (typeof THREE ==
'undefined')
13 throw new Error(
'THREE is not defined',
'JSRootGeoBase.js');
14 if (typeof ThreeBSP ==
'undefined')
15 throw new Error(
'ThreeBSP is not defined',
'JSRootGeoBase.js');
16 factory(JSROOT, THREE, ThreeBSP);
18 } (
function( JSROOT, THREE, ThreeBSP ) {
32 kVisOverride : JSROOT.BIT(0),
33 kVisNone : JSROOT.BIT(1),
34 kVisThis : JSROOT.BIT(2),
35 kVisDaughters : JSROOT.BIT(3),
36 kVisOneLevel : JSROOT.BIT(4),
37 kVisStreamed : JSROOT.BIT(5),
38 kVisTouched : JSROOT.BIT(6),
39 kVisOnScreen : JSROOT.BIT(7),
40 kVisContainers : JSROOT.BIT(12),
41 kVisOnly : JSROOT.BIT(13),
42 kVisBranch : JSROOT.BIT(14),
43 kVisRaytrace : JSROOT.BIT(15)
47 JSROOT.GEO.TestBit =
function(volume, f) {
48 var att = volume.fGeoAtt;
49 return att === undefined ?
false : ((att & f) !== 0);
53 JSROOT.GEO.SetBit =
function(volume, f, value) {
54 if (volume.fGeoAtt === undefined)
return;
55 volume.fGeoAtt = value ? (volume.fGeoAtt | f) : (volume.fGeoAtt & ~f);
59 JSROOT.GEO.ToggleBit =
function(volume, f) {
60 if (volume.fGeoAtt !== undefined)
61 volume.fGeoAtt = volume.fGeoAtt ^ (f & 0xffffff);
66 JSROOT.GEO.InvisibleAll =
function(flag) {
67 if (flag===undefined) flag =
true;
69 JSROOT.GEO.SetBit(
this, JSROOT.GEO.BITS.kVisThis, !flag);
73 for (var n=0;n<this.fNodes.arr.length;++n) {
74 var sub = this.fNodes.arr[n].fVolume;
75 JSROOT.GEO.SetBit(sub, JSROOT.GEO.BITS.kVisThis, !flag);
82 JSROOT.GEO.warn =
function(msg) {
83 if (JSROOT.GEO._warn_msgs === undefined) JSROOT.GEO._warn_msgs = {};
84 if (JSROOT.GEO._warn_msgs[msg] !== undefined)
return;
85 JSROOT.GEO._warn_msgs[msg] =
true;
94 JSROOT.GEO.NodeKind =
function(obj) {
95 if ((obj === undefined) || (obj === null) || (typeof obj !==
'object'))
return -1;
96 return (
'fShape' in obj) && (
'fTrans' in obj) ? 1 : 0;
102 JSROOT.GEO.CountNumShapes =
function(shape) {
103 if (!shape)
return 0;
105 if (shape._typename!==
'TGeoCompositeShape')
return 1;
106 return JSROOT.GEO.CountNumShapes(shape.fNode.fLeft) + JSROOT.GEO.CountNumShapes(shape.fNode.fRight);
111 JSROOT.GEO.GeometryCreator =
function(numfaces) {
112 this.nfaces = numfaces;
114 this.pos =
new Float32Array(numfaces*9);
115 this.norm =
new Float32Array(numfaces*9);
120 JSROOT.GEO.GeometryCreator.prototype.AddFace3 =
function(x1,y1,z1,
123 var indx = this.indx, pos = this.pos;
134 this.indx = indx + 9;
137 JSROOT.GEO.GeometryCreator.prototype.StartPolygon =
function() {}
138 JSROOT.GEO.GeometryCreator.prototype.StopPolygon =
function() {}
140 JSROOT.GEO.GeometryCreator.prototype.AddFace4 =
function(x1,y1,z1,
149 var indx = this.indx, pos = this.pos;
177 this.last4 = (indx !== this.indx + 9);
181 JSROOT.GEO.GeometryCreator.prototype.SetNormal4 =
function(nx1,ny1,nz1,
189 if (this.last4 && reduce)
190 return console.error(
'missmatch between AddFace4 and SetNormal4 calls');
192 var indx = this.indx - (this.last4 ? 18 : 9), norm = this.norm;
220 JSROOT.GEO.GeometryCreator.prototype.RecalcZ =
function(func) {
223 indx = last - (this.last4 ? 18 : 9);
225 while (indx < last) {
226 pos[indx+2] = func(pos[indx], pos[indx+1], pos[indx+2]);
231 JSROOT.GEO.GetNormal =
function(x1,y1,z1,x2,y2,z2,x3,y3,z3) {
233 var pA =
new THREE.Vector3(x1,y1,z1),
234 pB =
new THREE.Vector3(x2,y2,z2),
235 pC =
new THREE.Vector3(x3,y3,z3),
236 cb =
new THREE.Vector3(),
237 ab =
new THREE.Vector3();
239 cb.subVectors( pC, pB );
240 ab.subVectors( pA, pB );
246 JSROOT.GEO.GeometryCreator.prototype.CalcNormal =
function() {
247 var indx = this.indx, norm = this.norm;
250 this.pA =
new THREE.Vector3();
251 this.pB =
new THREE.Vector3();
252 this.pC =
new THREE.Vector3();
253 this.cb =
new THREE.Vector3();
254 this.ab =
new THREE.Vector3();
257 this.pA.fromArray( this.pos, this.indx - 9 );
258 this.pB.fromArray( this.pos, this.indx - 6 );
259 this.pC.fromArray( this.pos, this.indx - 3 );
261 this.cb.subVectors( this.pC, this.pB );
262 this.ab.subVectors( this.pA, this.pB );
263 this.cb.cross( this.ab );
265 this.SetNormal(this.cb.x,
this.cb.y,
this.cb.z);
268 JSROOT.GEO.GeometryCreator.prototype.SetNormal =
function(nx,ny,nz) {
269 var indx = this.indx - 9, norm = this.norm;
271 norm[indx] = norm[indx+3] = norm[indx+6] = nx;
272 norm[indx+1] = norm[indx+4] = norm[indx+7] = ny;
273 norm[indx+2] = norm[indx+5] = norm[indx+8] = nz;
277 norm[indx] = norm[indx+3] = norm[indx+6] = nx;
278 norm[indx+1] = norm[indx+4] = norm[indx+7] = ny;
279 norm[indx+2] = norm[indx+5] = norm[indx+8] = nz;
283 JSROOT.GEO.GeometryCreator.prototype.SetNormal_12_34 =
function(nx12,ny12,nz12,nx34,ny34,nz34,reduce) {
285 if (reduce===undefined) reduce = 0;
287 var indx = this.indx - ((reduce>0) ? 9 : 18), norm = this.norm;
316 JSROOT.GEO.GeometryCreator.prototype.Create =
function() {
317 if (this.nfaces !== this.indx/9)
318 console.error(
'Mismatch with created ' + this.nfaces +
' and filled ' + this.indx/9 +
' number of faces');
320 var geometry =
new THREE.BufferGeometry();
321 geometry.addAttribute(
'position',
new THREE.BufferAttribute(
this.pos, 3 ) );
322 geometry.addAttribute(
'normal',
new THREE.BufferAttribute(
this.norm, 3 ) );
330 JSROOT.GEO.PolygonsCreator =
function() {
334 JSROOT.GEO.PolygonsCreator.prototype.StartPolygon =
function(normal) {
336 this.mnormal = normal;
339 JSROOT.GEO.PolygonsCreator.prototype.StopPolygon =
function() {
340 if (!this.multi)
return;
342 console.error(
'Polygon should be already closed at this moment');
345 JSROOT.GEO.PolygonsCreator.prototype.AddFace3 =
function(x1,y1,z1,
348 this.AddFace4(x1,y1,z1,x2,y2,z2,x3,y3,z3,x3,y3,z3,2);
352 JSROOT.GEO.PolygonsCreator.prototype.AddFace4 =
function(x1,y1,z1,
361 if (reduce === undefined) reduce = 0;
363 this.v1 =
new ThreeBSP.Vertex( x1, y1, z1, 0, 0, 0 );
364 this.v2 = (reduce===1) ? null :
new ThreeBSP.Vertex( x2, y2, z2, 0, 0, 0 );
365 this.v3 =
new ThreeBSP.Vertex( x3, y3, z3, 0, 0, 0 );
366 this.v4 = (reduce===2) ? null :
new ThreeBSP.Vertex( x4, y4, z4, 0, 0, 0 );
368 this.reduce = reduce;
372 if (reduce!==2) console.error(
'polygon not supported for not-reduced faces');
376 if (this.multi++ === 1) {
377 polygon =
new ThreeBSP.Polygon;
379 polygon.vertices.push(this.mnormal ? this.v2 : this.v3);
380 this.polygons.push(polygon);
382 polygon = this.polygons[this.polygons.length-1];
384 var last = this.mnormal ? polygon.vertices[polygon.vertices.length-1] : polygon.vertices[0],
385 comp = this.mnormal ? this.v2 : this.v3;
387 if (comp.diff(last) > 1e-12)
388 console.error(
'vertex missmatch when building polygon');
391 var first = this.mnormal ? polygon.vertices[0] : polygon.vertices[polygon.vertices.length-1],
392 next = this.mnormal ? this.v3 : this.v2;
394 if (next.diff(first) < 1e-12) {
399 polygon.vertices.push(this.v3);
401 polygon.vertices.unshift(this.v2);
408 var polygon =
new ThreeBSP.Polygon;
411 case 0: polygon.vertices.push(this.v1, this.v2, this.v3, this.v4);
break;
412 case 1: polygon.vertices.push(this.v1, this.v3, this.v4);
break;
413 case 2: polygon.vertices.push(this.v1, this.v2, this.v3);
break;
416 this.polygons.push(polygon);
419 JSROOT.GEO.PolygonsCreator.prototype.SetNormal4 =
function(nx1,ny1,nz1,
424 this.v1.setnormal(nx1,ny1,nz1);
425 if (this.v2) this.v2.setnormal(nx2,ny2,nz2);
426 this.v3.setnormal(nx3,ny3,nz3);
427 if (this.v4) this.v4.setnormal(nx4,ny4,nz4);
430 JSROOT.GEO.PolygonsCreator.prototype.SetNormal_12_34 =
function(nx12,ny12,nz12,nx34,ny34,nz34,reduce) {
432 this.v1.setnormal(nx12,ny12,nz12);
433 if (this.v2) this.v2.setnormal(nx12,ny12,nz12);
434 this.v3.setnormal(nx34,ny34,nz34);
435 if (this.v4) this.v4.setnormal(nx34,ny34,nz34);
438 JSROOT.GEO.PolygonsCreator.prototype.CalcNormal =
function() {
441 this.pA =
new THREE.Vector3();
442 this.pB =
new THREE.Vector3();
443 this.pC =
new THREE.Vector3();
444 this.cb =
new THREE.Vector3();
445 this.ab =
new THREE.Vector3();
448 this.pA.set( this.v1.x,
this.v1.y,
this.v1.z);
450 if (this.reduce!==1) {
451 this.pB.set( this.v2.x,
this.v2.y,
this.v2.z);
452 this.pC.set( this.v3.x,
this.v3.y,
this.v3.z);
454 this.pB.set( this.v3.x,
this.v3.y,
this.v3.z);
455 this.pC.set( this.v4.x,
this.v4.y,
this.v4.z);
458 this.cb.subVectors( this.pC, this.pB );
459 this.ab.subVectors( this.pA, this.pB );
460 this.cb.cross( this.ab );
462 this.SetNormal(this.cb.x,
this.cb.y,
this.cb.z);
466 JSROOT.GEO.PolygonsCreator.prototype.SetNormal =
function(nx,ny,nz) {
467 this.v1.setnormal(nx,ny,nz);
468 if (this.v2) this.v2.setnormal(nx,ny,nz);
469 this.v3.setnormal(nx,ny,nz);
470 if (this.v4) this.v4.setnormal(nx,ny,nz);
473 JSROOT.GEO.PolygonsCreator.prototype.RecalcZ =
function(func) {
474 this.v1.z = func(this.v1.x,
this.v1.y,
this.v1.z);
475 if (this.v2) this.v2.z = func(this.v2.x,
this.v2.y,
this.v2.z);
476 this.v3.z = func(this.v3.x,
this.v3.y,
this.v3.z);
477 if (this.v4) this.v4.z = func(this.v4.x,
this.v4.y,
this.v4.z);
480 JSROOT.GEO.PolygonsCreator.prototype.Create =
function() {
481 return { polygons: this.polygons };
487 JSROOT.GEO.createCubeBuffer =
function(shape, faces_limit) {
489 if (faces_limit < 0)
return 12;
491 var dx = shape.fDX, dy = shape.fDY, dz = shape.fDZ;
493 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(12);
495 creator.AddFace4(dx,dy,dz, dx,-dy,dz, dx,-dy,-dz, dx,dy,-dz); creator.SetNormal(1,0,0);
497 creator.AddFace4(-dx,dy,-dz, -dx,-dy,-dz, -dx,-dy,dz, -dx,dy,dz); creator.SetNormal(-1,0,0);
499 creator.AddFace4(-dx,dy,-dz, -dx,dy,dz, dx,dy,dz, dx,dy,-dz); creator.SetNormal(0,1,0);
501 creator.AddFace4(-dx,-dy,dz, -dx,-dy,-dz, dx,-dy,-dz, dx,-dy,dz); creator.SetNormal(0,-1,0);
503 creator.AddFace4(-dx,dy,dz, -dx,-dy,dz, dx,-dy,dz, dx,dy,dz); creator.SetNormal(0,0,1);
505 creator.AddFace4(dx,dy,-dz, dx,-dy,-dz, -dx,-dy,-dz, -dx,dy,-dz); creator.SetNormal(0,0,-1);
507 return creator.Create();
511 JSROOT.GEO.create8edgesBuffer =
function( v, faces_limit ) {
513 var indicies = [ 4,7,6,5, 0,3,7,4, 4,5,1,0, 6,2,1,5, 7,3,2,6, 1,2,3,0 ];
515 var creator = (faces_limit > 0) ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(12);
517 for (var n=0;n<indicies.length;n+=4) {
518 var i1 = indicies[n]*3,
519 i2 = indicies[n+1]*3,
520 i3 = indicies[n+2]*3,
521 i4 = indicies[n+3]*3;
522 creator.AddFace4(v[i1], v[i1+1], v[i1+2], v[i2], v[i2+1], v[i2+2],
523 v[i3], v[i3+1], v[i3+2], v[i4], v[i4+1], v[i4+2]);
524 if (n===0) creator.SetNormal(0,0,1);
else
525 if (n===20) creator.SetNormal(0,0,-1);
else creator.CalcNormal();
528 return creator.Create();
532 JSROOT.GEO.createParaBuffer =
function( shape, faces_limit ) {
534 if (faces_limit < 0)
return 12;
536 var txy = shape.fTxy, txz = shape.fTxz, tyz = shape.fTyz;
539 -shape.fZ*txz-txy*shape.fY-shape.fX, -shape.fY-shape.fZ*tyz, -shape.fZ,
540 -shape.fZ*txz+txy*shape.fY-shape.fX, shape.fY-shape.fZ*tyz, -shape.fZ,
541 -shape.fZ*txz+txy*shape.fY+shape.fX, shape.fY-shape.fZ*tyz, -shape.fZ,
542 -shape.fZ*txz-txy*shape.fY+shape.fX, -shape.fY-shape.fZ*tyz, -shape.fZ,
543 shape.fZ*txz-txy*shape.fY-shape.fX, -shape.fY+shape.fZ*tyz, shape.fZ,
544 shape.fZ*txz+txy*shape.fY-shape.fX, shape.fY+shape.fZ*tyz, shape.fZ,
545 shape.fZ*txz+txy*shape.fY+shape.fX, shape.fY+shape.fZ*tyz, shape.fZ,
546 shape.fZ*txz-txy*shape.fY+shape.fX, -shape.fY+shape.fZ*tyz, shape.fZ ];
548 return JSROOT.GEO.create8edgesBuffer(v, faces_limit );
552 JSROOT.GEO.createTrapezoidBuffer =
function( shape, faces_limit ) {
554 if (faces_limit < 0)
return 12;
557 if (shape._typename ==
"TGeoTrd1") {
560 y1 = shape.fDy1; y2 = shape.fDy2;
564 -shape.fDx1, y1, -shape.fDZ,
565 shape.fDx1, y1, -shape.fDZ,
566 shape.fDx1, -y1, -shape.fDZ,
567 -shape.fDx1, -y1, -shape.fDZ,
568 -shape.fDx2, y2, shape.fDZ,
569 shape.fDx2, y2, shape.fDZ,
570 shape.fDx2, -y2, shape.fDZ,
571 -shape.fDx2, -y2, shape.fDZ
574 return JSROOT.GEO.create8edgesBuffer(v, faces_limit );
579 JSROOT.GEO.createArb8Buffer =
function( shape, faces_limit ) {
581 if (faces_limit < 0)
return 12;
584 shape.fXY[0][0], shape.fXY[0][1], -shape.fDZ,
585 shape.fXY[1][0], shape.fXY[1][1], -shape.fDZ,
586 shape.fXY[2][0], shape.fXY[2][1], -shape.fDZ,
587 shape.fXY[3][0], shape.fXY[3][1], -shape.fDZ,
588 shape.fXY[4][0], shape.fXY[4][1], shape.fDZ,
589 shape.fXY[5][0], shape.fXY[5][1], shape.fDZ,
590 shape.fXY[6][0], shape.fXY[6][1], shape.fDZ,
591 shape.fXY[7][0], shape.fXY[7][1], shape.fDZ
594 4,7,6, 6,5,4, 3,7,4, 4,0,3,
595 5,1,0, 0,4,5, 6,2,1, 1,5,6,
596 7,3,2, 2,6,7, 1,2,3, 3,0,1 ];
599 for (var side=0;side<vertices.length;side += vertices.length/2)
600 for (var n1 = side; n1 < side + vertices.length/2 - 3 ; n1+=3)
601 for (var n2 = n1+3; n2 < side + vertices.length/2 ; n2+=3)
602 if ((vertices[n1] === vertices[n2]) &&
603 (vertices[n1+1] === vertices[n2+1]) &&
604 (vertices[n1+2] === vertices[n2+2])) {
605 for (var k=0;k<indicies.length;++k)
606 if (indicies[k] === n2/3) indicies[k] = n1/3;
613 for (var k=0;k<indicies.length;k+=3) {
614 var id1 = indicies[k]*100 + indicies[k+1]*10 + indicies[k+2],
615 id2 = indicies[k+1]*100 + indicies[k+2]*10 + indicies[k],
616 id3 = indicies[k+2]*100 + indicies[k]*10 + indicies[k+1];
618 if ((indicies[k] == indicies[k+1]) || (indicies[k] == indicies[k+2]) || (indicies[k+1] == indicies[k+2]) ||
619 (map.indexOf(id1)>=0) || (map.indexOf(id2)>=0) || (map.indexOf(id3)>=0)) {
620 indicies[k] = indicies[k+1] = indicies[k+2] = -1;
622 map.push(id1,id2,id3);
627 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(numfaces);
631 for (var n=0; n < indicies.length; n+=6) {
632 var i1 = indicies[n] * 3,
633 i2 = indicies[n+1] * 3,
634 i3 = indicies[n+2] * 3,
635 i4 = indicies[n+3] * 3,
636 i5 = indicies[n+4] * 3,
637 i6 = indicies[n+5] * 3,
640 if ((i1>=0) && (i4>=0) && faces_limit) {
642 if (n===0) norm =
new THREE.Vector3(0,0,1);
else
643 if (n===30) norm =
new THREE.Vector3(0,0,-1);
else {
644 var norm1 = JSROOT.GEO.GetNormal(vertices[i1], vertices[i1+1], vertices[i1+2],
645 vertices[i2], vertices[i2+1], vertices[i2+2],
646 vertices[i3], vertices[i3+1], vertices[i3+2]);
650 var norm2 = JSROOT.GEO.GetNormal(vertices[i4], vertices[i4+1], vertices[i4+2],
651 vertices[i5], vertices[i5+1], vertices[i5+2],
652 vertices[i6], vertices[i6+1], vertices[i6+2]);
656 if (norm1.distanceToSquared(norm2) < 1e-12) norm = norm1;
661 creator.AddFace4(vertices[i1], vertices[i1+1], vertices[i1+2],
662 vertices[i2], vertices[i2+1], vertices[i2+2],
663 vertices[i3], vertices[i3+1], vertices[i3+2],
664 vertices[i5], vertices[i5+1], vertices[i5+2]);
665 creator.SetNormal(norm.x, norm.y, norm.z);
668 creator.AddFace3(vertices[i1], vertices[i1+1], vertices[i1+2],
669 vertices[i2], vertices[i2+1], vertices[i2+2],
670 vertices[i3], vertices[i3+1], vertices[i3+2]);
671 creator.CalcNormal();
674 creator.AddFace3(vertices[i4], vertices[i4+1], vertices[i4+2],
675 vertices[i5], vertices[i5+1], vertices[i5+2],
676 vertices[i6], vertices[i6+1], vertices[i6+2]);
677 creator.CalcNormal();
682 return creator.Create();
686 JSROOT.GEO.createSphereBuffer =
function( shape, faces_limit ) {
687 var radius = [shape.fRmax, shape.fRmin],
688 phiStart = shape.fPhi1,
689 phiLength = shape.fPhi2 - shape.fPhi1,
690 thetaStart = shape.fTheta1,
691 thetaLength = shape.fTheta2 - shape.fTheta1,
692 widthSegments = shape.fNseg,
693 heightSegments = shape.fNz,
694 noInside = (radius[1] <= 0);
699 if (faces_limit > 0) {
700 var fact = (noInside ? 2 : 4) * widthSegments * heightSegments / faces_limit;
703 widthSegments = Math.max(4, Math.floor(widthSegments/Math.sqrt(fact)));
704 heightSegments = Math.max(4, Math.floor(heightSegments/Math.sqrt(fact)));
708 var numoutside = widthSegments * heightSegments * 2,
709 numtop = widthSegments * 2,
710 numbottom = widthSegments * 2,
711 numcut = phiLength === 360 ? 0 : heightSegments * (noInside ? 2 : 4),
714 if (noInside) numbottom = numtop = widthSegments;
716 if (faces_limit < 0)
return numoutside * (noInside ? 1 : 2) + numtop + numbottom + numcut;
718 var _sinp =
new Float32Array(widthSegments+1),
719 _cosp =
new Float32Array(widthSegments+1),
720 _sint =
new Float32Array(heightSegments+1),
721 _cost =
new Float32Array(heightSegments+1);
723 for (var n=0;n<=heightSegments;++n) {
724 var theta = (thetaStart + thetaLength/heightSegments*n)*Math.PI/180;
725 _sint[n] = Math.sin(theta);
726 _cost[n] = Math.cos(theta);
729 for (var n=0;n<=widthSegments;++n) {
730 var phi = (phiStart + phiLength/widthSegments*n)*Math.PI/180;
731 _sinp[n] = Math.sin(phi);
732 _cosp[n] = Math.cos(phi);
735 if (Math.abs(_sint[0]) <= epsilon) { numoutside -= widthSegments; numtop = 0; }
736 if (Math.abs(_sint[heightSegments]) <= epsilon) { numoutside -= widthSegments; numbottom = 0; }
738 var numfaces = numoutside * (noInside ? 1 : 2) + numtop + numbottom + numcut;
740 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(numfaces);
744 for (var side=0;side<2;++side) {
745 if ((side===1) && noInside)
break;
747 var r = radius[side],
748 s = (side===0) ? 1 : -1,
749 d1 = 1 - side, d2 = 1 - d1;
752 for (var k=0;k<heightSegments;++k) {
754 var k1 = k + d1, k2 = k + d2;
757 if (Math.abs(_sint[k1]) <= epsilon) skip = 1;
else
758 if (Math.abs(_sint[k2]) <= epsilon) skip = 2;
760 for (var n=0;n<widthSegments;++n) {
762 r*_sint[k1]*_cosp[n], r*_sint[k1] *_sinp[n], r*_cost[k1],
763 r*_sint[k1]*_cosp[n+1], r*_sint[k1] *_sinp[n+1], r*_cost[k1],
764 r*_sint[k2]*_cosp[n+1], r*_sint[k2] *_sinp[n+1], r*_cost[k2],
765 r*_sint[k2]*_cosp[n], r*_sint[k2] *_sinp[n], r*_cost[k2],
768 s*_sint[k1]*_cosp[n], s*_sint[k1] *_sinp[n], s*_cost[k1],
769 s*_sint[k1]*_cosp[n+1], s*_sint[k1] *_sinp[n+1], s*_cost[k1],
770 s*_sint[k2]*_cosp[n+1], s*_sint[k2] *_sinp[n+1], s*_cost[k2],
771 s*_sint[k2]*_cosp[n], s*_sint[k2] *_sinp[n], s*_cost[k2],
778 for (var side=0; side<=heightSegments; side+=heightSegments)
779 if (Math.abs(_sint[side]) >= epsilon) {
780 var ss = _sint[side], cc = _cost[side],
781 d1 = (side===0) ? 0 : 1, d2 = 1 - d1;
782 for (var n=0;n<widthSegments;++n) {
784 radius[1] * ss * _cosp[n+d1], radius[1] * ss * _sinp[n+d1], radius[1] * cc,
785 radius[0] * ss * _cosp[n+d1], radius[0] * ss * _sinp[n+d1], radius[0] * cc,
786 radius[0] * ss * _cosp[n+d2], radius[0] * ss * _sinp[n+d2], radius[0] * cc,
787 radius[1] * ss * _cosp[n+d2], radius[1] * ss * _sinp[n+d2], radius[1] * cc,
789 creator.CalcNormal();
794 if (phiLength < 360) {
795 for (var side=0;side<=widthSegments;side+=widthSegments) {
796 var ss = _sinp[side], cc = _cosp[side],
797 d1 = (side === 0) ? 1 : 0, d2 = 1 - d1;
799 for (var k=0;k<heightSegments;++k) {
801 radius[1] * _sint[k+d1] * cc, radius[1] * _sint[k+d1] * ss, radius[1] * _cost[k+d1],
802 radius[0] * _sint[k+d1] * cc, radius[0] * _sint[k+d1] * ss, radius[0] * _cost[k+d1],
803 radius[0] * _sint[k+d2] * cc, radius[0] * _sint[k+d2] * ss, radius[0] * _cost[k+d2],
804 radius[1] * _sint[k+d2] * cc, radius[1] * _sint[k+d2] * ss, radius[1] * _cost[k+d2],
806 creator.CalcNormal();
811 return creator.Create();
815 JSROOT.GEO.createTubeBuffer =
function( shape, faces_limit) {
817 if ((shape._typename ==
"TGeoCone") || (shape._typename ==
"TGeoConeSeg")) {
818 outerR = [ shape.fRmax2, shape.fRmax1 ];
819 innerR = [ shape.fRmin2, shape.fRmin1 ];
821 outerR = [ shape.fRmax, shape.fRmax ];
822 innerR = [ shape.fRmin, shape.fRmin ];
825 var hasrmin = (innerR[0] > 0) || (innerR[1] > 0),
826 thetaStart = 0, thetaLength = 360;
828 if ((shape._typename ==
"TGeoConeSeg") || (shape._typename ==
"TGeoTubeSeg") || (shape._typename ==
"TGeoCtub")) {
829 thetaStart = shape.fPhi1;
830 thetaLength = shape.fPhi2 - shape.fPhi1;
833 var radiusSegments = Math.max(4, Math.round(thetaLength/JSROOT.GEO.GradPerSegm));
836 var numfaces = radiusSegments * (((outerR[0] <= 0) || (outerR[1] <= 0)) ? 1 : 2);
840 numfaces += radiusSegments * (((innerR[0] <= 0) || (innerR[1] <= 0)) ? 1 : 2);
843 if (outerR[0] > 0) numfaces += radiusSegments * ((innerR[0]>0) ? 2 : 1);
845 if (outerR[1] > 0) numfaces += radiusSegments * ((innerR[1]>0) ? 2 : 1);
847 if (thetaLength < 360)
848 numfaces += ((outerR[0] > innerR[0]) ? 2 : 0) + ((outerR[1] > innerR[1]) ? 2 : 0);
850 if (faces_limit < 0)
return numfaces;
852 var phi0 = thetaStart*Math.PI/180,
853 dphi = thetaLength/radiusSegments*Math.PI/180,
854 _sin =
new Float32Array(radiusSegments+1),
855 _cos =
new Float32Array(radiusSegments+1);
857 for (var seg=0; seg<=radiusSegments; ++seg) {
858 _cos[seg] = Math.cos(phi0+seg*dphi);
859 _sin[seg] = Math.sin(phi0+seg*dphi);
862 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(numfaces);
868 if (shape._typename ==
"TGeoCtub")
869 calcZ =
function(x,y,z) {
870 var arr = (z<0) ? shape.fNlow : shape.fNhigh;
871 return ((z<0) ? -shape.fDz : shape.fDz) - (x*arr[0] + y*arr[1]) / arr[2];
875 for (var side = 0; side<2; ++side) {
876 if ((side === 1) && !hasrmin)
break;
878 var R = (side === 0) ? outerR : innerR,
879 d1 = side, d2 = 1 - side, nxy = 1., nz = 0;
882 var angle = Math.atan2((R[1]-R[0]), 2*shape.fDZ);
883 nxy = Math.cos(angle);
884 nz = Math.sin(angle);
887 if (side === 1) { nxy *= -1; nz *= -1; };
890 if (R[0] <= 0) reduce = 2;
else
891 if (R[1] <= 0) reduce = 1;
893 for (var seg=0;seg<radiusSegments;++seg) {
895 R[0] * _cos[seg+d1], R[0] * _sin[seg+d1], shape.fDZ,
896 R[1] * _cos[seg+d1], R[1] * _sin[seg+d1], -shape.fDZ,
897 R[1] * _cos[seg+d2], R[1] * _sin[seg+d2], -shape.fDZ,
898 R[0] * _cos[seg+d2], R[0] * _sin[seg+d2], shape.fDZ,
901 if (calcZ) creator.RecalcZ(calcZ);
903 creator.SetNormal_12_34(nxy*_cos[seg+d1], nxy*_sin[seg+d1], nz,
904 nxy*_cos[seg+d2], nxy*_sin[seg+d2], nz,
910 for (var side = 0; side<2; ++side) {
911 if (outerR[side] <= 0)
continue;
913 var d1 = side, d2 = 1- side,
914 sign = (side == 0) ? 1 : -1,
915 reduce = (innerR[side] <= 0) ? 2 : 0;
916 if ((reduce==2) && (thetaLength === 360) && !calcZ ) creator.StartPolygon(side===0);
917 for (var seg=0;seg<radiusSegments;++seg) {
919 innerR[side] * _cos[seg+d1], innerR[side] * _sin[seg+d1], sign*shape.fDZ,
920 outerR[side] * _cos[seg+d1], outerR[side] * _sin[seg+d1], sign*shape.fDZ,
921 outerR[side] * _cos[seg+d2], outerR[side] * _sin[seg+d2], sign*shape.fDZ,
922 innerR[side] * _cos[seg+d2], innerR[side] * _sin[seg+d2], sign*shape.fDZ,
925 creator.RecalcZ(calcZ);
926 creator.CalcNormal();
928 creator.SetNormal(0,0,sign);
932 creator.StopPolygon();
936 if (thetaLength < 360) {
937 creator.AddFace4(innerR[1] * _cos[0], innerR[1] * _sin[0], -shape.fDZ,
938 outerR[1] * _cos[0], outerR[1] * _sin[0], -shape.fDZ,
939 outerR[0] * _cos[0], outerR[0] * _sin[0], shape.fDZ,
940 innerR[0] * _cos[0], innerR[0] * _sin[0], shape.fDZ,
941 (outerR[0] === innerR[0]) ? 2 : ((innerR[1]===outerR[1]) ? 1 : 0) );
942 if (calcZ) creator.RecalcZ(calcZ);
943 creator.CalcNormal();
945 creator.AddFace4(innerR[0] * _cos[radiusSegments], innerR[0] * _sin[radiusSegments], shape.fDZ,
946 outerR[0] * _cos[radiusSegments], outerR[0] * _sin[radiusSegments], shape.fDZ,
947 outerR[1] * _cos[radiusSegments], outerR[1] * _sin[radiusSegments], -shape.fDZ,
948 innerR[1] * _cos[radiusSegments], innerR[1] * _sin[radiusSegments], -shape.fDZ,
949 (outerR[0] === innerR[0]) ? 1 : ((innerR[1]===outerR[1]) ? 2 : 0));
951 if (calcZ) creator.RecalcZ(calcZ);
952 creator.CalcNormal();
955 return creator.Create();
959 JSROOT.GEO.createEltuBuffer =
function( shape , faces_limit ) {
960 var radiusSegments = Math.max(4, Math.round(360/JSROOT.GEO.GradPerSegm));
962 if (faces_limit < 0)
return radiusSegments*4;
965 var x =
new Float32Array(radiusSegments+1),
966 y =
new Float32Array(radiusSegments+1);
967 for (var seg=0; seg<=radiusSegments; ++seg) {
968 var phi = seg/radiusSegments*2*Math.PI;
969 x[seg] = shape.fRmin*Math.cos(phi);
970 y[seg] = shape.fRmax*Math.sin(phi);
973 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(radiusSegments*4),
974 nx1 = 1, ny1 = 0, nx2 = 1, ny2 = 0;
977 for (var seg=0; seg<radiusSegments; ++seg) {
978 creator.AddFace4(x[seg], y[seg], +shape.fDZ,
979 x[seg], y[seg], -shape.fDZ,
980 x[seg+1], y[seg+1], -shape.fDZ,
981 x[seg+1], y[seg+1], shape.fDZ);
984 nx1 = nx2; ny1 = ny2;
985 nx2 = x[seg+1] * shape.fRmax / shape.fRmin;
986 ny2 = y[seg+1] * shape.fRmin / shape.fRmax;
987 var dist = Math.sqrt(nx2*nx2 + ny2*ny2);
988 nx2 = nx2 / dist; ny2 = ny2/dist;
990 creator.SetNormal_12_34(nx1,ny1,0,nx2,ny2,0);
994 for (var side=0;side<2;++side) {
995 var sign = (side===0) ? 1 : -1, d1 = side, d2 = 1 - side;
996 for (var seg=0; seg<radiusSegments; ++seg) {
997 creator.AddFace3(0, 0, sign*shape.fDZ,
998 x[seg+d1], y[seg+d1], sign*shape.fDZ,
999 x[seg+d2], y[seg+d2], sign*shape.fDZ);
1000 creator.SetNormal(0, 0, sign);
1004 return creator.Create();
1008 JSROOT.GEO.createTorusBuffer =
function( shape, faces_limit ) {
1009 var radius = shape.fR,
1010 radialSegments = Math.max(6, Math.round(360/JSROOT.GEO.GradPerSegm)),
1011 tubularSegments = Math.max(8, Math.round(shape.fDphi/JSROOT.GEO.GradPerSegm));
1013 var numfaces = (shape.fRmin > 0 ? 4 : 2) * radialSegments * (tubularSegments + (shape.fDphi !== 360 ? 1 : 0));
1015 if (faces_limit < 0)
return numfaces;
1017 if ((faces_limit > 0) && (numfaces > faces_limit)) {
1018 radialSegments = Math.floor(radialSegments/Math.sqrt(numfaces / faces_limit));
1019 tubularSegments = Math.floor(tubularSegments/Math.sqrt(numfaces / faces_limit));
1020 numfaces = (shape.fRmin > 0 ? 4 : 2) * radialSegments * (tubularSegments + (shape.fDphi !== 360 ? 1 : 0));
1023 var _sinr =
new Float32Array(radialSegments+1),
1024 _cosr =
new Float32Array(radialSegments+1),
1025 _sint =
new Float32Array(tubularSegments+1),
1026 _cost =
new Float32Array(tubularSegments+1);
1028 for (var n=0;n<=radialSegments;++n) {
1029 _sinr[n] = Math.sin(n/radialSegments*2*Math.PI);
1030 _cosr[n] = Math.cos(n/radialSegments*2*Math.PI);
1033 for (var t=0;t<=tubularSegments;++t) {
1034 var angle = (shape.fPhi1 + shape.fDphi*t/tubularSegments)/180*Math.PI;
1035 _sint[t] = Math.sin(angle);
1036 _cost[t] = Math.cos(angle);
1039 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(numfaces);
1042 var p1 =
new THREE.Vector3(), p2 =
new THREE.Vector3(), p3 =
new THREE.Vector3(), p4 =
new THREE.Vector3(),
1043 n1 =
new THREE.Vector3(), n2 =
new THREE.Vector3(), n3 =
new THREE.Vector3(), n4 =
new THREE.Vector3(),
1044 center1 =
new THREE.Vector3(), center2 =
new THREE.Vector3();
1046 for (var side=0;side<2;++side) {
1047 if ((side > 0) && (shape.fRmin <= 0))
break;
1048 var tube = (side > 0) ? shape.fRmin : shape.fRmax,
1049 d1 = 1 - side, d2 = 1 - d1, ns = side>0 ? -1 : 1;
1051 for (var t=0;t<tubularSegments;++t) {
1052 var t1 = t + d1, t2 = t + d2;
1053 center1.x = radius * _cost[t1]; center1.y = radius * _sint[t1];
1054 center2.x = radius * _cost[t2]; center2.y = radius * _sint[t2];
1056 for (var n=0;n<radialSegments;++n) {
1057 p1.x = (radius + tube * _cosr[n]) * _cost[t1]; p1.y = (radius + tube * _cosr[n]) * _sint[t1]; p1.z = tube*_sinr[n];
1058 p2.x = (radius + tube * _cosr[n+1]) * _cost[t1]; p2.y = (radius + tube * _cosr[n+1]) * _sint[t1]; p2.z = tube*_sinr[n+1];
1059 p3.x = (radius + tube * _cosr[n+1]) * _cost[t2]; p3.y = (radius + tube * _cosr[n+1]) * _sint[t2]; p3.z = tube*_sinr[n+1];
1060 p4.x = (radius + tube * _cosr[n]) * _cost[t2]; p4.y = (radius + tube * _cosr[n]) * _sint[t2]; p4.z = tube*_sinr[n];
1062 creator.AddFace4(p1.x, p1.y, p1.z,
1067 n1.subVectors( p1, center1 ).normalize();
1068 n2.subVectors( p2, center1 ).normalize();
1069 n3.subVectors( p3, center2 ).normalize();
1070 n4.subVectors( p4, center2 ).normalize();
1072 creator.SetNormal4(ns*n1.x, ns*n1.y, ns*n1.z,
1073 ns*n2.x, ns*n2.y, ns*n2.z,
1074 ns*n3.x, ns*n3.y, ns*n3.z,
1075 ns*n4.x, ns*n4.y, ns*n4.z);
1080 if (shape.fDphi !== 360)
1081 for (var t=0;t<=tubularSegments;t+=tubularSegments) {
1082 var tube1 = shape.fRmax, tube2 = shape.fRmin,
1083 d1 = (t>0) ? 0 : 1, d2 = 1 - d1,
1084 skip = (shape.fRmin) > 0 ? 0 : 1,
1085 nsign = t>0 ? 1 : -1;
1086 for (var n=0;n<radialSegments;++n) {
1087 creator.AddFace4((radius + tube1 * _cosr[n+d1]) * _cost[t], (radius + tube1 * _cosr[n+d1]) * _sint[t], tube1*_sinr[n+d1],
1088 (radius + tube2 * _cosr[n+d1]) * _cost[t], (radius + tube2 * _cosr[n+d1]) * _sint[t], tube2*_sinr[n+d1],
1089 (radius + tube2 * _cosr[n+d2]) * _cost[t], (radius + tube2 * _cosr[n+d2]) * _sint[t], tube2*_sinr[n+d2],
1090 (radius + tube1 * _cosr[n+d2]) * _cost[t], (radius + tube1 * _cosr[n+d2]) * _sint[t], tube1*_sinr[n+d2], skip);
1091 creator.SetNormal(-nsign* _sint[t], nsign * _cost[t], 0);
1095 return creator.Create();
1100 JSROOT.GEO.createPolygonBuffer =
function( shape, faces_limit ) {
1101 var thetaStart = shape.fPhi1,
1102 thetaLength = shape.fDphi,
1103 radiusSegments = 60, factor = 1;
1105 if (shape._typename ==
"TGeoPgon") {
1106 radiusSegments = shape.fNedges;
1107 factor = 1. / Math.cos(Math.PI/180 * thetaLength / radiusSegments / 2);
1109 radiusSegments = Math.max(5, Math.round(thetaLength/JSROOT.GEO.GradPerSegm));
1112 var usage =
new Int16Array(2*shape.fNz), numusedlayers = 0, hasrmin =
false;
1114 for (var layer=0; layer < shape.fNz; ++layer)
1115 if (shape.fRmin[layer] > 0) hasrmin =
true;
1118 if (faces_limit < 0)
return (hasrmin ? 4 : 2) * radiusSegments * (shape.fNz-1);
1121 var pnts = (thetaLength === 360) ? null : [];
1124 for (var side = 0; side < 2; ++side) {
1125 var rside = (side === 0) ?
'fRmax' :
'fRmin';
1127 for (var layer=0; layer < shape.fNz; ++layer) {
1130 var layerz = shape.fZ[layer], rad = shape[rside][layer];
1132 usage[layer*2+side] = 0;
1134 if ((layer > 0) && (layer < shape.fNz-1))
1135 if (((shape.fZ[layer-1] === layerz) && (shape[rside][layer-1] === rad)) ||
1136 ((shape[rside][layer+1] === rad) && (shape[rside][layer-1] === rad))) {
1144 if ((layer>0) && ((side === 0) || hasrmin)) {
1145 usage[layer*2+side] = 1;
1149 if (pnts !== null) {
1151 pnts.push(
new THREE.Vector2(factor*rad, layerz));
1152 }
else if (rad < shape.fRmax[layer]) {
1153 pnts.unshift(
new THREE.Vector2(factor*rad, layerz));
1159 var numfaces = numusedlayers*radiusSegments*2;
1160 if (shape.fRmin[0] !== shape.fRmax[0]) numfaces += radiusSegments * (hasrmin ? 2 : 1);
1161 if (shape.fRmin[shape.fNz-1] !== shape.fRmax[shape.fNz-1]) numfaces += radiusSegments * (hasrmin ? 2 : 1);
1163 var cut_faces = null;
1166 if (pnts.length === shape.fNz * 2) {
1169 for (var layer = shape.fNz-1; layer>0; --layer) {
1170 if (shape.fZ[layer] === shape.fZ[layer-1])
continue;
1171 var right = 2*shape.fNz - 1 - layer;
1172 cut_faces.push([right, layer - 1, layer]);
1173 cut_faces.push([right, right + 1, layer-1]);
1179 cut_faces = THREE.ShapeUtils.triangulateShape(pnts, []);
1181 numfaces += cut_faces.length*2;
1184 var phi0 = thetaStart*Math.PI/180, dphi = thetaLength/radiusSegments*Math.PI/180;
1187 var _sin =
new Float32Array(radiusSegments+1),
1188 _cos =
new Float32Array(radiusSegments+1);
1189 for (var seg=0;seg<=radiusSegments;++seg) {
1190 _cos[seg] = Math.cos(phi0+seg*dphi);
1191 _sin[seg] = Math.sin(phi0+seg*dphi);
1194 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(numfaces);
1197 for (var side = 0; side < 2; ++side) {
1198 var rside = (side === 0) ?
'fRmax' :
'fRmin',
1199 z1 = shape.fZ[0], r1 = factor*shape[rside][0],
1200 d1 = 1 - side, d2 = side;
1202 for (var layer=0; layer < shape.fNz; ++layer) {
1204 if (usage[layer*2+side] === 0)
continue;
1206 var z2 = shape.fZ[layer], r2 = factor*shape[rside][layer],
1210 var angle = Math.atan2((r2-r1), (z2-z1));
1211 nxy = Math.cos(angle);
1212 nz = Math.sin(angle);
1215 if (side>0) { nxy*=-1; nz*=-1; }
1217 for (var seg=0;seg < radiusSegments;++seg) {
1218 creator.AddFace4(r1 * _cos[seg+d1], r1 * _sin[seg+d1], z1,
1219 r2 * _cos[seg+d1], r2 * _sin[seg+d1], z2,
1220 r2 * _cos[seg+d2], r2 * _sin[seg+d2], z2,
1221 r1 * _cos[seg+d2], r1 * _sin[seg+d2], z1);
1222 creator.SetNormal_12_34(nxy*_cos[seg+d1], nxy*_sin[seg+d1], nz, nxy*_cos[seg+d2], nxy*_sin[seg+d2], nz);
1230 for (var layer=0; layer < shape.fNz; layer += (shape.fNz-1)) {
1232 var rmin = factor*shape.fRmin[layer], rmax = factor*shape.fRmax[layer];
1234 if (rmin === rmax)
continue;
1236 var layerz = shape.fZ[layer],
1237 d1 = (layer===0) ? 1 : 0, d2 = 1 - d1,
1238 normalz = (layer===0) ? -1: 1;
1240 if (!hasrmin && !cut_faces) creator.StartPolygon(layer>0);
1242 for (var seg=0;seg < radiusSegments;++seg) {
1243 creator.AddFace4(rmin * _cos[seg+d1], rmin * _sin[seg+d1], layerz,
1244 rmax * _cos[seg+d1], rmax * _sin[seg+d1], layerz,
1245 rmax * _cos[seg+d2], rmax * _sin[seg+d2], layerz,
1246 rmin * _cos[seg+d2], rmin * _sin[seg+d2], layerz,
1248 creator.SetNormal(0, 0, normalz);
1251 creator.StopPolygon();
1255 for (var seg = 0; seg <= radiusSegments; seg += radiusSegments) {
1256 var d1 = (seg === 0) ? 1 : 2, d2 = 3 - d1;
1257 for (var n=0;n<cut_faces.length;++n) {
1258 var a = pnts[cut_faces[n][0]],
1259 b = pnts[cut_faces[n][d1]],
1260 c = pnts[cut_faces[n][d2]];
1262 creator.AddFace3(a.x * _cos[seg], a.x * _sin[seg], a.y,
1263 b.x * _cos[seg], b.x * _sin[seg], b.y,
1264 c.x * _cos[seg], c.x * _sin[seg], c.y);
1266 creator.CalcNormal();
1270 return creator.Create();
1274 JSROOT.GEO.createXtruBuffer =
function( shape, faces_limit ) {
1275 var nfaces = (shape.fNz-1) * shape.fNvert * 2;
1277 if (faces_limit < 0)
return nfaces + shape.fNvert*3;
1281 for (var vert = 0; vert < shape.fNvert; ++vert)
1282 pnts.push(
new THREE.Vector2(shape.fX[vert], shape.fY[vert]));
1285 var faces = THREE.ShapeUtils.triangulateShape(pnts , []);
1286 if (faces.length < pnts.length-2) {
1287 JSROOT.GEO.warn(
'Problem with XTRU shape ' +shape.fName +
' with ' + pnts.length +
' vertices');
1290 nfaces += faces.length * 2;
1293 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(nfaces);
1295 for (var layer = 0; layer < shape.fNz-1; ++layer) {
1296 var z1 = shape.fZ[layer], scale1 = shape.fScale[layer],
1297 z2 = shape.fZ[layer+1], scale2 = shape.fScale[layer+1],
1298 x01 = shape.fX0[layer], x02 = shape.fX0[layer+1],
1299 y01 = shape.fY0[layer], y02 = shape.fY0[layer+1];
1301 for (var vert1 = 0; vert1 < shape.fNvert; ++vert1) {
1302 var vert2 = (vert1+1) % shape.fNvert;
1303 creator.AddFace4(scale1 * shape.fX[vert1] + x01, scale1 * shape.fY[vert1] + y01, z1,
1304 scale2 * shape.fX[vert1] + x02, scale2 * shape.fY[vert1] + y02, z2,
1305 scale2 * shape.fX[vert2] + x02, scale2 * shape.fY[vert2] + y02, z2,
1306 scale1 * shape.fX[vert2] + x01, scale1 * shape.fY[vert2] + y01, z1);
1307 creator.CalcNormal();
1311 for (layer = 0; layer <= shape.fNz-1; layer+=(shape.fNz-1)) {
1312 var z = shape.fZ[layer], scale = shape.fScale[layer],
1313 x0 = shape.fX0[layer], y0 = shape.fY0[layer];
1315 for (var n=0;n<faces.length;++n) {
1316 var face = faces[n],
1317 pnt1 = pnts[face[0]],
1318 pnt2 = pnts[face[(layer===0) ? 2 : 1]],
1319 pnt3 = pnts[face[(layer===0) ? 1 : 2]];
1321 creator.AddFace3(scale * pnt1.x + x0, scale * pnt1.y + y0, z,
1322 scale * pnt2.x + x0, scale * pnt2.y + y0, z,
1323 scale * pnt3.x + x0, scale * pnt3.y + y0, z);
1324 creator.SetNormal(0,0,layer===0 ? -1 : 1);
1328 return creator.Create();
1332 JSROOT.GEO.createParaboloidBuffer =
function( shape, faces_limit ) {
1334 var radiusSegments = Math.max(4, Math.round(360/JSROOT.GEO.GradPerSegm)),
1335 heightSegments = 30;
1337 if (faces_limit > 0) {
1338 var fact = 2*radiusSegments*(heightSegments+1) / faces_limit;
1340 radiusSegments = Math.max(5, Math.floor(radiusSegments/Math.sqrt(fact)));
1341 heightSegments = Math.max(5, Math.floor(heightSegments/Math.sqrt(fact)));
1345 var zmin = -shape.fDZ, zmax = shape.fDZ, rmin = shape.fRlo, rmax = shape.fRhi;
1348 if (shape.fA >= 0) {
1349 if (shape.fB > zmin) zmin = shape.fB;
1351 if (shape.fB < zmax) zmax = shape.fB;
1354 var ttmin = Math.atan2(zmin, rmin), ttmax = Math.atan2(zmax, rmax);
1356 var numfaces = (heightSegments+1)*radiusSegments*2;
1357 if (rmin===0) numfaces -= radiusSegments*2;
1358 if (rmax===0) numfaces -= radiusSegments*2;
1360 if (faces_limit < 0)
return numfaces;
1363 var _sin =
new Float32Array(radiusSegments+1),
1364 _cos =
new Float32Array(radiusSegments+1);
1365 for (var seg=0;seg<=radiusSegments;++seg) {
1366 _cos[seg] = Math.cos(seg/radiusSegments*2*Math.PI);
1367 _sin[seg] = Math.sin(seg/radiusSegments*2*Math.PI);
1370 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(numfaces);
1372 var lastz = zmin, lastr = 0, lastnxy = 0, lastnz = -1;
1374 for (var layer = 0; layer <= heightSegments + 1; ++layer) {
1376 var layerz = 0, radius = 0, nxy = 0, nz = -1;
1378 if ((layer === 0) && (rmin===0))
continue;
1380 if ((layer === heightSegments + 1) && (lastr === 0))
break;
1383 case 0: layerz = zmin; radius = rmin;
break;
1384 case heightSegments: layerz = zmax; radius = rmax;
break;
1385 case heightSegments + 1: layerz = zmax; radius = 0;
break;
1387 var tt = Math.tan(ttmin + (ttmax-ttmin) * layer / heightSegments);
1388 var delta = tt*tt - 4*shape.fA*shape.fB;
1389 radius = 0.5*(tt+Math.sqrt(delta))/shape.fA;
1390 if (radius < 1e-6) radius = 0;
1395 nxy = shape.fA * radius;
1396 nz = (shape.fA > 0) ? -1 : 1;
1399 if (lastr === 0) skip = 1;
else
1400 if (radius === 0) skip = 2;
1402 for (var seg=0; seg<radiusSegments; ++seg) {
1403 creator.AddFace4(radius*_cos[seg], radius*_sin[seg], layerz,
1404 lastr*_cos[seg], lastr*_sin[seg], lastz,
1405 lastr*_cos[seg+1], lastr*_sin[seg+1], lastz,
1406 radius*_cos[seg+1], radius*_sin[seg+1], layerz, skip);
1410 if ((skip===0) || ((layer===1) && (rmin===0)) || ((layer===heightSegments+1) && (rmax===0)))
1411 creator.SetNormal4(nxy*_cos[seg], nxy*_sin[seg], nz,
1412 lastnxy*_cos[seg], lastnxy*_sin[seg], lastnz,
1413 lastnxy*_cos[seg+1], lastnxy*_sin[seg+1], lastnz,
1414 nxy*_cos[seg+1], nxy*_sin[seg+1], nz, skip);
1416 creator.SetNormal(0, 0, (layer < heightSegments) ? -1 : 1);
1419 lastz = layerz; lastr = radius;
1420 lastnxy = nxy; lastnz = nz;
1423 return creator.Create();
1427 JSROOT.GEO.createHypeBuffer =
function( shape, faces_limit ) {
1429 if ((shape.fTin===0) && (shape.fTout===0))
1430 return JSROOT.GEO.createTubeBuffer(shape, faces_limit);
1432 var radiusSegments = Math.max(4, Math.round(360/JSROOT.GEO.GradPerSegm)),
1433 heightSegments = 30;
1435 var numfaces = radiusSegments * (heightSegments + 1) * ((shape.fRmin > 0) ? 4 : 2);
1437 if (faces_limit < 0)
return numfaces;
1439 if ((faces_limit > 0) && (faces_limit > numfaces)) {
1440 radiusSegments = Math.max(4, Math.floor(radiusSegments/Math.sqrt(numfaces/faces_limit)));
1441 heightSegments = Math.max(4, Math.floor(heightSegments/Math.sqrt(numfaces/faces_limit)));
1442 numfaces = radiusSegments * (heightSegments + 1) * ((shape.fRmin > 0) ? 4 : 2);
1446 var _sin =
new Float32Array(radiusSegments+1), _cos =
new Float32Array(radiusSegments+1);
1447 for (var seg=0;seg<=radiusSegments;++seg) {
1448 _cos[seg] = Math.cos(seg/radiusSegments*2*Math.PI);
1449 _sin[seg] = Math.sin(seg/radiusSegments*2*Math.PI);
1452 var creator = faces_limit ?
new JSROOT.GEO.PolygonsCreator :
new JSROOT.GEO.GeometryCreator(numfaces);
1455 for (var side=0;side<2;++side) {
1456 if ((side > 0) && (shape.fRmin <= 0))
break;
1458 var r0 = (side > 0) ? shape.fRmin : shape.fRmax,
1459 tsq = (side > 0) ? shape.fTinsq : shape.fToutsq,
1460 d1 = 1- side, d2 = 1 - d1;
1463 for (var layer=0;layer<heightSegments;++layer) {
1464 var z1 = -shape.fDz + layer/heightSegments*2*shape.fDz,
1465 z2 = -shape.fDz + (layer+1)/heightSegments*2*shape.fDz,
1466 r1 = Math.sqrt(r0*r0+tsq*z1*z1),
1467 r2 = Math.sqrt(r0*r0+tsq*z2*z2);
1469 for (var seg=0; seg<radiusSegments; ++seg) {
1470 creator.AddFace4(r1 * _cos[seg+d1], r1 * _sin[seg+d1], z1,
1471 r2 * _cos[seg+d1], r2 * _sin[seg+d1], z2,
1472 r2 * _cos[seg+d2], r2 * _sin[seg+d2], z2,
1473 r1 * _cos[seg+d2], r1 * _sin[seg+d2], z1);
1474 creator.CalcNormal();
1480 for(var layer=0; layer<2; ++layer) {
1481 var z = (layer === 0) ? shape.fDz : -shape.fDz,
1482 r1 = Math.sqrt(shape.fRmax*shape.fRmax + shape.fToutsq*z*z),
1483 r2 = (shape.fRmin > 0) ? Math.sqrt(shape.fRmin*shape.fRmin + shape.fTinsq*z*z) : 0,
1484 skip = (shape.fRmin > 0) ? 0 : 1,
1485 d1 = 1 - layer, d2 = 1 - d1;
1486 for (var seg=0; seg<radiusSegments; ++seg) {
1487 creator.AddFace4(r1 * _cos[seg+d1], r1 * _sin[seg+d1], z,
1488 r2 * _cos[seg+d1], r2 * _sin[seg+d1], z,
1489 r2 * _cos[seg+d2], r2 * _sin[seg+d2], z,
1490 r1 * _cos[seg+d2], r1 * _sin[seg+d2], z, skip);
1491 creator.SetNormal(0,0, (layer===0) ? 1 : -1)
1496 return creator.Create();
1501 JSROOT.GEO.createMatrix =
function(matrix) {
1503 if (!matrix)
return null;
1505 var translation = null, rotation = null, scale = null;
1507 switch (matrix._typename) {
1508 case 'TGeoTranslation': translation = matrix.fTranslation;
break;
1509 case 'TGeoRotation': rotation = matrix.fRotationMatrix;
break;
1510 case 'TGeoScale': scale = matrix.fScale;
break;
1511 case 'TGeoGenTrans':
1512 scale = matrix.fScale;
1513 case 'TGeoCombiTrans':
1514 translation = matrix.fTranslation;
1515 if (matrix.fRotation) rotation = matrix.fRotation.fRotationMatrix;
1518 translation = matrix.fTranslation;
1519 rotation = matrix.fRotationMatrix;
1520 scale = matrix.fScale;
1522 case 'TGeoIdentity':
1525 console.warn(
'unsupported matrix ' + matrix._typename);
1528 if (!translation && !rotation && !scale)
return null;
1530 var res =
new THREE.Matrix4();
1533 res.set(rotation[0], rotation[1], rotation[2], 0,
1534 rotation[3], rotation[4], rotation[5], 0,
1535 rotation[6], rotation[7], rotation[8], 0,
1539 res.setPosition(
new THREE.Vector3(translation[0], translation[1], translation[2]));
1542 res.scale(
new THREE.Vector3(scale[0], scale[1], scale[2]));
1548 JSROOT.GEO.getNodeMatrix =
function(kind, node) {
1557 matrix =
new THREE.Matrix4();
1560 matrix.set(node.fTrans[0], node.fTrans[4], node.fTrans[8], 0,
1561 node.fTrans[1], node.fTrans[5], node.fTrans[9], 0,
1562 node.fTrans[2], node.fTrans[6], node.fTrans[10], 0,
1565 matrix.setPosition({ x: node.fTrans[12], y: node.fTrans[13], z: node.fTrans[14] });
1567 }
else if (node.fMatrix) {
1568 matrix = JSROOT.GEO.createMatrix(node.fMatrix);
1569 }
else if ((node._typename ==
"TGeoNodeOffset") && node.fFinder) {
1570 var kPatternReflected = JSROOT.BIT(14);
1571 if ((node.fFinder.fBits & kPatternReflected) !== 0)
1572 JSROOT.GEO.warn(
'Unsupported reflected pattern ' + node.fFinder._typename);
1579 switch(node.fFinder._typename) {
1580 case 'TGeoPatternX':
1581 case 'TGeoPatternY':
1582 case 'TGeoPatternZ':
1583 case 'TGeoPatternParaX':
1584 case 'TGeoPatternParaY':
1585 case 'TGeoPatternParaZ':
1586 var _shift = node.fFinder.fStart + (node.fIndex + 0.5) * node.fFinder.fStep;
1588 matrix =
new THREE.Matrix4();
1590 switch (node.fFinder._typename[node.fFinder._typename.length-1]) {
1591 case 'X': matrix.setPosition(
new THREE.Vector3(_shift, 0, 0));
break;
1592 case 'Y': matrix.setPosition(
new THREE.Vector3(0, _shift, 0));
break;
1593 case 'Z': matrix.setPosition(
new THREE.Vector3(0, 0, _shift));
break;
1597 case 'TGeoPatternCylPhi':
1598 var phi = (Math.PI/180)*(node.fFinder.fStart+(node.fIndex+0.5)*node.fFinder.fStep),
1599 _cos = Math.cos(phi), _sin = Math.sin(phi);
1601 matrix =
new THREE.Matrix4();
1603 matrix.set(_cos, -_sin, 0, 0,
1609 case 'TGeoPatternCylR':
1611 matrix =
new THREE.Matrix4();
1614 case 'TGeoPatternTrapZ':
1615 var dz = node.fFinder.fStart + (node.fIndex+0.5)*node.fFinder.fStep;
1616 matrix =
new THREE.Matrix4();
1617 matrix.setPosition(
new THREE.Vector3(node.fFinder.fTxz*dz, node.fFinder.fTyz*dz, dz));
1621 JSROOT.GEO.warn(
'Unsupported pattern type ' + node.fFinder._typename);
1630 JSROOT.GEO.createComposite =
function ( shape, faces_limit ) {
1650 if (faces_limit < 0)
1651 return JSROOT.GEO.createGeometry(shape.fNode.fLeft, -10) +
1652 JSROOT.GEO.createGeometry(shape.fNode.fRight, -10);
1654 var geom1, geom2, bsp1, bsp2, return_bsp =
false,
1655 matrix1 = JSROOT.GEO.createMatrix(shape.fNode.fLeftMat),
1656 matrix2 = JSROOT.GEO.createMatrix(shape.fNode.fRightMat);
1659 if (faces_limit === 0) faces_limit = (JSROOT.browser && JSROOT.browser.isIE) ? 2000 : 4000;
1660 else return_bsp =
true;
1662 if (matrix1 && (matrix1.determinant() < -0.9))
1663 JSROOT.GEO.warn(
'Axis reflection in left composite shape - not supported');
1665 if (matrix2 && (matrix2.determinant() < -0.9))
1666 JSROOT.GEO.warn(
'Axis reflections in right composite shape - not supported');
1668 if (shape.fNode.fLeft._typename ==
"TGeoHalfSpace") {
1669 geom1 = JSROOT.GEO.createHalfSpace(shape.fNode.fLeft);
1671 geom1 = JSROOT.GEO.createGeometry(shape.fNode.fLeft, faces_limit);
1674 if (!geom1)
return null;
1676 var n1 = JSROOT.GEO.numGeometryFaces(geom1), n2 = 0;
1677 if (geom1._exceed_limit) n1 += faces_limit;
1679 if (n1 < faces_limit) {
1681 if (shape.fNode.fRight._typename ==
"TGeoHalfSpace") {
1682 geom2 = JSROOT.GEO.createHalfSpace(shape.fNode.fRight, geom1);
1684 geom2 = JSROOT.GEO.createGeometry(shape.fNode.fRight, faces_limit);
1687 n2 = JSROOT.GEO.numGeometryFaces(geom2);
1690 if ((n1 + n2 >= faces_limit) || !geom2) {
1691 if (geom1.polygons) {
1692 geom1 = ThreeBSP.CreateBufferGeometry(geom1.polygons);
1693 n1 = JSROOT.GEO.numGeometryFaces(geom1);
1695 if (matrix1) geom1.applyMatrix(matrix1);
1697 geom1._exceed_limit =
true;
1701 bsp1 =
new ThreeBSP.Geometry(geom1, matrix1, JSROOT.GEO.CompressComp ? 0 : undefined);
1703 bsp2 =
new ThreeBSP.Geometry(geom2, matrix2, bsp1.maxid);
1706 bsp1.maxid = bsp2.maxid;
1708 switch(shape.fNode._typename) {
1709 case 'TGeoIntersection': bsp1.direct_intersect(bsp2);
break;
1710 case 'TGeoUnion': bsp1.direct_union(bsp2);
break;
1711 case 'TGeoSubtraction': bsp1.direct_subtract(bsp2);
break;
1713 JSROOT.GEO.warn(
'unsupported bool operation ' + shape.fNode._typename +
', use first geom');
1716 if (JSROOT.GEO.numGeometryFaces(bsp1) === 0) {
1717 JSROOT.GEO.warn(
'Zero faces in comp shape'
1718 +
' left: ' + shape.fNode.fLeft._typename +
' ' + JSROOT.GEO.numGeometryFaces(geom1) +
' faces'
1719 +
' right: ' + shape.fNode.fRight._typename +
' ' + JSROOT.GEO.numGeometryFaces(geom2) +
' faces'
1721 bsp1 =
new ThreeBSP.Geometry(geom1, matrix1);
1724 return return_bsp ? { polygons: bsp1.toPolygons() } : bsp1.toBufferGeometry();
1728 JSROOT.GEO.projectGeometry =
function(geom, matrix, projection, position, flippedMesh) {
1730 if (!geom.boundingBox) geom.computeBoundingBox();
1732 var box = geom.boundingBox.clone();
1734 box.applyMatrix4(matrix);
1736 if (!position) position = 0;
1738 if (((box.min[projection]>=position) && (box.max[projection]>=position)) ||
1739 ((box.min[projection]<=position) && (box.max[projection]<=position))) {
1743 var bsp1 =
new ThreeBSP.Geometry(geom, matrix, 0, flippedMesh),
1744 sizex = 2*Math.max(Math.abs(box.min.x), Math.abs(box.max.x)),
1745 sizey = 2*Math.max(Math.abs(box.min.y), Math.abs(box.max.y)),
1746 sizez = 2*Math.max(Math.abs(box.min.z), Math.abs(box.max.z)),
1749 switch (projection) {
1750 case "x": size = Math.max(sizey,sizez);
break;
1751 case "y": size = Math.max(sizex,sizez);
break;
1752 case "z": size = Math.max(sizex,sizey);
break;
1755 var bsp2 = ThreeBSP.CreateNormal(projection, position, size);
1757 bsp1.cut_from_plane(bsp2);
1759 return bsp2.toBufferGeometry();
1769 JSROOT.GEO.createGeometry =
function( shape, limit ) {
1770 if (limit === undefined) limit = 0;
1773 switch (shape._typename) {
1774 case "TGeoBBox":
return JSROOT.GEO.createCubeBuffer( shape, limit );
1775 case "TGeoPara":
return JSROOT.GEO.createParaBuffer( shape, limit );
1777 case "TGeoTrd2":
return JSROOT.GEO.createTrapezoidBuffer( shape, limit );
1780 case "TGeoGtra":
return JSROOT.GEO.createArb8Buffer( shape, limit );
1781 case "TGeoSphere":
return JSROOT.GEO.createSphereBuffer( shape , limit );
1786 case "TGeoCtub":
return JSROOT.GEO.createTubeBuffer( shape, limit );
1787 case "TGeoEltu":
return JSROOT.GEO.createEltuBuffer( shape, limit );
1788 case "TGeoTorus":
return JSROOT.GEO.createTorusBuffer( shape, limit );
1790 case "TGeoPgon":
return JSROOT.GEO.createPolygonBuffer( shape, limit );
1791 case "TGeoXtru":
return JSROOT.GEO.createXtruBuffer( shape, limit );
1792 case "TGeoParaboloid":
return JSROOT.GEO.createParaboloidBuffer( shape, limit );
1793 case "TGeoHype":
return JSROOT.GEO.createHypeBuffer( shape, limit );
1794 case "TGeoCompositeShape":
return JSROOT.GEO.createComposite( shape, limit );
1795 case "TGeoShapeAssembly":
break;
1796 case "TGeoScaledShape": {
1797 var res = JSROOT.GEO.createGeometry(shape.fShape, limit);
1798 if (shape.fScale && (limit>=0) && (typeof res ===
'object') && (typeof res.scale ===
'function'))
1799 res.scale(shape.fScale.fScale[0],shape.fScale.fScale[1],shape.fScale.fScale[2]);
1802 case "TGeoHalfSpace":
1803 if (limit < 0)
return 1;
1805 default: JSROOT.GEO.warn(
'unsupported shape type ' + shape._typename);
1809 if (e.stack !== undefined) {
1810 place = e.stack.split(
"\n")[0];
1811 if (place.indexOf(e.message) >= 0) place = e.stack.split(
"\n")[1];
1812 else place =
" at: " + place;
1814 JSROOT.GEO.warn(shape._typename +
" err: " + e.message + place);
1817 return limit < 0 ? 0 : null;
1824 JSROOT.GEO.createHalfSpace =
function(shape, geom) {
1825 if (!shape || !shape.fN || !shape.fP)
return null;
1829 var vertex =
new THREE.Vector3(shape.fP[0], shape.fP[1], shape.fP[2]);
1831 var normal =
new THREE.Vector3(shape.fN[0], shape.fN[1], shape.fN[2]);
1837 var box = JSROOT.GEO.geomBoundingBox(geom);
1838 if (box) sz = box.getSize(
new THREE.Vector3()).length() * 1000;
1843 var v1 =
new THREE.Vector3(-sz, -sz/2, 0),
1844 v2 =
new THREE.Vector3(0, sz, 0),
1845 v3 =
new THREE.Vector3(sz, -sz/2, 0),
1846 v4 =
new THREE.Vector3(0, 0, -sz);
1848 var geometry =
new THREE.Geometry();
1850 geometry.vertices.push(v1, v2, v3, v4);
1852 geometry.faces.push(
new THREE.Face3( 0, 2, 1 ) );
1853 geometry.faces.push(
new THREE.Face3( 0, 1, 3 ) );
1854 geometry.faces.push(
new THREE.Face3( 1, 2, 3 ) );
1855 geometry.faces.push(
new THREE.Face3( 2, 0, 3 ) );
1857 geometry.lookAt(normal);
1858 geometry.computeFaceNormals();
1889 JSROOT.GEO.provideInfo =
function(obj) {
1890 var info = [], shape = null;
1892 if (obj.fVolume !== undefined) shape = obj.fVolume.fShape;
else
1893 if (obj.fShape !== undefined) shape = obj.fShape;
else
1894 if ((obj.fShapeBits !== undefined) && (obj.fShapeId !== undefined)) shape = obj;
1897 info.push(obj._typename);
1901 var sz = Math.max(shape.fDX, shape.fDY, shape.fDZ);
1902 var useexp = (sz>1e7) || (sz<1e-7);
1905 if (v===undefined)
return "???";
1906 if ((v==Math.round(v) && v<1e7))
return Math.round(v);
1907 return useexp ? v.toExponential(4) : v.toPrecision(7);
1910 info.push(shape._typename);
1912 info.push(
"DX="+conv(shape.fDX) +
" DY="+conv(shape.fDY) +
" DZ="+conv(shape.fDZ));
1914 switch (shape._typename) {
1915 case "TGeoBBox":
break;
1916 case "TGeoPara": info.push(
"Alpha=" + shape.fAlpha +
" Phi=" + shape.fPhi +
" Theta=" + shape.fTheta);
break;
1917 case "TGeoTrd2": info.push(
"Dy1=" + conv(shape.fDy1) +
" Dy2=" + conv(shape.fDy1));
1918 case "TGeoTrd1": info.push(
"Dx1=" + conv(shape.fDx1) +
" Dx2=" + conv(shape.fDx1));
break;
1919 case "TGeoArb8":
break;
1920 case "TGeoTrap":
break;
1921 case "TGeoGtra":
break;
1923 info.push(
"Rmin=" + conv(shape.fRmin) +
" Rmax=" + conv(shape.fRmax));
1924 info.push(
"Phi1=" + shape.fPhi1 +
" Phi2=" + shape.fPhi2);
1925 info.push(
"Theta1=" + shape.fTheta1 +
" Theta2=" + shape.fTheta2);
1928 info.push(
"Phi1=" + shape.fPhi1 +
" Phi2=" + shape.fPhi2);
1930 info.push(
"Rmin1=" + conv(shape.fRmin1) +
" Rmax1=" + conv(shape.fRmax1));
1931 info.push(
"Rmin2=" + conv(shape.fRmin2) +
" Rmax2=" + conv(shape.fRmax2));
1935 info.push(
"Phi1=" + shape.fPhi1 +
" Phi2=" + shape.fPhi2);
1938 info.push(
"Rmin=" + conv(shape.fRmin) +
" Rmax=" + conv(shape.fRmax));
1941 info.push(
"Rmin=" + conv(shape.fRmin) +
" Rmax=" + conv(shape.fRmax));
1942 info.push(
"Phi1=" + shape.fPhi1 +
" Dphi=" + shape.fDphi);
1945 case "TGeoPgon":
break;
1946 case "TGeoXtru":
break;
1947 case "TGeoParaboloid":
1948 info.push(
"Rlo=" + conv(shape.fRlo) +
" Rhi=" + conv(shape.fRhi));
1949 info.push(
"A=" + conv(shape.fA) +
" B=" + conv(shape.fB));
1952 info.push(
"Rmin=" + conv(shape.fRmin) +
" Rmax=" + conv(shape.fRmax));
1953 info.push(
"StIn=" + conv(shape.fStIn) +
" StOut=" + conv(shape.fStOut));
1955 case "TGeoCompositeShape":
break;
1956 case "TGeoShapeAssembly":
break;
1957 case "TGeoScaledShape":
1958 info = JSROOT.GEO.provideInfo(shape.fShape);
1960 info.unshift(
'Scale X=' + shape.fScale.fScale[0] +
" Y=" + shape.fScale.fScale[1] +
" Z=" + shape.fScale.fScale[2]);
1968 JSROOT.GEO.CreateProjectionMatrix =
function(camera) {
1969 var cameraProjectionMatrix =
new THREE.Matrix4();
1971 camera.updateMatrixWorld();
1972 camera.matrixWorldInverse.getInverse( camera.matrixWorld );
1973 cameraProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse);
1975 return cameraProjectionMatrix;
1979 JSROOT.GEO.CreateFrustum =
function(source) {
1980 if (!source)
return null;
1982 if (source instanceof THREE.PerspectiveCamera)
1983 source = JSROOT.GEO.CreateProjectionMatrix(source);
1985 var frustum =
new THREE.Frustum();
1986 frustum.setFromMatrix(source);
1988 frustum.corners =
new Float32Array([
2000 frustum.test =
new THREE.Vector3(0,0,0);
2002 frustum.CheckShape =
function(matrix, shape) {
2003 var pnt = this.test, len = this.corners.length, corners = this.corners, i;
2005 for (i = 0; i < len; i+=3) {
2006 pnt.x = corners[i] * shape.fDX;
2007 pnt.y = corners[i+1] * shape.fDY;
2008 pnt.z = corners[i+2] * shape.fDZ;
2009 if (this.containsPoint(pnt.applyMatrix4(matrix)))
return true;
2015 frustum.CheckBox =
function(box) {
2016 var pnt = this.test, cnt = 0;
2017 pnt.set(box.min.x, box.min.y, box.min.z);
2018 if (this.containsPoint(pnt)) cnt++;
2019 pnt.set(box.min.x, box.min.y, box.max.z);
2020 if (this.containsPoint(pnt)) cnt++;
2021 pnt.set(box.min.x, box.max.y, box.min.z);
2022 if (this.containsPoint(pnt)) cnt++;
2023 pnt.set(box.min.x, box.max.y, box.max.z);
2024 if (this.containsPoint(pnt)) cnt++;
2025 pnt.set(box.max.x, box.max.y, box.max.z);
2026 if (this.containsPoint(pnt)) cnt++;
2027 pnt.set(box.max.x, box.min.y, box.max.z);
2028 if (this.containsPoint(pnt)) cnt++;
2029 pnt.set(box.max.x, box.max.y, box.min.z);
2030 if (this.containsPoint(pnt)) cnt++;
2031 pnt.set(box.max.x, box.max.y, box.max.z);
2032 if (this.containsPoint(pnt)) cnt++;
2040 JSROOT.GEO.VisibleByCamera =
function(camera, matrix, shape) {
2041 var frustum =
new THREE.Frustum();
2042 var cameraProjectionMatrix =
new THREE.Matrix4();
2044 camera.updateMatrixWorld();
2045 camera.matrixWorldInverse.getInverse( camera.matrixWorld );
2046 cameraProjectionMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse);
2047 frustum.setFromMatrix( cameraProjectionMatrix );
2050 new THREE.Vector3( shape.fDX/2.0, shape.fDY/2.0, shape.fDZ/2.0 ),
2051 new THREE.Vector3( shape.fDX/2.0, shape.fDY/2.0, -shape.fDZ/2.0 ),
2052 new THREE.Vector3( shape.fDX/2.0, -shape.fDY/2.0, shape.fDZ/2.0 ),
2053 new THREE.Vector3( shape.fDX/2.0, -shape.fDY/2.0, -shape.fDZ/2.0 ),
2054 new THREE.Vector3( -shape.fDX/2.0, shape.fDY/2.0, shape.fDZ/2.0 ),
2055 new THREE.Vector3( -shape.fDX/2.0, shape.fDY/2.0, -shape.fDZ/2.0 ),
2056 new THREE.Vector3( -shape.fDX/2.0, -shape.fDY/2.0, shape.fDZ/2.0 ),
2057 new THREE.Vector3( -shape.fDX/2.0, -shape.fDY/2.0, -shape.fDZ/2.0 )
2059 for (var i = 0; i < corners.length; i++) {
2060 if (frustum.containsPoint(corners[i].applyMatrix4(matrix)))
return true;
2069 JSROOT.GEO.numGeometryFaces =
function(geom) {
2070 if (!geom)
return 0;
2072 if (geom instanceof ThreeBSP.Geometry)
2073 return geom.tree.numPolygons();
2075 if (geom.type ==
'BufferGeometry') {
2076 var attr = geom.getAttribute(
'position');
2077 return attr && attr.count ? Math.round(attr.count / 3) : 0;
2082 return geom.polygons.length;
2084 return geom.faces.length;
2090 JSROOT.GEO.numGeometryVertices =
function(geom) {
2091 if (!geom)
return 0;
2093 if (geom instanceof ThreeBSP.Geometry)
2094 return geom.tree.numPolygons() * 3;
2096 if (geom.type ==
'BufferGeometry') {
2097 var attr = geom.getAttribute(
'position');
2098 return attr ? attr.count : 0;
2102 return geom.polygons.length * 4;
2104 return geom.vertices.length;
2109 JSROOT.GEO.geomBoundingBox =
function(geom) {
2110 if (!geom)
return null;
2112 var polygons = null;
2114 if (geom instanceof ThreeBSP.Geometry)
2115 polygons = geom.tree.collectPolygons([]);
2116 else if (geom.polygons)
2117 polygons = geom.polygons;
2119 if (polygons!==null) {
2120 var box =
new THREE.Box3();
2121 for (var n=0;n<polygons.length;++n) {
2122 var polygon = polygons[n], nvert = polygon.vertices.length;
2123 for (var k=0;k<nvert;++k)
2124 box.expandByPoint(polygon.vertices[k]);
2129 if (!geom.boundingBox) geom.computeBoundingBox();
2131 return geom.boundingBox.clone();
2137 JSROOT.GEO.CompareStacks =
function(stack1, stack2) {
2138 if (!stack1 || !stack2)
return 0;
2139 if (stack1 === stack2)
return stack1.length;
2140 var len = Math.min(stack1.length, stack2.length);
2141 for (var k=0;k<len;++k)
2142 if (stack1[k] !== stack2[k])
return k;
2149 JSROOT.GEO.IsSameStack =
function(stack1, stack2) {
2150 if (!stack1 || !stack2)
return false;
2151 if (stack1 === stack2)
return true;
2152 if (stack1.length !== stack2.length)
return false;
2153 for (var k=0;k<stack1.length;++k)
2154 if (stack1[k] !== stack2[k])
return false;
2163 JSROOT.GEO.ClonedNodes =
function(obj, clones) {
2164 this.toplevel =
true;
2165 this.name_prefix =
"";
2168 this.maxnodes = 10000;
2171 if (obj.$geoh) this.toplevel =
false;
2172 this.CreateClones(obj);
2173 }
else if (clones) {
2174 this.nodes = clones;
2179 JSROOT.GEO.ClonedNodes.prototype.SetVisLevel =
function(lvl) {
2180 this.vislevel = lvl && !isNaN(lvl) ? lvl : 4;
2184 JSROOT.GEO.ClonedNodes.prototype.GetVisLevel =
function() {
2185 return this.vislevel;
2189 JSROOT.GEO.ClonedNodes.prototype.SetMaxVisNodes =
function(v) {
2190 this.maxnodes = !isNaN(v) ? v : 10000;
2193 JSROOT.GEO.ClonedNodes.prototype.GetMaxVisNodes =
function() {
2194 return this.maxnodes;
2198 JSROOT.GEO.ClonedNodes.prototype.updateNode =
function(node) {
2199 if (node && !isNaN(node.id) && (node.id < this.nodes.length))
2200 this.nodes[node.id] = node;
2204 JSROOT.GEO.ClonedNodes.prototype.GetNodeShape =
function(indx) {
2205 if (!this.origin || !this.nodes)
return null;
2206 var obj = this.origin[indx], clone = this.nodes[indx];
2207 if (!obj || !clone)
return null;
2208 if (clone.kind === 0) {
2209 if (obj.fVolume)
return obj.fVolume.fShape;
2216 JSROOT.GEO.ClonedNodes.prototype.Cleanup =
function(drawnodes, drawshapes) {
2221 for (var n=0;n<drawnodes.length;++n) {
2222 delete drawnodes[n].stack;
2223 drawnodes[n] = undefined;
2228 for (var n=0;n<drawshapes.length;++n) {
2229 delete drawshapes[n].geom;
2230 drawshapes[n] = undefined;
2235 for (var n=0;n<this.nodes.length;++n) {
2237 delete this.nodes[n].chlds;
2244 delete this.sortmap;
2249 JSROOT.GEO.ClonedNodes.prototype.CreateClones =
function(obj, sublevel, kind) {
2252 if (obj && obj._typename ==
"$$Shape$$")
2253 return this.CreateClonesForShape(obj);
2257 kind = JSROOT.GEO.NodeKind(obj);
2260 if ((kind < 0) || !obj || (
'_refid' in obj))
return;
2262 obj._refid = this.origin.length;
2263 this.origin.push(obj);
2264 if (sublevel>this.maxdepth) this.maxdepth = sublevel;
2268 chlds = (obj.fVolume && obj.fVolume.fNodes) ? obj.fVolume.fNodes.arr : null;
2270 chlds = obj.fElements ? obj.fElements.arr : null;
2272 if (chlds !== null) {
2273 JSROOT.GEO.CheckDuplicates(obj, chlds);
2274 for (var i = 0; i < chlds.length; ++i)
2275 this.CreateClones(chlds[i], sublevel+1, kind);
2278 if (sublevel > 1)
return;
2285 for (var n=0; n<this.origin.length; ++n) {
2286 var obj = this.origin[n];
2287 var node = {
id: n, kind: kind, vol: 0, nfaces: 0 };
2288 this.nodes.push(node);
2293 for (var n=0;n<this.origin.length;++n) {
2294 var obj = this.origin[n], clone = this.nodes[n];
2296 var chlds = null, shape = null;
2300 if (obj.fElements) chlds = obj.fElements.arr;
2301 }
else if (obj.fVolume) {
2302 shape = obj.fVolume.fShape;
2303 if (obj.fVolume.fNodes) chlds = obj.fVolume.fNodes.arr;
2306 var matrix = JSROOT.GEO.getNodeMatrix(kind, obj);
2308 clone.matrix = matrix.elements;
2309 if (clone.matrix[0] === 1) {
2310 var issimple =
true;
2311 for (var k=1;(k<clone.matrix.length) && issimple;++k)
2312 issimple = (clone.matrix[k] === ((k===5) || (k===10) || (k===15) ? 1 : 0));
2313 if (issimple)
delete clone.matrix;
2317 clone.fDX = shape.fDX;
2318 clone.fDY = shape.fDY;
2319 clone.fDZ = shape.fDZ;
2320 clone.vol = shape.fDX*shape.fDY*shape.fDZ;
2321 if (shape.$nfaces === undefined)
2322 shape.$nfaces = JSROOT.GEO.createGeometry(shape, -1);
2323 clone.nfaces = shape.$nfaces;
2324 if (clone.nfaces <= 0) clone.vol = 0;
2327 if (!chlds)
continue;
2330 clone.chlds =
new Array(chlds.length);
2331 for (var k=0;k<chlds.length;++k)
2332 clone.chlds[k] = chlds[k]._refid;
2336 for (var n=0;n<this.origin.length;++n)
2337 delete this.origin[n]._refid;
2340 sortarr.sort(
function(a,b) {
return b.vol - a.vol; });
2343 this.sortmap =
new Array(this.nodes.length);
2344 for (var n=0;n<this.nodes.length;++n) {
2345 this.sortmap[n] = sortarr[n].id;
2346 sortarr[n].sortid = n;
2352 JSROOT.GEO.ClonedNodes.prototype.CreateClonesForShape =
function(obj) {
2356 this.plain_shape = obj;
2359 id: 0, sortid: 0, kind: 2,
2362 fDX: 1, fDY: 1, fDZ: 1, vol: 1,
2366 this.nodes = [ node ];
2370 JSROOT.GEO.ClonedNodes.prototype.CountVisibles =
function() {
2373 for (var k=0;k<this.nodes.length;++k)
2374 if (this.nodes[k].vis)
2380 JSROOT.GEO.ClonedNodes.prototype.MarkVisibles =
function(on_screen, copy_bits, hide_top_volume) {
2381 if (this.plain_shape)
return 1;
2382 if (!this.origin || !this.nodes)
return 0;
2386 for (var n=0;n<this.nodes.length;++n) {
2387 var clone = this.nodes[n],
2388 obj = this.origin[n];
2391 delete clone.nochlds;
2393 if (clone.kind === 0) {
2397 clone.vis = JSROOT.GEO.TestBit(obj.fVolume, JSROOT.GEO.BITS.kVisOnScreen) ? 99 : 0;
2399 if ((n==0) && clone.vis && hide_top_volume) clone.vis = 0;
2402 JSROOT.GEO.SetBit(obj.fVolume, JSROOT.GEO.BITS.kVisNone,
false);
2403 JSROOT.GEO.SetBit(obj.fVolume, JSROOT.GEO.BITS.kVisThis, (clone.vis > 0));
2404 JSROOT.GEO.SetBit(obj.fVolume, JSROOT.GEO.BITS.kVisDaughters,
true);
2407 clone.vis = !JSROOT.GEO.TestBit(obj.fVolume, JSROOT.GEO.BITS.kVisNone) &&
2408 JSROOT.GEO.TestBit(obj.fVolume, JSROOT.GEO.BITS.kVisThis) ? 99 : 0;
2410 if (!JSROOT.GEO.TestBit(obj, JSROOT.GEO.BITS.kVisDaughters) ||
2411 !JSROOT.GEO.TestBit(obj.fVolume, JSROOT.GEO.BITS.kVisDaughters)) clone.nochlds =
true;
2414 if ((clone.vis > 0) && clone.chlds && !clone.nochlds) clone.vis = 1;
2418 if (hide_top_volume) clone.vis = 0;
2419 delete clone.nochlds;
2424 clone.vis = obj.fRnrSelf ? 99 : 0;
2427 if ((n===0) && (this.nodes.length===1)) clone.vis = 99;
2429 this.vislevel = 9999;
2433 if ((clone.vol <= 0) || (clone.nfaces <= 0)) clone.vis = 0;
2435 if (clone.vis) res++;
2442 JSROOT.GEO.ClonedNodes.prototype.ProduceIdShits =
function() {
2443 for (var k=0;k<this.nodes.length;++k)
2444 this.nodes[k].idshift = -1;
2446 function scan_func(nodes, node) {
2447 if (node.idshift < 0) {
2450 for(var k = 0; k<node.chlds.length; ++k)
2451 node.idshift += scan_func(nodes, nodes[node.chlds[k]]);
2454 return node.idshift + 1;
2457 scan_func(this.nodes, this.nodes[0]);
2461 JSROOT.GEO.ClonedNodes.prototype.GetVisibleFlags =
function() {
2462 var res =
new Array(this.nodes.length);
2463 for (var n=0;n<this.nodes.length;++n)
2464 res[n] = { vis: this.nodes[n].vis, nochlds: this.nodes[n].nochlds };
2469 JSROOT.GEO.ClonedNodes.prototype.SetVisibleFlags =
function(flags) {
2470 if (!this.nodes || !flags || !flags.length !=
this.nodes.length)
2474 for (var n=0;n<this.nodes.length;++n) {
2475 var clone = this.nodes[n];
2477 clone.vis = flags[n].vis;
2478 clone.nochlds = flags[n].nochlds;
2479 if (clone.vis) res++;
2488 JSROOT.GEO.ClonedNodes.prototype.ScanVisible =
function(arg, vislvl) {
2490 if (!this.nodes)
return 0;
2492 if (vislvl === undefined) {
2495 vislvl = arg.vislvl || this.vislevel || 4;
2496 if (vislvl > 88) vislvl = 88;
2498 arg.stack =
new Array(100);
2502 arg.CopyStack =
function(factor) {
2503 var entry = { nodeid: this.nodeid, seqid: this.counter, stack:
new Array(this.last) };
2504 if (factor) entry.factor = factor;
2505 for (var n=0;n<this.last;++n) entry.stack[n] =
this.stack[n+1];
2511 arg.mpool = [
new THREE.Matrix4() ];
2512 arg.getmatrix =
function() {
return this.matrices[this.last]; }
2516 var res = 0, node = this.nodes[arg.nodeid];
2519 if (!arg.mpool[arg.last+1])
2520 arg.mpool[arg.last+1] =
new THREE.Matrix4();
2522 var prnt = (arg.last > 0) ? arg.matrices[arg.last-1] :
new THREE.Matrix4();
2524 arg.matrices[arg.last] = arg.mpool[arg.last].fromArray(prnt.elements);
2525 arg.matrices[arg.last].multiply(arg.mpool[arg.last+1].fromArray(node.matrix));
2527 arg.matrices[arg.last] = prnt;
2531 if (node.nochlds) vislvl = 0;
2533 if (node.vis > vislvl) {
2534 if (!arg.func || arg.func(node)) res++;
2539 if ((vislvl > 0) && node.chlds) {
2541 for (var i = 0; i < node.chlds.length; ++i) {
2542 arg.nodeid = node.chlds[i];
2543 arg.stack[arg.last] = i;
2544 res += this.ScanVisible(arg, vislvl-1);
2548 arg.counter += (node.idshift || 0);
2551 if (arg.last === 0) {
2554 delete arg.CopyStack;
2556 delete arg.matrices;
2558 delete arg.getmatrix;
2567 JSROOT.GEO.ClonedNodes.prototype.GetNodeName =
function(nodeid) {
2569 var obj = this.origin[nodeid];
2570 return obj ? JSROOT.GEO.ObjectName(obj) :
"";
2572 var node = this.nodes[nodeid];
2573 return node ? node.name :
"";
2578 JSROOT.GEO.ClonedNodes.prototype.ResolveStack =
function(stack, withmatrix) {
2580 var res = {
id: 0, obj: null, node: this.nodes[0], name: this.name_prefix };
2585 res.matrix =
new THREE.Matrix4();
2586 if (res.node.matrix) res.matrix.fromArray(res.node.matrix);
2590 res.obj = this.origin[0];
2596 for(var lvl=0;lvl<stack.length;++lvl) {
2597 res.id = res.node.chlds[stack[lvl]];
2598 res.node = this.nodes[res.id];
2601 res.obj = this.origin[res.id];
2603 var subname = this.GetNodeName(res.id);
2605 if (res.name) res.name+=
"/";
2606 res.name += subname;
2609 if (withmatrix && res.node.matrix)
2610 res.matrix.multiply(
new THREE.Matrix4().fromArray(res.node.matrix));
2618 JSROOT.GEO.ClonedNodes.prototype.MakeStackByIds =
function(ids) {
2619 if (!ids)
return null;
2622 console.error(
'wrong ids - first should be 0');
2626 var node = this.nodes[0], stack = [];
2628 for (var k=1;k<ids.length;++k) {
2629 var nodeid = ids[k];
2630 if (!node)
return null;
2631 var chindx = node.chlds.indexOf(nodeid);
2633 console.error(
'wrong nodes ids ' + ids[k] +
' is not child of ' + ids[k-1]);
2638 node = this.nodes[nodeid];
2645 JSROOT.GEO.ClonedNodes.prototype.MakeIdsByStack =
function(stack) {
2646 if (!stack)
return null;
2647 var node = this.nodes[0], ids = [0];
2648 for (var k=0;k<stack.length;++k) {
2649 var
id = node.chlds[stack[k]];
2651 node = this.nodes[id];
2657 JSROOT.GEO.ClonedNodes.prototype.IsNodeInStack =
function(nodeid, stack) {
2659 if (!nodeid)
return true;
2661 var node = this.nodes[0],
id = 0;
2663 for(var lvl = 0; lvl < stack.length; ++lvl) {
2664 id = node.chlds[stack[lvl]];
2665 if (
id == nodeid)
return true;
2666 node = this.nodes[id];
2673 JSROOT.GEO.ClonedNodes.prototype.FindStackByName =
function(fullname) {
2675 var names = fullname.split(
'/'), currid = 0, stack = [];
2677 if (this.GetNodeName(currid) !== names[0])
return null;
2679 for (var n=1;n<names.length;++n) {
2680 var node = this.nodes[currid];
2681 if (!node.chlds)
return null;
2683 for (var k=0;k<node.chlds.length;++k) {
2684 var chldid = node.chlds[k];
2685 if (this.GetNodeName(chldid) === names[n]) { stack.push(k); currid = chldid;
break; }
2689 if (stack.length === n - 1)
return null;
2696 JSROOT.GEO.ClonedNodes.prototype.SetDefaultColors =
function(on) {
2697 this.use_dflt_colors = on;
2698 if (this.use_dflt_colors && !this.dflt_table) {
2700 var dflt = { kWhite:0, kBlack:1, kGray:920,
2701 kRed:632, kGreen:416, kBlue:600, kYellow:400, kMagenta:616, kCyan:432,
2702 kOrange:800, kSpring:820, kTeal:840, kAzure:860, kViolet:880, kPink:900 };
2704 var nmax = 110, col = [];
2705 for (var i=0;i<nmax;i++) col.push(dflt.kGray);
2709 col[ 3] = dflt.kYellow-10;
2710 col[ 4] = col[ 5] = dflt.kGreen-10;
2711 col[ 6] = col[ 7] = dflt.kBlue-7;
2712 col[ 8] = col[ 9] = dflt.kMagenta-3;
2713 col[10] = col[11] = dflt.kRed-10;
2714 col[12] = dflt.kGray+1;
2715 col[13] = dflt.kBlue-10;
2716 col[14] = dflt.kOrange+7;
2717 col[16] = dflt.kYellow+1;
2718 col[20] = dflt.kYellow-10;
2719 col[24] = col[25] = col[26] = dflt.kBlue-8;
2720 col[29] = dflt.kOrange+9;
2721 col[79] = dflt.kOrange-2;
2723 this.dflt_table = col;
2729 JSROOT.GEO.ClonedNodes.prototype.getDrawEntryProperties =
function(entry) {
2731 var clone = this.nodes[entry.nodeid];
2734 if (clone.kind === 2) {
2735 var prop = { name: clone.name, nname: clone.name, shape: null, material: null, chlds: null };
2736 var _opacity = entry.opacity || 1;
2737 prop.fillcolor =
new THREE.Color( entry.color ?
"rgb(" + entry.color +
")" :
"blue" );
2738 prop.material =
new THREE.MeshLambertMaterial( { transparent: _opacity < 1,
2739 opacity: _opacity, wireframe:
false, color: prop.fillcolor,
2740 side: THREE.FrontSide , vertexColors: THREE.NoColors ,
2741 depthWrite: _opacity == 1 } );
2742 prop.material.inherentOpacity = _opacity;
2748 console.error(
'origin not there - kind', clone.kind, entry.nodeid, clone);
2752 var node = this.origin[entry.nodeid];
2754 if (clone.kind === 1) {
2757 var prop = { name: JSROOT.GEO.ObjectName(node), nname: JSROOT.GEO.ObjectName(node), shape: node.fShape, material: null, chlds: null };
2759 if (node.fElements !== null) prop.chlds = node.fElements.arr;
2762 var _opacity = Math.min(1, node.fRGBA[3]);
2763 prop.fillcolor =
new THREE.Color( node.fRGBA[0], node.fRGBA[1], node.fRGBA[2] );
2764 prop.material =
new THREE.MeshLambertMaterial( { transparent: _opacity < 1,
2765 opacity: _opacity, wireframe:
false, color: prop.fillcolor,
2766 side: THREE.FrontSide , vertexColors: THREE.NoColors ,
2767 depthWrite: _opacity == 1 } );
2768 prop.material.inherentOpacity = _opacity;
2774 var volume = node.fVolume;
2776 var prop = { name: JSROOT.GEO.ObjectName(volume), nname: JSROOT.GEO.ObjectName(node), volume: node.fVolume, shape: volume.fShape, material: null, chlds: null };
2778 if (node.fVolume.fNodes !== null) prop.chlds = node.fVolume.fNodes.arr;
2780 if (volume) prop.linewidth = volume.fLineWidth;
2785 if (entry.custom_color)
2786 prop.fillcolor = entry.custom_color;
2787 else if ((volume.fFillColor > 1) && (volume.fLineColor == 1))
2788 prop.fillcolor = JSROOT.Painter.root_colors[volume.fFillColor];
2789 else if (volume.fLineColor >= 0)
2790 prop.fillcolor = JSROOT.Painter.root_colors[volume.fLineColor];
2792 if (volume.fMedium && volume.fMedium.fMaterial) {
2793 var mat = volume.fMedium.fMaterial,
2794 fillstyle = mat.fFillStyle,
2795 transparency = (fillstyle < 3000 || fillstyle > 3100) ? 0 : fillstyle - 3000;
2797 if (this.use_dflt_colors) {
2798 var matZ = Math.round(mat.fZ),
2799 icol = this.dflt_table[matZ];
2800 prop.fillcolor = JSROOT.Painter.root_colors[icol];
2801 if (mat.fDensity < 0.1) transparency = 60;
2804 if (transparency > 0)
2805 _opacity = (100.0 - transparency) / 100.0;
2806 if (prop.fillcolor === undefined)
2807 prop.fillcolor = JSROOT.Painter.root_colors[mat.fFillColor];
2809 if (prop.fillcolor === undefined)
2810 prop.fillcolor =
"lightgrey";
2812 prop.material =
new THREE.MeshLambertMaterial( { transparent: _opacity < 1,
2813 opacity: _opacity, wireframe:
false, color: prop.fillcolor,
2814 side: THREE.FrontSide , vertexColors: THREE.NoColors ,
2815 depthWrite: _opacity == 1 } );
2816 prop.material.inherentOpacity = _opacity;
2827 JSROOT.GEO.ClonedNodes.prototype.CreateObject3D =
function(stack, toplevel, options) {
2829 var node = this.nodes[0], three_prnt = toplevel, draw_depth = 0,
2830 force = (typeof options ==
'object') || (options===
'force');
2832 for(var lvl=0; lvl<=stack.length; ++lvl) {
2833 var nchld = (lvl > 0) ? stack[lvl-1] : 0;
2835 if (lvl>0) node = this.nodes[node.chlds[nchld]];
2837 var obj3d = undefined;
2839 if (three_prnt.children)
2840 for (var i=0;i<three_prnt.children.length;++i) {
2841 if (three_prnt.children[i].nchld === nchld) {
2842 obj3d = three_prnt.children[i];
2849 if (obj3d.$jsroot_drawable) draw_depth++;
2853 if (!force)
return null;
2855 obj3d =
new THREE.Object3D();
2859 obj3d.matrix.fromArray(node.matrix);
2860 obj3d.matrix.decompose( obj3d.position, obj3d.quaternion, obj3d.scale );
2864 obj3d.nchld = nchld;
2867 three_prnt.add(obj3d);
2870 if ((lvl==0) && (typeof options ==
'object') && options.scale) {
2871 if ((options.scale.x<0) || (options.scale.y<0) || (options.scale.z<0)) {
2872 obj3d.scale.copy(options.scale);
2873 obj3d.updateMatrix();
2877 obj3d.updateMatrixWorld();
2882 if ((options ===
'mesh') || (options ===
'delete_mesh')) {
2885 for (var n=0; (n<three_prnt.children.length) && !mesh;++n) {
2886 var chld = three_prnt.children[n];
2887 if ((chld.type ===
'Mesh') && (chld.nchld === undefined)) mesh = chld;
2890 if ((options ===
'mesh') || !mesh)
return mesh;
2892 var res = three_prnt;
2893 while (mesh && (mesh !== toplevel)) {
2894 three_prnt = mesh.parent;
2895 three_prnt.remove(mesh);
2896 mesh = (three_prnt.children.length == 0) ? three_prnt : null;
2903 three_prnt.$jsroot_drawable =
true;
2904 three_prnt.$jsroot_depth = draw_depth;
2910 JSROOT.GEO.ClonedNodes.prototype.GetVolumeBoundary =
function(viscnt, facelimit, nodeslimit) {
2912 var result = { min: 0, max: 1, sortidcut: 0 };
2914 if (!this.sortmap) {
2915 console.error(
'sorting map do not exist');
2919 var maxNode, currNode, cnt=0, facecnt=0;
2921 for (var n = 0; (n < this.sortmap.length) && (cnt < nodeslimit) && (facecnt < facelimit); ++n) {
2922 var
id = this.sortmap[n];
2923 if (viscnt[
id] === 0)
continue;
2924 currNode = this.nodes[id];
2925 if (!maxNode) maxNode = currNode;
2927 facecnt += viscnt[id] * currNode.nfaces;
2931 console.error(
'no volumes selected');
2936 result.max = maxNode.vol;
2937 result.min = currNode.vol;
2938 result.sortidcut = currNode.sortid;
2944 JSROOT.GEO.ClonedNodes.prototype.CollectVisibles =
function(maxnumfaces, frustum) {
2947 if (this.plain_shape)
2948 return { lst: [ { nodeid: 0, seqid: 0, stack: [], factor: 1, shapeid: 0, server_shape: this.plain_shape } ], complete:
true };
2952 viscnt:
new Array(this.nodes.length),
2953 vislvl: this.GetVisLevel(),
2957 for (var n=0;n<this.viscnt.length;++n) this.viscnt[n] = 0;
2960 func:
function(node) {
2962 this.facecnt += node.nfaces;
2963 this.viscnt[node.id]++;
2970 var total = this.ScanVisible(arg),
2971 maxnumnodes = this.GetMaxVisNodes();
2973 if (maxnumnodes > 0) {
2974 while ((total > maxnumnodes) && (arg.vislvl > 1)) {
2977 total = this.ScanVisible(arg);
2981 this.actual_level = arg.vislvl;
2983 var minVol = 0, maxVol = 0, camVol = -1, camFact = 10, sortidcut = this.nodes.length + 1;
2985 console.log(
'Total visible nodes ' + total +
' numfaces ' + arg.facecnt);
2987 if (arg.facecnt > maxnumfaces) {
2989 var bignumfaces = maxnumfaces * (frustum ? 0.8 : 1.0),
2990 bignumnodes = maxnumnodes * (frustum ? 0.8 : 1.0);
2993 var boundary = this.GetVolumeBoundary(arg.viscnt, bignumfaces, bignumnodes);
2995 minVol = boundary.min;
2996 maxVol = boundary.max;
2997 sortidcut = boundary.sortidcut;
3000 arg.domatrix =
true;
3001 arg.frustum = frustum;
3003 arg.func =
function(node) {
3004 if (node.vol <= minVol)
3005 if (this.frustum.CheckShape(
this.getmatrix(), node)) {
3006 this.viscnt[node.id]++;
3007 this.totalcam += node.nfaces;
3013 for (var n=0;n<arg.viscnt.length;++n) arg.viscnt[n] = 0;
3015 this.ScanVisible(arg);
3017 if (arg.totalcam > maxnumfaces*0.2)
3018 camVol = this.GetVolumeBoundary(arg.viscnt, maxnumfaces*0.2, maxnumnodes*0.2).min;
3022 camFact = maxVol / ((camVol>0) ? (camVol>0) : minVol);
3030 arg.func =
function(node) {
3031 if (node.sortid < sortidcut) {
3032 this.items.push(this.CopyStack());
3033 }
else if ((camVol >= 0) && (node.vol > camVol)) {
3034 if (this.frustum.CheckShape(
this.getmatrix(), node))
3035 this.items.push(this.CopyStack(camFact));
3040 this.ScanVisible(arg);
3042 return { lst: arg.items, complete: minVol === 0 };
3048 JSROOT.GEO.ClonedNodes.prototype.MergeVisibles =
function(current, prev) {
3050 var indx2 = 0, del = [];
3051 for (var indx1=0; (indx1<current.length) && (indx2<prev.length); ++indx1) {
3053 while ((indx2 < prev.length) && (prev[indx2].seqid < current[indx1].seqid)) {
3054 del.push(prev[indx2++]);
3057 if ((indx2 < prev.length) && (prev[indx2].seqid === current[indx1].seqid)) {
3058 if (prev[indx2].done) current[indx1].done =
true;
3064 while (indx2<prev.length)
3065 del.push(prev[indx2++]);
3072 JSROOT.GEO.ClonedNodes.prototype.CollectShapes =
function(lst) {
3075 if (this.plain_shape)
3076 return [ this.plain_shape ];
3080 for (var i=0;i<lst.length;++i) {
3082 var shape = this.GetNodeShape(entry.nodeid);
3084 if (!shape)
continue;
3086 if (shape._id === undefined) {
3087 shape._id = shapes.length;
3089 shapes.push({
id: shape._id, shape: shape, vol: this.nodes[entry.nodeid].vol, refcnt: 1, factor: 1, ready:
false });
3093 shapes[shape._id].refcnt++;
3096 entry.shape = shapes[shape._id];
3099 if (entry.factor && (entry.factor>entry.shape.factor))
3100 entry.shape.factor = entry.factor;
3104 shapes.sort(
function(a,b) {
return b.vol*b.factor - a.vol*a.factor; })
3107 for (var n=0;n<shapes.length;++n) {
3108 var item = shapes[n];
3110 delete item.shape._id;
3114 for (var i=0;i<lst.length;++i) {
3117 entry.shapeid = entry.shape.id;
3125 JSROOT.GEO.ClonedNodes.prototype.MergeShapesLists =
function(oldlst, newlst) {
3127 if (!oldlst)
return newlst;
3130 for (var n=0;n<oldlst.length;++n) {
3131 var item = oldlst[n];
3133 item.shape._geom = item.geom;
3136 if (item.geomZ!==undefined) {
3137 item.shape._geomZ = item.geomZ;
3143 for (var n=0;n<newlst.length;++n) {
3144 var item = newlst[n];
3146 if (item.shape._geom !== undefined) {
3147 item.geom = item.shape._geom;
3148 delete item.shape._geom;
3151 if (item.shape._geomZ !== undefined) {
3152 item.geomZ = item.shape._geomZ;
3153 delete item.shape._geomZ;
3158 for (var n=0;n<oldlst.length;++n) {
3159 var item = oldlst[n];
3160 delete item.shape._geom;
3161 delete item.shape._geomZ;
3167 JSROOT.GEO.ClonedNodes.prototype.BuildShapes =
function(lst, limit, timelimit) {
3170 tm1 =
new Date().getTime(),
3171 res = { done:
false, shapes: 0, faces: 0, notusedshapes: 0 };
3173 for (var n=0;n<lst.length;++n) {
3177 if (res.done) { item.ready =
true;
continue; }
3180 item._typename =
"$$Shape$$";
3182 if (item.geom === undefined) {
3183 item.geom = JSROOT.GEO.createGeometry(item.shape);
3184 if (item.geom) created++;
3186 item.nfaces = JSROOT.GEO.numGeometryFaces(item.geom);
3190 if (!item.used) res.notusedshapes++;
3191 res.faces += item.nfaces*item.refcnt;
3193 if (res.faces >= limit) {
3195 }
else if ((created > 0.01*lst.length) && (timelimit!==undefined)) {
3196 var tm2 =
new Date().getTime();
3197 if (tm2-tm1 > timelimit)
return res;
3208 JSROOT.GEO.ObjectName =
function(obj) {
3209 if (!obj || !obj.fName)
return "";
3210 return obj.fName + (obj.$geo_suffix ? obj.$geo_suffix :
"");
3213 JSROOT.GEO.CheckDuplicates =
function(parent, chlds) {
3215 if (parent.$geo_checked)
return;
3216 parent.$geo_checked =
true;
3219 var names = [], cnts = [], obj = null;
3220 for (var k=0;k<chlds.length;++k) {
3221 var chld = chlds[k];
3222 if (!chld || !chld.fName)
continue;
3223 if (!chld.$geo_suffix) {
3224 var indx = names.indexOf(chld.fName);
3226 var cnt = cnts[indx] || 1;
3227 while(names.indexOf(chld.fName+
"#"+cnt)>=0) ++cnt;
3228 chld.$geo_suffix =
"#" + cnt;
3232 names.push(JSROOT.GEO.ObjectName(chld));
3238 JSROOT.GEO.createFlippedMesh =
function(parent, shape, material) {
3240 var flip =
new THREE.Vector3(1,1,-1);
3242 if (shape.geomZ === undefined) {
3244 if (shape.geom.type ==
'BufferGeometry') {
3246 var pos = shape.geom.getAttribute(
'position').array,
3247 norm = shape.geom.getAttribute(
'normal').array,
3248 index = shape.geom.getIndex();
3252 var arr = index.array,
3253 i0 = shape.geom.drawRange.start,
3254 ilen = shape.geom.drawRange.count;
3255 if (i0 + ilen > arr.length) ilen = arr.length - i0;
3257 var dpos =
new Float32Array(ilen*3), dnorm =
new Float32Array(ilen*3);
3258 for (var ii = 0; ii < ilen; ++ii) {
3259 var k = arr[i0 + ii];
3260 if ((k<0) || (k*3>=pos.length)) console.log(
'strange index', k*3, pos.length);
3261 dpos[ii*3] = pos[k*3];
3262 dpos[ii*3+1] = pos[k*3+1];
3263 dpos[ii*3+2] = pos[k*3+2];
3264 dnorm[ii*3] = norm[k*3];
3265 dnorm[ii*3+1] = norm[k*3+1];
3266 dnorm[ii*3+2] = norm[k*3+2];
3269 pos = dpos; norm = dnorm;
3272 var len = pos.length, n, shift = 0,
3273 newpos =
new Float32Array(len),
3274 newnorm =
new Float32Array(len);
3277 for (n=0; n<len; n+=3) {
3278 newpos[n] = pos[n+shift];
3279 newpos[n+1] = pos[n+1+shift];
3280 newpos[n+2] = -pos[n+2+shift];
3282 newnorm[n] = norm[n+shift];
3283 newnorm[n+1] = norm[n+1+shift];
3284 newnorm[n+2] = -norm[n+2+shift];
3286 shift+=3;
if (shift===6) shift=-3;
3289 shape.geomZ =
new THREE.BufferGeometry();
3290 shape.geomZ.addAttribute(
'position',
new THREE.BufferAttribute( newpos, 3 ) );
3291 shape.geomZ.addAttribute(
'normal',
new THREE.BufferAttribute( newnorm, 3 ) );
3297 shape.geomZ = shape.geom.clone();
3299 shape.geomZ.scale(flip.x, flip.y, flip.z);
3302 while(n < shape.geomZ.faces.length) {
3303 face = geom.faces[n++];
3304 d = face.b; face.b = face.c; face.c = d;
3312 var mesh =
new THREE.Mesh( shape.geomZ, material );
3313 mesh.scale.copy(flip);
3314 mesh.updateMatrix();
3316 mesh._flippedMesh =
true;
3323 JSROOT.GEO.cleanupShape =
function(shape) {
3326 if (shape.geom && (typeof shape.geom.dispose ==
'funciton'))
3327 shape.geom.dispose();
3329 if (shape.geomZ && (typeof shape.geomZ.dispose ==
'funciton'))
3330 shape.geomZ.dispose();
3341 JSROOT.GEO.produceRenderOrder =
function(toplevel, origin, method, clones) {
3343 var raycast =
new THREE.Raycaster();
3345 function setdefaults(top) {
3347 top.traverse(
function(obj) {
3348 obj.renderOrder = 0;
3349 if (obj.material) obj.material.depthWrite =
true;
3353 function traverse(obj, lvl, arr) {
3357 if (!obj.children)
return;
3359 for (var k=0;k<obj.children.length;++k) {
3360 var chld = obj.children[k];
3361 if (chld.$jsroot_order === lvl) {
3362 if (chld.material) {
3363 if (chld.material.transparent) {
3364 chld.material.depthWrite =
false;
3370 }
else if ((obj.$jsroot_depth===undefined) || (obj.$jsroot_depth < lvl)) {
3371 traverse(chld, lvl, arr);
3376 function sort(arr, minorder, maxorder) {
3380 if (arr.length > 300) {
3382 for (var i=0;i<arr.length;++i) arr[i].renderOrder = (minorder + maxorder)/2;
3386 var tmp_vect =
new THREE.Vector3();
3391 for (var i=0;i<arr.length;++i) {
3393 box3 = mesh.$jsroot_box3;
3396 mesh.$jsroot_box3 = box3 = JSROOT.GEO.getBoundingBox(mesh);
3398 if (method ===
'size') {
3399 mesh.$jsroot_distance = box3.getSize(
new THREE.Vector3());
3403 if (method ===
"pnt") {
3404 mesh.$jsroot_distance = origin.distanceTo(box3.getCenter(tmp_vect));
3408 var dist = Math.min(dist, origin.distanceTo(box3.min), origin.distanceTo(box3.max));
3410 var pnt =
new THREE.Vector3(box3.min.x, box3.min.y, box3.max.z);
3411 dist = Math.min(dist, origin.distanceTo(pnt));
3412 pnt.set(box3.min.x, box3.max.y, box3.min.z)
3413 dist = Math.min(dist, origin.distanceTo(pnt));
3414 pnt.set(box3.max.x, box3.min.y, box3.min.z)
3415 dist = Math.min(dist, origin.distanceTo(pnt));
3417 pnt.set(box3.max.x, box3.max.y, box3.min.z)
3418 dist = Math.min(dist, origin.distanceTo(pnt));
3420 pnt.set(box3.max.x, box3.min.y, box3.max.z)
3421 dist = Math.min(dist, origin.distanceTo(pnt));
3423 pnt.set(box3.min.x, box3.max.y, box3.max.z)
3424 dist = Math.min(dist, origin.distanceTo(pnt));
3426 mesh.$jsroot_distance = dist;
3429 arr.sort(
function(a,b) {
return a.$jsroot_distance - b.$jsroot_distance; });
3431 var resort =
new Array(arr.length);
3433 for (var i=0;i<arr.length;++i) {
3434 arr[i].$jsroot_index = i;
3439 for (var i=arr.length-1;i>=0;--i) {
3441 box3 = mesh.$jsroot_box3,
3442 direction = box3.getCenter(tmp_vect);
3444 for(var ntry=0; ntry<2;++ntry) {
3446 direction.sub(origin).normalize();
3448 raycast.set( origin, direction );
3450 var intersects = raycast.intersectObjects(arr,
false);
3454 for (var k1=0;k1<intersects.length;++k1) {
3455 if (unique.indexOf(intersects[k1].object)<0) unique.push(intersects[k1].object);
3459 intersects = unique;
3461 if ((intersects.indexOf(mesh)<0) && (ntry>0))
3462 console.log(
'MISS', clones ? clones.ResolveStack(mesh.stack).name :
"???");
3464 if ((intersects.indexOf(mesh)>=0) || (ntry>0))
break;
3466 var pos = mesh.geometry.attributes.position.array;
3468 direction =
new THREE.Vector3((pos[0]+pos[3]+pos[6])/3, (pos[1]+pos[4]+pos[7])/3, (pos[2]+pos[5]+pos[8])/3);
3470 direction.applyMatrix4(mesh.matrixWorld);
3474 for (var k1=0;k1<intersects.length-1;++k1) {
3475 var mesh1 = intersects[k1], mesh2 = intersects[k1+1],
3476 i1 = mesh1.$jsroot_index, i2 = mesh2.$jsroot_index;
3477 if (i1<i2)
continue;
3478 for (var ii=i2;ii<i1;++ii) {
3479 resort[ii] = resort[ii+1];
3480 resort[ii].$jsroot_index = ii;
3483 mesh2.$jsroot_index = i1;
3488 for (var i=0;i<resort.length;++i) {
3489 resort[i].renderOrder = maxorder - (i+1) / (resort.length+1) * (maxorder-minorder);
3490 delete resort[i].$jsroot_index;
3491 delete resort[i].$jsroot_distance;
3497 function process(obj, lvl, minorder, maxorder) {
3498 var arr = [], did_sort =
false;
3500 traverse(obj, lvl, arr);
3502 if (!arr.length)
return;
3504 if (minorder === maxorder) {
3505 for (var k=0;k<arr.length;++k)
3506 arr[k].renderOrder = minorder;
3508 did_sort = sort(arr, minorder, maxorder);
3509 if (!did_sort) minorder = maxorder = (minorder + maxorder) / 2;
3512 for (var k=0;k<arr.length;++k) {
3513 var next = arr[k].parent, min = minorder, max = maxorder;
3516 max = arr[k].renderOrder;
3517 min = max - (maxorder - minorder) / (arr.length + 2);
3520 process(next, lvl+1, min, max);
3524 if (!method || (method===
"dflt"))
3525 setdefaults(toplevel);
3527 process(toplevel, 0, 1, 1000000);
3537 JSROOT.GEO.build =
function(obj, opt, call_back) {
3543 if (!opt.numfaces) opt.numfaces = 100000;
3544 if (!opt.numnodes) opt.numnodes = 1000;
3545 if (!opt.frustum) opt.frustum = null;
3547 opt.res_mesh = opt.res_faces = 0;
3549 var shape = null, hide_top =
false;
3551 if ((
'fShapeBits' in obj) && (
'fShapeId' in obj)) {
3552 shape = obj; obj = null;
3553 }
else if ((obj._typename ===
'TGeoVolumeAssembly') || (obj._typename ===
'TGeoVolume')) {
3555 }
else if ((obj._typename ===
"TEveGeoShapeExtract") || (obj._typename ===
"ROOT::Experimental::TEveGeoShapeExtract") ) {
3557 }
else if (obj._typename ===
'TGeoManager') {
3558 obj = obj.fMasterVolume;
3559 hide_top = !opt.showtop;
3561 }
else if (obj.fVolume) {
3562 shape = obj.fVolume.fShape;
3567 if (opt.composite && shape && (shape._typename ==
'TGeoCompositeShape') && shape.fNode)
3568 obj = JSROOT.GEO.buildCompositeVolume(shape);
3571 obj = JSROOT.extend(JSROOT.Create(
"TEveGeoShapeExtract"),
3572 { fTrans: null, fShape: shape, fRGBA: [0, 1, 0, 1], fElements: null, fRnrSelf:
true });
3574 if (!obj)
return null;
3576 if (obj._typename.indexOf(
'TGeoVolume') === 0)
3577 obj = { _typename:
"TGeoNode", fVolume: obj, fName: obj.fName, $geoh: obj.$geoh, _proxy:
true };
3579 var clones =
new JSROOT.GEO.ClonedNodes(obj);
3580 clones.SetVisLevel(opt.vislevel);
3581 clones.SetMaxVisNodes(opt.numnodes);
3583 if (opt.dflt_colors)
3584 clones.SetDefaultColors(
true);
3586 var uniquevis = opt.no_screen ? 0 : clones.MarkVisibles(
true);
3588 uniquevis = clones.MarkVisibles(
false,
false, hide_top);
3590 uniquevis = clones.MarkVisibles(
true,
true, hide_top);
3592 clones.ProduceIdShits();
3595 var res = clones.CollectVisibles(opt.numfaces, opt.frustum);
3597 var draw_nodes = res.lst;
3600 var shapes = clones.CollectShapes(draw_nodes);
3602 clones.BuildShapes(shapes, opt.numfaces);
3604 var toplevel =
new THREE.Object3D();
3606 for (var n=0; n < draw_nodes.length;++n) {
3607 var entry = draw_nodes[n];
3608 if (entry.done)
continue;
3610 var shape = shapes[entry.shapeid];
3612 console.warn(
'shape marked as not ready when should');
3618 if (!shape.geom || (shape.nfaces === 0)) {
3620 clones.CreateObject3D(entry.stack, toplevel,
'delete_mesh');
3624 var prop = clones.getDrawEntryProperties(entry);
3627 opt.res_faces += shape.nfaces;
3629 var obj3d = clones.CreateObject3D(entry.stack, toplevel, opt);
3631 prop.material.wireframe = opt.wireframe;
3633 prop.material.side = opt.doubleside ? THREE.DoubleSide : THREE.FrontSide;
3637 if (obj3d.matrixWorld.determinant() > -0.9) {
3638 mesh =
new THREE.Mesh( shape.geom, prop.material );
3640 mesh = JSROOT.GEO.createFlippedMesh(obj3d, shape, prop.material);
3651 JSROOT.CallBack(call_back, toplevel);
3659 JSROOT.GEO.getBoundingBox =
function(node, box3, local_coordinates) {
3660 if (!node || !node.geometry)
return box3;
3662 if (!box3) { box3 =
new THREE.Box3(); box3.makeEmpty(); }
3664 if (!local_coordinates) node.updateMatrixWorld();
3666 var v1 =
new THREE.Vector3(),
3667 geometry = node.geometry;
3669 if ( geometry.isGeometry ) {
3670 var vertices = geometry.vertices;
3671 for (var i = 0, l = vertices.length; i < l; i ++ ) {
3672 v1.copy( vertices[ i ] );
3673 if (!local_coordinates) v1.applyMatrix4( node.matrixWorld );
3674 box3.expandByPoint( v1 );
3676 }
else if ( geometry.isBufferGeometry ) {
3677 var attribute = geometry.attributes.position;
3678 if ( attribute !== undefined ) {
3679 for (var i = 0, l = attribute.count; i < l; i ++ ) {
3681 v1.fromBufferAttribute( attribute, i );
3682 if (!local_coordinates) v1.applyMatrix4( node.matrixWorld );
3683 box3.expandByPoint( v1 );