potree
Version:
WebGL point cloud viewer - WORK IN PROGRESS
535 lines (423 loc) • 14.8 kB
JavaScript
Potree.Measure = class Measure extends THREE.Object3D {
constructor () {
super();
this.constructor.counter = (this.constructor.counter === undefined) ? 0 : this.constructor.counter + 1;
this.name = 'Measure_' + this.constructor.counter;
this.points = [];
this._showDistances = true;
this._showCoordinates = false;
this._showArea = false;
this._closed = true;
this._showAngles = false;
this._showHeight = false;
this.maxMarkers = Number.MAX_SAFE_INTEGER;
this.sphereGeometry = new THREE.SphereGeometry(0.4, 10, 10);
this.color = new THREE.Color(0xff0000);
this.lengthUnit = {code: 'm'};
this.spheres = [];
this.edges = [];
this.sphereLabels = [];
this.edgeLabels = [];
this.angleLabels = [];
this.coordinateLabels = [];
// this.heightEdge;
// this.heightLabel;
{ // height stuff
{ // height line
let lineGeometry = new THREE.Geometry();
lineGeometry.vertices.push(
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3(),
new THREE.Vector3());
lineGeometry.colors.push(this.color, this.color, this.color);
let lineMaterial = new THREE.LineDashedMaterial(
{ color: 0xff0000, dashSize: 5, gapSize: 2 });
lineMaterial.depthTest = false;
this.heightEdge = new THREE.Line(lineGeometry, lineMaterial);
this.heightEdge.visible = false;
this.add(this.heightEdge);
}
{ // height label
this.heightLabel = new Potree.TextSprite('');
this.heightLabel.setBorderColor({r: 0, g: 0, b: 0, a: 0.8});
this.heightLabel.setBackgroundColor({r: 0, g: 0, b: 0, a: 0.3});
this.heightLabel.setTextColor({r: 180, g: 220, b: 180, a: 1.0});
this.heightLabel.material.depthTest = false;
this.heightLabel.material.opacity = 1;
this.heightLabel.visible = false; ;
this.add(this.heightLabel);
}
}
this.areaLabel = new Potree.TextSprite('');
this.areaLabel.setBorderColor({r: 0, g: 0, b: 0, a: 0.8});
this.areaLabel.setBackgroundColor({r: 0, g: 0, b: 0, a: 0.3});
this.areaLabel.setTextColor({r: 180, g: 220, b: 180, a: 1.0});
this.areaLabel.material.depthTest = false;
this.areaLabel.material.opacity = 1;
this.areaLabel.visible = false; ;
this.add(this.areaLabel);
}
createSphereMaterial () {
let sphereMaterial = new THREE.MeshLambertMaterial({
shading: THREE.SmoothShading,
color: this.color,
depthTest: false,
depthWrite: false}
);
return sphereMaterial;
};
addMarker (point) {
if (point instanceof THREE.Vector3) {
point = {position: point};
}
this.points.push(point);
// sphere
let sphere = new THREE.Mesh(this.sphereGeometry, this.createSphereMaterial());
this.add(sphere);
this.spheres.push(sphere);
{ // edges
let lineGeometry = new THREE.Geometry();
lineGeometry.vertices.push(new THREE.Vector3(), new THREE.Vector3());
lineGeometry.colors.push(this.color, this.color, this.color);
let lineMaterial = new THREE.LineBasicMaterial({
linewidth: 1
});
lineMaterial.depthTest = false;
let edge = new THREE.Line(lineGeometry, lineMaterial);
edge.visible = true;
this.add(edge);
this.edges.push(edge);
}
{ // edge labels
let edgeLabel = new Potree.TextSprite();
edgeLabel.setBorderColor({r: 0, g: 0, b: 0, a: 0.8});
edgeLabel.setBackgroundColor({r: 0, g: 0, b: 0, a: 0.3});
edgeLabel.material.depthTest = false;
edgeLabel.visible = false;
this.edgeLabels.push(edgeLabel);
this.add(edgeLabel);
}
{ // angle labels
let angleLabel = new Potree.TextSprite();
angleLabel.setBorderColor({r: 0, g: 0, b: 0, a: 0.8});
angleLabel.setBackgroundColor({r: 0, g: 0, b: 0, a: 0.3});
angleLabel.material.depthTest = false;
angleLabel.material.opacity = 1;
angleLabel.visible = false;
this.angleLabels.push(angleLabel);
this.add(angleLabel);
}
{ // coordinate labels
let coordinateLabel = new Potree.TextSprite();
coordinateLabel.setBorderColor({r: 0, g: 0, b: 0, a: 0.8});
coordinateLabel.setBackgroundColor({r: 0, g: 0, b: 0, a: 0.3});
coordinateLabel.material.depthTest = false;
coordinateLabel.material.opacity = 1;
coordinateLabel.visible = false;
this.coordinateLabels.push(coordinateLabel);
this.add(coordinateLabel);
}
{ // Event Listeners
let drag = (e) => {
let I = Potree.utils.getMousePointCloudIntersection(
e.drag.end,
e.viewer.scene.camera,
e.viewer.renderer,
e.viewer.scene.pointclouds);
if (I) {
let i = this.spheres.indexOf(e.drag.object);
if (i !== -1) {
let point = this.points[i];
for (let key of Object.keys(I.point).filter(e => e !== 'position')) {
point[key] = I.point[key];
}
this.setPosition(i, I.location);
}
}
};
let drop = e => {
let i = this.spheres.indexOf(e.drag.object);
if (i !== -1) {
this.dispatchEvent({
'type': 'marker_dropped',
'measurement': this,
'index': i
});
}
};
let mouseover = (e) => e.object.material.emissive.setHex(0x888888);
let mouseleave = (e) => e.object.material.emissive.setHex(0x000000);
sphere.addEventListener('drag', drag);
sphere.addEventListener('drop', drop);
sphere.addEventListener('mouseover', mouseover);
sphere.addEventListener('mouseleave', mouseleave);
}
let event = {
type: 'marker_added',
measurement: this,
sphere: sphere
};
this.dispatchEvent(event);
this.setMarker(this.points.length - 1, point);
};
removeMarker (index) {
this.points.splice(index, 1);
this.remove(this.spheres[index]);
let edgeIndex = (index === 0) ? 0 : (index - 1);
this.remove(this.edges[edgeIndex]);
this.edges.splice(edgeIndex, 1);
this.remove(this.edgeLabels[edgeIndex]);
this.edgeLabels.splice(edgeIndex, 1);
this.coordinateLabels.splice(index, 1);
this.spheres.splice(index, 1);
this.update();
this.dispatchEvent({type: 'marker_removed', measurement: this});
};
setMarker (index, point) {
this.points[index] = point;
let event = {
type: 'marker_moved',
measure: this,
index: index,
position: point.position.clone()
};
this.dispatchEvent(event);
this.update();
}
setPosition (index, position) {
let point = this.points[index];
point.position.copy(position);
let event = {
type: 'marker_moved',
measure: this,
index: index,
position: position.clone()
};
this.dispatchEvent(event);
this.update();
};
getArea () {
let area = 0;
let j = this.points.length - 1;
for (let i = 0; i < this.points.length; i++) {
let p1 = this.points[i].position;
let p2 = this.points[j].position;
area += (p2.x + p1.x) * (p1.y - p2.y);
j = i;
}
return Math.abs(area / 2);
};
getTotalDistance () {
if (this.points.length === 0) {
return 0;
}
let distance = 0;
for (let i = 1; i < this.points.length; i++) {
let prev = this.points[i - 1].position;
let curr = this.points[i].position;
let d = prev.distanceTo(curr);
distance += d;
}
if (this.closed && this.points.length > 1) {
let first = this.points[0].position;
let last = this.points[this.points.length - 1].position;
let d = last.distanceTo(first);
distance += d;
}
return distance;
}
getAngleBetweenLines (cornerPoint, point1, point2) {
let v1 = new THREE.Vector3().subVectors(point1.position, cornerPoint.position);
let v2 = new THREE.Vector3().subVectors(point2.position, cornerPoint.position);
return v1.angleTo(v2);
};
getAngle (index) {
if (this.points.length < 3 || index >= this.points.length) {
return 0;
}
let previous = (index === 0) ? this.points[this.points.length - 1] : this.points[index - 1];
let point = this.points[index];
let next = this.points[(index + 1) % (this.points.length)];
return this.getAngleBetweenLines(point, previous, next);
};
update () {
if (this.points.length === 0) {
return;
} else if (this.points.length === 1) {
let point = this.points[0];
let position = point.position;
this.spheres[0].position.copy(position);
{ // coordinate labels
let coordinateLabel = this.coordinateLabels[0];
let labelPos = position.clone().add(new THREE.Vector3(0, 1, 0));
coordinateLabel.position.copy(labelPos);
/* let msg = Potree.utils.addCommas(position.x.toFixed(2))
+ " / " + Potree.utils.addCommas(position.y.toFixed(2))
+ " / " + Potree.utils.addCommas(position.z.toFixed(2)); */
let msg = Potree.utils.addCommas(position.z.toFixed(2) + ' ' + this.lengthUnit.code);
coordinateLabel.setText(msg);
coordinateLabel.visible = this.showCoordinates;
}
return;
}
let lastIndex = this.points.length - 1;
let centroid = new THREE.Vector3();
for (let i = 0; i <= lastIndex; i++) {
let point = this.points[i];
centroid.add(point.position);
}
centroid.divideScalar(this.points.length);
for (let i = 0; i <= lastIndex; i++) {
let index = i;
let nextIndex = (i + 1 > lastIndex) ? 0 : i + 1;
let previousIndex = (i === 0) ? lastIndex : i - 1;
let point = this.points[index];
let nextPoint = this.points[nextIndex];
let previousPoint = this.points[previousIndex];
let sphere = this.spheres[index];
// spheres
sphere.position.copy(point.position);
sphere.material.color = this.color;
{ // edges
let edge = this.edges[index];
edge.material.color = this.color;
edge.position.copy(point.position);
edge.geometry.vertices[0].set(0, 0, 0);
edge.geometry.vertices[1].copy(nextPoint.position).sub(point.position);
edge.geometry.verticesNeedUpdate = true;
edge.geometry.computeBoundingSphere();
edge.visible = index < lastIndex || this.closed;
}
{ // edge labels
let edgeLabel = this.edgeLabels[i];
let center = new THREE.Vector3().add(point.position);
center.add(nextPoint.position);
center = center.multiplyScalar(0.5);
let distance = point.position.distanceTo(nextPoint.position);
edgeLabel.position.copy(center);
edgeLabel.setText(Potree.utils.addCommas(distance.toFixed(2)) + ' ' + this.lengthUnit.code);
edgeLabel.visible = this.showDistances && (index < lastIndex || this.closed) && this.points.length >= 2 && distance > 0;
}
{ // angle labels
let angleLabel = this.angleLabels[i];
let angle = this.getAngleBetweenLines(point, previousPoint, nextPoint);
let dir = nextPoint.position.clone().sub(previousPoint.position);
dir.multiplyScalar(0.5);
dir = previousPoint.position.clone().add(dir).sub(point.position).normalize();
let dist = Math.min(point.position.distanceTo(previousPoint.position), point.position.distanceTo(nextPoint.position));
dist = dist / 9;
let labelPos = point.position.clone().add(dir.multiplyScalar(dist));
angleLabel.position.copy(labelPos);
let msg = Potree.utils.addCommas((angle * (180.0 / Math.PI)).toFixed(1)) + '\u00B0';
angleLabel.setText(msg);
angleLabel.visible = this.showAngles && (index < lastIndex || this.closed) && this.points.length >= 3 && angle > 0;
}
{ // coordinate labels
let coordinateLabel = this.coordinateLabels[0];
let labelPos = point.position.clone().add(new THREE.Vector3(0, 1, 0));
coordinateLabel.position.copy(labelPos);
/* let msg = Potree.utils.addCommas(point.position.x.toFixed(2))
+ " / " + Potree.utils.addCommas(point.position.y.toFixed(2))
+ " / " + Potree.utils.addCommas(point.position.z.toFixed(2)); */
let msg = Potree.utils.addCommas(point.position.z.toFixed(2) + ' ' + this.lengthUnit.code);
coordinateLabel.setText(msg);
// coordinateLabel.visible = this.showCoordinates && (index < lastIndex || this.closed);
coordinateLabel.visible = this.showCoordinates;
}
}
{ // update height stuff
let heightEdge = this.heightEdge;
heightEdge.visible = this.showHeight;
this.heightLabel.visible = this.showHeight;
if (this.showHeight) {
let sorted = this.points.slice().sort((a, b) => a.position.z - b.position.z);
let lowPoint = sorted[0].position.clone();
let highPoint = sorted[sorted.length - 1].position.clone();
let min = lowPoint.z;
let max = highPoint.z;
let height = max - min;
let start = new THREE.Vector3(highPoint.x, highPoint.y, min);
let end = new THREE.Vector3(highPoint.x, highPoint.y, max);
heightEdge.position.copy(lowPoint);
heightEdge.geometry.vertices[0].set(0, 0, 0);
heightEdge.geometry.vertices[1].copy(start).sub(lowPoint);
heightEdge.geometry.vertices[2].copy(start).sub(lowPoint);
heightEdge.geometry.vertices[3].copy(end).sub(lowPoint);
heightEdge.geometry.verticesNeedUpdate = true;
// heightEdge.geometry.computeLineDistances();
// heightEdge.geometry.lineDistancesNeedUpdate = true;
heightEdge.geometry.computeBoundingSphere();
// heightEdge.material.dashSize = height / 40;
// heightEdge.material.gapSize = height / 40;
let heightLabelPosition = start.clone().add(end).multiplyScalar(0.5);
this.heightLabel.position.copy(heightLabelPosition);
let msg = Potree.utils.addCommas(height.toFixed(2)) + ' ' + this.lengthUnit.code;
this.heightLabel.setText(msg);
}
}
{ // update area label
this.areaLabel.position.copy(centroid);
this.areaLabel.visible = this.showArea && this.points.length >= 3;
let msg = Potree.utils.addCommas(this.getArea().toFixed(1)) + ' ' + this.lengthUnit.code + '\u00B2';
this.areaLabel.setText(msg);
}
};
raycast (raycaster, intersects) {
for (let i = 0; i < this.points.length; i++) {
let sphere = this.spheres[i];
sphere.raycast(raycaster, intersects);
}
// recalculate distances because they are not necessarely correct
// for scaled objects.
// see https://github.com/mrdoob/three.js/issues/5827
// TODO: remove this once the bug has been fixed
for (let i = 0; i < intersects.length; i++) {
let I = intersects[i];
I.distance = raycaster.ray.origin.distanceTo(I.point);
}
intersects.sort(function (a, b) { return a.distance - b.distance; });
};
get showCoordinates () {
return this._showCoordinates;
}
set showCoordinates (value) {
this._showCoordinates = value;
this.update();
}
get showAngles () {
return this._showAngles;
}
set showAngles (value) {
this._showAngles = value;
this.update();
}
get showHeight () {
return this._showHeight;
}
set showHeight (value) {
this._showHeight = value;
this.update();
}
get showArea () {
return this._showArea;
}
set showArea (value) {
this._showArea = value;
this.update();
}
get closed () {
return this._closed;
}
set closed (value) {
this._closed = value;
this.update();
}
get showDistances () {
return this._showDistances;
}
set showDistances (value) {
this._showDistances = value;
this.update();
}
};