MeasuringTool.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. import * as THREE from "../../libs/three.js/build/three.module.js";
  2. import {Measure} from "./Measure.js";
  3. import {Utils} from "../utils.js";
  4. import {CameraMode} from "../defines.js";
  5. import { EventDispatcher } from "../EventDispatcher.js";
  6. //old
  7. function updateAzimuth(viewer, measure){
  8. const azimuth = measure.azimuth;
  9. const isOkay = measure.points.length === 2;
  10. azimuth.node.visible = isOkay && measure.showAzimuth;
  11. if(!azimuth.node.visible){
  12. return;
  13. }
  14. const camera = viewer.scene.getActiveCamera();
  15. const renderAreaSize = viewer.renderer.getSize(new THREE.Vector2());
  16. const width = renderAreaSize.width;
  17. const height = renderAreaSize.height;
  18. const [p0, p1] = measure.points;
  19. const r = p0.position.distanceTo(p1.position);
  20. const northVec = Utils.getNorthVec(p0.position, r, viewer.getProjection());
  21. const northPos = p0.position.clone().add(northVec);
  22. azimuth.center.position.copy(p0.position);
  23. azimuth.center.scale.set(2, 2, 2);
  24. azimuth.center.visible = false;
  25. // azimuth.target.visible = false;
  26. { // north
  27. azimuth.north.position.copy(northPos);
  28. azimuth.north.scale.set(2, 2, 2);
  29. let distance = azimuth.north.position.distanceTo(camera.position);
  30. let pr = Utils.projectedRadius(1, camera, distance, width, height);
  31. let scale = (5 / pr);
  32. azimuth.north.scale.set(scale, scale, scale);
  33. }
  34. { // target
  35. azimuth.target.position.copy(p1.position);
  36. azimuth.target.position.z = azimuth.north.position.z;
  37. let distance = azimuth.target.position.distanceTo(camera.position);
  38. let pr = Utils.projectedRadius(1, camera, distance, width, height);
  39. let scale = (5 / pr);
  40. azimuth.target.scale.set(scale, scale, scale);
  41. }
  42. azimuth.circle.position.copy(p0.position);
  43. azimuth.circle.scale.set(r, r, r);
  44. azimuth.circle.material.resolution.set(width, height);
  45. // to target
  46. azimuth.centerToTarget.geometry.setPositions([
  47. 0, 0, 0,
  48. ...p1.position.clone().sub(p0.position).toArray(),
  49. ]);
  50. azimuth.centerToTarget.position.copy(p0.position);
  51. azimuth.centerToTarget.geometry.verticesNeedUpdate = true;
  52. azimuth.centerToTarget.geometry.computeBoundingSphere();
  53. azimuth.centerToTarget.computeLineDistances();
  54. azimuth.centerToTarget.material.resolution.set(width, height);
  55. // to target ground
  56. azimuth.centerToTargetground.geometry.setPositions([
  57. 0, 0, 0,
  58. p1.position.x - p0.position.x,
  59. p1.position.y - p0.position.y,
  60. 0,
  61. ]);
  62. azimuth.centerToTargetground.position.copy(p0.position);
  63. azimuth.centerToTargetground.geometry.verticesNeedUpdate = true;
  64. azimuth.centerToTargetground.geometry.computeBoundingSphere();
  65. azimuth.centerToTargetground.computeLineDistances();
  66. azimuth.centerToTargetground.material.resolution.set(width, height);
  67. // to north
  68. azimuth.centerToNorth.geometry.setPositions([
  69. 0, 0, 0,
  70. northPos.x - p0.position.x,
  71. northPos.y - p0.position.y,
  72. 0,
  73. ]);
  74. azimuth.centerToNorth.position.copy(p0.position);
  75. azimuth.centerToNorth.geometry.verticesNeedUpdate = true;
  76. azimuth.centerToNorth.geometry.computeBoundingSphere();
  77. azimuth.centerToNorth.computeLineDistances();
  78. azimuth.centerToNorth.material.resolution.set(width, height);
  79. // label
  80. const radians = Utils.computeAzimuth(p0.position, p1.position, viewer.getProjection());
  81. let degrees = THREE.Math.radToDeg(radians);
  82. if(degrees < 0){
  83. degrees = 360 + degrees;
  84. }
  85. const txtDegrees = `${degrees.toFixed(2)}°`;
  86. const labelDir = northPos.clone().add(p1.position).multiplyScalar(0.5).sub(p0.position);
  87. if(labelDir.length() > 0){
  88. labelDir.z = 0;
  89. labelDir.normalize();
  90. const labelVec = labelDir.clone().multiplyScalar(r);
  91. const labelPos = p0.position.clone().add(labelVec);
  92. azimuth.label.position.copy(labelPos);
  93. }
  94. azimuth.label.setText(txtDegrees);
  95. let distance = azimuth.label.position.distanceTo(camera.position);
  96. let pr = Utils.projectedRadius(1, camera, distance, width, height);
  97. let scale = (70 / pr);
  98. azimuth.label.scale.set(scale, scale, scale);
  99. }
  100. export class MeasuringTool extends EventDispatcher{
  101. constructor (viewer) {
  102. super();
  103. this.viewer = viewer;
  104. this.renderer = viewer.renderer;
  105. this.addEventListener('start_inserting_measurement', e => {
  106. this.viewer.dispatchEvent({
  107. type: 'cancel_insertions'
  108. });
  109. });
  110. this.showLabels = true;
  111. this.scene = new THREE.Scene();
  112. this.scene.name = 'scene_measurement';
  113. this.light = new THREE.PointLight(0xffffff, 1.0);
  114. this.scene.add(this.light);
  115. this.viewer.inputHandler.registerInteractiveScene(this.scene);
  116. this.onRemove = (e) => { this.scene.remove(e.measurement);};
  117. this.onAdd = e => {this.scene.add(e.measurement);};
  118. for(let measurement of viewer.scene.measurements){
  119. this.onAdd({measurement: measurement});
  120. }
  121. viewer.addEventListener("update", this.update.bind(this));
  122. viewer.addEventListener("render.pass.perspective_overlay", this.render.bind(this));
  123. viewer.addEventListener("scene_changed", this.onSceneChange.bind(this));
  124. viewer.scene.addEventListener('measurement_added', this.onAdd);
  125. viewer.scene.addEventListener('measurement_removed', this.onRemove);
  126. }
  127. onSceneChange(e){
  128. if(e.oldScene){
  129. e.oldScene.removeEventListener('measurement_added', this.onAdd);
  130. e.oldScene.removeEventListener('measurement_removed', this.onRemove);
  131. }
  132. e.scene.addEventListener('measurement_added', this.onAdd);
  133. e.scene.addEventListener('measurement_removed', this.onRemove);
  134. }
  135. startInsertion (args = {}) {
  136. let domElement = this.viewer.renderer.domElement;
  137. let measure = new Measure();
  138. this.dispatchEvent({
  139. type: 'start_inserting_measurement',
  140. measure: measure
  141. });
  142. const pick = (defaul, alternative) => {
  143. if(defaul != null){
  144. return defaul;
  145. }else{
  146. return alternative;
  147. }
  148. };
  149. measure.showDistances = (args.showDistances === null) ? true : args.showDistances;
  150. measure.showArea = pick(args.showArea, false);
  151. measure.showAngles = pick(args.showAngles, false);
  152. measure.showCoordinates = pick(args.showCoordinates, false);
  153. measure.showHeight = pick(args.showHeight, false);
  154. measure.showCircle = pick(args.showCircle, false);
  155. measure.showAzimuth = pick(args.showAzimuth, false);
  156. measure.showEdges = pick(args.showEdges, true);
  157. measure.closed = pick(args.closed, false);
  158. measure.maxMarkers = pick(args.maxMarkers, Infinity);
  159. measure.name = args.name || 'Measurement';
  160. this.scene.add(measure);
  161. let cancel = {
  162. removeLastMarker: measure.maxMarkers > 3,
  163. callback: null
  164. };
  165. let insertionCallback = (e) => {
  166. if (e.button === THREE.MOUSE.LEFT) {
  167. measure.addMarker(measure.points[measure.points.length - 1].position.clone());
  168. if (measure.points.length >= measure.maxMarkers) {
  169. cancel.callback();
  170. }
  171. this.viewer.inputHandler.startDragging(
  172. measure.spheres[measure.spheres.length - 1]);
  173. } else if (e.button === THREE.MOUSE.RIGHT) {
  174. cancel.callback();
  175. }
  176. };
  177. cancel.callback = e => {
  178. if (cancel.removeLastMarker) {
  179. measure.removeMarker(measure.points.length - 1);
  180. }
  181. domElement.removeEventListener('mouseup', insertionCallback, false);
  182. this.viewer.removeEventListener('cancel_insertions', cancel.callback);
  183. };
  184. if (measure.maxMarkers > 1) {
  185. this.viewer.addEventListener('cancel_insertions', cancel.callback);
  186. domElement.addEventListener('mouseup', insertionCallback, false);
  187. }
  188. measure.addMarker(new THREE.Vector3(0, 0, 0));
  189. this.viewer.inputHandler.startDragging(
  190. measure.spheres[measure.spheres.length - 1]);
  191. this.viewer.scene.addMeasurement(measure);
  192. return measure;
  193. }
  194. update(){
  195. let camera = this.viewer.scene.getActiveCamera();
  196. let domElement = this.renderer.domElement;
  197. let measurements = this.viewer.scene.measurements;
  198. const renderAreaSize = this.renderer.getSize(new THREE.Vector2());
  199. let clientWidth = renderAreaSize.width;
  200. let clientHeight = renderAreaSize.height;
  201. this.light.position.copy(camera.position);
  202. // make size independant of distance
  203. for (let measure of measurements) {
  204. measure.lengthUnit = this.viewer.lengthUnit;
  205. measure.lengthUnitDisplay = this.viewer.lengthUnitDisplay;
  206. measure.update();
  207. updateAzimuth(this.viewer, measure);
  208. // spheres
  209. for(let sphere of measure.spheres){
  210. let distance = camera.position.distanceTo(sphere.getWorldPosition(new THREE.Vector3()));
  211. let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
  212. let scale = (15 / pr);
  213. sphere.scale.set(scale, scale, scale);
  214. }
  215. // labels
  216. let labels = measure.edgeLabels.concat(measure.angleLabels);
  217. for(let label of labels){
  218. let distance = camera.position.distanceTo(label.getWorldPosition(new THREE.Vector3()));
  219. let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
  220. let scale = (70 / pr);
  221. if(Potree.debug.scale){
  222. scale = (Potree.debug.scale / pr);
  223. }
  224. label.scale.set(scale, scale, scale);
  225. }
  226. // coordinate labels
  227. for (let j = 0; j < measure.coordinateLabels.length; j++) {
  228. let label = measure.coordinateLabels[j];
  229. let sphere = measure.spheres[j];
  230. let distance = camera.position.distanceTo(sphere.getWorldPosition(new THREE.Vector3()));
  231. let screenPos = sphere.getWorldPosition(new THREE.Vector3()).clone().project(camera);
  232. screenPos.x = Math.round((screenPos.x + 1) * clientWidth / 2);
  233. screenPos.y = Math.round((-screenPos.y + 1) * clientHeight / 2);
  234. screenPos.z = 0;
  235. screenPos.y -= 30;
  236. let labelPos = new THREE.Vector3(
  237. (screenPos.x / clientWidth) * 2 - 1,
  238. -(screenPos.y / clientHeight) * 2 + 1,
  239. 0.5 );
  240. labelPos.unproject(camera);
  241. if(this.viewer.scene.cameraMode == CameraMode.PERSPECTIVE) {
  242. let direction = labelPos.sub(camera.position).normalize();
  243. labelPos = new THREE.Vector3().addVectors(
  244. camera.position, direction.multiplyScalar(distance));
  245. }
  246. label.position.copy(labelPos);
  247. let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
  248. let scale = (70 / pr);
  249. label.scale.set(scale, scale, scale);
  250. }
  251. // height label
  252. if (measure.showHeight) {
  253. let label = measure.heightLabel;
  254. {
  255. let distance = label.position.distanceTo(camera.position);
  256. let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
  257. let scale = (70 / pr);
  258. label.scale.set(scale, scale, scale);
  259. }
  260. { // height edge
  261. let edge = measure.heightEdge;
  262. let sorted = measure.points.slice().sort((a, b) => a.position.z - b.position.z);
  263. let lowPoint = sorted[0].position.clone();
  264. let highPoint = sorted[sorted.length - 1].position.clone();
  265. let min = lowPoint.z;
  266. let max = highPoint.z;
  267. let start = new THREE.Vector3(highPoint.x, highPoint.y, min);
  268. let end = new THREE.Vector3(highPoint.x, highPoint.y, max);
  269. let lowScreen = lowPoint.clone().project(camera);
  270. let startScreen = start.clone().project(camera);
  271. let endScreen = end.clone().project(camera);
  272. let toPixelCoordinates = v => {
  273. let r = v.clone().addScalar(1).divideScalar(2);
  274. r.x = r.x * clientWidth;
  275. r.y = r.y * clientHeight;
  276. r.z = 0;
  277. return r;
  278. };
  279. let lowEL = toPixelCoordinates(lowScreen);
  280. let startEL = toPixelCoordinates(startScreen);
  281. let endEL = toPixelCoordinates(endScreen);
  282. let lToS = lowEL.distanceTo(startEL);
  283. let sToE = startEL.distanceTo(endEL);
  284. edge.geometry.lineDistances = [0, lToS, lToS, lToS + sToE];
  285. edge.geometry.lineDistancesNeedUpdate = true;
  286. edge.material.dashSize = 10;
  287. edge.material.gapSize = 10;
  288. }
  289. }
  290. { // area label
  291. let label = measure.areaLabel;
  292. let distance = label.position.distanceTo(camera.position);
  293. let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
  294. let scale = (70 / pr);
  295. label.scale.set(scale, scale, scale);
  296. }
  297. { // radius label
  298. let label = measure.circleRadiusLabel;
  299. let distance = label.position.distanceTo(camera.position);
  300. let pr = Utils.projectedRadius(1, camera, distance, clientWidth, clientHeight);
  301. let scale = (70 / pr);
  302. label.scale.set(scale, scale, scale);
  303. }
  304. { // edges
  305. const materials = [
  306. measure.circleRadiusLine.material,
  307. ...measure.edges.map( (e) => e.material),
  308. measure.heightEdge.material,
  309. measure.circleLine.material,
  310. ];
  311. for(const material of materials){
  312. material.resolution.set(clientWidth, clientHeight);
  313. }
  314. }
  315. if(!this.showLabels){
  316. const labels = [
  317. ...measure.sphereLabels,
  318. ...measure.edgeLabels,
  319. ...measure.angleLabels,
  320. ...measure.coordinateLabels,
  321. measure.heightLabel,
  322. measure.areaLabel,
  323. measure.circleRadiusLabel,
  324. ];
  325. for(const label of labels){
  326. label.visible = false;
  327. }
  328. }
  329. }
  330. }
  331. render(){
  332. this.viewer.renderer.render(this.scene, this.viewer.scene.getActiveCamera());
  333. }
  334. };