Skip to content

Commit

Permalink
simplify and triangulate large meshes (#201)
Browse files Browse the repository at this point in the history
* simplify and triangulate large meshes

* more clear logic with adding points

* apply coeff to inner rings

* fix indexError
  • Loading branch information
KatKatKateryna authored Jun 30, 2024
1 parent 822b1cb commit f7564d3
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 29 deletions.
13 changes: 6 additions & 7 deletions speckle/converter/geometry/mesh.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import inspect
import math
from typing import List, Tuple, Union
from specklepy.objects.geometry import Mesh, Point
from specklepy.objects.other import RenderMaterial
Expand Down Expand Up @@ -210,27 +211,25 @@ def meshPartsFromPolygon(
iterations = 0

coef = 1
maxPoints = 5000
maxPoints = 500

Check warning on line 214 in speckle/converter/geometry/mesh.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/mesh.py#L214

Added line #L214 was not covered by tests
if len(polyBorder) >= maxPoints:
coef = int(len(polyBorder) / maxPoints)
coef = math.floor(len(polyBorder) / maxPoints)

Check warning on line 216 in speckle/converter/geometry/mesh.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/mesh.py#L216

Added line #L216 was not covered by tests

if len(polyBorder) < 3:
return None, None, None, None, None

col = featureColorfromNativeRenderer(feature, layer)

if (
len(voidsAsPts) == 0
): # only if there is a mesh with no voids and large amount of points
if len(voidsAsPts) == 0: # only if there is a mesh with no voids

Check warning on line 223 in speckle/converter/geometry/mesh.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/mesh.py#L223

Added line #L223 was not covered by tests
# floor: need positive - clockwise (looking down); cap need negative (counter-clockwise)
polyBorder = fix_orientation(polyBorder, True, coef) # clockwise

if height is None:
polyBorder.reverse() # when no extrusions: face up, counter-clockwise

for k, ptt in enumerate(polyBorder): # pointList:
pt = polyBorder[k * coef]
if k < maxPoints:
pt = polyBorder[k * coef]

Check warning on line 232 in speckle/converter/geometry/mesh.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/mesh.py#L232

Added line #L232 was not covered by tests
vertices.extend([pt.x, pt.y, pt.z])
total_vertices += 1
else:
Expand Down Expand Up @@ -351,7 +350,7 @@ def meshPartsFromPolygon(

# get points from original geometry #################################
triangulated_geom, vertices3d_original, iterations = triangulatePolygon(
feature_geom, dataStorage, xform
feature_geom, dataStorage, coef, xform
)

# temporary solution, as the list of points is not the same anymore:
Expand Down
73 changes: 51 additions & 22 deletions speckle/converter/geometry/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ def projectToPolygon(
return z


def getPolyPtsSegments(geom: Any, dataStorage: "DataStorage", xform):
def getPolyPtsSegments(
geom: Any, dataStorage: "DataStorage", coef: Union[int, None] = None, xform=None
):
vertices = []
vertices3d = []
segmList = []
Expand All @@ -145,18 +147,27 @@ def getPolyPtsSegments(geom: Any, dataStorage: "DataStorage", xform):
pt_iterator = geom.vertices()

# get boundary points and segments
pointListLocal = []
pointListLocalOuter = []

Check warning on line 150 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L150

Added line #L150 was not covered by tests
startLen = len(vertices)
for i, pt in enumerate(pt_iterator):
if (
len(pointListLocal) > 0
and pt.x() == pointListLocal[0].x()
and pt.y() == pointListLocal[0].y()
): # don't repeat 1st point
len(pointListLocalOuter) > 0
and pt.x() == pointListLocalOuter[0].x()
and pt.y() == pointListLocalOuter[0].y()
):
# don't repeat 1st point
pass
elif coef is None:
pointListLocalOuter.append(pt)

Check warning on line 161 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L160-L161

Added lines #L160 - L161 were not covered by tests
else:
pointListLocal.append(pt)
for i, pt in enumerate(pointListLocal):
if i % coef == 0:
pointListLocalOuter.append(pt)

Check warning on line 164 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L163-L164

Added lines #L163 - L164 were not covered by tests
else:
# don't add points, which are in-between specified step (coeff)
# e.g. if coeff=5, we skip ponts 1,2,3,4, but add points 0 and 5
pass

Check warning on line 168 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L168

Added line #L168 was not covered by tests

for i, pt in enumerate(pointListLocalOuter):

Check warning on line 170 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L170

Added line #L170 was not covered by tests
x, y = apply_pt_offsets_rotation_on_send(pt.x(), pt.y(), dataStorage)
vertices.append([x, y])
try:
Expand All @@ -182,18 +193,29 @@ def getPolyPtsSegments(geom: Any, dataStorage: "DataStorage", xform):
pt_list = list(pt_iterator)
pointListLocal = []
startLen = len(vertices)

for i, pt in enumerate(pt_list):
if (
len(pointListLocal) > 0
and pt.x() == pointListLocal[0].x()
and pt.y() == pointListLocal[0].y()
): # don't repeat 1st point
):
# don't repeat 1st point
continue
elif [
pt.x(),
pt.y(),
] not in vertices: # in case it's not the inner part of geometry
pointListLocal.append(pt)
elif pt not in pointListLocalOuter:

Check warning on line 205 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L205

Added line #L205 was not covered by tests
# make sure it's not already included in the outer part of geometry

if coef is None or len(pt_list) / coef < 5:

Check warning on line 208 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L208

Added line #L208 was not covered by tests
# coef was calculated by the outer ring.
# We need to make sure inner ring will have at least 4 points, otherwise ignore coeff.
pointListLocal.append(pt)

Check warning on line 211 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L211

Added line #L211 was not covered by tests
else:
if i % coef == 0:
pointListLocal.append(pt)

Check warning on line 214 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L213-L214

Added lines #L213 - L214 were not covered by tests
else:
# don't add points, which are in-between specified step (coeff)
# e.g. if coeff=5, we skip ponts 1,2,3,4, but add points 0 and 5
pass

Check warning on line 218 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L218

Added line #L218 was not covered by tests

if len(pointListLocal) > 2:
holes.append(
Expand Down Expand Up @@ -322,14 +344,14 @@ def to_triangles(data: dict, attempt: int = 0) -> Tuple[Union[dict, None], int]:


def triangulatePolygon(
geom: Any, dataStorage: "DataStorage", xform=None
geom: Any, dataStorage: "DataStorage", coef: Union[int, None] = None, xform=None
) -> Tuple[dict, Union[List[List[float]], None], int]:
try:
# import triangle as tr
vertices = [] # only outer
segments = [] # including holes
holes = []
pack = getPolyPtsSegments(geom, dataStorage, xform)
pack = getPolyPtsSegments(geom, dataStorage, coef, xform)

Check warning on line 354 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L354

Added line #L354 was not covered by tests

vertices, vertices3d, segments, holes = pack

Expand Down Expand Up @@ -432,13 +454,20 @@ def fix_orientation(
index = k + 1
if k == len(polyBorder) - 1:
index = 0
pt = polyBorder[k * coef]
pt2 = polyBorder[index * coef]

if isinstance(pt, Point) and isinstance(pt2, Point):
sum_orientation += (pt2.x - pt.x) * (pt2.y + pt.y) # if Speckle Points
else:
sum_orientation += (pt2.x() - pt.x()) * (pt2.y() + pt.y()) # if QGIS Points
try:
pt = polyBorder[k * coef]
pt2 = polyBorder[index * coef]

if isinstance(pt, Point) and isinstance(pt2, Point):
sum_orientation += (pt2.x - pt.x) * (pt2.y + pt.y) # if Speckle Points
else:
sum_orientation += (pt2.x() - pt.x()) * (

Check warning on line 465 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L465

Added line #L465 was not covered by tests
pt2.y() + pt.y()
) # if QGIS Points
except IndexError:
break

Check warning on line 469 in speckle/converter/geometry/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/geometry/utils.py#L468-L469

Added lines #L468 - L469 were not covered by tests

if positive is True:
if sum_orientation < 0:
polyBorder.reverse()
Expand Down
2 changes: 2 additions & 0 deletions speckle/converter/layers/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,8 @@ def isAppliedLayerTransformByKeywords(

def getElevationLayer(dataStorage):
elevationLayer = dataStorage.elevationLayer
if elevationLayer is None:
return None

Check warning on line 599 in speckle/converter/layers/utils.py

View check run for this annotation

Codecov / codecov/patch

speckle/converter/layers/utils.py#L598-L599

Added lines #L598 - L599 were not covered by tests
try:
# check if layer was not deleted
name = elevationLayer.name()
Expand Down

0 comments on commit f7564d3

Please sign in to comment.