Example #1
0
def make_array(base_object,
               arg1,
               arg2,
               arg3,
               arg4=None,
               arg5=None,
               arg6=None,
               use_link=True):
    """Create a Draft Array of the given object.

    Rectangular array
    -----------------
    make_array(object,xvector,yvector,xnum,ynum,[name])
    makeArray(object,xvector,yvector,zvector,xnum,ynum,znum,[name])

    xnum of iterations in the x direction
    at xvector distance between iterations, same for y direction
    with yvector and ynum, same for z direction with zvector and znum.

    Polar array
    -----------
    makeArray(object,center,totalangle,totalnum,[name]) for polar array, or

    center is a vector, totalangle is the angle to cover (in degrees)
    and totalnum is the number of objects, including the original.

    Circular array
    --------------
    makeArray(object,rdistance,tdistance,axis,center,ncircles,symmetry,[name])

    In case of a circular array, rdistance is the distance of the
    circles, tdistance is the distance within circles, axis the rotation-axis,
    center the center of rotation, ncircles the number of circles
    and symmetry the number of symmetry-axis of the distribution.

    To Do
    -----
    The `Array` class currently handles three types of arrays,
    orthogonal, polar, and circular. In the future, probably they should be
    split in separate classes so that they are easier to manage.
    """
    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(_tr("No active document. Aborting."))
        return None

    if use_link:
        # The Array class must be called in this special way
        # to make it a LinkArray
        new_obj = doc.addObject("Part::FeaturePython", "Array", Array(None),
                                None, True)
    else:
        new_obj = doc.addObject("Part::FeaturePython", "Array")
        Array(new_obj)

    new_obj.Base = base_object
    if arg6:
        if isinstance(arg1, (int, float, App.Units.Quantity)):
            new_obj.ArrayType = "circular"
            new_obj.RadialDistance = arg1
            new_obj.TangentialDistance = arg2
            new_obj.Axis = arg3
            new_obj.Center = arg4
            new_obj.NumberCircles = arg5
            new_obj.Symmetry = arg6
        else:
            new_obj.ArrayType = "ortho"
            new_obj.IntervalX = arg1
            new_obj.IntervalY = arg2
            new_obj.IntervalZ = arg3
            new_obj.NumberX = arg4
            new_obj.NumberY = arg5
            new_obj.NumberZ = arg6
    elif arg4:
        new_obj.ArrayType = "ortho"
        new_obj.IntervalX = arg1
        new_obj.IntervalY = arg2
        new_obj.NumberX = arg3
        new_obj.NumberY = arg4
    else:
        new_obj.ArrayType = "polar"
        new_obj.Center = arg1
        new_obj.Angle = arg2
        new_obj.NumberPolar = arg3

    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(new_obj.ViewObject)
        else:
            ViewProviderDraftArray(new_obj.ViewObject)
            gui_utils.format_object(new_obj, new_obj.Base)

            if hasattr(new_obj.Base.ViewObject, "DiffuseColor"):
                if len(new_obj.Base.ViewObject.DiffuseColor) > 1:
                    new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject)

        new_obj.Base.ViewObject.hide()
        gui_utils.select(new_obj)

    return new_obj
Example #2
0
def make_path_array(base_object,
                    path_object,
                    count=4,
                    extra=App.Vector(0, 0, 0),
                    subelements=None,
                    align=False,
                    align_mode="Original",
                    tan_vector=App.Vector(1, 0, 0),
                    force_vertical=False,
                    vertical_vector=App.Vector(0, 0, 1),
                    use_link=True):
    """Make a Draft PathArray object.

    Distribute copies of a `base_object` along `path_object`
    or `subelements` from `path_object`.

    Parameters
    ----------
    base_object: Part::Feature or str
        Any of object that has a `Part::TopoShape` that can be duplicated.
        This means most 2D and 3D objects produced with any workbench.
        If it is a string, it must be the `Label` of that object.
        Since a label is not guaranteed to be unique in a document,
        it will use the first object found with this label.

    path_object: Part::Feature or str
        Path object like a polyline, B-Spline, or bezier curve that should
        contain edges.
        Just like `base_object` it can also be `Label`.

    count: int, float, optional
        It defaults to 4.
        Number of copies to create along the `path_object`.
        It must be at least 2.
        If a `float` is provided, it will be truncated by `int(count)`.

    extra: Base.Vector3, optional
        It defaults to `App.Vector(0, 0, 0)`.
        It translates each copy by the value of `extra`.
        This is useful to adjust for the difference between shape centre
        and shape reference point.

    subelements: list or tuple of str, optional
        It defaults to `None`.
        It should be a list of names of edges that must exist in `path_object`.
        Then the path array will be created along these edges only,
        and not the entire `path_object`.
        ::
            subelements = ['Edge1', 'Edge2']

        The edges must be contiguous, meaning that it is not allowed to
        input `'Edge1'` and `'Edge3'` if they do not touch each other.

        A single string value is also allowed.
        ::
            subelements = 'Edge1'

    align: bool, optional
        It defaults to `False`.
        If it is `True` it will align `base_object` to tangent, normal,
        or binormal to the `path_object`, depending on the value
        of `tan_vector`.

    align_mode: str, optional
        It defaults to `'Original'` which is the traditional alignment.
        It can also be `'Frenet'` or `'Tangent'`.

        - Original. It does not calculate curve normal.
          `X` is curve tangent, `Y` is normal parameter, Z is the cross
          product `X` x `Y`.
        - Frenet. It defines a local coordinate system along the path.
          `X` is tangent to curve, `Y` is curve normal, `Z` is curve binormal.
          If normal cannot be computed, for example, in a straight path,
          a default is used.
        - Tangent. It is similar to `'Original'` but includes a pre-rotation
          to align the base object's `X` to the value of `tan_vector`,
          then `X` follows curve tangent.

    tan_vector: Base::Vector3, optional
        It defaults to `App.Vector(1, 0, 0)` or the +X axis.
        It aligns the tangent of the path to this local unit vector
        of the object.

    force_vertical: Base::Vector3, optional
        It defaults to `False`.
        If it is `True`, the value of `vertical_vector`
        will be used when `align_mode` is `'Original'` or `'Tangent'`.

    vertical_vector: Base::Vector3, optional
        It defaults to `App.Vector(0, 0, 1)` or the +Z axis.
        It will force this vector to be the vertical direction
        when `force_vertical` is `True`.

    use_link: bool, optional
        It defaults to `True`, in which case the copies are `App::Link`
        elements. Otherwise, the copies are shape copies which makes
        the resulting array heavier.

    Returns
    -------
    Part::FeaturePython
        The scripted object of type `'PathArray'`.
        Its `Shape` is a compound of the copies of the original object.

    None
        If there is a problem it will return `None`.
    """
    _name = "make_path_array"
    utils.print_header(_name, "Path array")

    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(_tr("No active document. Aborting."))
        return None

    if isinstance(base_object, str):
        base_object_str = base_object

    found, base_object = utils.find_object(base_object, doc)
    if not found:
        _msg("base_object: {}".format(base_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("base_object: {}".format(base_object.Label))

    if isinstance(path_object, str):
        path_object_str = path_object

    found, path_object = utils.find_object(path_object, doc)
    if not found:
        _msg("path_object: {}".format(path_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("path_object: {}".format(path_object.Label))

    _msg("count: {}".format(count))
    try:
        utils.type_check([(count, (int, float))], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a number."))
        return None
    count = int(count)

    _msg("extra: {}".format(extra))
    try:
        utils.type_check([(extra, App.Vector)], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a vector."))
        return None

    _msg("subelements: {}".format(subelements))
    if subelements:
        try:
            # Make a list
            if isinstance(subelements, str):
                subelements = [subelements]

            utils.type_check([(subelements, (list, tuple, str))], name=_name)
        except TypeError:
            _err(
                _tr("Wrong input: must be a list or tuple of strings. "
                    "Or a single string."))
            return None

        # The subelements list is used to build a special list
        # called a LinkSubList, which includes the path_object.
        # Old style: [(path_object, "Edge1"), (path_object, "Edge2")]
        # New style: [(path_object, ("Edge1", "Edge2"))]
        #
        # If a simple list is given ["a", "b"], this will create an old-style
        # SubList.
        # If a nested list is given [["a", "b"]], this will create a new-style
        # SubList.
        # In any case, the property of the object accepts both styles.
        #
        # If the old style is deprecated then this code should be updated
        # to create new style lists exclusively.
        sub_list = list()
        for sub in subelements:
            sub_list.append((path_object, sub))
    else:
        sub_list = None

    align = bool(align)
    _msg("align: {}".format(align))

    _msg("align_mode: {}".format(align_mode))
    try:
        utils.type_check([(align_mode, str)], name=_name)

        if align_mode not in ("Original", "Frenet", "Tangent"):
            raise TypeError
    except TypeError:
        _err(_tr("Wrong input: must be "
                 "'Original', 'Frenet', or 'Tangent'."))
        return None

    _msg("tan_vector: {}".format(tan_vector))
    try:
        utils.type_check([(tan_vector, App.Vector)], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a vector."))
        return None

    force_vertical = bool(force_vertical)
    _msg("force_vertical: {}".format(force_vertical))

    _msg("vertical_vector: {}".format(vertical_vector))
    try:
        utils.type_check([(vertical_vector, App.Vector)], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a vector."))
        return None

    use_link = bool(use_link)
    _msg("use_link: {}".format(use_link))

    if use_link:
        # The PathArray class must be called in this special way
        # to make it a PathLinkArray
        new_obj = doc.addObject("Part::FeaturePython", "PathArray",
                                PathArray(None), None, True)
    else:
        new_obj = doc.addObject("Part::FeaturePython", "PathArray")
        PathArray(new_obj)

    new_obj.Base = base_object
    new_obj.PathObject = path_object
    new_obj.Count = count
    new_obj.ExtraTranslation = extra
    new_obj.PathSubelements = sub_list
    new_obj.Align = align
    new_obj.AlignMode = align_mode
    new_obj.TangentVector = tan_vector
    new_obj.ForceVertical = force_vertical
    new_obj.VerticalVector = vertical_vector

    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(new_obj.ViewObject)
        else:
            ViewProviderDraftArray(new_obj.ViewObject)
            gui_utils.formatObject(new_obj, new_obj.Base)

            if hasattr(new_obj.Base.ViewObject, "DiffuseColor"):
                if len(new_obj.Base.ViewObject.DiffuseColor) > 1:
                    new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject)

        new_obj.Base.ViewObject.hide()
        gui_utils.select(new_obj)

    return new_obj
Example #3
0
def make_path_twisted_array(base_object,
                            path_object,
                            count=15,
                            rot_factor=0.25,
                            use_link=True):
    """Create a Path twisted array."""
    _name = "make_path_twisted_array"
    utils.print_header(_name, "Path twisted array")

    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(_tr("No active document. Aborting."))
        return None

    if isinstance(base_object, str):
        base_object_str = base_object

    found, base_object = utils.find_object(base_object, doc)
    if not found:
        _msg("base_object: {}".format(base_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("base_object: {}".format(base_object.Label))

    if isinstance(path_object, str):
        path_object_str = path_object

    found, path_object = utils.find_object(path_object, doc)
    if not found:
        _msg("path_object: {}".format(path_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("path_object: {}".format(path_object.Label))

    _msg("count: {}".format(count))
    try:
        utils.type_check([(count, (int, float))], name=_name)
    except TypeError:
        _err(_tr("Wrong input: must be a number."))
        return None
    count = int(count)

    use_link = bool(use_link)
    _msg("use_link: {}".format(use_link))

    if use_link:
        # The PathTwistedArray class must be called in this special way
        # to make it a PathTwistLinkArray
        new_obj = doc.addObject("Part::FeaturePython", "PathTwistedArray",
                                PathTwistedArray(None), None, True)
    else:
        new_obj = doc.addObject("Part::FeaturePython", "PathTwistedArray")
        PathTwistedArray(new_obj)

    new_obj.Base = base_object
    new_obj.PathObject = path_object
    new_obj.Count = count
    new_obj.RotationFactor = rot_factor

    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(new_obj.ViewObject)
        else:
            ViewProviderDraftArray(new_obj.ViewObject)
            gui_utils.formatObject(new_obj, new_obj.Base)

        if hasattr(new_obj.Base.ViewObject, "DiffuseColor"):
            if len(new_obj.Base.ViewObject.DiffuseColor) > 1:
                new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject)

        new_obj.Base.ViewObject.hide()
        gui_utils.select(new_obj)

    return new_obj
Example #4
0
def make_path_array(baseobject,pathobject,count,xlate=None,align=False,pathobjsubs=[],use_link=False):
    """make_path_array(docobj, path, count, xlate, align, pathobjsubs, use_link)
    
    Make a Draft PathArray object.
    
    Distribute count copies of a document baseobject along a pathobject 
    or subobjects of a pathobject. 

    
    Parameters
    ----------
    docobj : 
        Object to array

    path : 
        Path object

    pathobjsubs : 
        TODO: Complete documentation

    align : 
        Optionally aligns baseobject to tangent/normal/binormal of path. TODO: verify

    count : 
        TODO: Complete documentation

    xlate : Base.Vector
        Optionally translates each copy by FreeCAD.Vector xlate direction
        and distance to adjust for difference in shape centre vs shape reference point.
        
    use_link :
        TODO: Complete documentation
    """

    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return

    if use_link:
        obj = App.ActiveDocument.addObject("Part::FeaturePython","PathArray", PathArray(None), None, True)
    else:
        obj = App.ActiveDocument.addObject("Part::FeaturePython","PathArray")
        PathArray(obj)

    obj.Base = baseobject
    obj.PathObj = pathobject

    if pathobjsubs:
        sl = []
        for sub in pathobjsubs:
            sl.append((obj.PathObj,sub))
        obj.PathSubs = list(sl)

    if count > 1:
        obj.Count = count

    if xlate:
        obj.Xlate = xlate

    obj.Align = align

    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(obj.ViewObject)
        else:
            ViewProviderDraftArray(obj.ViewObject)
            gui_utils.formatObject(obj,obj.Base)
            if hasattr(obj.Base.ViewObject, "DiffuseColor"):
                if len(obj.Base.ViewObject.DiffuseColor) > 1:
                    obj.ViewObject.Proxy.resetColors(obj.ViewObject)
        baseobject.ViewObject.hide()
        gui_utils.select(obj)
    return obj
Example #5
0
def make_array(baseobject,
               arg1,
               arg2,
               arg3,
               arg4=None,
               arg5=None,
               arg6=None,
               name="Array",
               use_link=False):
    """ 
    Creates a Draft Array of the given object.


    Rectangular array
    -----------------
    make_array(object,xvector,yvector,xnum,ynum,[name])
    makeArray(object,xvector,yvector,zvector,xnum,ynum,znum,[name])

    xnum of iterations in the x direction
    at xvector distance between iterations, same for y direction with yvector and ynum,
    same for z direction with zvector and znum. 


    Polar array
    -----------
    makeArray(object,center,totalangle,totalnum,[name]) for polar array, or

    center is a vector, totalangle is the angle to cover (in degrees) and totalnum 
    is the number of objects, including the original. 
    

    Circular array
    --------------
    makeArray(object,rdistance,tdistance,axis,center,ncircles,symmetry,[name])

    In case of a circular array, rdistance is the distance of the
    circles, tdistance is the distance within circles, axis the rotation-axes, center the
    center of rotation, ncircles the number of circles and symmetry the number
    of symmetry-axis of the distribution. The result is a parametric Draft Array.
    """

    if not App.ActiveDocument:
        App.Console.PrintError("No active document. Aborting\n")
        return
    if use_link:
        obj = App.ActiveDocument.addObject("Part::FeaturePython", name,
                                           Array(None), None, True)
    else:
        obj = App.ActiveDocument.addObject("Part::FeaturePython", name)
        Array(obj)
    obj.Base = baseobject
    if arg6:
        if isinstance(arg1, (int, float, App.Units.Quantity)):
            obj.ArrayType = "circular"
            obj.RadialDistance = arg1
            obj.TangentialDistance = arg2
            obj.Axis = arg3
            obj.Center = arg4
            obj.NumberCircles = arg5
            obj.Symmetry = arg6
        else:
            obj.ArrayType = "ortho"
            obj.IntervalX = arg1
            obj.IntervalY = arg2
            obj.IntervalZ = arg3
            obj.NumberX = arg4
            obj.NumberY = arg5
            obj.NumberZ = arg6
    elif arg4:
        obj.ArrayType = "ortho"
        obj.IntervalX = arg1
        obj.IntervalY = arg2
        obj.NumberX = arg3
        obj.NumberY = arg4
    else:
        obj.ArrayType = "polar"
        obj.Center = arg1
        obj.Angle = arg2
        obj.NumberPolar = arg3
    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(obj.ViewObject)
        else:
            ViewProviderDraftArray(obj.ViewObject)
            gui_utils.format_object(obj, obj.Base)
            if len(obj.Base.ViewObject.DiffuseColor) > 1:
                obj.ViewObject.Proxy.resetColors(obj.ViewObject)
        baseobject.ViewObject.hide()
        gui_utils.select(obj)
    return obj
Example #6
0
def make_point_array(base_object, point_object, extra=None, use_link=True):
    """Make a Draft PointArray object.

    Distribute copies of a `base_object` in the points
    defined by `point_object`.

    Parameters
    ----------
    base_object: Part::Feature or str
        Any of object that has a `Part::TopoShape` that can be duplicated.
        This means most 2D and 3D objects produced with any workbench.
        If it is a string, it must be the `Label` of that object.
        Since a label is not guaranteed to be unique in a document,
        it will use the first object found with this label.

    point_object: Part::Feature or str
        An object that is a type of container for holding points.
        This object must have one of the following properties `Geometry`,
        `Links`, or `Components`, which themselves must contain objects
        with `X`, `Y`, and `Z` properties.

        This object could be:

        - A `Sketcher::SketchObject`, as it has a `Geometry` property.
          The sketch can contain different elements but it must contain
          at least one `Part::GeomPoint`.

        - A `Part::Compound`, as it has a `Links` property. The compound
          can contain different elements but it must contain at least
          one object that has `X`, `Y`, and `Z` properties,
          like a `Draft Point` or a `Part::Vertex`.

        - A `Draft Block`, as it has a `Components` property. This `Block`
          behaves essentially the same as a `Part::Compound`. It must
          contain at least a point or vertex object.

    extra: Base::Placement, Base::Vector3, or Base::Rotation, optional
        It defaults to `None`.
        If it is provided, it is an additional placement that is applied
        to each copy of the array.
        The input could be a full placement, just a vector indicating
        the additional translation, or just a rotation.

    Returns
    -------
    Part::FeaturePython
        A scripted object of type `'PointArray'`.
        Its `Shape` is a compound of the copies of the original object.

    None
        If there is a problem it will return `None`.
    """
    _name = "make_point_array"
    utils.print_header(_name, "Point array")

    found, doc = utils.find_doc(App.activeDocument())
    if not found:
        _err(_tr("No active document. Aborting."))
        return None

    if isinstance(base_object, str):
        base_object_str = base_object

    found, base_object = utils.find_object(base_object, doc)
    if not found:
        _msg("base_object: {}".format(base_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("base_object: {}".format(base_object.Label))

    if isinstance(point_object, str):
        point_object_str = point_object

    found, point_object = utils.find_object(point_object, doc)
    if not found:
        _msg("point_object: {}".format(point_object_str))
        _err(_tr("Wrong input: object not in document."))
        return None

    _msg("point_object: {}".format(point_object.Label))
    if (not hasattr(point_object, "Geometry")
            and not hasattr(point_object, "Links")
            and not hasattr(point_object, "Components")):
        _err(
            _tr("Wrong input: point object doesn't have "
                "'Geometry', 'Links', or 'Components'."))
        return None

    _msg("extra: {}".format(extra))
    if not extra:
        extra = App.Placement()
    try:
        utils.type_check([(extra, (App.Placement, App.Vector, App.Rotation))],
                         name=_name)
    except TypeError:
        _err(
            _tr("Wrong input: must be a placement, a vector, "
                "or a rotation."))
        return None

    # Convert the vector or rotation to a full placement
    if isinstance(extra, App.Vector):
        extra = App.Placement(extra, App.Rotation())
    elif isinstance(extra, App.Rotation):
        extra = App.Placement(App.Vector(), extra)

    if use_link:
        # The PointArray class must be called in this special way
        # to make it a LinkArray
        new_obj = doc.addObject("Part::FeaturePython", "PointArray",
                                PointArray(None), None, True)
    else:
        new_obj = doc.addObject("Part::FeaturePython", "PointArray")
        PointArray(new_obj)

    new_obj.Base = base_object
    new_obj.PointObject = point_object
    new_obj.ExtraPlacement = extra

    if App.GuiUp:
        if use_link:
            ViewProviderDraftLink(new_obj.ViewObject)
        else:
            ViewProviderDraftArray(new_obj.ViewObject)
            gui_utils.format_object(new_obj, new_obj.Base)

            if hasattr(new_obj.Base.ViewObject, "DiffuseColor"):
                if len(new_obj.Base.ViewObject.DiffuseColor) > 1:
                    new_obj.ViewObject.Proxy.resetColors(new_obj.ViewObject)

        new_obj.Base.ViewObject.hide()
        gui_utils.select(new_obj)

    return new_obj