Fixed extensions for round holes.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user