Now, one easy way to get the trimming curves is simply to create a sketch (the default one, on XY plane), and draw a closed wire that contours the point cloud and jump to the
surface trimming part.
However, we may also want to extract the points that are on the boundary of the point cloud and use them to create approximating curves.
For this purpose, we need to draw a bounding wire in a sketch, as above, but this time, the wire must always be outside of the point cloud, by an offset.
- pc_approx_2.png (20.53 KiB) Viewed 8344 times
These curves will be used to detect the closest points of the point cloud.
Then :
- Select the Contour Sketch, and the flat Point Cloud and run the following script in the python console :
Code: Select all
"""Select the flat point cloud
and the sketch that contours it
This will create objects that contain boundary vertexes"""
# Number of samples by unit length
scan_factor = 60
detection_radius = 5
def reduce_point_cloud(point_cloud, edge, fac=5):
"""returns the vertexes of point_cloud that are near to edge"""
d, pts, info = point_cloud.distToShape(edge)
tube = Part.makeTube(edge, d * fac)
f1 = Part.Face(Part.Wire([tube.Edge2]))
f2 = Part.Face(Part.Wire([tube.Edge3]))
so = Part.Solid(Part.makeShell([tube,f1,f2]))
com = so.common(point_cloud)
return com
s = FreeCADGui.Selection.getSelection()
for so in s:
if hasattr(so, "Constraints"):
ie = so.Shape.Edges
elif hasattr(so, "Shape"):
pc = so.Shape
for i, e in enumerate(ie):
red_pc = reduce_point_cloud(pc, e, detection_radius) # to reduce compute time
points = [FreeCAD.Vector(1e50, 0, 0)]
num = int(scan_factor * e.Length)
FreeCAD.Console.PrintMessage(f"Searching Boundary Points for Edge{i+1} with {num} samples\n")
progressbar = FreeCAD.Base.ProgressIndicator()
progressbar.start("Searching Boundary Points ...", num)
for p in e.discretize(num):
iv = Part.Vertex(p)
d, pts, info = red_pc.distToShape(iv)
if pts[0][0].distanceToPoint(points[-1]) > 1e-7:
points.append(pts[0][0])
else:
print("ignoring duplicate point")
progressbar.next()
progressbar.stop()
verts = [Part.Vertex(p) for p in points[1:]]
Part.show(Part.Compound(verts), f"Boundary Points {i+1}")
It should generate a "Boundary Points" object for each edge of the sketch, as below :
- pc_approx_3.png (64.57 KiB) Viewed 8344 times
In the above script, two variables can be tweaked :
Code: Select all
scan_factor = 60
detection_radius = 5
scan_factor is the number of samples on each edge, used to search for the boundary points, it can be increased if the script miss some points, but it will increase compute time accordingly. Or it can be reduced to make the script faster.
detection_radius is used in a pre-filter phase where a tube is built along the detection edges, to isolate the candidate boundary points.
If the detection edge is at a constant offset distance from point cloud, this can be set to 2 or 3. But if the script miss full portions of boundary points, this value probably needs to be increased.