-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathObjectNDManager.js
553 lines (446 loc) · 19.2 KB
/
ObjectNDManager.js
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
const OBJECTNDMANAGER_NO_FORMAT = 0
const OBJECTNDMANAGER_POL_FORMAT = 1
const OBJECTNDMANAGER_NDP_FORMAT = 2
const OBJECTND_RENDERINGMODE_LINES = 0
const OBJECTND_RENDERINGMODE_FACES = 1
function setTrueIfShared(arrayToSet, v1, v2){
v1 = new Set(v1)
v2 = new Set(v2)
let intersect = new Set([...v1].filter(i => v2.has(i)));
for (i of intersect){
arrayToSet.value = true;
}
return;
// Old code to return to if the approach above fails to work
let count1 = 0;
let count2 = 0;
while (count1 < rows(v1) && count2 < rows(v2)){
if(get(v1, count1, 0) > get(v2, count2, 0)){
count2++;
} else if(get(v1, count1, 0) < get(v2, count2, 0)){
count1++;
} else {
arrayToSet.value[get(v1, count1, 0)] = true;
count1++;
count2++;
}
}
}
function getPolitopesKDThatComposePolytopeND(polytopeLists, N, K, index){
assert(N > K);
if(N == K + 1){
return polytopeLists[N][index];
} else {
let searchedPolytope = polytopeLists[N][index];
let resultSet = Set();
for(let i = 0; i < searchedPolytope.length; i++){
let thisPolytopesComponents = getPolitopesKDThatComposePolytopeND(polytopeLists, N - 1, K, searchedPolytope[i]);
thisPolytopesComponents.forEach(item => resultSet.add(item))
}
return resultSet;
}
}
function readIntLine(lines, lineNumber){
return lines[lineNumber].split(' ').filter(x=>x).map(x => parseInt(x));
}
function readFloatLine(lines, lineNumber){
return lines[lineNumber].split(' ').filter(x=>x).map(x => parseFloat(x));
}
class ObjectNDManager{
readFromNDPFormatStream(fileContent){
fileContent = fileContent.trim().split("\n");
let currentLineIndex = 0;
let [numDimensions, dimensionOfHighestStructure] = fileContent[currentLineIndex++].split(' ').filter(x=>x).map(x => parseInt(x))
this.numberOfDimensions = numDimensions;
//Initializing polytope vector
this.composingPolytopes = []
for (let i = 0; i < dimensionOfHighestStructure + 1; i++) {
this.composingPolytopes[i] = []
}
for(let currentDimension = 0; currentDimension < dimensionOfHighestStructure + 1; currentDimension++){
fileContent[currentLineIndex++]
let numberOfPolytopes = parseInt(fileContent[currentLineIndex++])
for(let i = 0; i < numberOfPolytopes; i++){
let currentLine = fileContent[currentLineIndex++];
currentLine = currentLine.split(' ').filter(x=>x).map(x => parseFloat(x))
// Add is as vector if it is a point, add it as array if it isn't
if(currentDimension == 0){
this.composingPolytopes[currentDimension].push(math.matrix(currentLine))
} else {
this.composingPolytopes[currentDimension].push(currentLine.sort(function (a, b) { return a - b; }))
}
}
print("Dim " + currentDimension + " -> " + this.composingPolytopes[currentDimension].length)
}
}
readFromPOLFormatStream(fileContent){
fileContent = fileContent.trim().split("\n");
let currentLineIndex = 0;
let [startingDimension, finalDimension] = readIntLine(fileContent, currentLineIndex++)
console.log("startingDimension:", startingDimension);
console.log("finalDimension:", finalDimension);
this.numberOfDimensions = startingDimension;
let sizeGrid = readIntLine(fileContent, currentLineIndex++)
console.log("sizeGrid:", sizeGrid);
currentLineIndex++; //skip an empty line
let composingPolytopeToIndexMap = [];
//Initializing the vectors
for (let i = 0; i < startingDimension - finalDimension + 1; i++) {
this.composingPolytopes.push([]);
composingPolytopeToIndexMap.push({});
}
let currentPolytope = 0;
let labelLine = readIntLine(fileContent, currentLineIndex++)
var currentPolytopeLabel = labelLine.shift();
while (currentPolytopeLabel !== -1) {
console.log("Label ", currentPolytopeLabel);
let currentCube = labelLine
console.log("Current Cube: ", currentCube);
let numConvexComponents = parseInt(fileContent[currentLineIndex++]);
console.log("numConvexComponents ", numConvexComponents);
for (let currentConvexComponent = 0; currentConvexComponent < numConvexComponents; currentConvexComponent++) {
let vertexList = this.composingPolytopes[0];
let vertexMap = composingPolytopeToIndexMap[0];
// Getting the vertices that compose the Polytope
let numVertexes = parseInt(fileContent[currentLineIndex++]);
console.log("numVertexes ", numVertexes);
let mappingOfCurrentPolytopesToGlobal = [];
for (let currentVertexIndex = 0; currentVertexIndex < numVertexes; currentVertexIndex++) {
let componentLine = readFloatLine(fileContent, currentLineIndex++)
let currentVertexLine = [];
for (let i = 0; i < startingDimension; i++) {
currentVertexLine.unshift(componentLine.pop());
}
// We don't actually use the component labels for now, so they are dropped
// But if we ever need to use them, at this point they are stored on what is left of componentLine
//Reads the vertex
let currentVertex = math.matrix(currentVertexLine);
let vertexName = currentVertex + ''
if (!(vertexName in vertexMap)) {
//Adds it to the list of vertexes, if it isn't there already
vertexList.push(currentVertex);
vertexMap[vertexName] = vertexList.length - 1;
//Maps it to the newest position
mappingOfCurrentPolytopesToGlobal.push(vertexList.length - 1);
}
else {
//Maps it to found position
mappingOfCurrentPolytopesToGlobal.push(vertexMap[vertexName]);
}
}
//Reading edges and higher dimensional structures
for (let currentDimensionOfComposingPolytopes = 1; currentDimensionOfComposingPolytopes < startingDimension - finalDimension + 1; currentDimensionOfComposingPolytopes++) {
let cdocp = currentDimensionOfComposingPolytopes;
let mappingOfPreviousPolytopesToGlobal = mappingOfCurrentPolytopesToGlobal;
mappingOfCurrentPolytopesToGlobal = [];
let numberOfPolytopesOfCurrentDimension = parseInt(fileContent[currentLineIndex++]);;
for (let i = 0; i < numberOfPolytopesOfCurrentDimension; i++) {
//Read polytope
let facesOfPolytope = readIntLine(fileContent, currentLineIndex++)
//Transform it to its global indexes
let polytopeAfterMapping = [];
for (let j = 0; j < facesOfPolytope.length; j++) {
polytopeAfterMapping.push(mappingOfPreviousPolytopesToGlobal[facesOfPolytope[j] - 1]);
}
//Order it
polytopeAfterMapping.sort(function(lhs, rhs) { return lhs < rhs; })
let polytopeName = polytopeAfterMapping+''
if (!(polytopeName in composingPolytopeToIndexMap[cdocp])) {
this.composingPolytopes[cdocp].push(polytopeAfterMapping);
composingPolytopeToIndexMap[cdocp][polytopeName] = this.composingPolytopes[cdocp].length - 1;
//Maps it to the newest position
mappingOfCurrentPolytopesToGlobal.push(this.composingPolytopes[cdocp].length - 1);
} else {
//Maps it to found position
mappingOfCurrentPolytopesToGlobal.push(composingPolytopeToIndexMap[cdocp][polytopeName]);
}
}
}
// Starts next Polytope
currentPolytope++;
}
currentLineIndex++;
labelLine = fileContent[currentLineIndex++].split(' ').filter(x=>x).map(x => parseInt(x));
currentPolytopeLabel = labelLine.shift();
}
}
constructor(formatType, fileContent, parent){
this.parent = parent;
this.renderingMode = OBJECTND_RENDERINGMODE_LINES;
this.hasCalculatedCulling = false;
this.useCulling = false;
this.currentComposingPolytopes = null;
this.composingPolytopes = [];
if(formatType == OBJECTNDMANAGER_NDP_FORMAT){
console.log("READING NDP");
this.readFromNDPFormatStream(fileContent);
} else if(formatType == OBJECTNDMANAGER_POL_FORMAT){
console.log("READING POL");
this.readFromPOLFormatStream(fileContent);
} else {
this.numberOfDimensions = 0;
}
// We shift every point to the same random minimal direction
if(this.composingPolytopes.length > 0 && rows(this.composingPolytopes[0][0]) > 0){
let aux = [...Array(rows(this.composingPolytopes[0][0])).keys()].map(x => math.sin(x)*0.0000001)
aux = math.matrix(aux)
for(let i = 0; i < this.composingPolytopes[0].length; i++){
this.composingPolytopes[0][i] = math.add(this.composingPolytopes[0][i], aux)
}
}
}
printStructure(){
let s = "";
for (let i = 0; i < this.currentComposingPolytopes.length; i++){
s += "LAYER " + i + '\n'
for(let j = 0; j < this.currentComposingPolytopes[i].length; j++){
s += j + ": " + this.currentComposingPolytopes[i][j] + "\n"
}
}
console.log(s)
}
checkSharedEdgesAndFaces(currentComposingPolytopes, edgeIsShared, faceIsShared){
if(currentComposingPolytopes.length > 3){
let faceList = currentComposingPolytopes[2];
let spaceList = currentComposingPolytopes[3];
// Check to see which edges should be rendered
// Compare each edge of each space against every edge of every other space
for(let i = 0; i < spaceList.length; i++){
let facesOfSpaceA = spaceList[i];
let otherSpacesFaces = new Set();
for (let j = i + 1; j < spaceList.length; j++){
let facesOfSpaceB = spaceList[j];
for (let k = 0; k < facesOfSpaceB.length; k++){
otherSpacesFaces.add(facesOfSpaceB[k])
}
}
for(let j = 0; j < facesOfSpaceA.length; j++){
let edgesA = faceList[facesOfSpaceA]
for (let edgeB of otherSpacesFaces){
setTrueIfShared(edgeIsShared, edgesA, faceList[edgeB])
}
}
}
// Check to see which faces should be rendered
for(let i = 0; i < spaceList.length; i++){
let facesOfSpaceA = spaceList[i];
for(let j = i +1; j < spaceList.length; j++){
setTrueIfShared(faceIsShared, facesOfSpaceA, spaceList[j])
}
}
}
}
generateEdgeAndFaceLists(
currentComposingPolytopes,
edgeListWithoutCulling,
edgeListWithCulling,
faceListWithoutCulling,
faceListWithCulling){
if(!currentComposingPolytopes || currentComposingPolytopes.length < 2){ return; }
let edgeList = currentComposingPolytopes[1];
let edgeIsShared = {value: new Array(edgeList.length).map(_=>false)}
let sizeFaces = 0;
if(currentComposingPolytopes.length > 2){
sizeFaces = currentComposingPolytopes[2].length
}
let faceIsShared = {value: new Array(sizeFaces.length).map(_=>false)}
this.hasCalculatedCulling = false;
if(this.useCulling){
this.checkSharedEdgesAndFaces(currentComposingPolytopes, edgeIsShared, faceIsShared)
this.hasCalculatedCulling = true;
}
// Get size of each list
let sizeEdgeListWithCulling = 0;
let sizeFaceListWithCulling = 0;
let sizeFaceListWithoutCulling = 0;
for (let i = 0; i < currentComposingPolytopes[1].length; i++) {
sizeEdgeListWithCulling += !edgeIsShared.value[i] ? 1 : 0;
}
//Create lists. Here we create and mantain in memory the list of edges with culling and without,
//Because it is faster than recalculating it every time we change rendering
//First we do Edges
let edgeCullCount = 0;
edgeListWithoutCulling.value = new Array(edgeList.length)
edgeListWithCulling.value = new Array(sizeEdgeListWithCulling.length)
for (let i = 0; i < edgeList.length; i++) {
edgeListWithoutCulling.value[i] = edgeList[i]
if (!edgeIsShared.value[i]) {
edgeListWithCulling[edgeCullCount] = edgeList[i]
edgeCullCount++;
}
}
// Now we do faces
if(currentComposingPolytopes.length > 2){
let faceList = currentComposingPolytopes[2];
for (let i = 0; i < faceList.length; i++){
sizeFaceListWithoutCulling += faceList[i].length - 2;
sizeFaceListWithCulling += (!faceIsShared.value[i] ? 1 : 0) * (faceList[i].length - 2);
}
faceListWithoutCulling.value = [];
faceListWithCulling.value = [];
for(let i = 0; i < faceList.length; i++){
let currentFace = faceList[i]
let numberOfTrianglesInThisFace = currentFace.length - 2;
let trianglesList = new Array(numberOfTrianglesInThisFace);
let currentLocationOnTrianglesList = 0;
let baseVertex = edgeList[currentFace[0]][0];
for(let j = 1; j < currentFace.length && currentLocationOnTrianglesList != numberOfTrianglesInThisFace; j++){
let currentEdge = edgeList[currentFace[j]];
if(!(currentEdge[0] == baseVertex || currentEdge[1] == baseVertex)){
let auxVec = [baseVertex, currentEdge[0], currentEdge[1]];
trianglesList[currentLocationOnTrianglesList] = auxVec;
currentLocationOnTrianglesList++;
}
}
faceListWithoutCulling.value.push(...trianglesList);
if(!faceIsShared.value[i]){
faceListWithCulling.value.push(...trianglesList);
}
}
}
}
setUsingCulling(value){
this.useCulling = value;
if(!this.hasCalculatedCulling && this.useCulling && this.currentObjects.length){
let edgeListWithoutCulling = {value:[]};
let edgeListWithCulling = {value:[]};
let faceListWithoutCulling = {value:[]};
let faceListWithCulling = {value:[]};
//after cuts
this.generateEdgeAndFaceLists(this.currentComposingPolytopes, edgeListWithoutCulling, edgeListWithCulling, faceListWithoutCulling, faceListWithCulling);
//Create object and add it to render list
this.currentObjects[0].edgesWithCullingList = edgeListWithCulling;
this.currentObjects[0].facesWithCullingList = faceListWithCulling;
this.hasCalculatedCulling = true;
}
for(let obj of this.currentObjects){
obj.setUseCulling(value);
}
}
updateObjects(cutsLocation, camerasND){
this.currentObjects = [];
if(this.composingPolytopes.length < 2){ return; }
let maxDimension = this.composingPolytopes.length - 1;
let resultingDimension = camerasND.length + 3;
let currentComposingPolytopes = [];
for(let i = 0; i < this.composingPolytopes.length; i++){
let aux = []
for (let j = 0; j < this.composingPolytopes[i].length; j++){
aux.push(this.composingPolytopes[i][j])
}
currentComposingPolytopes.push(aux)
}
//calculate cuts
for(let currentCutIndex = 0; currentCutIndex < cutsLocation.length; currentCutIndex++){
let vertexList = currentComposingPolytopes[0]
let edgeList = currentComposingPolytopes[1]
if(vertexList.length == 0){ break; }
let newPoints = []
let edgeToPointsMapping = [];
let currentCutLocation = cutsLocation[currentCutIndex];
// We calculate the cut over every edge
for(let i = 0; i < edgeList.length; i++){
edgeToPointsMapping[i] = -1;
let pointAIndex = edgeList[i][0];
let pointBIndex = edgeList[i][1];
let startVertex = vertexList[pointAIndex];
let endVertex = vertexList[pointBIndex];
let lastCoordStart = getLast(startVertex);
let lastCoordEnd = getLast(endVertex);
let maxLastCoord, minLastCoord;
if (lastCoordStart > lastCoordEnd) {
maxLastCoord = startVertex;
minLastCoord = endVertex;
} else {
maxLastCoord = endVertex;
minLastCoord = startVertex;
}
let crossesCut = getLast(minLastCoord) < currentCutLocation && currentCutLocation < getLast(maxLastCoord);
if(crossesCut){
let newPoint = math.subtract(startVertex, endVertex);
newPoint = math.divide(newPoint, getLast(newPoint));
newPoint = math.multiply(newPoint, currentCutLocation - getLast(minLastCoord));
newPoint = math.add(newPoint, minLastCoord);
newPoint.resize([rows(newPoint) - 1, cols(newPoint)]);
edgeToPointsMapping[i] = newPoints.length;
newPoints.push(newPoint);
}
}
//Now we have every single point that our collection of polytopes contains
//Every edge has generated either 0 or 1 point
//We create the structure that will hold our new polytopes
let newComposingPolytopes = []
//The first line shall be our newly found points
newComposingPolytopes.push(newPoints);
//The rest are initialized empty, and will be built in the next step
for (let i = 1; i < currentComposingPolytopes.length - 1; i++) {
newComposingPolytopes.push([]);
}
let newDimension = vertexList[0].length - 1;
//We start building the structure from the bottom up
let previousPolytopeLayerMapping = edgeToPointsMapping;
let currentPolytopeLayerMapping;
edgeToPointsMapping = null;
for(let currentPolitopeDimension = 1; currentPolitopeDimension < newComposingPolytopes.length; currentPolitopeDimension++) {
let previousPolytopeList = currentComposingPolytopes[currentPolitopeDimension];
let currentPolytopeList = currentComposingPolytopes[currentPolitopeDimension + 1];
currentPolytopeLayerMapping = new Array(currentPolytopeList.length);
for (let currentPolytopeIndex = 0; currentPolytopeIndex < currentPolytopeList.length; currentPolytopeIndex++) {
let listOfLowerDimensionPolitopesFound = [];
let currentPolytope = currentPolytopeList[currentPolytopeIndex];
currentPolytopeLayerMapping[currentPolytopeIndex] = -1;
for (let i = 0; i < currentPolytope.length; i++) {
let aux = previousPolytopeLayerMapping[currentPolytope[i]];
if (previousPolytopeLayerMapping[currentPolytope[i]] != -1) {
listOfLowerDimensionPolitopesFound.push(previousPolytopeLayerMapping[currentPolytope[i]]);
}
}
if (listOfLowerDimensionPolitopesFound.length != 0 && listOfLowerDimensionPolitopesFound.length > currentPolitopeDimension) {
if (currentPolitopeDimension == 1 && listOfLowerDimensionPolitopesFound.length > 2) {
while (listOfLowerDimensionPolitopesFound.length > 2) {
listOfLowerDimensionPolitopesFound.shift();
}
}
let aux = new Array(listOfLowerDimensionPolitopesFound.length);
for (let i = 0; i < listOfLowerDimensionPolitopesFound.length; i++) {
aux[i] = listOfLowerDimensionPolitopesFound[i];
}
currentPolytopeLayerMapping[currentPolytopeIndex] = newComposingPolytopes[currentPolitopeDimension].length;
newComposingPolytopes[currentPolitopeDimension].push(aux);
}
//delete listOfLowerDimensionPolitopesFound;
}
//delete previousPolytopeLayerMapping;
previousPolytopeLayerMapping = currentPolytopeLayerMapping;
}
currentComposingPolytopes = newComposingPolytopes;
}
let edgeListWithoutCulling = {value:[]}
let edgeListWithCulling = {value:[]}
let faceListWithoutCulling = {value:[]}
let faceListWithCulling = {value:[]}
//after cuts
this.hasCalculatedCulling = false;
this.generateEdgeAndFaceLists(currentComposingPolytopes, edgeListWithoutCulling, edgeListWithCulling, faceListWithoutCulling, faceListWithCulling);
//Create Vertex Matrix
let vertexMatrix = currentComposingPolytopes[0].map(x=>math.matrix(math.flatten(x)).clone());
vertexMatrix = vertexMatrix.map(x=>x.clone())
this.currentComposingPolytopes = currentComposingPolytopes;
let newObj = new ObjectND(this.numberOfDimensions, vertexMatrix, edgeListWithoutCulling, edgeListWithCulling, faceListWithoutCulling, faceListWithCulling, camerasND, this.parent, this.renderingMode);
this.currentObjects.push(newObj)
}
render(){
for(let o of this.currentObjects){
o.render()
}
}
setRenderingMode(value){
this.renderingMode = value;
for(let o of this.currentObjects){
o.setRenderingMode(value);
}
}
delete(){
// TODO: Delete objects
}
}