Triangulation of three .js polygons fails at pseudo-duplicate points

There is a triangulateShape() function in three.js . Now I am faced with the inability to triangulate polygons, which are simplified using the Javascript Clipper. Simplification in Clipper is done with Unioning. A Wikipedia article defines union as a search for simple polygons or polygons that contain a region inside any of two simple polygons. The same article says that in a simple polygon "exactly two edges meet at each vertex", it also defines a weakly simple polygon where edges can meet, but does not say anything about the edge case when edges do not meet, but some or many vertices intersect. Therefore, it is a little unclear whether this case is a simple polygon or a weakly simple polygon.

Clipper chose a permissive approach: a simple polygon can have vertices such as touching (or pseudo-doubles). This Clipper-style resolving approach leads to the fact that the generated simple polygons are not simple in the sense of what the three .js: s triangulateShape() expects.

The following figure shows two examples of this case. The left polygon is one “simple” polygon, the red dot is a “duplicate”. The correct one is also one “simple” polygon, but the red dot is the “duplicate”.

enter image description here

triangulateShape() not executed in these cases, since it tracks the points in the allPointsMap array and checks from there if the point is duplicated. To remove these duplicates, I have two options:


OPTION 1.

Modify the internal Javascript Clipper code to process them using an additional parameter, for example. breakPolygonByWeakDuplicates for SimplifyPolygon() and SimplifyPolygons() . As Angus Johnson described in his post , the change would be something like this:

In the IntersectEdges () method, change the value from ...

  if (e1Contributing && e2contributing)
 {
   if (e1stops || e2stops || 
     (e1Wc! = 0 && e1Wc! = 1) ||  (e2Wc! = 0 && e2Wc! = 1) ||
     (e1-> polyType! = e2-> polyType && m_ClipType! = ctXor))
       AddLocalMaxPoly (e1, e2, pt); 
   else
       DoBothEdges (e1, e2, pt);
 }

to ...

 if (e1Contributing && e2contributing)
 {
     AddLocalMaxPoly (e1, e2, pt); 
     AddLocalMinPoly (e1, e2, pt);
 }

The change is very simple, but then the original Angus Johnson Clipper and Javascript Clipper will not be more compatible. Of course, if the original Clipper makes a change, the Javascript Clipper will follow.


OPTION 2.

To modify the source code of three.js triangulateShape() to accept also pseudo duplicates.


My question is: In the end, should this be done as a simplification simplification procedure? The first end is the side of creation (Clipper), and the other end is the side of triangulation (three.js).

I do not know the multifaceted triangulation routines in various three-dimensional libraries, so I can not imagine how permissive triangulation procedures in general. If someone knows this area, he / she can give a more complex answer.

Also, I don't know how other logical libraries handle the union or simplification of this type of pseudo duplicate. Of course, the reason is that Clipper is solvable by means of a simple polygon (for example, compatibility with other Boolean libraries), but it definitely creates problems with triangulating polygons in three .js.

For reference, here is the triangulating code three.js:

 triangulateShape: function ( contour, holes ) { var shapeWithoutHoles = THREE.Shape.Utils.removeHoles( contour, holes ); var shape = shapeWithoutHoles.shape, allpoints = shapeWithoutHoles.allpoints, isolatedPts = shapeWithoutHoles.isolatedPts; var triangles = THREE.FontUtils.Triangulate( shape, false ); // True returns indices for points of spooled shape // To maintain reference to old shape, one must match coordinates, or offset the indices from original arrays. It probably easier to do the first. //console.log( "triangles",triangles, triangles.length ); //console.log( "allpoints",allpoints, allpoints.length ); var i, il, f, face, key, index, allPointsMap = {}, isolatedPointsMap = {}; // prepare all points map for ( i = 0, il = allpoints.length; i < il; i ++ ) { key = allpoints[ i ].x + ":" + allpoints[ i ].y; if ( allPointsMap[ key ] !== undefined ) { console.log( "Duplicate point", key ); } allPointsMap[ key ] = i; } // check all face vertices against all points map for ( i = 0, il = triangles.length; i < il; i ++ ) { face = triangles[ i ]; for ( f = 0; f < 3; f ++ ) { key = face[ f ].x + ":" + face[ f ].y; index = allPointsMap[ key ]; if ( index !== undefined ) { face[ f ] = index; } } } // check isolated points vertices against all points map for ( i = 0, il = isolatedPts.length; i < il; i ++ ) { face = isolatedPts[ i ]; for ( f = 0; f < 3; f ++ ) { key = face[ f ].x + ":" + face[ f ].y; index = allPointsMap[ key ]; if ( index !== undefined ) { face[ f ] = index; } } } return triangles.concat( isolatedPts ); }, // end triangulate shapes 

UPDATE: I made one SVG http://jsbin.com/ugimab/1 , which shows an example of a polygon with a dot (150,150), which is a weak duplicate or pseudo-double. The different ways to represent this polygon are shown below:

  var weakDuplicate1 = [{"X": 100, "Y": 200}, {"X": 150, "Y": 150}, {"X": 100, "Y": 100}, {"X" : 200, "Y": 100}, {"X": 150, "Y": 150}, {"X": 200, "Y": 200}];

 var weakDuplicate2 = [100,200, 150,150, 100,100, 200,100, 150,150, 200,200];

 var weakDuplicate3 = "M100,200 L150,150 L100,100 L200,100 L150,150 L200,200Z";


UPDATE: if someone managed to find a solution for triangulating also polygons that have this as weakly duplicating points, it would be very useful if you published your results.


UPDATE: Tested option 1, but failed: http://jsbin.com/owivew/1 . The polygon remains in one piece, although it must be divided into two parts. Perhaps Angus Johnson (creator of Clipper) has a better solution to provide.


UPDATE: here is a more complex “simple” polygon (after simplification in Clipper). All points that seem together are exactly identical. To divide this into truly simple polygons, it would require that it be divided into pieces. My eyes say that there are four lower polygons and one (larger) upper polygon with a hole, since a complete simplification of this will lead to 5 external polygons and 1 hole. Or, alternatively, one external polygon having 5 holes. Or maybe some other combination of outsiders and holes. It can be simplified in many ways.

The violin is at http://jsbin.com/ugimab/3 (also the JSON version of the polygon).

enter image description here

And here are the points numbered from 0 to 25:

enter image description here

In the image, the vertices 2,11,14,25 are the same coordinate, therefore it is a "pseudo-multiple vertex". Vertex3 is not a duplicate, but it touches the edge of 6-7.


UPDATE:

The proposed method based on moving duplicate points seems to work. If the duplicate point is replaced by two points located at a certain distance from the duplicated coordinate, creating the effect of a “broken pen", the triangulation works because the polygons obtained are true simple polygons, which is a requirement for the triangulator. Also, duplicates between the contour and the holes, as well as between the holes and holes are not allowed. The following figure shows the effect of this method. The distance here is 10px to show the effect, but actually, for example. 0.001 is enough to make polygons simple. Also, the default triangulator in Three.js r58 does not work as expected, but if it is changed to Poly2tri, then everything will be fine. The process is described in this rather long bug report: https://github.com/mrdoob/three.js/issues/3386 .

enter image description here

+6
source share
2 answers

You can write a function that detects duplicate vertices and moves them back 1px to make them discrete (they no longer have a common edge). Thus, there will be no more common edges, and there will be no errors, but the visual result still looks the same.

Kind of crude solution, but it may work.

+3
source

There are several problems with the triangulation solution used in three.js . There are several other javascript triangulation libraries, and it is possible that in the future the current library will be exchanged for something else, for example, earcut.js . This is stated in this release on GitHub .

Your problem with self-intersecting ribs is not a problem for the ear chart, as shown here in this multivision .


If you already want to use another solution for triangulation in your project, I would like to refer to the triangulation library three.js (adapter) I did. The adapter allows you to connect three other triangulation libraries to your three.js project:

  • earcut - ear triangulation library
  • poly2tri - poly2tri triangulation library
  • libtess - libtess tessellation library

All you have to do is include the triangulation.js file:

 <script src="triangulation.js"></script> 

And install the library of your choice using the setLibrary method:

 THREE.Triangulation.setLibrary('earcut'); 

Depending on the library you choose, you obviously need to embed files for the library itself. Currently, libtess needs additional tessy.js , which can be found in the repository.

Read more about the project here on GitHub .

0
source

All Articles