1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099 |
- import * as THREE from "../libs/three.js/build/three.module.js";
- import {XHRFactory} from "./XHRFactory.js";
- import {Volume} from "./utils/Volume.js";
- import {Profile} from "./utils/Profile.js";
- import {Measure} from "./utils/Measure.js";
- import {PolygonClipVolume} from "./utils/PolygonClipVolume.js";
- export class Utils {
- static async loadShapefileFeatures (file, callback) {
- let features = [];
- let handleFinish = () => {
- callback(features);
- };
- let source = await shapefile.open(file);
- while(true){
- let result = await source.read();
- if (result.done) {
- handleFinish();
- break;
- }
- if (result.value && result.value.type === 'Feature' && result.value.geometry !== undefined) {
- features.push(result.value);
- }
- }
- }
- static toString (value) {
- if (value.x != null) {
- return value.x.toFixed(2) + ', ' + value.y.toFixed(2) + ', ' + value.z.toFixed(2);
- } else {
- return '' + value + '';
- }
- }
- static normalizeURL (url) {
- let u = new URL(url);
- return u.protocol + '//' + u.hostname + u.pathname.replace(/\/+/g, '/');
- };
- static pathExists (url) {
- let req = XHRFactory.createXMLHttpRequest();
- req.open('GET', url, false);
- req.send(null);
- if (req.status !== 200) {
- return false;
- }
- return true;
- };
- static debugSphere(parent, position, scale, color){
- let geometry = new THREE.SphereGeometry(1, 8, 8);
- let material;
- if(color !== undefined){
- material = new THREE.MeshBasicMaterial({color: color});
- }else{
- material = new THREE.MeshNormalMaterial();
- }
- let sphere = new THREE.Mesh(geometry, material);
- sphere.position.copy(position);
- sphere.scale.set(scale, scale, scale);
- parent.add(sphere);
- return sphere;
- }
- static debugLine(parent, start, end, color){
- let material = new THREE.LineBasicMaterial({ color: color });
- let geometry = new THREE.Geometry();
- const p1 = new THREE.Vector3(0, 0, 0);
- const p2 = end.clone().sub(start);
- geometry.vertices.push(p1, p2);
- let tl = new THREE.Line( geometry, material );
- tl.position.copy(start);
- parent.add(tl);
- let line = {
- node: tl,
- set: (start, end) => {
- geometry.vertices[0].copy(start);
- geometry.vertices[1].copy(end);
- geometry.verticesNeedUpdate = true;
- },
- };
- return line;
- }
- static debugCircle(parent, center, radius, normal, color){
- let material = new THREE.LineBasicMaterial({ color: color });
- let geometry = new THREE.Geometry();
- let n = 32;
- for(let i = 0; i <= n; i++){
- let u0 = 2 * Math.PI * (i / n);
- let u1 = 2 * Math.PI * (i + 1) / n;
- let p0 = new THREE.Vector3(
- Math.cos(u0),
- Math.sin(u0),
- 0
- );
- let p1 = new THREE.Vector3(
- Math.cos(u1),
- Math.sin(u1),
- 0
- );
- geometry.vertices.push(p0, p1);
- }
- let tl = new THREE.Line( geometry, material );
- tl.position.copy(center);
- tl.scale.set(radius, radius, radius);
- parent.add(tl);
- }
- static debugBox(parent, box, transform = new THREE.Matrix4(), color = 0xFFFF00){
-
- let vertices = [
- [box.min.x, box.min.y, box.min.z],
- [box.min.x, box.min.y, box.max.z],
- [box.min.x, box.max.y, box.min.z],
- [box.min.x, box.max.y, box.max.z],
- [box.max.x, box.min.y, box.min.z],
- [box.max.x, box.min.y, box.max.z],
- [box.max.x, box.max.y, box.min.z],
- [box.max.x, box.max.y, box.max.z],
- ].map(v => new THREE.Vector3(...v));
- let edges = [
- [0, 4], [4, 5], [5, 1], [1, 0],
- [2, 6], [6, 7], [7, 3], [3, 2],
- [0, 2], [4, 6], [5, 7], [1, 3]
- ];
- let center = box.getCenter(new THREE.Vector3());
- let centroids = [
- {position: [box.min.x, center.y, center.z], color: 0xFF0000},
- {position: [box.max.x, center.y, center.z], color: 0x880000},
- {position: [center.x, box.min.y, center.z], color: 0x00FF00},
- {position: [center.x, box.max.y, center.z], color: 0x008800},
- {position: [center.x, center.y, box.min.z], color: 0x0000FF},
- {position: [center.x, center.y, box.max.z], color: 0x000088},
- ];
- for(let vertex of vertices){
- let pos = vertex.clone().applyMatrix4(transform);
- Utils.debugSphere(parent, pos, 0.1, 0xFF0000);
- }
- for(let edge of edges){
- let start = vertices[edge[0]].clone().applyMatrix4(transform);
- let end = vertices[edge[1]].clone().applyMatrix4(transform);
- Utils.debugLine(parent, start, end, color);
- }
- for(let centroid of centroids){
- let pos = new THREE.Vector3(...centroid.position).applyMatrix4(transform);
- Utils.debugSphere(parent, pos, 0.1, centroid.color);
- }
- }
- static debugPlane(parent, plane, size = 1, color = 0x0000FF){
- let planehelper = new THREE.PlaneHelper(plane, size, color);
- parent.add(planehelper);
- }
- /**
- * adapted from mhluska at https://github.com/mrdoob/three.js/issues/1561
- */
- static computeTransformedBoundingBox (box, transform) {
- let vertices = [
- new THREE.Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform),
- new THREE.Vector3(box.min.x, box.min.y, box.min.z).applyMatrix4(transform),
- new THREE.Vector3(box.max.x, box.min.y, box.min.z).applyMatrix4(transform),
- new THREE.Vector3(box.min.x, box.max.y, box.min.z).applyMatrix4(transform),
- new THREE.Vector3(box.min.x, box.min.y, box.max.z).applyMatrix4(transform),
- new THREE.Vector3(box.min.x, box.max.y, box.max.z).applyMatrix4(transform),
- new THREE.Vector3(box.max.x, box.max.y, box.min.z).applyMatrix4(transform),
- new THREE.Vector3(box.max.x, box.min.y, box.max.z).applyMatrix4(transform),
- new THREE.Vector3(box.max.x, box.max.y, box.max.z).applyMatrix4(transform)
- ];
- let boundingBox = new THREE.Box3();
- boundingBox.setFromPoints(vertices);
- return boundingBox;
- };//感觉就是bound.applyMatrix4(transform)
- /**
- * add separators to large numbers
- *
- * @param nStr
- * @returns
- */
- static addCommas (nStr) {
- nStr += '';
- let x = nStr.split('.');
- let x1 = x[0];
- let x2 = x.length > 1 ? '.' + x[1] : '';
- let rgx = /(\d+)(\d{3})/;
- while (rgx.test(x1)) {
- x1 = x1.replace(rgx, '$1' + ',' + '$2');
- }
- return x1 + x2;
- };
- static removeCommas (str) {
- return str.replace(/,/g, '');
- }
- /**
- * create worker from a string
- *
- * code from http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string
- */
- static createWorker (code) {
- let blob = new Blob([code], {type: 'application/javascript'});
- let worker = new Worker(URL.createObjectURL(blob));
- return worker;
- };
- static moveTo(scene, endPosition, endTarget){
- let view = scene.view;
- let camera = scene.getActiveCamera();
- let animationDuration = 500;
- let easing = TWEEN.Easing.Quartic.Out;
- { // animate camera position
- let tween = new TWEEN.Tween(view.position).to(endPosition, animationDuration);
- tween.easing(easing);
- tween.start();
- }
- { // animate camera target
- let camTargetDistance = camera.position.distanceTo(endTarget);
- let target = new THREE.Vector3().addVectors(
- camera.position,
- camera.getWorldDirection(new THREE.Vector3()).clone().multiplyScalar(camTargetDistance)
- );
- let tween = new TWEEN.Tween(target).to(endTarget, animationDuration);
- tween.easing(easing);
- tween.onUpdate(() => {
- view.lookAt(target);
- });
- tween.onComplete(() => {
- view.lookAt(target);
- });
- tween.start();
- }
- }
- static loadSkybox (path) {
- let parent = new THREE.Object3D("skybox_root");
- let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 100000);
- camera.up.set(0, 0, 1);
- let scene = new THREE.Scene();
- let format = '.jpg';
- let urls = [
- path + 'px' + format, path + 'nx' + format,
- path + 'py' + format, path + 'ny' + format,
- path + 'pz' + format, path + 'nz' + format
- ];
- let materialArray = [];
- {
- for (let i = 0; i < 6; i++) {
- let material = new THREE.MeshBasicMaterial({
- map: null,
- side: THREE.BackSide,
- depthTest: false,
- depthWrite: false,
- color: 0x424556
- });
- materialArray.push(material);
- let loader = new THREE.TextureLoader();
- loader.load(urls[i],
- function loaded (texture) {
- material.map = texture;
- material.needsUpdate = true;
- material.color.setHex(0xffffff);
- }, function progress (xhr) {
- // console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
- }, function error (xhr) {
- console.log('An error happened', xhr);
- }
- );
- }
- }
- let skyGeometry = new THREE.CubeGeometry(700, 700, 700);
- let skybox = new THREE.Mesh(skyGeometry, materialArray);
- scene.add(skybox);
- scene.traverse(n => n.frustumCulled = false);
- // z up
- scene.rotation.x = Math.PI / 2;
- parent.children.push(camera);
- camera.parent = parent;
- return {camera, scene, parent};
- };
- static createGrid (width, length, spacing, color) {
- let material = new THREE.LineBasicMaterial({
- color: color || 0x888888
- });
- let geometry = new THREE.Geometry();
- for (let i = 0; i <= length; i++) {
- geometry.vertices.push(new THREE.Vector3(-(spacing * width) / 2, i * spacing - (spacing * length) / 2, 0));
- geometry.vertices.push(new THREE.Vector3(+(spacing * width) / 2, i * spacing - (spacing * length) / 2, 0));
- }
- for (let i = 0; i <= width; i++) {
- geometry.vertices.push(new THREE.Vector3(i * spacing - (spacing * width) / 2, -(spacing * length) / 2, 0));
- geometry.vertices.push(new THREE.Vector3(i * spacing - (spacing * width) / 2, +(spacing * length) / 2, 0));
- }
- let line = new THREE.LineSegments(geometry, material, THREE.LinePieces);
- line.receiveShadow = true;
- return line;
- }
- static createBackgroundTexture (width, height) {
- function gauss (x, y) {
- return (1 / (2 * Math.PI)) * Math.exp(-(x * x + y * y) / 2);
- };
- // map.magFilter = THREE.NearestFilter;
- let size = width * height;
- let data = new Uint8Array(3 * size);
- let chroma = [1, 1.5, 1.7];
- let max = gauss(0, 0);
- for (let x = 0; x < width; x++) {
- for (let y = 0; y < height; y++) {
- let u = 2 * (x / width) - 1;
- let v = 2 * (y / height) - 1;
- let i = x + width * y;
- let d = gauss(2 * u, 2 * v) / max;
- let r = (Math.random() + Math.random() + Math.random()) / 3;
- r = (d * 0.5 + 0.5) * r * 0.03;
- r = r * 0.4;
- // d = Math.pow(d, 0.6);
- data[3 * i + 0] = 255 * (d / 15 + 0.05 + r) * chroma[0];
- data[3 * i + 1] = 255 * (d / 15 + 0.05 + r) * chroma[1];
- data[3 * i + 2] = 255 * (d / 15 + 0.05 + r) * chroma[2];
- }
- }
- let texture = new THREE.DataTexture(data, width, height, THREE.RGBFormat);
- texture.needsUpdate = true;
- return texture;
- }
- static getMousePointCloudIntersection (mouse, camera, viewer, pointclouds, params = {}) {
-
- let renderer = viewer.renderer;
-
- let nmouse = {
- x: (mouse.x / renderer.domElement.clientWidth) * 2 - 1,
- y: -(mouse.y / renderer.domElement.clientHeight) * 2 + 1
- };
- let pickParams = {};
- if(params.pickClipped){
- pickParams.pickClipped = params.pickClipped;
- }
- pickParams.x = mouse.x;
- pickParams.y = renderer.domElement.clientHeight - mouse.y;
- let raycaster = new THREE.Raycaster();
- raycaster.setFromCamera(nmouse, camera);
- let ray = raycaster.ray;
- let selectedPointcloud = null;
- let closestDistance = Infinity;
- let closestIntersection = null;
- let closestPoint = null;
-
- for(let pointcloud of pointclouds){
- let point = pointcloud.pick(viewer, camera, ray, pickParams);
-
- if(!point){
- continue;
- }
- let distance = camera.position.distanceTo(point.position);
- if (distance < closestDistance) {
- closestDistance = distance;
- selectedPointcloud = pointcloud;
- closestIntersection = point.position;
- closestPoint = point;
- }
- }
- if (selectedPointcloud) {
- return {
- location: closestIntersection,
- distance: closestDistance,
- pointcloud: selectedPointcloud,
- point: closestPoint
- };
- } else {
- return null;
- }
- }
- static pixelsArrayToImage (pixels, width, height) {
- let canvas = document.createElement('canvas');
- canvas.width = width;
- canvas.height = height;
- let context = canvas.getContext('2d');
- pixels = new pixels.constructor(pixels);
- for (let i = 0; i < pixels.length; i++) {
- pixels[i * 4 + 3] = 255;
- }
- let imageData = context.createImageData(width, height);
- imageData.data.set(pixels);
- context.putImageData(imageData, 0, 0);
- let img = new Image();
- img.src = canvas.toDataURL();
- // img.style.transform = "scaleY(-1)";
- return img;
- }
- static pixelsArrayToDataUrl(pixels, width, height) {
- let canvas = document.createElement('canvas');
- canvas.width = width;
- canvas.height = height;
- let context = canvas.getContext('2d');
- pixels = new pixels.constructor(pixels);
- for (let i = 0; i < pixels.length; i++) {
- pixels[i * 4 + 3] = 255;
- }
- let imageData = context.createImageData(width, height);
- imageData.data.set(pixels);
- context.putImageData(imageData, 0, 0);
- let dataURL = canvas.toDataURL();
- return dataURL;
- }
- static pixelsArrayToCanvas(pixels, width, height){
- let canvas = document.createElement('canvas');
- canvas.width = width;
- canvas.height = height;
- let context = canvas.getContext('2d');
- pixels = new pixels.constructor(pixels);
- //for (let i = 0; i < pixels.length; i++) {
- // pixels[i * 4 + 3] = 255;
- //}
- // flip vertically
- let bytesPerLine = width * 4;
- for(let i = 0; i < parseInt(height / 2); i++){
- let j = height - i - 1;
- let lineI = pixels.slice(i * bytesPerLine, i * bytesPerLine + bytesPerLine);
- let lineJ = pixels.slice(j * bytesPerLine, j * bytesPerLine + bytesPerLine);
- pixels.set(lineJ, i * bytesPerLine);
- pixels.set(lineI, j * bytesPerLine);
- }
- let imageData = context.createImageData(width, height);
- imageData.data.set(pixels);
- context.putImageData(imageData, 0, 0);
- return canvas;
- }
- static removeListeners(dispatcher, type){
- if (dispatcher._listeners === undefined) {
- return;
- }
- if (dispatcher._listeners[ type ]) {
- delete dispatcher._listeners[ type ];
- }
- }
- static mouseToRay(mouse, camera, width, height){
- let normalizedMouse = {
- x: (mouse.x / width) * 2 - 1,
- y: -(mouse.y / height) * 2 + 1
- };
- let vector = new THREE.Vector3(normalizedMouse.x, normalizedMouse.y, 0.5);
- let origin = camera.position.clone();
- vector.unproject(camera);
- let direction = new THREE.Vector3().subVectors(vector, origin).normalize();
- let ray = new THREE.Ray(origin, direction);
- return ray;
- }
- static projectedRadius(radius, camera, distance, screenWidth, screenHeight){
- if(camera instanceof THREE.OrthographicCamera){
- return Utils.projectedRadiusOrtho(radius, camera.projectionMatrix, screenWidth, screenHeight);
- }else if(camera instanceof THREE.PerspectiveCamera){
- return Utils.projectedRadiusPerspective(radius, camera.fov * Math.PI / 180, distance, screenHeight);
- }else{
- throw new Error("invalid parameters");
- }
- }
- static projectedRadiusPerspective(radius, fov, distance, screenHeight) {
- let projFactor = (1 / Math.tan(fov / 2)) / distance;
- projFactor = projFactor * screenHeight / 2;
- return radius * projFactor;
- }
- static projectedRadiusOrtho(radius, proj, screenWidth, screenHeight) {
- let p1 = new THREE.Vector4(0);
- let p2 = new THREE.Vector4(radius);
- p1.applyMatrix4(proj);
- p2.applyMatrix4(proj);
- p1 = new THREE.Vector3(p1.x, p1.y, p1.z);
- p2 = new THREE.Vector3(p2.x, p2.y, p2.z);
- p1.x = (p1.x + 1.0) * 0.5 * screenWidth;
- p1.y = (p1.y + 1.0) * 0.5 * screenHeight;
- p2.x = (p2.x + 1.0) * 0.5 * screenWidth;
- p2.y = (p2.y + 1.0) * 0.5 * screenHeight;
- return p1.distanceTo(p2);
- }
-
-
- static topView(camera, node){
- camera.position.set(0, 1, 0);
- camera.rotation.set(-Math.PI / 2, 0, 0);
- camera.zoomTo(node, 1);
- }
- static frontView (camera, node) {
- camera.position.set(0, 0, 1);
- camera.rotation.set(0, 0, 0);
- camera.zoomTo(node, 1);
- }
- static leftView (camera, node) {
- camera.position.set(-1, 0, 0);
- camera.rotation.set(0, -Math.PI / 2, 0);
- camera.zoomTo(node, 1);
- }
- static rightView (camera, node) {
- camera.position.set(1, 0, 0);
- camera.rotation.set(0, Math.PI / 2, 0);
- camera.zoomTo(node, 1);
- }
-
- static findClosestGpsTime(target, viewer){
- const start = performance.now();
- const nodes = [];
- for(const pc of viewer.scene.pointclouds){
- nodes.push(pc.root);
- for(const child of pc.root.children){
- if(child){
- nodes.push(child);
- }
- }
- }
- let closestNode = null;
- let closestIndex = Infinity;
- let closestDistance = Infinity;
- let closestValue = 0;
- for(const node of nodes){
- const isOkay = node.geometryNode != null
- && node.geometryNode.geometry != null
- && node.sceneNode != null;
- if(!isOkay){
- continue;
- }
- let geometry = node.geometryNode.geometry;
- let gpsTime = geometry.attributes["gps-time"];
- let range = gpsTime.potree.range;
- for(let i = 0; i < gpsTime.array.length; i++){
- let value = gpsTime.array[i];
- value = value * (range[1] - range[0]) + range[0];
- const distance = Math.abs(target - value);
- if(distance < closestDistance){
- closestIndex = i;
- closestDistance = distance;
- closestValue = value;
- closestNode = node;
- //console.log("found a closer one: " + value);
- }
- }
- }
- const geometry = closestNode.geometryNode.geometry;
- const position = new THREE.Vector3(
- geometry.attributes.position.array[3 * closestIndex + 0],
- geometry.attributes.position.array[3 * closestIndex + 1],
- geometry.attributes.position.array[3 * closestIndex + 2],
- );
- position.applyMatrix4(closestNode.sceneNode.matrixWorld);
- const end = performance.now();
- const duration = (end - start);
- console.log(`duration: ${duration.toFixed(3)}ms`);
- return {
- node: closestNode,
- index: closestIndex,
- position: position,
- };
- }
- /**
- *
- * 0: no intersection
- * 1: intersection
- * 2: fully inside
- */
- static frustumSphereIntersection (frustum, sphere) {
- let planes = frustum.planes;
- let center = sphere.center;
- let negRadius = -sphere.radius;
- let minDistance = Number.MAX_VALUE;
- for (let i = 0; i < 6; i++) {
- let distance = planes[ i ].distanceToPoint(center);
- if (distance < negRadius) {
- return 0;
- }
- minDistance = Math.min(minDistance, distance);
- }
- return (minDistance >= sphere.radius) ? 2 : 1;
- }
- // code taken from three.js
- // ImageUtils - generateDataTexture()
- static generateDataTexture (width, height, color) {
- let size = width * height;
- let data = new Uint8Array(4 * width * height);
- let r = Math.floor(color.r * 255);
- let g = Math.floor(color.g * 255);
- let b = Math.floor(color.b * 255);
- for (let i = 0; i < size; i++) {
- data[ i * 3 ] = r;
- data[ i * 3 + 1 ] = g;
- data[ i * 3 + 2 ] = b;
- }
- let texture = new THREE.DataTexture(data, width, height, THREE.RGBAFormat);
- texture.needsUpdate = true;
- texture.magFilter = THREE.NearestFilter;
- return texture;
- }
- // from http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
- static getParameterByName (name) {
- name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
- let regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
- let results = regex.exec(document.location.search);
- return results === null ? null : decodeURIComponent(results[1].replace(/\+/g, ' '));
- }
- static setParameter (name, value) {
- // value = encodeURIComponent(value);
- name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
- let regex = new RegExp('([\\?&])(' + name + '=([^&#]*))');
- let results = regex.exec(document.location.search);
- let url = window.location.href;
- if (results === null) {
- if (window.location.search.length === 0) {
- url = url + '?';
- } else {
- url = url + '&';
- }
- url = url + name + '=' + value;
- } else {
- let newValue = name + '=' + value;
- url = url.replace(results[2], newValue);
- }
- window.history.replaceState({}, '', url);
- }
- static createChildAABB(aabb, index){
- let min = aabb.min.clone();
- let max = aabb.max.clone();
- let size = new THREE.Vector3().subVectors(max, min);
- if ((index & 0b0001) > 0) {
- min.z += size.z / 2;
- } else {
- max.z -= size.z / 2;
- }
- if ((index & 0b0010) > 0) {
- min.y += size.y / 2;
- } else {
- max.y -= size.y / 2;
- }
- if ((index & 0b0100) > 0) {
- min.x += size.x / 2;
- } else {
- max.x -= size.x / 2;
- }
- return new THREE.Box3(min, max);
- }
- // see https://stackoverflow.com/questions/400212/how-do-i-copy-to-the-clipboard-in-javascript
- static clipboardCopy(text){
- let textArea = document.createElement("textarea");
- textArea.style.position = 'fixed';
- textArea.style.top = 0;
- textArea.style.left = 0;
- textArea.style.width = '2em';
- textArea.style.height = '2em';
- textArea.style.padding = 0;
- textArea.style.border = 'none';
- textArea.style.outline = 'none';
- textArea.style.boxShadow = 'none';
- textArea.style.background = 'transparent';
- textArea.value = text;
- document.body.appendChild(textArea);
- textArea.select();
- try {
- let success = document.execCommand('copy');
- if(success){
- console.log("copied text to clipboard");
- }else{
- console.log("copy to clipboard failed");
- }
- } catch (err) {
- console.log("error while trying to copy to clipboard");
- }
- document.body.removeChild(textArea);
- }
- static getMeasurementIcon(measurement){
- if (measurement instanceof Measure) {
- if (measurement.showDistances && !measurement.showArea && !measurement.showAngles) {
- return `${Potree.resourcePath}/icons/distance.svg`;
- } else if (measurement.showDistances && measurement.showArea && !measurement.showAngles) {
- return `${Potree.resourcePath}/icons/area.svg`;
- } else if (measurement.maxMarkers === 1) {
- return `${Potree.resourcePath}/icons/point.svg`;
- } else if (!measurement.showDistances && !measurement.showArea && measurement.showAngles) {
- return `${Potree.resourcePath}/icons/angle.png`;
- } else if (measurement.showHeight) {
- return `${Potree.resourcePath}/icons/height.svg`;
- } else {
- return `${Potree.resourcePath}/icons/distance.svg`;
- }
- } else if (measurement instanceof Profile) {
- return `${Potree.resourcePath}/icons/profile.svg`;
- } else if (measurement instanceof Volume) {
- return `${Potree.resourcePath}/icons/volume.svg`;
- } else if (measurement instanceof PolygonClipVolume) {
- return `${Potree.resourcePath}/icons/clip-polygon.svg`;
- }
- }
- static lineToLineIntersection(P0, P1, P2, P3){
- const P = [P0, P1, P2, P3];
- const d = (m, n, o, p) => {
- let result =
- (P[m].x - P[n].x) * (P[o].x - P[p].x)
- + (P[m].y - P[n].y) * (P[o].y - P[p].y)
- + (P[m].z - P[n].z) * (P[o].z - P[p].z);
- return result;
- };
- const mua = (d(0, 2, 3, 2) * d(3, 2, 1, 0) - d(0, 2, 1, 0) * d(3, 2, 3, 2))
- /**-----------------------------------------------------------------**/ /
- (d(1, 0, 1, 0) * d(3, 2, 3, 2) - d(3, 2, 1, 0) * d(3, 2, 1, 0));
- const mub = (d(0, 2, 3, 2) + mua * d(3, 2, 1, 0))
- /**--------------------------------------**/ /
- d(3, 2, 3, 2);
- const P01 = P1.clone().sub(P0);
- const P23 = P3.clone().sub(P2);
-
- const Pa = P0.clone().add(P01.multiplyScalar(mua));
- const Pb = P2.clone().add(P23.multiplyScalar(mub));
- const center = Pa.clone().add(Pb).multiplyScalar(0.5);
- return center;
- }
- static computeCircleCenter(A, B, C){
- const AB = B.clone().sub(A);
- const AC = C.clone().sub(A);
- const N = AC.clone().cross(AB).normalize();
- const ab_dir = AB.clone().cross(N).normalize();
- const ac_dir = AC.clone().cross(N).normalize();
- const ab_origin = A.clone().add(B).multiplyScalar(0.5);
- const ac_origin = A.clone().add(C).multiplyScalar(0.5);
- const P0 = ab_origin;
- const P1 = ab_origin.clone().add(ab_dir);
- const P2 = ac_origin;
- const P3 = ac_origin.clone().add(ac_dir);
- const center = Utils.lineToLineIntersection(P0, P1, P2, P3);
- return center;
- // Potree.Utils.debugLine(viewer.scene.scene, P0, P1, 0x00ff00);
- // Potree.Utils.debugLine(viewer.scene.scene, P2, P3, 0x0000ff);
- // Potree.Utils.debugSphere(viewer.scene.scene, center, 0.03, 0xff00ff);
- // const radius = center.distanceTo(A);
- // Potree.Utils.debugCircle(viewer.scene.scene, center, radius, new THREE.Vector3(0, 0, 1), 0xff00ff);
- }
- static getNorthVec(p1, distance, projection){
- if(projection){
- // if there is a projection, transform coordinates to WGS84
- // and compute angle to north there
- proj4.defs("pointcloud", projection);
- const transform = proj4("pointcloud", "WGS84");
- const llP1 = transform.forward(p1.toArray());
- let llP2 = transform.forward([p1.x, p1.y + distance]);
- const polarRadius = Math.sqrt((llP2[0] - llP1[0]) ** 2 + (llP2[1] - llP1[1]) ** 2);
- llP2 = [llP1[0], llP1[1] + polarRadius];
- const northVec = transform.inverse(llP2);
-
- return new THREE.Vector3(...northVec, p1.z).sub(p1);
- }else{
- // if there is no projection, assume [0, 1, 0] as north direction
- const vec = new THREE.Vector3(0, 1, 0).multiplyScalar(distance);
-
- return vec;
- }
- }
- static computeAzimuth(p1, p2, projection){
- let azimuth = 0;
- if(projection){
- // if there is a projection, transform coordinates to WGS84
- // and compute angle to north there
- let transform;
- if (projection.includes('EPSG')) {
- transform = proj4(projection, "WGS84");
- } else {
- proj4.defs("pointcloud", projection);
- transform = proj4("pointcloud", "WGS84");
- }
- const llP1 = transform.forward(p1.toArray());
- const llP2 = transform.forward(p2.toArray());
- const dir = [
- llP2[0] - llP1[0],
- llP2[1] - llP1[1],
- ];
- azimuth = Math.atan2(dir[1], dir[0]) - Math.PI / 2;
- }else{
- // if there is no projection, assume [0, 1, 0] as north direction
- const dir = [p2.x - p1.x, p2.y - p1.y];
- azimuth = Math.atan2(dir[1], dir[0]) - Math.PI / 2;
- }
- // make clockwise
- azimuth = -azimuth;
- return azimuth;
- }
- static async loadScript(url){
- return new Promise( resolve => {
- const element = document.getElementById(url);
- if(element){
- resolve();
- }else{
- const script = document.createElement("script");
- script.id = url;
- script.onload = () => {
- resolve();
- };
- script.src = url;
- document.body.appendChild(script);
- }
- });
- }
- static createSvgGradient(scheme){
- // this is what we are creating:
- //
- //<svg width="1em" height="3em" xmlns="http://www.w3.org/2000/svg">
- // <defs>
- // <linearGradient id="gradientID" gradientTransform="rotate(90)">
- // <stop offset="0%" stop-color="rgb(93, 78, 162)" />
- // ...
- // <stop offset="100%" stop-color="rgb(157, 0, 65)" />
- // </linearGradient>
- // </defs>
- //
- // <rect width="100%" height="100%" fill="url('#myGradient')" stroke="black" stroke-width="0.1em"/>
- //</svg>
- const gradientId = `${Math.random()}_${Date.now()}`;
-
- const svgn = "http://www.w3.org/2000/svg";
- const svg = document.createElementNS(svgn, "svg");
- svg.setAttributeNS(null, "width", "2em");
- svg.setAttributeNS(null, "height", "3em");
-
- { // <defs>
- const defs = document.createElementNS(svgn, "defs");
-
- const linearGradient = document.createElementNS(svgn, "linearGradient");
- linearGradient.setAttributeNS(null, "id", gradientId);
- linearGradient.setAttributeNS(null, "gradientTransform", "rotate(90)");
- for(let i = scheme.length - 1; i >= 0; i--){
- const stopVal = scheme[i];
- const percent = parseInt(100 - stopVal[0] * 100);
- const [r, g, b] = stopVal[1].toArray().map(v => parseInt(v * 255));
- const stop = document.createElementNS(svgn, "stop");
- stop.setAttributeNS(null, "offset", `${percent}%`);
- stop.setAttributeNS(null, "stop-color", `rgb(${r}, ${g}, ${b})`);
- linearGradient.appendChild(stop);
- }
- defs.appendChild(linearGradient);
- svg.appendChild(defs);
- }
- const rect = document.createElementNS(svgn, "rect");
- rect.setAttributeNS(null, "width", `100%`);
- rect.setAttributeNS(null, "height", `100%`);
- rect.setAttributeNS(null, "fill", `url("#${gradientId}")`);
- rect.setAttributeNS(null, "stroke", `black`);
- rect.setAttributeNS(null, "stroke-width", `0.1em`);
- svg.appendChild(rect);
-
- return svg;
- }
- static async waitAny(promises){
-
- return new Promise( (resolve) => {
- promises.map( promise => {
- promise.then( () => {
- resolve();
- });
- });
- });
- }
- }
- Utils.screenPass = new function () {
- this.screenScene = new THREE.Scene();
- this.screenQuad = new THREE.Mesh(new THREE.PlaneBufferGeometry(2, 2, 1));
- this.screenQuad.material.depthTest = true;
- this.screenQuad.material.depthWrite = true;
- this.screenQuad.material.transparent = true;
- this.screenScene.add(this.screenQuad);
- this.camera = new THREE.Camera();
- this.render = function (renderer, material, target) {
- this.screenQuad.material = material;
- if (typeof target === 'undefined') {
- renderer.render(this.screenScene, this.camera);
- } else {
- renderer.render(this.screenScene, this.camera, target);
- }
- };
- }();
|