-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
147 lines (132 loc) · 5.52 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Three.js shortest path example</title>
<script src="three.js"></script>
<script src="PLYLoader.js"></script>
<script src="OrbitControls.js"></script>
</head>
<body>
<script>
const renderer = new THREE.WebGLRenderer;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000 );
const light = new THREE.PointLight(0xFFFFFF);
const controls = new THREE.OrbitControls(camera, renderer.domElement);
const raycaster = new THREE.Raycaster();
const clickedFaces = [];
const meshLoader = new THREE.PLYLoader();
let mesh;
renderer.domElement.addEventListener('mousedown', selectClickedFace);
function selectClickedFace(evt) {
raycaster.setFromCamera(
new THREE.Vector2(
( event.clientX / window.innerWidth ) * 2 - 1, // Mouse position normalized to between -1 and +1
- ( event.clientY / window.innerHeight ) * 2 + 1),
camera
);
let intersects = raycaster.intersectObjects([mesh]);
// Color the clicked face blue
if (intersects.length > 0) {
clickedFaces.push(intersects[0].face);
intersects[0].face.color.set("blue");
mesh.geometry.colorsNeedUpdate = true;
};
// Draw a polygon segment between last two clicked faces
if (clickedFaces.length > 1){
bfs(...clickedFaces.slice(-2));
let familyLine = [];
traverseParents(clickedFaces.slice(-1)[0], familyLine);
familyLine.forEach(function(face){
face.color.set("red");
mesh.geometry.colorsNeedUpdate = true;
})
};
};
function setup(){
controls.addEventListener('change',render);
light.position.set(0.5,-10,4);
scene.add(light);
camera.position.set(1, -5, 5);
camera.lookAt(new THREE.Vector3(0,0,0));
document.body.appendChild( renderer.domElement );
renderer.setSize( window.innerWidth, window.innerHeight );
meshLoader.load('boulder.ply', function(bufferGeometry){
// use normal geom instead of buffergeometry b/c easier to color faces
let geometry = new THREE.Geometry().fromBufferGeometry( bufferGeometry );
const material = new THREE.MeshPhongMaterial({color: "grey",
vertexColors: THREE.FaceColors,
side: THREE.DoubleSide});
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
addAdjacencyDataToFaces(mesh.geometry);
render()
});
}
function render(){
requestAnimationFrame( render );
renderer.render(scene, camera);
};
function addAdjacencyDataToFaces(geom){
// Create an array of sets, one for each vertex, to store what faces each vertex is connected to
// To do that, wse every single ES6 feature in a single line. Woah nelly
let verticesWithAdjFaces = [...Array(geom.vertices.length).keys()].map(i=>new Set());
for (face of geom.faces){
face.connectedFaces = new Set();
}
for (face of geom.faces){
for (vertex of [face.a, face.b, face.c]){
verticesWithAdjFaces[vertex].add(face);
}
}
for (vertexFaces of verticesWithAdjFaces){
for (face of vertexFaces){
// Might be more efficient to deduplicate at the end?
face.connectedFaces = face.connectedFaces.union(vertexFaces);
// Don't keep link to self
face.connectedFaces.delete(face);
}
}
}
function bfs(source, destination){
// TODO this leaves parent on each mesh.geometry.face. Kinda bad since they are specific to src & dst
S = new Set([source]);
Q = [];
Q.push(source);
while(Q.length > 0){
renderer.render(scene, camera);
current = Q.shift();
if (current == destination){
return current;
}
current.connectedFaces.forEach(function(face) {
if (!S.has(face)) {
S.add(face);
face.color.setHSL(0.5, S.size / 200 % 1, 0.5);
mesh.geometry.colorsNeedUpdate = true;
face.parent = current;
Q.push(face);
}
});
}
}
function traverseParents(face, familyLine){
var familyLine = familyLine || [];
familyLine.push(face);
if (face.hasOwnProperty('parent')){
traverseParents(face.parent, familyLine)
}
}
// Convenience function from https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set
Set.prototype.union = function(setB) {
var union = new Set(this);
for (var elem of setB) {
union.add(elem);
}
return union;
}
setup();
</script>
</body>
</html>