0

I have straight-forward functions for identifying edges in THREE.Geometry().

var edges = []; 

for(var i = 0, l = geometry.faces.length; i < l; i++) {  findAdjacentFaces(i, edges, geometry); }


function findAdjacentFaces(face_, edges_, geometry_){

    var adjacentFaces = [];

    if(face_ >= 0 && face_ < geometry_.faces.length){

            for(var i = 0, l = geometry_.faces.length; i < l; i++){

                if(i != face_){

                    if(checkAdjacent(geometry_.faces[face_], geometry_.faces[i]) == true ){

                            adjacentFaces.push(i);

                    }

                }

                if(adjacentFaces.length > 2) { break; }

            }

    }

    if(adjacentFaces.length == 2){

        edges_.push(setEdge(face_, adjacentFaces[0], adjacentFaces[1], geometry_));

    }

}

function checkAdjacent(faceA_, faceB_){

    var values = [faceA_.a, faceA_.b, faceA_.c, faceB_.a, faceB_.b, faceB_.c].sort();

    var counter = 0;
    var duplicates = {}; 

    values.forEach(function(x) { duplicates[x] = (duplicates[x] || 0) + 1; if(duplicates[x] > 1) { counter++; } });

    if(counter == 2) { return true; } else { return false; }

}

function setEdge(faceA_, faceB_, faceC_, geometry_){

    var vertices = [], peak, tmpA, tmpB;
    var values =  [ geometry_.faces[faceA_].a, geometry_.faces[faceA_].b, geometry_.faces[faceA_].c, 
                    geometry_.faces[faceB_].a, geometry_.faces[faceB_].b, geometry_.faces[faceB_].c, 
                    geometry_.faces[faceC_].a, geometry_.faces[faceC_].b, geometry_.faces[faceC_].c ].sort();

    var sideA = [geometry_.faces[faceA_].a, geometry_.faces[faceA_].b, geometry_.faces[faceA_].c];
    var sideB = [geometry_.faces[faceB_].a, geometry_.faces[faceB_].b, geometry_.faces[faceB_].c];
    var sideC = [geometry_.faces[faceC_].a, geometry_.faces[faceC_].b, geometry_.faces[faceC_].c];

    var counter = 0;
    var duplicates = {}; 

    values.forEach(function(x) { duplicates[x] = (duplicates[x] || 0) + 1; });

    for (const key of Object.keys(duplicates)) { 

        if(duplicates[key] == 2) { vertices.push(key); } 
        else if(duplicates[key] == 3) { peak = key; } 

    }

    return  [ Number(vertices[0]), Number(vertices[1]) ];

}

It returns all vertex indices on the edge of geometry. Work fine, but VERY slow. What could be optimized for speeding up?

TylerH
  • 20,799
  • 66
  • 75
  • 101
VVK
  • 435
  • 2
  • 27

3 Answers3

2

The initial code from the question inspects all the geometry faces for each geometry face to determine the pair of adjacent faces and the corresponding edges. While this extracts all non-boundary edges, this leads to quadratic algorithmic complexity in the number of faces the geometry / mesh (and thus quadratic complexity in the number of edges or vertices). There may be some additional practical overhead (e.g. setEdge() function) that can be removed.

Here is a proposal to extract all the edges of a mesh (this uses additional workspace on the order of the number of faces in the mesh) with quasilinear time complexity (N log N due to the edge sorting) and possibly less overhead:

  • Extract all unoriented edges from the mesh faces (as a sorted pair of two vertex indices),
  • Discard all unique unoriented edges to keep only the unoriented edges that belong to at least two faces.

And here is a possible implementation with an example:

// A cube
var faces = [
  {a: 0, b: 1, c: 2}, {a: 2, b: 3, c: 0}, // Front
  {a: 1, b: 5, c: 6}, {a: 6, b: 2, c: 1}, // Right
  {a: 5, b: 4, c: 7}, {a: 7, b: 6, c: 5}, // Back
  {a: 4, b: 0, c: 3}, {a: 3, b: 7, c: 4}, // Left
  {a: 3, b: 2, c: 6}, {a: 6, b: 7, c: 3}, // Top
  {a: 4, b: 5, c: 1}, {a: 1, b: 0, c: 4}, // Bottom
];

function compareEdges(edge1, edge2) {
  var i1 = edge1[0];
  var i2 = edge2[0];

  if (i1 != i2)
    return i1 < i2 ? -1 : 1;

  var j1 = edge1[1];
  var j2 = edge2[1];

  if (j1 != j2)
    return j1 < j2 ? -1 : 1;

  return 0;
}

function discardUniqueEdges(edges) {
  var i, j, n;
  var count;

  edges.sort(compareEdges);

  count = 0;
  j = 0; // The range [0, j[ of the array stores duplicated edges

  for (i = 0, n = edges.length; i < n; i++) {
    if (!count) {
      count = 1;
      continue;
    }

    if (!compareEdges(edges[i - 1], edges[i])) {
      ++count;
      continue;
    }

    // edges[i - 1] != edges[i]
    if (count <= 1)
      continue;

    // edges[i - 1] != edges[i] && count > 1
    edges[j][0] = edges[i - 1][0];
    edges[j][1] = edges[i - 1][1];
    j += 1;
    count = 1;
  }

  if (count > 1) {
    edges[j][0] = edges[i - 1][0];
    edges[j][1] = edges[i - 1][1];
    j += 1;
  }

  edges.length = j;

  return edges;
}

function extractEdges(faces) {
  var edges = [];
  var face;
  var i, n;

  // Store all edges
  for (i = 0, n = faces.length; i < n; i++) {
    face = faces[i];
    edges.push([face.a, face.b].sort());
    edges.push([face.b, face.c].sort());
    edges.push([face.c, face.a].sort());
  }

  return discardUniqueEdges(edges);
}

var edges = extractEdges(faces);
console.log(edges.length)
console.log(edges)
user3146587
  • 4,250
  • 1
  • 16
  • 25
  • Cheers! I will try to implement this. – VVK May 25 '17 at 18:39
  • This code works 1000x faster, but when I am trying to update it with looking for border edges only, it becomes messy. Border edges should have only one instance, so I was trying to add a filter in discardUniqueEdges to catch if an edge doesn't have any duplicates. – VVK May 30 '17 at 13:08
  • Are you simply looking for the list of unique / border edges in addition to the list of non-unique / inner edges? Then I would suggest that discardUniqueEdges is modified to internally build and return this list of unique edges, updated directly when the condition count <= 1 is true. – user3146587 May 30 '17 at 15:00
  • I am looking for ONLY borders, so you're maybe right with the last comment. – VVK May 30 '17 at 15:22
0
// A cube
var faces = [
  {a: 0, b: 1, c: 2}, {a: 2, b: 3, c: 0}, // Front
  {a: 1, b: 5, c: 6}, {a: 6, b: 2, c: 1}, // Right
  {a: 5, b: 4, c: 7}, {a: 7, b: 6, c: 5}, // Back
  {a: 4, b: 0, c: 3}, {a: 3, b: 7, c: 4}, // Left
  {a: 3, b: 2, c: 6}, {a: 6, b: 7, c: 3}, // Top
  {a: 4, b: 5, c: 1}, {a: 1, b: 0, c: 4}, // Bottom
];

function compareEdges(edge1, edge2) {

  if(edge1[0] == edge2[0] && edge1[1] == edge2[1]) { edge1[2]++; edge2[2]++; }

  var i1 = edge1[0];
  var i2 = edge2[0];

  if (i1 != i2)
    return i1 < i2 ? -1 : 1;

  var j1 = edge1[1];
  var j2 = edge2[1];

  if (j1 != j2)
    return j1 < j2 ? -1 : 1;

  return 0;
}

function discardUniqueEdges(edges) {
  var i, j, n;
  var count;

  edges.sort(compareEdges);

  count = 0;
  j = 0; // The range [0, j[ of the array stores duplicated edges

  for (i = 0, n = edges.length; i < n; i++) {
    if (!count) {
      count = 1;
      continue;
    }

    if (!compareEdges(edges[i - 1], edges[i])) {
      ++count;
      continue;
    }

    // edges[i - 1] != edges[i]
    if (count <= 1)
      continue;

    //edges[i - 1] != edges[i] && count > 1
    edges[j][0] = edges[i - 1][0];
    edges[j][1] = edges[i - 1][1];
    edges[j][2] = edges[i - 1][2];
    j += 1;
    count = 1;
  }

  if (count > 1) {
    edges[j][0] = edges[i - 1][0];
    edges[j][1] = edges[i - 1][1];
    edges[j][1] = edges[i - 1][2];
    j += 1;
  }

  edges.length = j;

  return edges;
}

function extractEdges(faces) {
  var edges = [];
  var face;
  var i, n;

  // Store all edges
  for (i = 0, n = faces.length; i < n; i++) {
    face = faces[i];
    edges.push([face.a, face.b].sort()); edges[edges.length - 1].push(0);
    edges.push([face.b, face.c].sort()); edges[edges.length - 1].push(0);
    edges.push([face.c, face.a].sort()); edges[edges.length - 1].push(0);
  }

  return discardUniqueEdges(edges);
}

var edges = extractEdges(faces);
console.log(edges.length)
console.log(edges)

I have tried to add another parameter [2] as duplicates counter and sort edges after your's extractEdges() function to catch those which don't have copies.

VVK
  • 435
  • 2
  • 27
0
function discardUniqueEdges(edges) {

var hash = {};

for(var i = 0, l = edges.length; i < l; i++){

    var ab = [edges[i][0], edges[i][1]];

    if (!(ab in hash)) { hash[ab] = 1; }

    if((ab in hash)) { hash[ab]++; }

}

var sorted = [];

Object.keys(hash).forEach(function(key) {
  if (hash[key] == 2) { sorted.push(key.split(",")); }
});

return sorted;

}

This works well in addition to @user3146587 proposal.

VVK
  • 435
  • 2
  • 27