From f7564d380562f74ce829755f7d553995c6e7913f Mon Sep 17 00:00:00 2001 From: KatKatKateryna <89912278+KatKatKateryna@users.noreply.github.com> Date: Sun, 30 Jun 2024 09:03:43 +0800 Subject: [PATCH] simplify and triangulate large meshes (#201) * simplify and triangulate large meshes * more clear logic with adding points * apply coeff to inner rings * fix indexError --- speckle/converter/geometry/mesh.py | 13 +++-- speckle/converter/geometry/utils.py | 73 ++++++++++++++++++++--------- speckle/converter/layers/utils.py | 2 + 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/speckle/converter/geometry/mesh.py b/speckle/converter/geometry/mesh.py index cbde658e..933106ac 100644 --- a/speckle/converter/geometry/mesh.py +++ b/speckle/converter/geometry/mesh.py @@ -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 @@ -210,18 +211,16 @@ def meshPartsFromPolygon( iterations = 0 coef = 1 - maxPoints = 5000 + maxPoints = 500 if len(polyBorder) >= maxPoints: - coef = int(len(polyBorder) / maxPoints) + coef = math.floor(len(polyBorder) / maxPoints) 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 # floor: need positive - clockwise (looking down); cap need negative (counter-clockwise) polyBorder = fix_orientation(polyBorder, True, coef) # clockwise @@ -229,8 +228,8 @@ def meshPartsFromPolygon( 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] vertices.extend([pt.x, pt.y, pt.z]) total_vertices += 1 else: @@ -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: diff --git a/speckle/converter/geometry/utils.py b/speckle/converter/geometry/utils.py index 667ae88a..a693256f 100644 --- a/speckle/converter/geometry/utils.py +++ b/speckle/converter/geometry/utils.py @@ -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 = [] @@ -145,18 +147,27 @@ def getPolyPtsSegments(geom: Any, dataStorage: "DataStorage", xform): pt_iterator = geom.vertices() # get boundary points and segments - pointListLocal = [] + pointListLocalOuter = [] 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) else: - pointListLocal.append(pt) - for i, pt in enumerate(pointListLocal): + if i % coef == 0: + pointListLocalOuter.append(pt) + 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 + + for i, pt in enumerate(pointListLocalOuter): x, y = apply_pt_offsets_rotation_on_send(pt.x(), pt.y(), dataStorage) vertices.append([x, y]) try: @@ -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: + # make sure it's not already included in the outer part of geometry + + if coef is None or len(pt_list) / coef < 5: + # 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) + else: + if i % coef == 0: + pointListLocal.append(pt) + 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 if len(pointListLocal) > 2: holes.append( @@ -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) vertices, vertices3d, segments, holes = pack @@ -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()) * ( + pt2.y() + pt.y() + ) # if QGIS Points + except IndexError: + break + if positive is True: if sum_orientation < 0: polyBorder.reverse() diff --git a/speckle/converter/layers/utils.py b/speckle/converter/layers/utils.py index 63b157a4..c8ba49f9 100644 --- a/speckle/converter/layers/utils.py +++ b/speckle/converter/layers/utils.py @@ -595,6 +595,8 @@ def isAppliedLayerTransformByKeywords( def getElevationLayer(dataStorage): elevationLayer = dataStorage.elevationLayer + if elevationLayer is None: + return None try: # check if layer was not deleted name = elevationLayer.name()