| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | """Provides various functions to work with offsets.""" |
| | |
| | |
| | |
| |
|
| | import lazy_loader.lazy_loader as lz |
| |
|
| | import FreeCAD as App |
| | import DraftVecUtils |
| |
|
| | from draftgeoutils.general import geomType, vec |
| | from draftgeoutils.geometry import get_normal |
| | from draftgeoutils.wires import isReallyClosed |
| | from draftgeoutils.intersections import wiresIntersect, connect |
| |
|
| | |
| | Part = lz.LazyLoader("Part", globals(), "Part") |
| |
|
| | |
| | |
| |
|
| |
|
| | def pocket2d(shape, offset): |
| | """Return a list of wires obtained from offsetting wires from the shape. |
| | |
| | Return a list of wires obtained from offsetting the wires |
| | from the given shape by the given offset, and intersection if needed. |
| | """ |
| | |
| | length = 0 |
| | outerWire = None |
| | innerWires = [] |
| | for w in shape.Wires: |
| | if w.BoundBox.DiagonalLength > length: |
| | outerWire = w |
| | length = w.BoundBox.DiagonalLength |
| |
|
| | if not outerWire: |
| | return [] |
| |
|
| | for w in shape.Wires: |
| | if w.hashCode() != outerWire.hashCode(): |
| | innerWires.append(w) |
| |
|
| | o = outerWire.makeOffset(-offset) |
| |
|
| | if not o.Wires: |
| | return [] |
| |
|
| | offsetWires = o.Wires |
| | |
| | if not innerWires: |
| | return offsetWires |
| |
|
| | for innerWire in innerWires: |
| | i = innerWire.makeOffset(offset) |
| | if len(innerWire.Edges) == 1: |
| | e = innerWire.Edges[0] |
| | if isinstance(e.Curve, Part.Circle): |
| | e = Part.makeCircle(e.Curve.Radius + offset, e.Curve.Center, e.Curve.Axis) |
| | i = Part.Wire(e) |
| | if i.Wires: |
| | |
| | for w in i.Wires: |
| | added = False |
| | |
| | k = list(range(len(offsetWires))) |
| | for j in k: |
| | |
| | ow = offsetWires[j] |
| | if ow: |
| | if wiresIntersect(w, ow): |
| | |
| | f1 = Part.Face(ow) |
| | f2 = Part.Face(w) |
| | f3 = f1.cut(f2) |
| | |
| | offsetWires[j] = f3.Wires[0] |
| | if len(f3.Wires) > 1: |
| | |
| | offsetWires.extend(f3.Wires[1:]) |
| | added = True |
| | else: |
| | a = w.BoundBox |
| | b = ow.BoundBox |
| | if ( |
| | (a.XMin <= b.XMin) |
| | and (a.YMin <= b.YMin) |
| | and (a.ZMin <= b.ZMin) |
| | and (a.XMax >= b.XMax) |
| | and (a.YMax >= b.YMax) |
| | and (a.ZMax >= b.ZMax) |
| | ): |
| | |
| | |
| | offsetWires[j] = None |
| | added = True |
| | |
| | |
| | if not added: |
| | |
| | offsetWires.append(w) |
| | offsetWires = [o for o in offsetWires if o is not None] |
| |
|
| | return offsetWires |
| |
|
| |
|
| | def offset(edge, vector, trim=False): |
| | """Return a copy of the edge at a certain vector offset. |
| | |
| | If the edge is an arc, the vector will be added at its first point |
| | and a complete circle will be returned. |
| | |
| | None if there is a problem. |
| | |
| | Parameters |
| | ---------- |
| | edge: Part.Shape |
| | the edge to offset |
| | vector: Base::Vector3 |
| | the vector by which the edge is to be offset |
| | trim: bool, optional |
| | If `edge` is an arc and `trim` is `True`, the resulting |
| | arc will be trimmed to the proper angle |
| | |
| | Returns |
| | ------- |
| | Part.Shape |
| | The offset shape |
| | """ |
| | if not isinstance(edge, Part.Shape) or not isinstance(vector, App.Vector): |
| | return None |
| |
|
| | if geomType(edge) == "Line": |
| | v1 = App.Vector.add(edge.Vertexes[0].Point, vector) |
| | v2 = App.Vector.add(edge.Vertexes[-1].Point, vector) |
| | return Part.LineSegment(v1, v2).toShape() |
| |
|
| | elif geomType(edge) == "Circle": |
| | rad = edge.Vertexes[0].Point.sub(edge.Curve.Center) |
| | curve = Part.Circle(edge.Curve) |
| | curve.Radius = App.Vector.add(rad, vector).Length |
| | if trim: |
| | return Part.ArcOfCircle(curve, edge.FirstParameter, edge.LastParameter).toShape() |
| | elif geomType(edge) == "Ellipse": |
| | rad = edge.Vertexes[0].Point.sub(edge.Curve.Center) |
| | curve = edge.Curve.copy() |
| | if vector.getAngle(rad) < 1: |
| | curve.MajorRadius = curve.MajorRadius + vector.Length |
| | curve.MinorRadius = curve.MinorRadius + vector.Length |
| | else: |
| | curve.MajorRadius = curve.MajorRadius - vector.Length |
| | curve.MinorRadius = curve.MinorRadius - vector.Length |
| | return curve.toShape() |
| | else: |
| | return None |
| |
|
| |
|
| | def offsetWire( |
| | wire, |
| | dvec, |
| | bind=False, |
| | occ=False, |
| | widthList=None, |
| | offsetMode=None, |
| | alignList=[], |
| | normal=None, |
| | basewireOffset=0, |
| | wireNedge=False, |
| | ): |
| | |
| | """Offset the wire along the given vector. |
| | |
| | Parameters |
| | ---------- |
| | wire as a sorted list of edges (the list is used directly), or as a |
| | wire or a face (Draft Wire with MakeFace True or False supported). |
| | |
| | The vector will be applied at the first vertex of the wire. If bind |
| | is True (and the shape is open), the original wire and the offsetted one |
| | are bound by 2 edges, forming a face. |
| | |
| | If widthList is provided (values only, not lengths - i.e. no unit), |
| | each value will be used to offset each corresponding edge in the wire. |
| | |
| | The 1st value overrides 'dvec' for 1st segment of wire; |
| | if a value is zero, value of 'widthList[0]' will follow; |
| | if widthList[0]' == 0, but dvec still provided, dvec will be followed |
| | |
| | offsetMode="BasewireMode" or None |
| | |
| | If alignList is provided, |
| | each value will be used to offset each corresponding edge |
| | in the wire with corresponding index. |
| | |
| | 'basewireOffset' corresponds to 'offset' in ArchWall which offset |
| | the basewire before creating the wall outline; or |
| | it can be a list, e.g. corresponds to 'overrideOffset' in ArchWall, so |
| | offset can be 'per segment' of wire, or 'per wall segment' in ArchWall |
| | |
| | OffsetWire() is now aware of width and align per edge |
| | Primarily for use with ArchWall based on Sketch object |
| | |
| | To Do |
| | ----- |
| | `dvec` vector to offset is now derived (and can be ignored) |
| | in this function if widthList and alignList are provided |
| | - 'dvec' to be obsolete in future? |
| | """ |
| |
|
| | if isinstance(wire, list) and isinstance(wire[0], Part.Edge): |
| | edges = wire.copy() |
| | wire = Part.Wire(edges) |
| | closed = wire.isClosed() |
| | elif isinstance(wire, Part.Wire): |
| | |
| | |
| | |
| | edges = Part.__sortEdges__(wire.Edges) |
| | wire = Part.Wire(edges) |
| | closed = wire.isClosed() |
| | elif isinstance(wire, Part.Face): |
| | |
| | edges = Part.__sortEdges__(wire.OuterWire.Edges) |
| | closed = True |
| | elif isinstance(wire, Part.Edge): |
| | edges = [wire] |
| | closed = wire.isClosed() |
| | else: |
| | print("Either Part.Wire or Part.Edges should be provided, " "returning None") |
| | return None |
| |
|
| | |
| | |
| | |
| | |
| | |
| | if normal: |
| | norm = normal |
| | else: |
| | norm = get_normal(wire) |
| | |
| | if norm is None: |
| | norm = App.Vector(0, 0, 1) |
| |
|
| | nedges = [] |
| | if occ: |
| | length = abs(dvec.Length) |
| | if not length: |
| | return None |
| |
|
| | if wire.Wires: |
| | wire = wire.Wires[0] |
| | else: |
| | wire = Part.Wire(edges) |
| |
|
| | try: |
| | off = wire.makeOffset(length) |
| | except Part.OCCError: |
| | return None |
| | else: |
| | return off |
| |
|
| | |
| | e = edges[0] |
| |
|
| | |
| | |
| | |
| | |
| | alignListC = list(alignList) |
| |
|
| | |
| | firstDir = None |
| | try: |
| | if alignListC[0] == "Left": |
| | firstDir = 1 |
| | firstAlign = "Left" |
| | elif alignListC[0] == "Right": |
| | firstDir = -1 |
| | firstAlign = "Right" |
| | elif alignListC[0] == "Center": |
| | firstDir = 1 |
| | firstAlign = "Center" |
| | except IndexError: |
| | |
| | |
| | pass |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | if not firstDir: |
| | |
| | if isinstance(e.Curve, (Part.Circle, Part.Ellipse)): |
| | v0 = e.tangentAt(e.FirstParameter).cross(norm) |
| | else: |
| | v0 = vec(e).cross(norm) |
| | |
| | |
| | |
| | v0.normalize() |
| | v1 = App.Vector(dvec).normalize() |
| | if v0.isEqual(v1, 0.0001): |
| | |
| | firstDir = 1 |
| | firstAlign = "Left" |
| | alignListC.append("Left") |
| | elif v0.isEqual(v1.negative(), 0.0001): |
| | |
| | firstDir = -1 |
| | firstAlign = "Right" |
| | alignListC.append("Right") |
| | else: |
| | print(" something wrong with firstDir ") |
| | firstAlign = "Left" |
| | alignListC.append("Left") |
| |
|
| | if not isinstance(basewireOffset, list): |
| | basewireOffset = [basewireOffset] |
| |
|
| | for i in range(len(edges)): |
| | |
| | |
| | curredge = Part.Shape(edges[i]).Edges[0] |
| |
|
| | |
| | if i == 0: |
| | |
| | |
| | firstOrientation = curredge.Vertexes[0].Orientation |
| | curOrientation = firstOrientation |
| | curDir = firstDir |
| | curAlign = firstAlign |
| | delta = dvec |
| |
|
| | |
| | if i != 0: |
| | |
| | if isinstance(curredge.Curve, (Part.Circle, Part.Ellipse)): |
| | |
| | |
| | delta = curredge.tangentAt(curredge.FirstParameter).cross(norm) |
| | else: |
| | delta = vec(curredge).cross(norm) |
| | |
| | curOrientation = curredge.Vertexes[0].Orientation |
| |
|
| | |
| | if widthList: |
| | try: |
| | if widthList[i] > 0: |
| | delta = DraftVecUtils.scaleTo(delta, widthList[i]) |
| | elif dvec: |
| | delta = DraftVecUtils.scaleTo(delta, dvec.Length) |
| | else: |
| | |
| | |
| | delta = DraftVecUtils.scaleTo(delta, 200) |
| | except Part.OCCError: |
| | if dvec: |
| | delta = DraftVecUtils.scaleTo(delta, dvec.Length) |
| | else: |
| | |
| | |
| | delta = DraftVecUtils.scaleTo(delta, 200) |
| | else: |
| | delta = DraftVecUtils.scaleTo(delta, dvec.Length) |
| |
|
| | |
| | try: |
| | currOffset = basewireOffset[i] |
| | except: |
| | currOffset = basewireOffset[0] |
| |
|
| | |
| | |
| | if i == 0: |
| | if alignListC[0] == "Center": |
| | delta = DraftVecUtils.scaleTo(delta, delta.Length / 2) |
| | |
| | |
| | |
| | if i != 0: |
| | try: |
| | if alignListC[i] == "Left": |
| | curDir = 1 |
| | curAlign = "Left" |
| | elif alignListC[i] == "Right": |
| | curDir = -1 |
| | curAlign = "Right" |
| | delta = delta.negative() |
| | elif alignListC[i] == "Center": |
| | curDir = 1 |
| | curAlign = "Center" |
| | delta = DraftVecUtils.scaleTo(delta, delta.Length / 2) |
| | except IndexError: |
| | curDir = firstDir |
| | curAlign = firstAlign |
| | if firstAlign == "Right": |
| | delta = delta.negative() |
| | elif firstAlign == "Center": |
| | delta = DraftVecUtils.scaleTo(delta, delta.Length / 2) |
| |
|
| | |
| | if offsetMode is None: |
| | |
| | |
| | |
| |
|
| | |
| | if (curOrientation == firstOrientation) != (curDir == firstDir): |
| | if curAlign in ["Left", "Right"]: |
| | |
| | |
| | |
| | if currOffset: |
| | delta = DraftVecUtils.scaleTo(delta, currOffset) |
| | nedge = offset(curredge, delta, trim=True) |
| | else: |
| | nedge = curredge |
| | elif curAlign == "Center": |
| | delta = delta.negative() |
| | nedge = offset(curredge, delta, trim=True) |
| | else: |
| | |
| | |
| | |
| | |
| | |
| | if currOffset: |
| | if curAlign in ["Left", "Right"]: |
| | delta = DraftVecUtils.scaleTo(delta, delta.Length + currOffset) |
| | nedge = offset(curredge, delta, trim=True) |
| |
|
| | |
| | |
| | if curOrientation == "Reversed": |
| | if not isinstance(curredge.Curve, (Part.Circle, Part.Ellipse)): |
| | |
| | nedge = Part.Edge(nedge.Vertexes[1], nedge.Vertexes[0]) |
| | elif nedge.isClosed(): |
| | pass |
| | else: |
| | midParameter = ( |
| | nedge.FirstParameter + (nedge.LastParameter - nedge.FirstParameter) / 2 |
| | ) |
| | midOfArc = nedge.valueAt(midParameter) |
| | nedge = Part.ArcOfCircle( |
| | nedge.Vertexes[1].Point, midOfArc, nedge.Vertexes[0].Point |
| | ).toShape() |
| | |
| | |
| |
|
| | elif offsetMode in ["BasewireMode"]: |
| | if not (curOrientation == firstOrientation) != (curDir == firstDir): |
| | if curAlign in ["Left", "Right"]: |
| | |
| | |
| | |
| | if currOffset: |
| | delta = DraftVecUtils.scaleTo(delta, currOffset) |
| | nedge = offset(curredge, delta, trim=True) |
| | else: |
| | nedge = curredge |
| | elif curAlign == "Center": |
| | delta = delta.negative() |
| | nedge = offset(curredge, delta, trim=True) |
| | else: |
| | if curAlign in ["Left", "Right"]: |
| | |
| | |
| | |
| | if currOffset: |
| | delta = DraftVecUtils.scaleTo(delta, delta.Length + currOffset) |
| | nedge = offset(curredge, delta, trim=True) |
| | elif curAlign == "Center": |
| | nedge = offset(curredge, delta, trim=True) |
| | if curOrientation == "Reversed": |
| | if not isinstance(curredge.Curve, (Part.Circle, Part.Ellipse)): |
| | |
| | nedge = Part.Edge(nedge.Vertexes[1], nedge.Vertexes[0]) |
| | elif nedge.isClosed(): |
| | pass |
| | else: |
| | midParameter = ( |
| | nedge.FirstParameter + (nedge.LastParameter - nedge.FirstParameter) / 2 |
| | ) |
| | midOfArc = nedge.valueAt(midParameter) |
| | nedge = Part.ArcOfCircle( |
| | nedge.Vertexes[1].Point, midOfArc, nedge.Vertexes[0].Point |
| | ).toShape() |
| | |
| | |
| | else: |
| | print(" something wrong ") |
| | return None |
| | if not nedge: |
| | return None |
| |
|
| | |
| | nedges.append(nedge) |
| |
|
| | if len(edges) > 1: |
| | |
| | |
| | wire, connectEdgesF, connectEdges = connect(nedges, closed, wireNedge=True) |
| | else: |
| | |
| | |
| | wire = Part.Wire(nedges[0]) |
| | connectEdgesF = connectEdges = nedges |
| |
|
| | if bind and not closed: |
| | e1 = Part.LineSegment(edges[0].Vertexes[0].Point, wire[0].Vertexes[0].Point).toShape() |
| | e2 = Part.LineSegment(edges[-1].Vertexes[-1].Point, wire[-1].Vertexes[-1].Point).toShape() |
| | |
| | alledges = edges.extend(nedges) |
| | alledges = alledges.extend([e1, e2]) |
| | w = Part.Wire(alledges) |
| | return w |
| | else: |
| | if wireNedge: |
| | return (wire, connectEdgesF, connectEdges, nedges) |
| | else: |
| | return wire |
| |
|
| |
|
| | |
| |
|