12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250 |
- 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;
- };
- /**
- * 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);
- if(!window.axisYup) 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 (viewport, mouse, pointer, camera, viewer, pointclouds, params = {}) {
-
- let renderer = viewer.renderer;
-
-
- let pickParams = {};
- if(params.pickClipped){
- pickParams.pickClipped = params.pickClipped;
- }
-
-
- if(viewport){ //转换到类似整个画面时
- pickParams.x = mouse.x / viewport.width;
- pickParams.y = renderer.domElement.clientHeight - mouse.y / viewport.height;
- }else{
- pickParams.x = mouse.x;
- pickParams.y = renderer.domElement.clientHeight - mouse.y;
- }
-
-
- let raycaster = new THREE.Raycaster();
- raycaster.setFromCamera(pointer, 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, compressRatio = 0.7) {
- 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);
- let dataURL = canvas.toDataURL(compressRatio);
- 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 mouseToRay(pointer, camera ){
-
- let vector = new THREE.Vector3(pointer.x, pointer.y, 1);
- let origin = new THREE.Vector3(pointer.x, pointer.y, -1); //不能用camera.position,在orbitCamera时不准
- vector.unproject(camera);
- origin.unproject(camera);
- let direction = new THREE.Vector3().subVectors(vector, origin).normalize();
- let ray = new THREE.Ray(origin, direction);
- return ray;
- }
-
- static getPos2d(point, camera, dom, viewport){//获取一个三维坐标对应屏幕中的二维坐标
-
- var pos = point.clone().project(camera) //比之前hotspot的计算方式写得简单 project用于3转2(求法同shader); unproject用于2转3 :new r.Vector3(e.x, e.y, -1).unproject(this.camera);
-
- var x,y,left,top;
- x = (pos.x + 1) / 2 * dom.clientWidth * viewport.width;
- y = (1 - (pos.y + 1) / 2) * dom.clientHeight * viewport.height;
- left = viewport.left * dom.clientWidth;
- top = (1- viewport.bottom - viewport.height) * dom.clientHeight;
-
-
- var inSight = pos.x <= 1 && pos.x >= -1 //是否在屏幕中
- && pos.x <= 1 && pos.y >= -1
-
-
- return {
- pos: new THREE.Vector2(left+x,top+y) ,// 屏幕像素坐标
- vector: pos, //(范围 -1 ~ 1)
- trueSide : pos.z<1, //trueSide为false时,即使在屏幕范围内可见,也是反方向的另一个不可以被渲染的点 参见Tag.update
- inSight : inSight, //在屏幕范围内可见,
- posInViewport: new THREE.Vector2(x,y)
- };
- }
-
- 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.setRenderTarget(target)
- renderer.render(this.screenScene, this.camera);
- }
- };
- }();
- //add
- Utils.computePointcloudsBound = function(pointclouds){
- var boundingBox = new THREE.Box3();
- pointclouds.forEach(pointcloud=>{
- var boundingBox_ = pointcloud.pcoGeometry.tightBoundingBox.clone().applyMatrix4(pointcloud.matrixWorld)
- pointcloud.bound = boundingBox_
- boundingBox.union(boundingBox_)
- })
- var boundSize = boundingBox.getSize(new THREE.Vector3)
- var center = boundingBox.getCenter(new THREE.Vector3)
- return {boundSize, center, boundingBox}
- }
- Utils.convertScreenPositionToNDC = function(pointer, mouse, width, height) {
- return pointer = pointer || new THREE.Vector2,
- pointer.x = mouse.x / width * 2 - 1,
- pointer.y = 2 * -(mouse.y / height) + 1,
- pointer
- }
- Utils.getOrthoCameraMoveVec = function(pointerDelta, camera ){//获取当camera为Ortho型时 屏幕点1 到 屏幕点2 的三维距离
-
- let cameraViewWidth = camera.right / camera.zoom
- let cameraViewHeight = camera.top / camera.zoom
- let moveVec = new THREE.Vector3;
- moveVec.set( pointerDelta.x * cameraViewWidth , pointerDelta.y * cameraViewHeight , 0).applyQuaternion(camera.quaternion)
- return moveVec
- }
- Utils.VectorFactory = {
- fromArray : function(t) {
- if (t) {
- if (t.length < 2 || t.length > 3)
- console.error("Wrong number of ordinates for a point!");
- return 3 === t.length ? (new THREE.Vector3).fromArray(t) : (new THREE.Vector2).fromArray(t)
- }
- },
- fromArray3 : function(t) {
- if (t) {
- if (3 !== t.length)
- console.error("Wrong number of ordinates for a point!");
- return (new THREE.Vector3).fromArray(t)
- }
- },
- fromArray2 : function(t) {
- if (t) {
- if (2 !== t.length)
- console.error("Wrong number of ordinates for a point!");
- return (new THREE.Vector2).fromArray(t)
- }
- },
- toString : function(t) {
- return t.x.toFixed(8) + "," + t.y.toFixed(8) + "," + t.z.toFixed(3)
- }
- }
-
- Utils.QuaternionFactory = {
- rot90 : (new THREE.Quaternion).setFromAxisAngle(new THREE.Vector3(0,0,1), THREE.Math.degToRad(-90)),
- fromArray : function(t) {
- if (t) {
- if (4 !== t.length)
- console.error("Wrong number of ordinates for a quaternion!");
- return new THREE.Quaternion(t[1],t[2],t[3],t[0]).multiply(this.rot90)
- }
- }
- ,
- toArray : function(t) {
- if (t) {
- var e = t.clone().multiply(a).toArray();
- return [e[3], e[0], e[1], e[2]]
- }
- }
- ,
- fromLonLat : function(t) {
- if (t)
- return (new THREE.Quaternion).setFromEuler(new THREE.Euler(t.lon,t.lat,0))
- }
- ,
- toLonLat : function(t) {
- if (t) {
- var e = (new THREE.Euler).setFromQuaternion(t);
- return {
- lon: e.x,
- lat: e.y
- }
- }
- }
-
-
- }
-
-
-
|