Esempio n. 1
0
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
Esempio n. 2
0
def removeInterVertices(wire):
    """removeInterVertices(wire) - remove unneeded vertices (those that
        are in the middle of a straight line) from a wire, returns a new wire."""
    edges = Part.__sortEdges__(wire.Edges)
    nverts = []

    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)

    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
Esempio n. 3
0
def circlefrom1Line2Points(edge, p1, p2):
    """circlefrom1Line2Points(edge, Vector, Vector)"""
    p1_p2 = edg(p1, p2)
    s = findIntersection(edge, p1_p2, True, True)
    if not s: return None
    s = s[0]
    v1 = p1.sub(s)
    v2 = p2.sub(s)
    projectedDist = math.sqrt(abs(v1.dot(v2)))
    edgeDir = vec(edge)
    edgeDir.normalize()
    projectedCen1 = Vector.add(s, Vector(edgeDir).multiply(projectedDist))
    projectedCen2 = Vector.add(s, Vector(edgeDir).multiply(-projectedDist))
    perpEdgeDir = edgeDir.cross(Vector(0, 0, 1))
    perpCen1 = Vector.add(projectedCen1, perpEdgeDir)
    perpCen2 = Vector.add(projectedCen2, perpEdgeDir)
    mid = findMidpoint(p1_p2)
    x = DraftVecUtils.crossproduct(vec(p1_p2))
    x.normalize()
    perp_mid = Vector.add(mid, x)
    cen1 = findIntersection(edg(projectedCen1, perpCen1), edg(mid, perp_mid),
                            True, True)
    cen2 = findIntersection(edg(projectedCen2, perpCen2), edg(mid, perp_mid),
                            True, True)
    circles = []
    if cen1:
        radius = DraftVecUtils.dist(projectedCen1, cen1[0])
        circles.append(Part.Circle(cen1[0], NORM, radius))
    if cen2:
        radius = DraftVecUtils.dist(projectedCen2, cen2[0])
        circles.append(Part.Circle(cen2[0], NORM, radius))

    if circles: return circles
    else: return None
Esempio n. 4
0
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
Esempio n. 5
0
def getNormal(shape):
    """Find the normal of a shape or list of points, if possible."""
    if isinstance(shape, (list, tuple)):
        if len(shape) >= 3:
            v1 = shape[1].sub(shape[0])
            v2 = shape[2].sub(shape[0])
            n = v2.cross(v1)
            if n.Length:
                return n
        return None

    n = FreeCAD.Vector(0, 0, 1)
    if shape.isNull():
        return n

    if (shape.ShapeType == "Face") and hasattr(shape, "normalAt"):
        n = shape.copy().normalAt(0.5, 0.5)
    elif shape.ShapeType == "Edge":
        if geomType(shape.Edges[0]) in ["Circle", "Ellipse"]:
            n = shape.Edges[0].Curve.Axis
        elif (geomType(shape.Edges[0]) == "BSplineCurve"
              or geomType(shape.Edges[0]) == "BezierCurve"):
            n = getSplineNormal(shape.Edges[0])
    else:
        for e in shape.Edges:
            if geomType(e) in ["Circle", "Ellipse"]:
                n = e.Curve.Axis
                break
            elif (geomType(e) == "BSplineCurve"
                  or geomType(e) == "BezierCurve"):
                n = getSplineNormal(e)
                break

            e1 = vec(shape.Edges[0])
            for i in range(1, len(shape.Edges)):
                e2 = vec(shape.Edges[i])
                if 0.1 < abs(e1.getAngle(e2)) < 3.14:
                    n = e1.cross(e2).normalize()
                    break

    # Check the 3D view to flip the normal if the GUI is available
    if FreeCAD.GuiUp:
        vdir = gui_utils.get_3d_view().getViewDirection()
        if n.getAngle(vdir) < 0.78:
            n = n.negative()

    if not n.Length:
        return None

    return n
Esempio n. 6
0
def findHomotheticCenterOfCircles(circle1, circle2):
    """Calculate the homothetic centers from two circles.

    Return None if the objects are not circles, or if they are concentric.

    http://en.wikipedia.org/wiki/Homothetic_center
    http://mathworld.wolfram.com/HomotheticCenter.html
    """
    if (geomType(circle1) == "Circle" and geomType(circle2) == "Circle"):
        print("debug: findHomotheticCenterOfCircles bad parameters!")
        return None

    if DraftVecUtils.equals(circle1.Curve.Center,
                            circle2.Curve.Center):
        return None

    cen1_cen2 = Part.LineSegment(circle1.Curve.Center,
                                 circle2.Curve.Center).toShape()
    cenDir = vec(cen1_cen2)
    cenDir.normalize()

    # Get the perpedicular vector.
    perpCenDir = cenDir.cross(App.Vector(0, 0, 1))
    perpCenDir.normalize()

    # Get point on first circle
    p1 = App.Vector.add(circle1.Curve.Center,
                        App.Vector(perpCenDir).multiply(circle1.Curve.Radius))

    centers = []
    # Calculate inner homothetic center
    # Get point on second circle
    p2_inner = App.Vector.add(circle1.Curve.Center,
                              App.Vector(perpCenDir).multiply(-circle1.Curve.Radius))
    hCenterInner = DraftVecUtils.intersect(circle1.Curve.Center,
                                           circle2.Curve.Center,
                                           p1, p2_inner,
                                           True, True)
    if hCenterInner:
        centers.append(hCenterInner)

    # Calculate outer homothetic center; it only exists if the circles
    # have different radii
    if circle1.Curve.Radius != circle2.Curve.Radius:
        # Get point on second circle
        p2_outer = App.Vector.add(circle1.Curve.Center,
                                  App.Vector(perpCenDir).multiply(circle1.Curve.Radius))
        hCenterOuter = DraftVecUtils.intersect(circle1.Curve.Center,
                                               circle2.Curve.Center,
                                               p1, p2_outer,
                                               True, True)
        if hCenterOuter:
            centers.append(hCenterOuter)

    if centers:
        return centers
    else:
        return None
Esempio n. 7
0
def findHomotheticCenterOfCircles(circle1, circle2):
    """Calculate the homothetic center(s) of two circles.

    http://en.wikipedia.org/wiki/Homothetic_center
    http://mathworld.wolfram.com/HomotheticCenter.html
    """

    if (geomType(circle1) == "Circle") and (geomType(circle2) == "Circle"):
        if DraftVecUtils.equals(circle1.Curve.Center, circle2.Curve.Center):
            return None

        cen1_cen2 = Part.LineSegment(circle1.Curve.Center,
                                     circle2.Curve.Center).toShape()
        cenDir = vec(cen1_cen2)
        cenDir.normalize()

        # Get the perpedicular vector.
        perpCenDir = cenDir.cross(Vector(0, 0, 1))
        perpCenDir.normalize()

        # Get point on first circle
        p1 = Vector.add(circle1.Curve.Center,
                        Vector(perpCenDir).multiply(circle1.Curve.Radius))

        centers = []
        # Calculate inner homothetic center
        # Get point on second circle
        p2_inner = Vector.add(
            circle1.Curve.Center,
            Vector(perpCenDir).multiply(-circle1.Curve.Radius))
        hCenterInner = DraftVecUtils.intersect(circle1.Curve.Center,
                                               circle2.Curve.Center, p1,
                                               p2_inner, True, True)
        if hCenterInner:
            centers.append(hCenterInner)

        # Calculate outer homothetic center (only exists of the circles have different radii)
        if circle1.Curve.Radius != circle2.Curve.Radius:
            # Get point on second circle
            p2_outer = Vector.add(
                circle1.Curve.Center,
                Vector(perpCenDir).multiply(circle1.Curve.Radius))
            hCenterOuter = DraftVecUtils.intersect(circle1.Curve.Center,
                                                   circle2.Curve.Center, p1,
                                                   p2_outer, True, True)
            if hCenterOuter:
                centers.append(hCenterOuter)

        if len(centers):
            return centers
        else:
            return None

    else:
        FreeCAD.Console.PrintMessage(
            "debug: findHomotheticCenterOfCirclescleFrom3tan bad parameters!\n"
        )
        return None
Esempio n. 8
0
def circleFrom2LinesRadius(edge1, edge2, radius):
    """Retun a list of circles from two edges and one radius.

    It calculates 4 centers.
    """
    intsec = findIntersection(edge1, edge2, True, True)
    if not intsec:
        return None

    intsec = intsec[0]
    bis12 = angleBisection(edge1, edge2)
    bis21 = Part.LineSegment(bis12.Vertexes[0].Point,
                             DraftVecUtils.rotate(vec(bis12), math.pi/2.0))
    ang12 = abs(DraftVecUtils.angle(vec(edge1), vec(edge2)))
    ang21 = math.pi - ang12
    dist12 = radius / math.sin(ang12 * 0.5)
    dist21 = radius / math.sin(ang21 * 0.5)

    circles = []
    cen = App.Vector.add(intsec, vec(bis12).multiply(dist12))
    circles.append(Part.Circle(cen, NORM, radius))

    cen = App.Vector.add(intsec, vec(bis12).multiply(-dist12))
    circles.append(Part.Circle(cen, NORM, radius))

    cen = App.Vector.add(intsec, vec(bis21).multiply(dist21))
    circles.append(Part.Circle(cen, NORM, radius))

    cen = App.Vector.add(intsec, vec(bis21).multiply(-dist21))
    circles.append(Part.Circle(cen, NORM, radius))

    return circles
Esempio n. 9
0
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())
    ]
Esempio n. 10
0
def circleFromPointLineRadius(point, edge, radius):
    """Return a list of circles from one point, one edge, and one radius.

    It calculates up to 2 possible centers.
    """
    dist = findDistance(point, edge, False)
    center1 = None
    center2 = None

    if dist.Length == 0:
        segment = vec(edge)
        perpVec = DraftVecUtils.crossproduct(segment)
        perpVec.normalize()
        normPoint_c1 = App.Vector(perpVec).multiply(radius)
        normPoint_c2 = App.Vector(perpVec).multiply(-radius)
        center1 = point.add(normPoint_c1)
        center2 = point.add(normPoint_c2)
    elif dist.Length > 2 * radius:
        return None
    elif dist.Length == 2 * radius:
        normPoint = point.add(findDistance(point, edge, False))
        dummy = (normPoint.sub(point)).multiply(0.5)
        cen = point.add(dummy)
        circ = Part.Circle(cen, NORM, radius)
        if circ:
            return [circ]
        else:
            return None
    else:
        normPoint = point.add(findDistance(point, edge, False))
        normDist = DraftVecUtils.dist(normPoint, point)
        dist = math.sqrt(radius**2 - (radius - normDist)**2)
        centerNormVec = DraftVecUtils.scaleTo(point.sub(normPoint), radius)
        edgeDir = edge.Vertexes[0].Point.sub(normPoint)
        edgeDir.normalize()
        center1 = centerNormVec.add(
            normPoint.add(App.Vector(edgeDir).multiply(dist)))
        center2 = centerNormVec.add(
            normPoint.add(App.Vector(edgeDir).multiply(-dist)))

    circles = []
    if center1:
        circ = Part.Circle(center1, NORM, radius)
        if circ:
            circles.append(circ)

    if center2:
        circ = Part.Circle(center2, NORM, radius)
        if circ:
            circles.append(circ)

    if circles:
        return circles
    else:
        return None
Esempio n. 11
0
def getTangent(edge, frompoint=None):
    """
        returns the tangent to an edge. If from point is given, it is used to
        calculate the tangent (only useful for an arc of course).
        """
    if geomType(edge) == "Line":
        return vec(edge)
    elif geomType(edge) == "BSplineCurve" or \
        geomType(edge) == "BezierCurve":
        if not frompoint:
            return None
        cp = edge.Curve.parameter(frompoint)
        return edge.Curve.tangent(cp)[0]
    elif geomType(edge) == "Circle":
        if not frompoint:
            v1 = edge.Vertexes[0].Point.sub(edge.Curve.Center)
        else:
            v1 = frompoint.sub(edge.Curve.Center)
        return v1.cross(edge.Curve.Axis)
    return None
Esempio n. 12
0
def circleFrom2PointsRadius(p1, p2, radius):
    """Return a list of circles from two points, and one radius.

    The two points must not be equal.

    It calculates up to 2 possible centers.
    """
    if DraftVecUtils.equals(p1, p2):
        return None

    p1_p2 = Part.LineSegment(p1, p2).toShape()
    dist_p1p2 = DraftVecUtils.dist(p1, p1)
    mid = findMidpoint(p1_p2)

    if dist_p1p2 == 2*radius:
        circle = Part.Circle(mid, NORM, radius)
        if circle:
            return [circle]
        else:
            return None

    _dir = vec(p1_p2)
    _dir.normalize()
    perpDir = _dir.cross(App.Vector(0, 0, 1))
    perpDir.normalize()
    dist = math.sqrt(radius**2 - (dist_p1p2 / 2.0)**2)
    cen1 = App.Vector.add(mid, App.Vector(perpDir).multiply(dist))
    cen2 = App.Vector.add(mid, App.Vector(perpDir).multiply(-dist))

    circles = []
    if cen1:
        circles.append(Part.Circle(cen1, NORM, radius))
    if cen2:
        circles.append(Part.Circle(cen2, NORM, radius))

    if circles:
        return circles
    else:
        return None
Esempio n. 13
0
def circleFrom2PointsRadius(p1, p2, radius):
    """circleFrom2PointsRadiust(Vector, Vector, radius)"""
    if DraftVecUtils.equals(p1, p2): return None

    p1_p2 = Part.LineSegment(p1, p2).toShape()
    dist_p1p2 = DraftVecUtils.dist(p1, p1)
    mid = findMidpoint(p1_p2)
    if dist_p1p2 == 2 * radius:
        circle = Part.Circle(mid, NORM, radius)
        if circle: return [circle]
        else: return None
    dir = vec(p1_p2)
    dir.normalize()
    perpDir = dir.cross(Vector(0, 0, 1))
    perpDir.normalize()
    dist = math.sqrt(radius**2 - (dist_p1p2 / 2.0)**2)
    cen1 = Vector.add(mid, Vector(perpDir).multiply(dist))
    cen2 = Vector.add(mid, Vector(perpDir).multiply(-dist))
    circles = []
    if cen1: circles.append(Part.Circle(cen1, NORM, radius))
    if cen2: circles.append(Part.Circle(cen2, NORM, radius))
    if circles: return circles
    else: return None
Esempio n. 14
0
def getTangent(edge, from_point=None):
    """Return the tangent to an edge, including BSpline and circular arcs.

    If from_point is given, it is used to calculate the tangent,
    only useful for a circular arc.
    """
    if geomType(edge) == "Line":
        return vec(edge)

    elif (geomType(edge) == "BSplineCurve" or geomType(edge) == "BezierCurve"):
        if not from_point:
            return None
        cp = edge.Curve.parameter(from_point)
        return edge.Curve.tangent(cp)[0]

    elif geomType(edge) == "Circle":
        if not from_point:
            v1 = edge.Vertexes[0].Point.sub(edge.Curve.Center)
        else:
            v1 = from_point.sub(edge.Curve.Center)
        return v1.cross(edge.Curve.Axis)

    return None
Esempio n. 15
0
def circleFrom2LinesRadius(edge1, edge2, radius):
    """circleFrom2LinesRadius(edge,edge,radius)"""
    int = findIntersection(edge1, edge2, True, True)
    if not int: return None
    int = int[0]
    bis12 = angleBisection(edge1, edge2)
    bis21 = Part.LineSegment(bis12.Vertexes[0].Point,
                             DraftVecUtils.rotate(vec(bis12), math.pi / 2.0))
    ang12 = abs(DraftVecUtils.angle(vec(edge1), vec(edge2)))
    ang21 = math.pi - ang12
    dist12 = radius / math.sin(ang12 * 0.5)
    dist21 = radius / math.sin(ang21 * 0.5)
    circles = []
    cen = Vector.add(int, vec(bis12).multiply(dist12))
    circles.append(Part.Circle(cen, NORM, radius))
    cen = Vector.add(int, vec(bis12).multiply(-dist12))
    circles.append(Part.Circle(cen, NORM, radius))
    cen = Vector.add(int, vec(bis21).multiply(dist21))
    circles.append(Part.Circle(cen, NORM, radius))
    cen = Vector.add(int, vec(bis21).multiply(-dist21))
    circles.append(Part.Circle(cen, NORM, radius))
    return circles
Esempio n. 16
0
def offsetWire(wire, dvec, bind=False, occ=False,
               widthList=None, offsetMode=None, alignList=[],
               normal=None, basewireOffset=0):
    """Offset the wire along the given vector.

    Parameters
    ----------
    wire as a list of edges (use the list directly),
    or previously 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

    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, Part.Wire) or isinstance(wire, Part.Face):
        # Seems has repeatedly sortEdges, remark out here
        # edges = Part.__sortEdges__(wire.Edges)
        edges = wire.Edges
    elif isinstance(wire, list):
        if isinstance(wire[0], Part.Edge):
            edges = wire.copy()
            # How to avoid __sortEdges__ again?
            # Make getNormal directly tackle edges?
            wire = Part.Wire(Part.__sortEdges__(edges))
    else:
        print("Either Part.Wire or Part.Edges should be provided, "
              "returning None")
        return None

    # For sketch with a number of wires, getNormal() may result
    # in different direction for each wire.
    # The 'normal' parameter, if provided e.g. by ArchWall,
    # allows normal over different wires e.g. in a Sketch be consistent
    # (over different calls of this function)
    if normal:
        norm = normal
    else:
        norm = getNormal(wire)  # norm = Vector(0, 0, 1)

    closed = isReallyClosed(wire)
    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

    # vec of first edge depends on its geometry
    e = edges[0]

    # Make a copy of alignList - to avoid changes in this function
    # become starting input of next call of this function?
    # https://www.dataquest.io/blog/tutorial-functions-modify-lists-dictionaries-python/
    # alignListC = alignList.copy()  # Only Python 3
    alignListC = list(alignList)  # Python 2 and 3

    # Check the direction / offset of starting edge
    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:
        # Should no longer happen for ArchWall
        # as aligns are 'filled in' by ArchWall
        pass

    # If not provided by alignListC checked above, check the direction
    # of offset in dvec (not 'align').

    # TODO Should check if dvec is provided or not
    # ('legacy/backward-compatible' mode)
    if not firstDir:
        # need to test against Part.Circle, not Part.ArcOfCircle
        if isinstance(e.Curve, Part.Circle):
            v0 = e.Vertexes[0].Point.sub(e.Curve.Center)
        else:
            v0 = vec(e).cross(norm)
        # check against dvec provided for the offset direction
        # would not know if dvec is vector of width (Left/Right Align)
        # or width/2 (Center Align)
        dvec0 = DraftVecUtils.scaleTo(v0, dvec.Length)
        if DraftVecUtils.equals(dvec0, dvec):
            # "Left Offset" (Left Align or 'left offset' in Centre Align)
            firstDir = 1
            firstAlign = 'Left'
            alignListC.append('Left')
        elif DraftVecUtils.equals(dvec0, dvec.negative()):
            # "Right Offset" (Right Align or 'right offset' in Centre Align)
            firstDir = -1
            firstAlign = 'Right'
            alignListC.append('Right')
        else:
            print(" something wrong with firstDir ")
            firstAlign = 'Left'
            alignListC.append('Left')

    for i in range(len(edges)):
        # make a copy so it do not reverse the self.baseWires edges
        # pointed to by _Wall.getExtrusionData()?
        curredge = edges[i].copy()

        # record first edge's Orientation, Dir, Align and set Delta
        if i == 0:
            # TODO Could be edge.Orientation in fact
            # "Forward" or "Reversed"
            firstOrientation = curredge.Vertexes[0].Orientation
            curOrientation = firstOrientation
            curDir = firstDir
            curAlign = firstAlign
            delta = dvec

        # record current edge's Orientation, and set Delta
        if i != 0:  # else:
            # TODO Should also calculate 1st edge direction above
            if isinstance(curredge.Curve, Part.Circle):
                delta = curredge.Vertexes[0].Point.sub(curredge.Curve.Center)
            else:
                delta = vec(curredge).cross(norm)
            # TODO Could be edge.Orientation in fact
            curOrientation = curredge.Vertexes[0].Orientation

        # Consider individual edge width
        if widthList:  # ArchWall should now always provide widthList
            try:
                if widthList[i] > 0:
                    delta = DraftVecUtils.scaleTo(delta, widthList[i])
                elif dvec:
                    delta = DraftVecUtils.scaleTo(delta, dvec.Length)
                else:
                    # just hardcoded default value as ArchWall would provide
                    # if dvec is not provided either
                    delta = DraftVecUtils.scaleTo(delta, 200)
            except Part.OCCError:
                if dvec:
                    delta = DraftVecUtils.scaleTo(delta, dvec.Length)
                else:
                    # just hardcoded default value as ArchWall would provide
                    # if dvec is not provided either
                    delta = DraftVecUtils.scaleTo(delta, 200)
        else:
            delta = DraftVecUtils.scaleTo(delta, dvec.Length)

        # Consider individual edge Align direction
        # - ArchWall should now always provide alignList
        if i == 0:
            if alignListC[0] == 'Center':
                delta = DraftVecUtils.scaleTo(delta, delta.Length/2)
            # No need to do anything for 'Left' and 'Right' as original dvec
            # have set both the direction and amount of offset correct
            # elif alignListC[i] == 'Left':  #elif alignListC[i] == 'Right':
        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)

        # Consider whether generating the 'offset wire' or the 'base wire'
        if offsetMode is None:
            # Consider if curOrientation and/or curDir match their
            # firstOrientation/firstDir - to determine whether
            # and how to offset the current edge

            # This is a xor
            if (curOrientation == firstOrientation) != (curDir == firstDir):
                if curAlign in ['Left', 'Right']:
                    # ArchWall has an Offset properties for user to offset
                    # the basewire before creating the base profile of wall
                    # (not applicable to 'Center' align)
                    if basewireOffset:
                        delta = DraftVecUtils.scaleTo(delta, basewireOffset)
                        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']:
                # elif curAlign == 'Center': # Both conditions same result.
                # ArchWall has an Offset properties for user to offset
                # the basewire before creating the base profile of wall
                # (not applicable to 'Center' align)
                if basewireOffset:
                    if curAlign in ['Left', 'Right']:
                        delta = DraftVecUtils.scaleTo(delta,
                                                      delta.Length + basewireOffset)
                    #else: # elif curAlign == 'Center': #pass # no need to add basewireOffset
                nedge = offset(curredge, delta, trim=True)

            # TODO arc always in counter-clockwise directinon
            # ... ( not necessarily 'reversed')
            if curOrientation == "Reversed":
                # need to test against Part.Circle, not Part.ArcOfCircle
                if not isinstance(curredge.Curve, Part.Circle):
                    # if not arc/circle, assume straight line, reverse it
                    nedge = Part.Edge(nedge.Vertexes[1], nedge.Vertexes[0])
                else:
                    # if arc/circle
                    # Part.ArcOfCircle(edge.Curve,
                    #                  edge.FirstParameter, edge.LastParameter,
                    #                  edge.Curve.Axis.z > 0)
                    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()
                    # TODO any better solution than to calculate midpoint
                    # of arc to reverse?

        elif offsetMode in ["BasewireMode"]:
            if (not (curOrientation == firstOrientation)
                    != (curDir == firstDir)):
                if curAlign in ['Left', 'Right']:
                    # ArchWall has an Offset properties for user to offset
                    # the basewire before creating the base profile of wall
                    # (not applicable to 'Center' align)
                    if basewireOffset:
                        delta = DraftVecUtils.scaleTo(delta, basewireOffset)
                        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']:
                    # ArchWall has an Offset properties for user to offset
                    # the basewire before creating the base profile of wall
                    # (not applicable to 'Center' align)
                    if basewireOffset:
                        delta = DraftVecUtils.scaleTo(delta,
                                                      delta.Length + basewireOffset)
                    nedge = offset(curredge, delta, trim=True)

                elif curAlign == 'Center':
                    nedge = offset(curredge, delta, trim=True)
            if curOrientation == "Reversed":
                # need to test against Part.Circle, not Part.ArcOfCircle
                if not isinstance(curredge.Curve, Part.Circle):
                    # if not arc/circle, assume straight line, reverse it
                    nedge = Part.Edge(nedge.Vertexes[1], nedge.Vertexes[0])
                else:
                    # if arc/circle
                    # Part.ArcOfCircle(edge.Curve,
                    #                  edge.FirstParameter,
                    #                  edge.LastParameter,
                    #                  edge.Curve.Axis.z > 0)
                    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()
                    # TODO any better solution than to calculate midpoint
                    # of arc to reverse?
        else:
            print(" something wrong ")
            return None
        if not nedge:
            return None

        nedges.append(nedge)

    if len(edges) > 1:
        nedges = connect(nedges, closed)
    else:
        nedges = Part.Wire(nedges[0])

    if bind and not closed:
        e1 = Part.LineSegment(edges[0].Vertexes[0].Point,
                              nedges[0].Vertexes[0].Point).toShape()
        e2 = Part.LineSegment(edges[-1].Vertexes[-1].Point,
                              nedges[-1].Vertexes[-1].Point).toShape()
        alledges = edges.extend(nedges)
        alledges = alledges.extend([e1, e2])
        w = Part.Wire(alledges)
        return w
    else:
        return nedges
Esempio n. 17
0
 def rot(ed):
     return Part.LineSegment(
         v1(ed),
         v1(ed).add(DraftVecUtils.rotate(vec(ed), math.pi / 2))).toShape()
Esempio n. 18
0
def findDistance(point, edge, strict=False):
    """Return a vector from the point to its closest point on the edge.

    If `strict` is `True`, the vector will be returned
    only if its endpoint lies on the `edge`.
    Edge can also be a list of 2 points.
    """
    if isinstance(point, App.Vector):
        if isinstance(edge, list):
            segment = edge[1].sub(edge[0])
            chord = edge[0].sub(point)
            norm = segment.cross(chord)
            perp = segment.cross(norm)
            dist = DraftVecUtils.project(chord, perp)

            if not dist:
                return None

            newpoint = point.add(dist)

            if dist.Length == 0:
                return None

            if strict:
                s1 = newpoint.sub(edge[0])
                s2 = newpoint.sub(edge[1])
                if (s1.Length <= segment.Length
                        and s2.Length <= segment.Length):
                    return dist
                else:
                    return None
            else:
                return dist

        elif geomType(edge) == "Line":
            segment = vec(edge)
            chord = edge.Vertexes[0].Point.sub(point)
            norm = segment.cross(chord)
            perp = segment.cross(norm)
            dist = DraftVecUtils.project(chord, perp)

            if not dist:
                return None

            newpoint = point.add(dist)

            if (dist.Length == 0):
                return None

            if strict:
                s1 = newpoint.sub(edge.Vertexes[0].Point)
                s2 = newpoint.sub(edge.Vertexes[-1].Point)
                if (s1.Length <= segment.Length
                        and s2.Length <= segment.Length):
                    return dist
                else:
                    return None
            else:
                return dist

        elif geomType(edge) == "Circle":
            ve1 = edge.Vertexes[0].Point
            if len(edge.Vertexes) > 1:
                ve2 = edge.Vertexes[-1].Point
            else:
                ve2 = None
            center = edge.Curve.Center
            segment = center.sub(point)

            if segment.Length == 0:
                return None

            ratio = (segment.Length - edge.Curve.Radius) / segment.Length
            dist = segment.multiply(ratio)
            newpoint = App.Vector.add(point, dist)

            if dist.Length == 0:
                return None

            if strict and ve2:
                ang1 = DraftVecUtils.angle(ve1.sub(center))
                ang2 = DraftVecUtils.angle(ve2.sub(center))
                angpt = DraftVecUtils.angle(newpoint.sub(center))
                if ((angpt <= ang2 and angpt >= ang1)
                        or (angpt <= ang1 and angpt >= ang2)):
                    return dist
                else:
                    return None
            else:
                return dist

        elif (geomType(edge) == "BSplineCurve"
              or geomType(edge) == "BezierCurve"):
            try:
                pr = edge.Curve.parameter(point)
                np = edge.Curve.value(pr)
                dist = np.sub(point)
            except Part.OCCError:
                print(
                    "DraftGeomUtils: Unable to get curve parameter "
                    "for point ", point)
                return None
            else:
                return dist
        else:
            print("DraftGeomUtils: Couldn't project point")
            return None
    else:
        print("DraftGeomUtils: Couldn't project point")
        return None
Esempio n. 19
0
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())
    ]
Esempio n. 20
0
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 []