kopia lustrzana https://github.com/inkstitch/inkstitch
				
				
				
			replacing shapely parallel_offset by buffer
							rodzic
							
								
									6916a33716
								
							
						
					
					
						commit
						f75c812aba
					
				|  | @ -2,7 +2,7 @@ from shapely.geometry.polygon import LinearRing, LineString | ||||||
| from shapely.geometry import Polygon, MultiLineString | from shapely.geometry import Polygon, MultiLineString | ||||||
| from shapely.ops import polygonize | from shapely.ops import polygonize | ||||||
| from shapely.geometry import MultiPolygon | from shapely.geometry import MultiPolygon | ||||||
| from anytree import AnyNode, PreOrderIter, LevelOrderGroupIter | from anytree import AnyNode, PreOrderIter, LevelOrderGroupIter, RenderTree | ||||||
| from shapely.geometry.polygon import orient | from shapely.geometry.polygon import orient | ||||||
| from depq import DEPQ | from depq import DEPQ | ||||||
| from enum import IntEnum | from enum import IntEnum | ||||||
|  | @ -10,79 +10,94 @@ from ..stitches import tangential_fill_stitch_pattern_creator | ||||||
| from ..stitches import constants | from ..stitches import constants | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def offset_linear_ring(ring, offset, side, resolution, join_style, mitre_limit): | def offset_linear_ring(ring, offset, resolution, join_style, mitre_limit): | ||||||
|     """ |     result = Polygon(ring).buffer(offset, resolution, cap_style=2, join_style=join_style, mitre_limit=mitre_limit, single_sided=True) | ||||||
|     Solves following problem: When shapely offsets a LinearRing the |  | ||||||
|     start/end point might be handled wrongly since they |  | ||||||
|     are only treated as LineString. |  | ||||||
|     (See e.g. https://i.stack.imgur.com/vVh56.png as a problematic example) |  | ||||||
|     This method checks first whether the start/end point form a problematic |  | ||||||
|     edge with respect to the offset side. If it is not a problematic |  | ||||||
|     edge we can use the normal offset_routine. Otherwise we need to |  | ||||||
|     perform two offsets: |  | ||||||
|     -offset the ring |  | ||||||
|     -offset the start/end point + its two neighbors left and right |  | ||||||
|     Finally both offsets are merged together to get the correct |  | ||||||
|     offset of a LinearRing |  | ||||||
|     """ |  | ||||||
| 
 | 
 | ||||||
|     coords = ring.coords[:] |     if result.geom_type == 'Polygon': | ||||||
|     # check whether edge at index 0 is concave or convex. Only for |         return result.exterior | ||||||
|     # concave edges we need to spend additional effort |  | ||||||
|     dx_seg1 = dy_seg1 = 0 |  | ||||||
|     if coords[0] != coords[-1]: |  | ||||||
|         dx_seg1 = coords[0][0] - coords[-1][0] |  | ||||||
|         dy_seg1 = coords[0][1] - coords[-1][1] |  | ||||||
|     else: |     else: | ||||||
|         dx_seg1 = coords[0][0] - coords[-2][0] |  | ||||||
|         dy_seg1 = coords[0][1] - coords[-2][1] |  | ||||||
|     dx_seg2 = coords[1][0] - coords[0][0] |  | ||||||
|     dy_seg2 = coords[1][1] - coords[0][1] |  | ||||||
|     # use cross product: |  | ||||||
|     crossvalue = dx_seg1 * dy_seg2 - dy_seg1 * dx_seg2 |  | ||||||
|     sidesign = 1 |  | ||||||
|     if side == "left": |  | ||||||
|         sidesign = -1 |  | ||||||
| 
 |  | ||||||
|     # We do not need to take care of the joint n-0 since we |  | ||||||
|     # offset along a concave edge: |  | ||||||
|     if sidesign * offset * crossvalue <= 0: |  | ||||||
|         return ring.parallel_offset(offset, side, resolution, join_style, mitre_limit) |  | ||||||
| 
 |  | ||||||
|     # We offset along a convex edge so we offset the joint n-0 separately: |  | ||||||
|     if coords[0] != coords[-1]: |  | ||||||
|         coords.append(coords[0]) |  | ||||||
|     offset_ring1 = ring.parallel_offset( |  | ||||||
|         offset, side, resolution, join_style, mitre_limit |  | ||||||
|     ) |  | ||||||
|     offset_ring2 = LineString((coords[-2], coords[0], coords[1])).parallel_offset( |  | ||||||
|         offset, side, resolution, join_style, mitre_limit |  | ||||||
|     ) |  | ||||||
| 
 |  | ||||||
|     # Next we need to merge the results: |  | ||||||
|     if offset_ring1.geom_type == "LineString": |  | ||||||
|         return LinearRing(offset_ring2.coords[:] + offset_ring1.coords[1:-1]) |  | ||||||
|     else: |  | ||||||
|         # We have more than one resulting LineString for offset of |  | ||||||
|         # the geometry (ring) = offset_ring1. |  | ||||||
|         # Hence we need to find the LineString which belongs to the |  | ||||||
|         # offset of element 0 in coords =offset_ring2 |  | ||||||
|         # in order to add offset_ring2 geometry to it: |  | ||||||
|         result_list = [] |         result_list = [] | ||||||
|         thresh = constants.offset_factor_for_adjacent_geometry * abs(offset) |         for poly in result: | ||||||
|         for offsets in offset_ring1: |             result_list.append(poly.exterior) | ||||||
|             if ( |  | ||||||
|                 abs(offsets.coords[0][0] - coords[0][0]) < thresh |  | ||||||
|                 and abs(offsets.coords[0][1] - coords[0][1]) < thresh |  | ||||||
|             ): |  | ||||||
|                 result_list.append( |  | ||||||
|                     LinearRing(offset_ring2.coords[:] + offsets.coords[1:-1]) |  | ||||||
|                 ) |  | ||||||
|             else: |  | ||||||
|                 result_list.append(LinearRing(offsets)) |  | ||||||
|         return MultiLineString(result_list) |         return MultiLineString(result_list) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | # """ | ||||||
|  | #     Solves following problem: When shapely offsets a LinearRing the | ||||||
|  | #     start/end point might be handled wrongly since they | ||||||
|  | #     are only treated as LineString. | ||||||
|  | #     (See e.g. https://i.stack.imgur.com/vVh56.png as a problematic example) | ||||||
|  | #     This method checks first whether the start/end point form a problematic | ||||||
|  | #     edge with respect to the offset side. If it is not a problematic | ||||||
|  | #     edge we can use the normal offset_routine. Otherwise we need to | ||||||
|  | #     perform two offsets: | ||||||
|  | #     -offset the ring | ||||||
|  | #     -offset the start/end point + its two neighbors left and right | ||||||
|  | #     Finally both offsets are merged together to get the correct | ||||||
|  | #     offset of a LinearRing | ||||||
|  | #     """ | ||||||
|  | 
 | ||||||
|  | #PROBLEM: Did not work in rare cases since it expects the point order be maintained after offsetting the curve  | ||||||
|  | #(e.g. the first point in the offsetted curve shall belong to the first point in the original curve). However, this | ||||||
|  | #assumption seems to be not always true that is why this code was replaced by the buffer routine. | ||||||
|  | 
 | ||||||
|  | #     coords = ring.coords[:] | ||||||
|  | #     # check whether edge at index 0 is concave or convex. Only for | ||||||
|  | #     # concave edges we need to spend additional effort | ||||||
|  | #     dx_seg1 = dy_seg1 = 0 | ||||||
|  | #     if coords[0] != coords[-1]: | ||||||
|  | #         dx_seg1 = coords[0][0] - coords[-1][0] | ||||||
|  | #         dy_seg1 = coords[0][1] - coords[-1][1] | ||||||
|  | #     else: | ||||||
|  | #         dx_seg1 = coords[0][0] - coords[-2][0] | ||||||
|  | #         dy_seg1 = coords[0][1] - coords[-2][1] | ||||||
|  | #     dx_seg2 = coords[1][0] - coords[0][0] | ||||||
|  | #     dy_seg2 = coords[1][1] - coords[0][1] | ||||||
|  | #     # use cross product: | ||||||
|  | #     crossvalue = dx_seg1 * dy_seg2 - dy_seg1 * dx_seg2 | ||||||
|  | #     sidesign = 1 | ||||||
|  | #     if side == "left": | ||||||
|  | #         sidesign = -1 | ||||||
|  | 
 | ||||||
|  | #     # We do not need to take care of the joint n-0 since we | ||||||
|  | #     # offset along a concave edge: | ||||||
|  | #     if sidesign * offset * crossvalue <= 0: | ||||||
|  | #         return ring.parallel_offset(offset, side, resolution, join_style, mitre_limit) | ||||||
|  | 
 | ||||||
|  | #     # We offset along a convex edge so we offset the joint n-0 separately: | ||||||
|  | #     if coords[0] != coords[-1]: | ||||||
|  | #         coords.append(coords[0]) | ||||||
|  | #     offset_ring1 = ring.parallel_offset( | ||||||
|  | #         offset, side, resolution, join_style, mitre_limit | ||||||
|  | #     ) | ||||||
|  | #     offset_ring2 = LineString((coords[-2], coords[0], coords[1])).parallel_offset( | ||||||
|  | #         offset, side, resolution, join_style, mitre_limit | ||||||
|  | #     ) | ||||||
|  | 
 | ||||||
|  | #     # Next we need to merge the results: | ||||||
|  | #     if offset_ring1.geom_type == "LineString": | ||||||
|  | #         return LinearRing(offset_ring2.coords[:] + offset_ring1.coords[1:-1]) | ||||||
|  | #     else: | ||||||
|  | #         # We have more than one resulting LineString for offset of | ||||||
|  | #         # the geometry (ring) = offset_ring1. | ||||||
|  | #         # Hence we need to find the LineString which belongs to the | ||||||
|  | #         # offset of element 0 in coords =offset_ring2 | ||||||
|  | #         # in order to add offset_ring2 geometry to it: | ||||||
|  | #         result_list = [] | ||||||
|  | #         thresh = constants.offset_factor_for_adjacent_geometry * abs(offset) | ||||||
|  | #         for offsets in offset_ring1: | ||||||
|  | #             if ( | ||||||
|  | #                 abs(offsets.coords[0][0] - coords[0][0]) < thresh | ||||||
|  | #                 and abs(offsets.coords[0][1] - coords[0][1]) < thresh | ||||||
|  | #             ): | ||||||
|  | #                 result_list.append( | ||||||
|  | #                     LinearRing(offset_ring2.coords[:] + offsets.coords[1:-1]) | ||||||
|  | #                 ) | ||||||
|  | #             else: | ||||||
|  | #                 result_list.append(LinearRing(offsets)) | ||||||
|  | #         return MultiLineString(result_list) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def take_only_valid_linear_rings(rings): | def take_only_valid_linear_rings(rings): | ||||||
|     """ |     """ | ||||||
|     Removes all geometries which do not form a "valid" LinearRing |     Removes all geometries which do not form a "valid" LinearRing | ||||||
|  | @ -99,13 +114,15 @@ def take_only_valid_linear_rings(rings): | ||||||
|             return LinearRing(new_list[0]) |             return LinearRing(new_list[0]) | ||||||
|         else: |         else: | ||||||
|             return MultiLineString(new_list) |             return MultiLineString(new_list) | ||||||
|     else: |     elif rings.geom_type == "LineString" or rings.geom_type == "LinearRing": | ||||||
|         if len(rings.coords) <= 2: |         if len(rings.coords) <= 2: | ||||||
|             return LinearRing() |             return LinearRing() | ||||||
|         elif len(rings.coords) == 3 and rings.coords[0] == rings.coords[-1]: |         elif len(rings.coords) == 3 and rings.coords[0] == rings.coords[-1]: | ||||||
|             return LinearRing() |             return LinearRing() | ||||||
|         else: |         else: | ||||||
|             return rings |             return rings | ||||||
|  |     else: | ||||||
|  |         return LinearRing() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def make_tree_uniform_ccw(root): | def make_tree_uniform_ccw(root): | ||||||
|  | @ -223,7 +240,6 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, | ||||||
|         outer = offset_linear_ring( |         outer = offset_linear_ring( | ||||||
|             current_poly.val, |             current_poly.val, | ||||||
|             offset, |             offset, | ||||||
|             "left", |  | ||||||
|             resolution=5, |             resolution=5, | ||||||
|             join_style=join_style, |             join_style=join_style, | ||||||
|             mitre_limit=10, |             mitre_limit=10, | ||||||
|  | @ -234,8 +250,7 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, | ||||||
|         for j in range(len(current_holes)): |         for j in range(len(current_holes)): | ||||||
|             inner = offset_linear_ring( |             inner = offset_linear_ring( | ||||||
|                 current_holes[j].val, |                 current_holes[j].val, | ||||||
|                 offset, |                 -offset,  #take negative offset for holes | ||||||
|                 "left", |  | ||||||
|                 resolution=5, |                 resolution=5, | ||||||
|                 join_style=join_style, |                 join_style=join_style, | ||||||
|                 mitre_limit=10, |                 mitre_limit=10, | ||||||
|  | @ -246,12 +261,12 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, | ||||||
|                 poly_inners.append(Polygon(inner)) |                 poly_inners.append(Polygon(inner)) | ||||||
|         if not outer.is_empty: |         if not outer.is_empty: | ||||||
|             if len(poly_inners) == 0: |             if len(poly_inners) == 0: | ||||||
|                 if outer.geom_type == "LineString": |                 if outer.geom_type == "LineString" or outer.geom_type == "LinearRing": | ||||||
|                     result = Polygon(outer) |                     result = Polygon(outer) | ||||||
|                 else: |                 else: | ||||||
|                     result = MultiPolygon(polygonize(outer)) |                     result = MultiPolygon(polygonize(outer)) | ||||||
|             else: |             else: | ||||||
|                 if outer.geom_type == "LineString": |                 if outer.geom_type == "LineString" or outer.geom_type == "LinearRing": | ||||||
|                     result = Polygon(outer).difference( |                     result = Polygon(outer).difference( | ||||||
|                         MultiPolygon(poly_inners)) |                         MultiPolygon(poly_inners)) | ||||||
|                 else: |                 else: | ||||||
|  | @ -312,7 +327,7 @@ def offset_poly(poly, offset, join_style, stitch_distance, min_stitch_distance, | ||||||
|             if previous_hole.parent is None: |             if previous_hole.parent is None: | ||||||
|                 previous_hole.parent = current_poly |                 previous_hole.parent = current_poly | ||||||
| 
 | 
 | ||||||
| 
 |     #print(RenderTree(root)) | ||||||
|     make_tree_uniform_ccw(root) |     make_tree_uniform_ccw(root) | ||||||
| 
 | 
 | ||||||
|     if strategy == StitchingStrategy.CLOSEST_POINT: |     if strategy == StitchingStrategy.CLOSEST_POINT: | ||||||
|  |  | ||||||
		Ładowanie…
	
		Reference in New Issue
	
	 Andreas
						Andreas