Fixed extensions for round holes.

This commit is contained in:
Markus Lampert
2019-06-29 18:05:13 -07:00
parent c4cf901c60
commit a162afc95a
2 changed files with 84 additions and 63 deletions

View File

@@ -47,7 +47,7 @@ __title__ = "Path Pocket Shape Operation"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Class and implementation of shape based Pocket operation."
__contributors__ = "mlampert [FreeCAD], russ4262 (Russell Johnson)"
__contributors__ = "russ4262 (Russell Johnson)"
__created__ = "2017"
__scriptVersion__ = "2g testing"
__lastModified__ = "2019-06-12 23:29 CST"
@@ -77,8 +77,11 @@ def endPoints(edgeOrWire):
if 1 == cnt:
unique.append(p)
return unique
return [edgeOrWire.valueAt(edgeOrWire.FirstParameter), edgeOrWire.valueAt(edgeOrWire.LastParameter)]
pfirst = edgeOrWire.valueAt(edgeOrWire.FirstParameter)
plast = edgeOrWire.valueAt(edgeOrWire.LastParameter)
if PathGeom.pointsCoincide(pfirst, plast):
return None
return [pfirst, plast]
def includesPoint(p, pts):
'''includesPoint(p, pts) ... answer True if the collection of pts includes the point p'''
@@ -102,42 +105,29 @@ def selectOffsetWire(feature, wires):
def extendWire(feature, wire, length):
'''extendWire(wire, length) ... return a closed Wire which extends wire by length'''
try:
off2D = wire.makeOffset2D(length)
except Exception as e:
PathLog.error("extendWire(): wire.makeOffset2D()")
PathLog.error(e)
return False
off2D = wire.makeOffset2D(length)
endPts = endPoints(wire)
edges = [e for e in off2D.Edges if Part.Circle != type(e.Curve) or not includesPoint(e.Curve.Center, endPts)]
wires = [Part.Wire(e) for e in Part.sortEdges(edges)]
offset = selectOffsetWire(feature, wires)
ePts = endPoints(offset)
l0 = (ePts[0] - endPts[0]).Length
l1 = (ePts[1] - endPts[0]).Length
edges = wire.Edges
if l0 < l1:
edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[0])))
edges.extend(offset.Edges)
edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[1])))
else:
endPts = endPoints(wire)
edges = [e for e in off2D.Edges if not isinstance(e.Curve, Part.Circle) or not includesPoint(e.Curve.Center, endPts)]
wires = [Part.Wire(e) for e in Part.sortEdges(edges)]
offset = selectOffsetWire(feature, wires)
ePts = endPoints(offset)
try:
l0 = (ePts[0] - endPts[0]).Length
except Exception as ee:
PathLog.error("extendWire(): (ePts[0] - endPts[0]).Length")
PathLog.error(ee)
return False
else:
l1 = (ePts[1] - endPts[0]).Length
edges = wire.Edges
if l0 < l1:
edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[0])))
edges.extend(offset.Edges)
edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[1])))
else:
edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0])))
edges.extend(offset.Edges)
edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1])))
return Part.Wire(edges)
edges.append(Part.Edge(Part.LineSegment(endPts[1], ePts[0])))
edges.extend(offset.Edges)
edges.append(Part.Edge(Part.LineSegment(endPts[0], ePts[1])))
return Part.Wire(edges)
class Extension(object):
DirectionNormal = 0
DirectionX = 1
DirectionY = 2
DirectionX = 1
DirectionY = 2
def __init__(self, obj, feature, sub, length, direction):
self.obj = obj
@@ -149,7 +139,8 @@ class Extension(object):
def getSubLink(self):
return "%s:%s" % (self.feature, self.sub)
def extendEdge(self, feature, e0, direction):
def _extendEdge(self, feature, e0, direction):
PathLog.track(feature, e0, direction)
if isinstance(e0.Curve, Part.Line) or isinstance(e0.Curve, Part.LineSegment):
e2 = e0.copy()
off = self.length.Value * direction
@@ -162,49 +153,73 @@ class Extension(object):
return wire
return extendWire(feature, Part.Wire([e0]), self.length.Value)
def getEdgeNumbers(self):
def _getEdgeNumbers(self):
if 'Wire' in self.sub:
return [nr for nr in self.sub[5:-1].split(',')]
return [self.sub[4:]]
def getEdgeNames(self):
return ["Edge%s" % nr for nr in self.getEdgeNumbers()]
def _getEdgeNames(self):
return ["Edge%s" % nr for nr in self._getEdgeNumbers()]
def getEdges(self):
return [self.obj.Shape.getElement(sub) for sub in self.getEdgeNames()]
def _getEdges(self):
return [self.obj.Shape.getElement(sub) for sub in self._getEdgeNames()]
def getDirection(self, wire):
def _getDirectedNormal(self, p0, normal):
poffPlus = p0 + 0.01 * normal
poffMinus = p0 - 0.01 * normal
if not self.obj.Shape.isInside(poffPlus, 0.005, True):
return normal
if not self.obj.Shape.isInside(poffMinus, 0.005, True):
return normal.negative()
return None
def _getDirection(self, wire):
e0 = wire.Edges[0]
midparam = e0.FirstParameter + 0.5 * (e0.LastParameter - e0.FirstParameter)
tangent = e0.tangentAt(midparam)
try:
normal = tangent.cross(FreeCAD.Vector(0, 0, 1)).normalize()
except Exception as e:
PathLog.error('getDirection(): tangent.cross(FreeCAD.Vector(0, 0, 1)).normalize()')
PathLog.error(e)
return None
else:
poffPlus = e0.valueAt(midparam) + 0.01 * normal
poffMinus = e0.valueAt(midparam) - 0.01 * normal
if not self.obj.Shape.isInside(poffPlus, 0.005, True):
return normal
if not self.obj.Shape.isInside(poffMinus, 0.005, True):
return normal.negative()
PathLog.track('tangent', tangent, self.feature, self.sub)
normal = tangent.cross(FreeCAD.Vector(0, 0, 1))
if PathGeom.pointsCoincide(normal, FreeCAD.Vector(0, 0, 0)):
return None
return self._getDirectedNormal(e0.valueAt(midparam), normal.normalize())
def getWire(self):
PathLog.track()
if PathGeom.isRoughly(0, self.length.Value) or not self.sub:
return None
feature = self.obj.Shape.getElement(self.feature)
edges = self.getEdges()
edges = self._getEdges()
sub = Part.Wire(edges)
if 1 == len(edges):
direction = self.getDirection(sub)
if direction is None:
edge = edges[0]
if Part.Circle == type(edge.Curve) and not endPoints(edge):
circle = edge.Curve
# for a circle we have to figure out if it's a hole or a cylinder
p0 = edge.valueAt(edge.FirstParameter)
normal = (edge.Curve.Center - p0).normalize()
direction = self._getDirectedNormal(p0, normal)
if direction is None:
return None
if PathGeom.pointsCoincide(normal, direction):
r = circle.Radius - self.length.Value
else:
r = circle.Radius + self.length.Value
# assuming the offset produces a valid circle - go for it
if r > 0:
c1 = Part.makeCircle(r, circle.Center, circle.Axis, edge.FirstParameter * 180 / math.pi, edge.LastParameter * 180 / math.pi)
return [Part.Wire([edge]), Part.Wire([c1])]
return None
return self.extendEdge(feature, edges[0], direction)
else:
PathLog.track(self.feature, self.sub, type(edge.Curve), endPoints(edge))
direction = self._getDirection(sub)
if direction is None:
return None
# return self._extendEdge(feature, edge, direction)
return self._extendEdge(feature, edges[0], direction)
return extendWire(feature, sub, self.length.Value)
@@ -212,7 +227,6 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
'''Proxy object for Pocket operation.'''
def areaOpFeatures(self, obj):
# return super(self.__class__, self).areaOpFeatures(obj) | PathOp.FeatureLocations | PathOp.FeatureRotation
return super(self.__class__, self).areaOpFeatures(obj) | PathOp.FeatureLocations
def initPocketOp(self, obj):
@@ -225,6 +239,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
obj.addProperty('App::PropertyLinkSubListGlobal', 'ExtensionFeature', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'List of features to extend.'))
if not hasattr(obj, 'ExtensionCorners'):
obj.addProperty('App::PropertyBool', 'ExtensionCorners', 'Extension', QtCore.QT_TRANSLATE_NOOP('PathPocketShape', 'When enabled connected extension edges are combined to wires.'))
obj.ExtensionCorners = True
if not hasattr(obj, 'ReverseDirection'):
obj.addProperty('App::PropertyBool', 'ReverseDirection', 'Rotation', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Reverse direction of pocket operation.'))
@@ -602,7 +617,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket):
'''areaOpSetDefaultValues(obj, job) ... set default values'''
obj.StepOver = 100
obj.ZigZagAngle = 45
obj.ExtensionCorners = False
obj.ExtensionCorners = True
obj.UseOutline = False
obj.ReverseDirection = False
obj.InverseAngle = False

View File

@@ -80,8 +80,14 @@ class _Extension(object):
if not ext is None:
wire = ext.getWire()
if wire:
poly = [p for p in wire.discretize(Deflection=0.01)][:-1]
polygon = [(p.x, p.y, p.z) for p in poly]
if isinstance(wire, (list, tuple)):
p0 = [p for p in wire[0].discretize(Deflection=0.02)]
p1 = [p for p in wire[1].discretize(Deflection=0.02)]
p2 = list(reversed(p1))
polygon = [(p.x, p.y, p.z) for p in (p0 + p2)]
else:
poly = [p for p in wire.discretize(Deflection=0.02)][:-1]
polygon = [(p.x, p.y, p.z) for p in poly]
crd.point.setValues(polygon)
else:
return None