You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

176 lines
6.0 KiB

import FreeCAD
import FreeCAD as App
import FreeCADGui as Gui
import numpy as np
import math
import Part
import shapely
from shapely.geometry import Polygon, Point
from pyslm import hatching as hatching
from pyslm.geometry.geometry import LayerGeometryType
from typing import List
import rdp
def project_to_face(compounds, face):
# proj = Part.makeCompound([])
proj = []
for c in compounds:
projection_result = []
for e in c.Edges:
projection_result.append(face.makeParallelProjection(e, App.Vector(0, 0, 1)))
# proj.add(Part.makeCompound(projection_result))
proj.append(projection_result)
return proj
def map_wire(wire, surface):
"""Map wire on target surface
Input wire must be on XY plane"""
plane = Part.Plane().toShape()
mapped_edges = []
for e in wire.Edges:
c, fp, lp = plane.curveOnSurface(e)
mapped_edges.append(c.toShape(surface, fp, lp))
return Part.Wire(mapped_edges)
def path2DToPathList(shapes: List[shapely.geometry.polygon.Polygon]) -> List[np.ndarray]:
"""
Returns the list of paths and coordinates from a cross-section (i.e. :class:`Trimesh.path.Path2D` objects).
This is required to be done for performing boolean operations and offsetting with the internal PyClipper package.
:param shapes: A list of Shapely Polygons representing a cross-section or container of
closed polygons
:return: A list of paths (Numpy Coordinate Arrays) describing fully closed and oriented paths.
"""
paths = []
for poly in shapes:
coords = np.array(poly.exterior.coords)
paths.append(coords)
for path in poly.interiors:
coords = np.array(path.coords)
paths.append(coords)
return paths
def get_polypoints_from_edges(edges):
# print("START NEW POLYGON")
poly_points = []
# print("edges:", len(edges))
for edge in Part.__sortEdges__(edges):
if type(edge.Curve) is Part.Circle and edge.Closed:
# print("Edge is Circle")
c = edge.Curve
center = Point(c.Center.x, c.Center.y)
radius = c.Radius
circle = center.buffer(radius)
poly_points = circle.exterior.coords
elif type(edge.Curve) in [Part.Ellipse, Part.BSplineCurve, Part.Circle]:
# print("Edge is Ellipse, BSpline or unclosed Circle")
n = math.floor(edge.Length/2.3)
if n > 200:
n = 200
if edge.Closed:
# print("Edge is closed Ellipse or BSpline")
for v in edge.discretize(Number=n):
poly_points.append((v.x, v.y))
else:
# print("Edge is unclosed Circle")
# for Circles at least 64 steps for full circle
for v in edge.discretize(Number=n, First=edge.FirstParameter, Last=edge.LastParameter):
poly_points.append((v.x, v.y))
else:
# print("Line Vertexes:", len(edge.Vertexes))
# print("Line Vertexes:", [(v.Point.x, v.Point.y) for v in edge.Vertexes])
for v in edge.Vertexes:
poly_points.append((v.Point.x, v.Point.y))
u = np.unique(poly_points, axis=0)
# print("UNIQUE:", u)
# print("END NEW POLYGON")
return poly_points
def tuple_is_equal(t1, t2):
if math.isclose(t1[0], t2[0]) and math.isclose(t1[1], t2[1]):
return True
return False
def get_polygon_from_subshape(subshape):
# print("START NEW POLYGON")
polygon = []
for edge in subshape.Edges: # Part.__sortEdges__(subshape.Edges):
poly_points = []
if type(edge.Curve) in [Part.Ellipse, Part.BSplineCurve, Part.Circle]:
n = math.floor(edge.Length/2.3)
for v in edge.discretize(Number=n, First=edge.FirstParameter, Last=edge.LastParameter):
poly_points.append((v.x, v.y))
else:
# print("Some polygon or rect:")
# print("Num Line Vertexes:", len(edge.Vertexes))
# print("Line Vertexes:", [(v.Point.x, v.Point.y) for v in edge.Vertexes])
for v in edge.Vertexes:
poly_points.append((v.Point.x, v.Point.y))
if len(polygon):
if tuple_is_equal(poly_points[0], polygon[-1]):
polygon.extend(poly_points[1:])
else:
polygon.extend(poly_points)
else:
polygon.extend(poly_points)
# print("END NEW POLYGON")
# return LinearRing(polygon)
return polygon
def get_polygon_from_wire(wire):
polygon = []
n = math.floor(wire.Length/2.3)
for v in wire.discretize(Number=n):
polygon.append((v.x, v.y))
return polygon
def get_coords_from_shape(face):
#outerpoly = get_polygon_from_subshape(face.OuterWire)
outerpoly = get_polygon_from_wire(face.OuterWire)
inner_polys = []
for inner_wire in face.SubShapes[1:]:
tmp = get_polygon_from_wire(inner_wire)
inner_polys.append(tmp)
poly = Polygon(shell=outerpoly, holes=inner_polys)
print("Polygon: ", poly)
return path2DToPathList([poly])
def create_hatch_lines(geoms):
hatchlines = []
for geom in geoms:
if geom.type() == LayerGeometryType.Hatch:
# print("Hatch with {} coords".format(len(geom.coords)))
hatches = np.vstack([geom.coords.reshape(-1, 2, 2)])
for line in hatches:
p0 = line[0]
p1 = line[1]
pp = Part.makeLine(App.Vector(p0[0], p0[1],0), App.Vector(p1[0],p1[1],0))
hatchlines.append(pp)
return hatchlines
def create_contour_lines(geoms):
contours = []
for geom in geoms:
if geom.type() == LayerGeometryType.Polygon:
# print("Contour with {} coords".format(len(geom.coords)))
coords = rdp.rdp(geom.coords, epsilon=0.2, algo="iter", return_mask=False)
#print("Simplfied Poly:", len(coords))
pp = Part.makePolygon([App.Vector(x,y,0) for (x,y) in coords])
contours.append(pp)
return contours