def isCubic(shape): """Return True if the shape is a parallelepiped (cuboid). A parallelepiped of cube-like shape has 8 vertices, 6 faces, 12 edges, and all angles are 90 degrees between its edges. """ # first we try fast methods if (len(shape.Vertexes) != 8 or len(shape.Faces) != 6 or len(shape.Edges) != 12): return False for e in shape.Edges: if geomType(e) != "Line": return False # if ok until now, let's do more advanced testing for f in shape.Faces: if len(f.Edges) != 4: return False for i in range(4): e1 = vec(f.Edges[i]) if i < 3: e2 = vec(f.Edges[i + 1]) else: e2 = vec(f.Edges[0]) rpi = [0.0, round(math.pi / 2, precision())] if round(e1.getAngle(e2), precision()) not in rpi: return False return True
def isCubic(shape): """isCubic(shape): verifies if a shape is cubic, that is, has 8 vertices, 6 faces, and all angles are 90 degrees.""" # first we try fast methods if len(shape.Vertexes) != 8: return False if len(shape.Faces) != 6: return False if len(shape.Edges) != 12: return False for e in shape.Edges: if geomType(e) != "Line": return False # if ok until now, let's do more advanced testing for f in shape.Faces: if len(f.Edges) != 4: return False for i in range(4): e1 = vec(f.Edges[i]) if i < 3: e2 = vec(f.Edges[i + 1]) else: e2 = vec(f.Edges[0]) rpi = [0.0, round(math.pi / 2, precision())] if not round(e1.getAngle(e2), precision()) in rpi: return False return True
def removeInterVertices(wire): """Remove middle vertices from a straight wire and return a new wire. Remove unneeded vertices, those that are in the middle of a straight line, from a wire, return a new wire. """ _pre = precision() edges = Part.__sortEdges__(wire.Edges) nverts = [] def getvec(v1, v2): if not abs(round(v1.getAngle(v2), _pre) in [0, round(math.pi, _pre)]): nverts.append(edges[i].Vertexes[-1].Point) for i in range(len(edges) - 1): vA = vec(edges[i]) vB = vec(edges[i + 1]) getvec(vA, vB) vA = vec(edges[-1]) vB = vec(edges[0]) getvec(vA, vB) if nverts: if wire.isClosed(): nverts.append(nverts[0]) w = Part.makePolygon(nverts) return w else: return wire
def isCoplanar(faces, tolerance=0): """Return True if all faces in the given list are coplanar. Tolerance is the maximum deviation to be considered coplanar. """ if len(faces) < 2: return True base = faces[0].normalAt(0, 0) for i in range(1, len(faces)): for v in faces[i].Vertexes: chord = v.Point.sub(faces[0].Vertexes[0].Point) dist = DraftVecUtils.project(chord, base) if round(dist.Length, precision()) > tolerance: return False return True
def getCubicDimensions(shape): """getCubicDimensions(shape): returns a list containing the placement, the length, the width and the height of a cubic shape. If not cubic, nothing is returned. The placement point is the lowest corner of the shape.""" if not isCubic(shape): return None # determine lowest face, which will be our base z = [10, 1000000000000] for i in range(len(shape.Faces)): if shape.Faces[i].CenterOfMass.z < z[1]: z = [i, shape.Faces[i].CenterOfMass.z] if z[0] > 5: return None base = shape.Faces[z[0]] basepoint = base.Edges[0].Vertexes[0].Point plpoint = base.CenterOfMass basenorm = base.normalAt(0.5, 0.5) # getting length and width vx = vec(base.Edges[0]) vy = vec(base.Edges[1]) # getting rotations rotZ = DraftVecUtils.angle(vx) rotY = DraftVecUtils.angle(vx, FreeCAD.Vector(vx.x, vx.y, 0)) rotX = DraftVecUtils.angle(vy, FreeCAD.Vector(vy.x, vy.y, 0)) # getting height vz = None rpi = round(math.pi / 2, precision()) for i in range(1, 6): for e in shape.Faces[i].Edges: if basepoint in [e.Vertexes[0].Point, e.Vertexes[1].Point]: vtemp = vec(e) # print(vtemp) if round(vtemp.getAngle(vx), precision()) == rpi: if round(vtemp.getAngle(vy), precision()) == rpi: vz = vtemp if not vz: return None mat = FreeCAD.Matrix() mat.move(plpoint) mat.rotateX(rotX) mat.rotateY(rotY) mat.rotateZ(rotZ) return [ FreeCAD.Placement(mat), round(vx.Length, precision()), round(vy.Length, precision()), round(vz.Length, precision()) ]
def getvec(v1, v2): if not abs( round(v1.getAngle(v2), precision()) in [0, round(math.pi, precision())]): nverts.append(edges[i].Vertexes[-1].Point)
def getCubicDimensions(shape): """Return a list containing the placement, and dimensions of the shape. The dimensios are length, width and height of a the parallelepiped, rounded to the value indicated by `precision`. The placement point is the lowest corner of the shape. If it is not a parallelepiped (cuboid), return None. """ if not isCubic(shape): return None # determine lowest face, which will be our base z = [10, 1000000000000] for i in range(len(shape.Faces)): if shape.Faces[i].CenterOfMass.z < z[1]: z = [i, shape.Faces[i].CenterOfMass.z] if z[0] > 5: return None base = shape.Faces[z[0]] basepoint = base.Edges[0].Vertexes[0].Point plpoint = base.CenterOfMass # basenorm = base.normalAt(0.5, 0.5) # getting length and width vx = vec(base.Edges[0]) vy = vec(base.Edges[1]) # getting rotations rotZ = DraftVecUtils.angle(vx) rotY = DraftVecUtils.angle(vx, App.Vector(vx.x, vx.y, 0)) rotX = DraftVecUtils.angle(vy, App.Vector(vy.x, vy.y, 0)) # getting height vz = None rpi = round(math.pi / 2, precision()) for i in range(1, 6): for e in shape.Faces[i].Edges: if basepoint in [e.Vertexes[0].Point, e.Vertexes[1].Point]: vtemp = vec(e) # print(vtemp) if round(vtemp.getAngle(vx), precision()) == rpi: if round(vtemp.getAngle(vy), precision()) == rpi: vz = vtemp if not vz: return None mat = App.Matrix() mat.move(plpoint) mat.rotateX(rotX) mat.rotateY(rotY) mat.rotateZ(rotZ) return [ App.Placement(mat), round(vx.Length, precision()), round(vy.Length, precision()), round(vz.Length, precision()) ]
def findIntersection(edge1, edge2, infinite1=False, infinite2=False, ex1=False, ex2=False, dts=True, findAll=False): """Return a list containing the intersection points of 2 edges. You can also feed 4 points instead of `edge1` and `edge2`. If `dts` is used, `Shape.distToShape()` is used, which can be buggy. """ def getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2): if pt1: # first check if we don't already have coincident endpoints if pt1 in [pt3, pt4]: return [pt1] elif (pt2 in [pt3, pt4]): return [pt2] norm1 = pt2.sub(pt1).cross(pt3.sub(pt1)) norm2 = pt2.sub(pt4).cross(pt3.sub(pt4)) if not DraftVecUtils.isNull(norm1): try: norm1.normalize() except Part.OCCError: return [] if not DraftVecUtils.isNull(norm2): try: norm2.normalize() except Part.OCCError: return [] if DraftVecUtils.isNull(norm1.cross(norm2)): vec1 = pt2.sub(pt1) vec2 = pt4.sub(pt3) if DraftVecUtils.isNull(vec1) or DraftVecUtils.isNull(vec2): return [] # One of the lines has zero-length try: vec1.normalize() vec2.normalize() except Part.OCCError: return [] norm3 = vec1.cross(vec2) denom = norm3.x + norm3.y + norm3.z if not DraftVecUtils.isNull(norm3) and denom != 0: k = ((pt3.z - pt1.z) * (vec2.x - vec2.y) + (pt3.y - pt1.y) * (vec2.z - vec2.x) + (pt3.x - pt1.x) * (vec2.y - vec2.z)) / denom vec1.scale(k, k, k) intp = pt1.add(vec1) if infinite1 is False and not isPtOnEdge(intp, edge1): return [] if infinite2 is False and not isPtOnEdge(intp, edge2): return [] return [intp] else: return [] # Lines have same direction else: return [] # Lines aren't on same plane # First, check bound boxes if (isinstance(edge1, Part.Edge) and isinstance(edge2, Part.Edge) and (not infinite1) and (not infinite2)): if not edge1.BoundBox.intersect(edge2.BoundBox): return [] # bound boxes don't intersect # First, try to use distToShape if possible if (dts and isinstance(edge1, Part.Edge) and isinstance(edge2, Part.Edge) and (not infinite1) and (not infinite2)): dist, pts, geom = edge1.distToShape(edge2) sol = [] if round(dist, precision()) == 0: for p in pts: if p not in sol: sol.append(p[0]) return sol pt1 = None if isinstance(edge1, FreeCAD.Vector) and isinstance(edge2, FreeCAD.Vector): # we got points directly pt1 = edge1 pt2 = edge2 pt3 = infinite1 pt4 = infinite2 infinite1 = ex1 infinite2 = ex2 return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) elif (geomType(edge1) == "Line") and (geomType(edge2) == "Line"): # we have 2 straight lines pt1, pt2, pt3, pt4 = [ edge1.Vertexes[0].Point, edge1.Vertexes[1].Point, edge2.Vertexes[0].Point, edge2.Vertexes[1].Point ] return getLineIntersections(pt1, pt2, pt3, pt4, infinite1, infinite2) elif ((geomType(edge1) == "Circle") and (geomType(edge2) == "Line") or (geomType(edge1) == "Line") and (geomType(edge2) == "Circle")): # deals with an arc or circle and a line edges = [edge1, edge2] for edge in edges: if geomType(edge) == "Line": line = edge else: arc = edge dirVec = vec(line) dirVec.normalize() pt1 = line.Vertexes[0].Point pt2 = line.Vertexes[1].Point pt3 = arc.Vertexes[0].Point pt4 = arc.Vertexes[-1].Point center = arc.Curve.Center int = [] # first check for coincident endpoints if DraftVecUtils.equals(pt1, pt3) or DraftVecUtils.equals(pt1, pt4): if findAll: int.append(pt1) else: return [pt1] elif pt2 in [pt3, pt4]: if findAll: int.append(pt2) else: return [pt2] if DraftVecUtils.isNull( pt1.sub(center).cross(pt2.sub(center)).cross(arc.Curve.Axis)): # Line and Arc are on same plane dOnLine = center.sub(pt1).dot(dirVec) onLine = FreeCAD.Vector(dirVec) onLine.scale(dOnLine, dOnLine, dOnLine) toLine = pt1.sub(center).add(onLine) if toLine.Length < arc.Curve.Radius: dOnLine = (arc.Curve.Radius**2 - toLine.Length**2)**(0.5) onLine = FreeCAD.Vector(dirVec) onLine.scale(dOnLine, dOnLine, dOnLine) int += [center.add(toLine).add(onLine)] onLine = FreeCAD.Vector(dirVec) onLine.scale(-dOnLine, -dOnLine, -dOnLine) int += [center.add(toLine).add(onLine)] elif round(toLine.Length - arc.Curve.Radius, precision()) == 0: int = [center.add(toLine)] else: return [] else: # Line isn't on Arc's plane if dirVec.dot(arc.Curve.Axis) != 0: toPlane = FreeCAD.Vector(arc.Curve.Axis) toPlane.normalize() d = pt1.dot(toPlane) if not d: return [] dToPlane = center.sub(pt1).dot(toPlane) toPlane = FreeCAD.Vector(pt1) toPlane.scale(dToPlane / d, dToPlane / d, dToPlane / d) ptOnPlane = toPlane.add(pt1) if round( ptOnPlane.sub(center).Length - arc.Curve.Radius, precision()) == 0: int = [ptOnPlane] else: return [] else: return [] if infinite1 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge1): del int[i] if infinite2 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge2): del int[i] return int elif (geomType(edge1) == "Circle") and (geomType(edge2) == "Circle"): # deals with 2 arcs or circles cent1, cent2 = edge1.Curve.Center, edge2.Curve.Center rad1, rad2 = edge1.Curve.Radius, edge2.Curve.Radius axis1, axis2 = edge1.Curve.Axis, edge2.Curve.Axis c2c = cent2.sub(cent1) if cent1.sub(cent2).Length == 0: # circles are concentric return [] if DraftVecUtils.isNull(axis1.cross(axis2)): if round(c2c.dot(axis1), precision()) == 0: # circles are on same plane dc2c = c2c.Length if not DraftVecUtils.isNull(c2c): c2c.normalize() if (round(rad1 + rad2 - dc2c, precision()) < 0 or round(rad1 - dc2c - rad2, precision()) > 0 or round(rad2 - dc2c - rad1, precision()) > 0): return [] else: norm = c2c.cross(axis1) if not DraftVecUtils.isNull(norm): norm.normalize() if DraftVecUtils.isNull(norm): x = 0 else: x = (dc2c**2 + rad1**2 - rad2**2) / (2 * dc2c) y = abs(rad1**2 - x**2)**(0.5) c2c.scale(x, x, x) if round(y, precision()) != 0: norm.scale(y, y, y) int = [cent1.add(c2c).add(norm)] int += [cent1.add(c2c).sub(norm)] else: int = [cent1.add(c2c)] else: return [] # circles are on parallel planes else: # circles aren't on same plane axis1.normalize() axis2.normalize() U = axis1.cross(axis2) V = axis1.cross(U) dToPlane = c2c.dot(axis2) d = V.add(cent1).dot(axis2) V.scale(dToPlane / d, dToPlane / d, dToPlane / d) PtOn2Planes = V.add(cent1) planeIntersectionVector = U.add(PtOn2Planes) intTemp = findIntersection(planeIntersectionVector, edge1, True, True) int = [] for pt in intTemp: if round(pt.sub(cent2).Length - rad2, precision()) == 0: int += [pt] if infinite1 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge1): del int[i] if infinite2 is False: for i in range(len(int) - 1, -1, -1): if not isPtOnEdge(int[i], edge2): del int[i] return int else: print("DraftGeomUtils: Unsupported curve type: " "(" + str(edge1.Curve) + ", " + str(edge2.Curve) + ")") return []
def get_extended_wire(wire, offset_start, offset_end): """Return a wire trimmed (negative offset) or extended (positive offset) at its first vertex, last vertex or both ends. get_extended_wire(wire, -100.0, 0.0) -> returns a copy of the wire with its first 100 mm removed get_extended_wire(wire, 0.0, 100.0) -> returns a copy of the wire extended by 100 mm after it's last vertex """ if min(offset_start, offset_end, offset_start + offset_end) <= -wire.Length: App.Console.PrintError( "debug: get_extended_wire error, wire's length insufficient for trimming.\n" ) return wire if offset_start < 0: # Trim the wire from the first vertex offset_start = -offset_start out_edges = [] for edge in wire.OrderedEdges: if offset_start >= edge.Length: # Remove entire edge offset_start -= edge.Length elif round(offset_start, precision() ) > 0: # Split edge, to remove the required length if edge.Orientation == "Forward": new_edge = edge.split( edge.getParameterByLength( offset_start)).OrderedEdges[1] else: new_edge = edge.split( edge.getParameterByLength( edge.Length - offset_start)).OrderedEdges[0] new_edge.Placement = edge.Placement # Strangely, edge.split discards the placement and orientation new_edge.Orientation = edge.Orientation out_edges.append(new_edge) offset_start = 0 else: # Keep the remaining entire edges out_edges.append(edge) wire = Part.Wire(out_edges) elif offset_start > 0: # Extend the first edge along its normal first_edge = wire.OrderedEdges[0] if first_edge.Orientation == "Forward": start, end = first_edge.FirstParameter, first_edge.LastParameter vec = first_edge.tangentAt(start).multiply(offset_start) else: start, end = first_edge.LastParameter, first_edge.FirstParameter vec = -first_edge.tangentAt(start).multiply(offset_start) if geomType( first_edge ) == "Line": # Replace first edge with the extended new edge new_edge = Part.LineSegment( first_edge.valueAt(start).sub(vec), first_edge.valueAt(end)).toShape() wire = Part.Wire([new_edge] + wire.OrderedEdges[1:]) else: # Add a straight edge before the first vertex new_edge = Part.LineSegment( first_edge.valueAt(start).sub(vec), first_edge.valueAt(start)).toShape() wire = Part.Wire([new_edge] + wire.OrderedEdges) if offset_end < 0: # Trim the wire from the last vertex offset_end = -offset_end out_edges = [] for edge in reversed(wire.OrderedEdges): if offset_end >= edge.Length: # Remove entire edge offset_end -= edge.Length elif round(offset_end, precision() ) > 0: # Split edge, to remove the required length if edge.Orientation == "Forward": new_edge = edge.split( edge.getParameterByLength(edge.Length - offset_end)).OrderedEdges[0] else: new_edge = edge.split( edge.getParameterByLength(offset_end)).OrderedEdges[1] new_edge.Placement = edge.Placement # Strangely, edge.split discards the placement and orientation new_edge.Orientation = edge.Orientation out_edges.insert(0, new_edge) offset_end = 0 else: # Keep the remaining entire edges out_edges.insert(0, edge) wire = Part.Wire(out_edges) elif offset_end > 0: # Extend the last edge along its normal last_edge = wire.OrderedEdges[-1] if last_edge.Orientation == "Forward": start, end = last_edge.FirstParameter, last_edge.LastParameter vec = last_edge.tangentAt(end).multiply(offset_end) else: start, end = last_edge.LastParameter, last_edge.FirstParameter vec = -last_edge.tangentAt(end).multiply(offset_end) if geomType( last_edge ) == "Line": # Replace last edge with the extended new edge new_edge = Part.LineSegment( last_edge.valueAt(start), last_edge.valueAt(end).add(vec)).toShape() wire = Part.Wire(wire.OrderedEdges[:-1] + [new_edge]) else: # Add a straight edge after the last vertex new_edge = Part.LineSegment( last_edge.valueAt(end), last_edge.valueAt(end).add(vec)).toShape() wire = Part.Wire(wire.OrderedEdges + [new_edge]) return wire
def fillet(lEdges, r, chamfer=False): """Return a list of sorted edges describing a round corner. Author: Jacques-Antoine Gaudin """ def getCurveType(edge, existingCurveType=None): """Build or complete a dictionary containing edges. The dictionary contains edges with keys 'Arc' and 'Line'. """ if not existingCurveType: existingCurveType = {'Line': [], 'Arc': []} if issubclass(type(edge.Curve), Part.LineSegment): existingCurveType['Line'] += [edge] elif issubclass(type(edge.Curve), Part.Line): existingCurveType['Line'] += [edge] elif issubclass(type(edge.Curve), Part.Circle): existingCurveType['Arc'] += [edge] else: raise ValueError("Edge's curve must be either Line or Arc") return existingCurveType rndEdges = lEdges[0:2] rndEdges = Part.__sortEdges__(rndEdges) if len(rndEdges) < 2: return rndEdges if r <= 0: print("DraftGeomUtils.fillet: Error: radius is negative.") return rndEdges curveType = getCurveType(rndEdges[0]) curveType = getCurveType(rndEdges[1], curveType) lVertexes = rndEdges[0].Vertexes + [rndEdges[1].Vertexes[-1]] if len(curveType['Line']) == 2: # Deals with 2-line-edges lists U1 = lVertexes[0].Point.sub(lVertexes[1].Point) U1.normalize() U2 = lVertexes[2].Point.sub(lVertexes[1].Point) U2.normalize() alpha = U1.getAngle(U2) if chamfer: # correcting r value so the size of the chamfer = r beta = math.pi - alpha / 2 r = (r / 2) / math.cos(beta) # Edges have same direction if (round(alpha, precision()) == 0 or round(alpha - math.pi, precision()) == 0): print("DraftGeomUtils.fillet: Warning: " "edges have same direction. Did nothing") return rndEdges dToCenter = r / math.sin(alpha / 2.0) dToTangent = (dToCenter**2 - r**2)**(0.5) dirVect = App.Vector(U1) dirVect.scale(dToTangent, dToTangent, dToTangent) arcPt1 = lVertexes[1].Point.add(dirVect) dirVect = U2.add(U1) dirVect.normalize() dirVect.scale(dToCenter - r, dToCenter - r, dToCenter - r) arcPt2 = lVertexes[1].Point.add(dirVect) dirVect = App.Vector(U2) dirVect.scale(dToTangent, dToTangent, dToTangent) arcPt3 = lVertexes[1].Point.add(dirVect) if (dToTangent > lEdges[0].Length) or (dToTangent > lEdges[1].Length): print("DraftGeomUtils.fillet: Error: radius value ", r, " is too high") return rndEdges if chamfer: rndEdges[1] = Part.Edge(Part.LineSegment(arcPt1, arcPt3)) else: rndEdges[1] = Part.Edge(Part.Arc(arcPt1, arcPt2, arcPt3)) if lVertexes[0].Point == arcPt1: # fillet consumes entire first edge rndEdges.pop(0) else: rndEdges[0] = Part.Edge( Part.LineSegment(lVertexes[0].Point, arcPt1)) if lVertexes[2].Point != arcPt3: # fillet does not consume entire second edge rndEdges += [ Part.Edge(Part.LineSegment(arcPt3, lVertexes[2].Point)) ] return rndEdges elif len(curveType['Arc']) == 1: # Deals with lists containing an arc and a line if lEdges[0] in curveType['Arc']: lineEnd = lVertexes[2] arcEnd = lVertexes[0] arcFirst = True else: lineEnd = lVertexes[0] arcEnd = lVertexes[2] arcFirst = False arcCenter = curveType['Arc'][0].Curve.Center arcRadius = curveType['Arc'][0].Curve.Radius arcAxis = curveType['Arc'][0].Curve.Axis arcLength = curveType['Arc'][0].Length U1 = lineEnd.Point.sub(lVertexes[1].Point) U1.normalize() toCenter = arcCenter.sub(lVertexes[1].Point) if arcFirst: # make sure the tangent points towards the arc T = arcAxis.cross(toCenter) else: T = toCenter.cross(arcAxis) projCenter = toCenter.dot(U1) if round(abs(projCenter), precision()) > 0: normToLine = U1.cross(T).cross(U1) else: normToLine = App.Vector(toCenter) normToLine.normalize() dCenterToLine = toCenter.dot(normToLine) - r if round(projCenter, precision()) > 0: newRadius = arcRadius - r elif (round(projCenter, precision()) < 0 or (round(projCenter, precision()) == 0 and U1.dot(T) > 0)): newRadius = arcRadius + r else: print("DraftGeomUtils.fillet: Warning: " "edges are already tangent. Did nothing") return rndEdges toNewCent = newRadius**2 - dCenterToLine**2 if toNewCent > 0: toNewCent = abs(abs(projCenter) - toNewCent**(0.5)) else: print("DraftGeomUtils.fillet: Error: radius value ", r, " is too high") return rndEdges U1.scale(toNewCent, toNewCent, toNewCent) normToLine.scale(r, r, r) newCent = lVertexes[1].Point.add(U1).add(normToLine) arcPt1 = lVertexes[1].Point.add(U1) arcPt2 = lVertexes[1].Point.sub(newCent) arcPt2.normalize() arcPt2.scale(r, r, r) arcPt2 = arcPt2.add(newCent) if newRadius == arcRadius - r: arcPt3 = newCent.sub(arcCenter) else: arcPt3 = arcCenter.sub(newCent) arcPt3.normalize() arcPt3.scale(r, r, r) arcPt3 = arcPt3.add(newCent) arcPt = [arcPt1, arcPt2, arcPt3] # Warning: In the following I used a trick for calling # the right element in arcPt or V: # arcFirst is a boolean so - not arcFirst is -0 or -1 # list[-1] is the last element of a list and list[0] the first # this way I don't have to proceed tests to know the position # of the arc myTrick = not arcFirst V = [arcPt3] V += [arcEnd.Point] toCenter.scale(-1, -1, -1) delLength = arcRadius * V[0].sub(arcCenter).getAngle(toCenter) if delLength > arcLength or toNewCent > curveType['Line'][0].Length: print("DraftGeomUtils.fillet: Error: radius value ", r, " is too high") return rndEdges arcAsEdge = arcFrom2Pts(V[-arcFirst], V[-myTrick], arcCenter, arcAxis) V = [lineEnd.Point, arcPt1] lineAsEdge = Part.Edge(Part.LineSegment(V[-arcFirst], V[myTrick])) rndEdges[not arcFirst] = arcAsEdge rndEdges[arcFirst] = lineAsEdge if chamfer: rndEdges[1:1] = [ Part.Edge(Part.LineSegment(arcPt[-arcFirst], arcPt[-myTrick])) ] else: rndEdges[1:1] = [ Part.Edge(Part.Arc(arcPt[-arcFirst], arcPt[1], arcPt[-myTrick])) ] return rndEdges elif len(curveType['Arc']) == 2: # Deals with lists of 2 arc-edges (arcCenter, arcRadius, arcAxis, arcLength, toCenter, T, newRadius) = [], [], [], [], [], [], [] for i in range(2): arcCenter += [curveType['Arc'][i].Curve.Center] arcRadius += [curveType['Arc'][i].Curve.Radius] arcAxis += [curveType['Arc'][i].Curve.Axis] arcLength += [curveType['Arc'][i].Length] toCenter += [arcCenter[i].sub(lVertexes[1].Point)] T += [arcAxis[0].cross(toCenter[0])] T += [toCenter[1].cross(arcAxis[1])] CentToCent = toCenter[1].sub(toCenter[0]) dCentToCent = CentToCent.Length sameDirection = (arcAxis[0].dot(arcAxis[1]) > 0) TcrossT = T[0].cross(T[1]) if sameDirection: if round(TcrossT.dot(arcAxis[0]), precision()) > 0: newRadius += [arcRadius[0] + r] newRadius += [arcRadius[1] + r] elif round(TcrossT.dot(arcAxis[0]), precision()) < 0: newRadius += [arcRadius[0] - r] newRadius += [arcRadius[1] - r] elif T[0].dot(T[1]) > 0: newRadius += [arcRadius[0] + r] newRadius += [arcRadius[1] + r] else: print("DraftGeomUtils.fillet: Warning: " "edges are already tangent. Did nothing") return rndEdges elif not sameDirection: if round(TcrossT.dot(arcAxis[0]), precision()) > 0: newRadius += [arcRadius[0] + r] newRadius += [arcRadius[1] - r] elif round(TcrossT.dot(arcAxis[0]), precision()) < 0: newRadius += [arcRadius[0] - r] newRadius += [arcRadius[1] + r] elif T[0].dot(T[1]) > 0: if arcRadius[0] > arcRadius[1]: newRadius += [arcRadius[0] - r] newRadius += [arcRadius[1] + r] elif arcRadius[1] > arcRadius[0]: newRadius += [arcRadius[0] + r] newRadius += [arcRadius[1] - r] else: print("DraftGeomUtils.fillet: Warning: " "arcs are coincident. Did nothing") return rndEdges else: print("DraftGeomUtils.fillet: Warning: " "edges are already tangent. Did nothing") return rndEdges if (newRadius[0] + newRadius[1] < dCentToCent or newRadius[0] - newRadius[1] > dCentToCent or newRadius[1] - newRadius[0] > dCentToCent): print("DraftGeomUtils.fillet: Error: radius value ", r, " is too high") return rndEdges x = ((dCentToCent**2 + newRadius[0]**2 - newRadius[1]**2) / (2 * dCentToCent)) y = (newRadius[0]**2 - x**2)**(0.5) CentToCent.normalize() toCenter[0].normalize() toCenter[1].normalize() if abs(toCenter[0].dot(toCenter[1])) != 1: normVect = CentToCent.cross(CentToCent.cross(toCenter[0])) else: normVect = T[0] normVect.normalize() CentToCent.scale(x, x, x) normVect.scale(y, y, y) newCent = arcCenter[0].add(CentToCent.add(normVect)) CentToNewCent = [newCent.sub(arcCenter[0]), newCent.sub(arcCenter[1])] for i in range(2): CentToNewCent[i].normalize() if newRadius[i] == arcRadius[i] + r: CentToNewCent[i].scale(-r, -r, -r) else: CentToNewCent[i].scale(r, r, r) toThirdPt = lVertexes[1].Point.sub(newCent) toThirdPt.normalize() toThirdPt.scale(r, r, r) arcPt1 = newCent.add(CentToNewCent[0]) arcPt2 = newCent.add(toThirdPt) arcPt3 = newCent.add(CentToNewCent[1]) arcPt = [arcPt1, arcPt2, arcPt3] arcAsEdge = [] for i in range(2): toCenter[i].scale(-1, -1, -1) delLength = (arcRadius[i] * arcPt[-i].sub(arcCenter[i]).getAngle(toCenter[i])) if delLength > arcLength[i]: print("DraftGeomUtils.fillet: Error: radius value ", r, " is too high") return rndEdges V = [arcPt[-i], lVertexes[-i].Point] arcAsEdge += [ arcFrom2Pts(V[i - 1], V[-i], arcCenter[i], arcAxis[i]) ] rndEdges[0] = arcAsEdge[0] rndEdges[1] = arcAsEdge[1] if chamfer: rndEdges[1:1] = [Part.Edge(Part.LineSegment(arcPt[0], arcPt[2]))] else: rndEdges[1:1] = [Part.Edge(Part.Arc(arcPt[0], arcPt[1], arcPt[2]))] return rndEdges