Fixed wire offsetting and orientation and added more test cases.
This commit is contained in:
@@ -33,7 +33,7 @@ import math
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
if False:
|
||||
if True:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
@@ -44,13 +44,40 @@ def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
def orientWire(w, forward=True):
|
||||
face = Part.Face(w)
|
||||
cw = 0 < face.Surface.Axis.z
|
||||
if forward != cw:
|
||||
PathLog.track('orientWire - needs flipping')
|
||||
return PathGeom.flipWire(w)
|
||||
PathLog.track('orientWire - ok')
|
||||
return w
|
||||
'''orientWire(w, forward=True) ... orients given wire in a specific direction.
|
||||
If forward = True (the default) the wire is oriented clockwise, looking down the negative Z axis.
|
||||
If forward = False the wire is oriented counter clockwise.
|
||||
If forward = None the orientation is determined by the order in which the edges appear in the wire.'''
|
||||
# first, we must ensure all edges are oriented the same way
|
||||
# one would thing this is the way it should be, but it turns out it isn't
|
||||
# on top of that, when creating a face the axis of the face seems to depend
|
||||
# the axis of any included arcs, and not in the order of the edges
|
||||
e0 = w.Edges[0]
|
||||
# well, even the very first edge could be misoriented, so let's try and connect it to the second
|
||||
if 1 < len(w.Edges):
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
e1 = w.Edges[1]
|
||||
if not PathGeom.pointsCoincide(last, e1.valueAt(e1.FirstParameter)) and not PathGeom.pointsCoincide(last, e1.valueAt(e1.LastParameter)):
|
||||
e0 = PathGeom.flipEdge(e0)
|
||||
|
||||
edges = [e0]
|
||||
last = e0.valueAt(e0.LastParameter)
|
||||
for e in w.Edges[1:]:
|
||||
edge = e if PathGeom.pointsCoincide(last, e.valueAt(e.FirstParameter)) else PathGeom.flipEdge(e)
|
||||
edges.append(edge)
|
||||
last = edge.valueAt(edge.LastParameter)
|
||||
wire = Part.Wire(edges)
|
||||
if forward is not None:
|
||||
# now that we have a wire where all edges are oriented in the same way which
|
||||
# also matches their order - we can create a face and get it's axis to determine
|
||||
# the orientation of the wire - which is all we need here
|
||||
face = Part.Face(wire)
|
||||
cw = 0 < face.Surface.Axis.z
|
||||
if forward != cw:
|
||||
PathLog.track('orientWire - needs flipping')
|
||||
return PathGeom.flipWire(wire)
|
||||
PathLog.track('orientWire - ok')
|
||||
return wire
|
||||
|
||||
def isCircleAt(edge, center):
|
||||
if Circel == type(edge.Curve) or ArcOfCircle == type(edge.Curve):
|
||||
@@ -58,21 +85,55 @@ def isCircleAt(edge, center):
|
||||
return False
|
||||
|
||||
def offsetWire(wire, base, offset, forward):
|
||||
'''offsetWire(wire, base, offset, forward) ... offsets the wire away from base and orients the wire accordingly.
|
||||
The function tries to avoid most of the pitfalls of Part.makeOffset2D which is possible because all offsetting
|
||||
happens in the XY plane.
|
||||
'''
|
||||
PathLog.track('offsetWire')
|
||||
|
||||
if 1 == len(wire.Edges):
|
||||
edge = wire.Edges[0]
|
||||
curve = edge.Curve
|
||||
if Part.Circle == type(curve) and wire.isClosed():
|
||||
# it's a full circle and there are some problems with that, see
|
||||
# http://www.freecadweb.org/wiki/Part%20Offset2D
|
||||
# it's easy to construct them manually though
|
||||
z = -1 if forward else 1
|
||||
edge = Part.makeCircle(curve.Radius + offset, curve.Center, FreeCAD.Vector(0, 0, z))
|
||||
if base.isInside(edge.Vertexes[0].Point, offset/2, True):
|
||||
if offset > curve.Radius or PathGeom.isRoughly(offset, curve.Radius):
|
||||
# offsetting a hole by its own radius (or more) makes the hole vanish
|
||||
return None
|
||||
edge = Part.makeCircle(curve.Radius - offset, curve.Center, FreeCAD.Vector(0, 0, -z))
|
||||
w = Part.Wire([edge])
|
||||
return w
|
||||
if Part.Line == type(curve) or Part.LineSegment == type(curve):
|
||||
# offsetting a single edge doesn't work because there is an infinite
|
||||
# possible planes into which the edge could be offset
|
||||
# luckily, the plane here must be the XY-plane ...
|
||||
n = (edge.Vertexes[1].Point - edge.Vertexes[0].Point).cross(FreeCAD.Vector(0, 0, 1))
|
||||
o = n.normalize() * offset
|
||||
edge.translate(o)
|
||||
if base.isInside(edge.valueAt((edge.FirstParameter + edge.LastParameter)/2), offset/2, True):
|
||||
edge.translate(-2 * o)
|
||||
w = Part.Wire([edge])
|
||||
return orientWire(w, forward)
|
||||
# if we get to this point the assumption is that makeOffset2D can deal with the edge
|
||||
pass
|
||||
|
||||
w = wire.makeOffset2D(offset)
|
||||
if 1 == len(w.Edges):
|
||||
e = w.Edges[0]
|
||||
e.Placement = FreeCAD.Placement()
|
||||
w = Part.Wire(e)
|
||||
|
||||
|
||||
if wire.isClosed():
|
||||
if not base.isInside(w.Edges[0].Vertexes[0].Point, offset/2, True):
|
||||
PathLog.track('closed - outside')
|
||||
return orientWire(w, forward)
|
||||
PathLog.track('closed - inside')
|
||||
w = wire.makeOffset2D(-offset)
|
||||
try:
|
||||
w = wire.makeOffset2D(-offset)
|
||||
except:
|
||||
# most likely offsetting didn't work because the wire is a hole
|
||||
# and the offset is too big - making the hole vanish
|
||||
return None
|
||||
return orientWire(w, forward)
|
||||
|
||||
# An edge is considered to be inside of shape if the mid point is inside
|
||||
@@ -192,7 +253,9 @@ class ObjectChamfer(PathEngraveBase.ObjectOp):
|
||||
|
||||
for w in self.adjustWirePlacement(obj, base, basewires):
|
||||
self.adjusted_basewires.append(w)
|
||||
wires.append(offsetWire(w, base.Shape, offset, True))
|
||||
wire = offsetWire(w, base.Shape, offset, True)
|
||||
if wire:
|
||||
wires.append(wire)
|
||||
|
||||
self.wires = wires
|
||||
self.buildpathocc(obj, wires, [depth], True)
|
||||
|
||||
Reference in New Issue
Block a user