Example #1
0
    def derivedExecute(self,obj):
        self.assureGenerator(obj)
        self.updateReadonlyness(obj)
        
        # Apply links
        if obj.AxisLink:
            if lattice2BaseFeature.isObjectLattice(obj.AxisLink):
                lattice2Executer.warning(obj,"For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape.")
                
            #resolve the link        
            if len(obj.AxisLinkSubelement) > 0:
                linkedShape = obj.AxisLink.Shape.getElement(obj.AxisLinkSubelement)
            else:
                linkedShape = obj.AxisLink.Shape

            #Type check
            if linkedShape.ShapeType != 'Edge':
                raise ValueError('Axis link must be an edge; it is '+linkedShape.ShapeType+' instead.')
            
            #prepare
            dir = App.Vector()
            point = App.Vector()
            if isinstance(linkedShape.Curve, Part.Line):
                dir = linkedShape.Curve.EndPoint - linkedShape.Curve.StartPoint
                point = linkedShape.Curve.StartPoint
            elif isinstance(linkedShape.Curve, Part.Circle):
                dir = linkedShape.Curve.Axis
                point = linkedShape.Curve.Center
            else:
                raise ValueError("Edge " + repr(linkedShape) + " can't be used to derive an axis. It must be either a line or a circle/arc.")
            
            #apply
            if obj.AxisDirIsDriven:
                obj.AxisDir = dir
            if obj.AxisPointIsDriven:
                obj.AxisPoint = point
        
        self.generator.execute()
        
        # cache properties into variables
        radius = float(obj.Radius)
        values = [float(strv) for strv in obj.Values]
        
        # compute initial vector. It is to be perpendicular to Axis
        rot_ini = lattice2GeomUtils.makeOrientationFromLocalAxes(ZAx= obj.AxisDir)
        overallPlacement = App.Placement(obj.AxisPoint, rot_ini)
        
        # Make the array
        output = [] # list of placements
        for ang in values:
            p = Part.Vertex()
            localrot = App.Rotation(App.Vector(0,0,1), ang)
            localtransl = localrot.multVec(App.Vector(radius,0,0))
            localplm = App.Placement(localtransl, localrot)
            resultplm = overallPlacement.multiply(localplm)
            if obj.OrientMode == 'None':
                resultplm.Rotation = App.Rotation()
            output.append(resultplm)

        return output
Example #2
0
def cmdExposeLinkSubs():
    sel = FreeCADGui.Selection.getSelectionEx()
    if len(sel) != 1:
        raise SelectionError(   "Bad selection", "Select one object, first! You have selected %i objects".replace("%i",str(len(sel)))   )
    App.ActiveDocument.openTransaction("Expose LinkSub of "+sel[0].Object.Name)
    obj = sel[0].Object
    cnt = 0
    try:
        for propname in obj.PropertiesList:
            if 'App::PropertyLinkSub' in obj.getTypeIdOfProperty(propname):
                if getattr(obj,propname) is None:  continue
                try:
                    if obj.isDerivedFrom("Part::Part2DObject") and propname == "Support":
                        if False == askYesNo("Support", "ExposeLinkSub is about to expose Support link of %feat. This will cause PartDesign additive operations to start new objects instead of adding to support solid, and subtractive PartDesign operations to fail. Expose the support link?"
                                                        .replace("%feat",obj.Label)):
                            continue
                    ExposeLinkSub(obj, propname)
                    cnt += 1
                except Exception as err:
                    Executer.warning(None,"Attempting to expose sublink property {prop} of {feat} caused an error:\n{err}"
                                          .format(prop= propname, feat= obj.Name, err= str(err))   )
        if cnt == 0:
            raise ValueError("No links to expose were found.")
    except Exception:
        App.ActiveDocument.abortTransaction()
        raise
def cmdExposeLinkSubs():
    sel = FreeCADGui.Selection.getSelectionEx()
    if len(sel) != 1:
        raise SelectionError(   "Bad selection", "Select one object, first! You have selected %i objects".replace("%i",str(len(sel)))   )
    App.ActiveDocument.openTransaction("Expose LinkSub of "+sel[0].Object.Name)
    obj = sel[0].Object
    cnt = 0
    try:
        for propname in obj.PropertiesList:
            if 'App::PropertyLinkSub' in obj.getTypeIdOfProperty(propname):
                if getattr(obj,propname) is None:  continue
                try:
                    if obj.isDerivedFrom("Part::Part2DObject") and propname == "Support":
                        if False == askYesNo("Support", "ExposeLinkSub is about to expose Support link of %feat. This will cause PartDesign additive operations to start new objects instead of adding to support solid, and subtractive PartDesign operations to fail. Expose the support link?"
                                                        .replace("%feat",obj.Label)):
                            continue
                    ExposeLinkSub(obj, propname)
                    cnt += 1
                except Exception as err:
                    Executer.warning(None,"Attempting to expose sublink property %prop of %feat caused an error:\n%err"
                                          .replace("%prop",propname)
                                          .replace("%feat",obj.Name)
                                          .replace("%err",err.message)   )
        if cnt == 0:
            raise ValueError("No links to expose were found.")
    except Exception:
        App.ActiveDocument.abortTransaction()
        raise
Example #4
0
    def Activated(self):
        try:
            if len(FreeCADGui.Selection.getSelection()) == 0:
                infoMessage(
                    "Make compound",
                    "Make compound command. Combines several shapes into one. The shapes are kept as-is. They are not fused together, and can be extracted unchanged.\n\n"
                    +
                    "Compounds can contain combination of shapes of any topology: one can compound some edges with some solids. Compound is effectively another kind of group. But unlike normal FreeCAD group, compound only accepts OCC geometry. Compound cannot include meshes, dimensions, labels, or other objects that provide no Shape property.\n\n"
                    +
                    "Note that compounds that have objects that touch or intersect are considered invalid by Part CheckGeometry. Such invalid compounds cannot be used for Part Cut/Common/Fuse."
                )
                return

            oldVal = lattice2Executer.globalIsCreatingLatticeFeature
            lattice2Executer.globalIsCreatingLatticeFeature = True

            sel = FreeCADGui.Selection.getSelectionEx()
            for s in sel:
                if isObjectLattice(s.Object):
                    lattice2Executer.warning(
                        None,
                        "For making a compound, generic shapes are expected, but some of the selected objects are placements/arrays of placements. These will be treated as generic shapes; results may be unexpected."
                    )
                    break
            FreeCADGui.runCommand("Part_Compound")
        except Exception as err:
            msgError(err)
        finally:
            lattice2Executer.globalIsCreatingLatticeFeature = oldVal
def DereferenceArray(obj,placements, lnkFrom, refmode):
    '''common implementation of treatment Referencing property. Returns a list of placements to use directly.
    obj - feature being executed (used for error reporting; can be None)
    placements - the array, converted into a list of placements.
    lnkFrom - object linked as a lattice of 'from' placements. Can be None, if mode is not 'Use PlacemenetsFrom'
    refmode - a string - enum property item'''
        
    plmDeref = App.Placement() #inverse placement of reference (reference is a substitute of origin)
    if lnkFrom is not None  and  refmode != "Use PlacementsFrom":
        lattice2Executer.warning(obj,"Referencing mode is '"+refmode+"', doesn't need PlacementsFrom link to be set. The link is set, but it will be ignored.")
    if refmode == "Origin":
        return placements
    elif refmode == "First item":
        plmDeref = placements[0].inverse()
    elif refmode == "Last item":
        plmDeref = placements[0].inverse()
    elif refmode == "Use PlacementsFrom":
        if lnkFrom is None:
            raise ValueError("Referencing mode is 'Move from to', but PlacementsFrom link is not set.")
        placementsFrom = lattice2BaseFeature.getPlacementsList(lnkFrom, obj)
        if len(placementsFrom) == 1:
            plmDeref = placementsFrom[0].inverse()
        elif len(placementsFrom) == len(placements):
            return [lattice2BaseFeature.makeMoveFromTo(placementsFrom[i], placements[i]) for i in range(0, len(placements))]
        else:
            lattice2Executer.warning(obj,"Lengths of arrays linked as PlacementsTo and PlacementsFrom must equal, or PlacementsFrom can be one placement. Violation: lengths are "+str(len(placements))+ " and "+str(len(placementsFrom)))
    else:
        raise ValueError("Referencing mode not implemented: "+refmode)
    
    return [plm.multiply(plmDeref) for plm in placements]
Example #6
0
def DereferenceArray(obj,placements, lnkFrom, refmode):
    '''common implementation of treatment Referencing property. Returns a list of placements to use directly.
    obj - feature being executed (used for error reporting; can be None)
    placements - the array, converted into a list of placements.
    lnkFrom - object linked as a lattice of 'from' placements. Can be None, if mode is not 'Use PlacemenetsFrom'
    refmode - a string - enum property item'''
        
    plmDeref = App.Placement() #inverse placement of reference (reference is a substitute of origin)
    if lnkFrom is not None  and  refmode != "Use PlacementsFrom":
        lattice2Executer.warning(obj,"Referencing mode is '"+refmode+"', doesn't need PlacementsFrom link to be set. The link is set, but it will be ignored.")
    if refmode == "Origin":
        return placements
    elif refmode == "First item":
        plmDeref = placements[0].inverse()
    elif refmode == "Last item":
        plmDeref = placements[0].inverse()
    elif refmode == "Use PlacementsFrom":
        if lnkFrom is None:
            raise ValueError("Referencing mode is 'Move from to', but PlacementsFrom link is not set.")
        placementsFrom = lattice2BaseFeature.getPlacementsList(lnkFrom, obj)
        if len(placementsFrom) == 1:
            plmDeref = placementsFrom[0].inverse()
        elif len(placementsFrom) == len(placements):
            return [lattice2BaseFeature.makeMoveFromTo(placementsFrom[i], placements[i]) for i in range(0, len(placements))]
        else:
            lattice2Executer.warning(obj,"Lengths of arrays linked as PlacementsTo and PlacementsFrom must equal, or PlacementsFrom can be one placement. Violation: lengths are "+str(len(placements))+ " and "+str(len(placementsFrom)))
    else:
        raise ValueError("Referencing mode not implemented: "+refmode)
    
    return [plm.multiply(plmDeref) for plm in placements]
Example #7
0
    def derivedExecute(self,obj):
        #validity check
        if not lattice2BaseFeature.isObjectLattice(obj.Base):
            lattice2Executer.warning(obj,"A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected.")

        output = [] #variable to receive the final list of placements
        leaves = LCE.AllLeaves(obj.Base.Shape)
        input = [leaf.Placement for leaf in leaves]
        if obj.FilterType == 'bypass':
            output = input
        elif obj.FilterType == 'specific items':
            flags = [False] * len(input)
            ranges = obj.items.split(';')
            for r in ranges:
                r_v = r.split(':')
                if len(r_v) == 1:
                    i = int(r_v[0])
                    output.append(input[i])
                    flags[i] = True
                elif len(r_v) == 2 or len(r_v) == 3:
                    if len(r_v) == 2:
                        r_v.append("") # fix issue #1: instead of checking length here and there, simply add the missing field =)
                    ifrom = None   if len(r_v[0].strip()) == 0 else   int(r_v[0])                    
                    ito = None     if len(r_v[1].strip()) == 0 else   int(r_v[1])
                    istep = None   if len(r_v[2].strip()) == 0 else   int(r_v[2])
                    output=output+input[ifrom:ito:istep]
                    for b in flags[ifrom:ito:istep]:
                        b = True
                else:
                    raise ValueError('index range cannot be parsed:'+r)
            if obj.Invert :
                output = []
                for i in xrange(0,len(input)):
                    if not flags[i]:
                        output.append(input[i])
        elif obj.FilterType == 'collision-pass':
            stencil = obj.Stencil.Shape
            for plm in input:
                pnt = Part.Vertex(plm.Base)
                d = pnt.distToShape(stencil)
                if bool(d[0] < DistConfusion) ^ bool(obj.Invert):
                    output.append(plm)
        elif obj.FilterType == 'window-distance':
            vals = [0.0] * len(input)
            for i in xrange(0,len(input)):
                if obj.FilterType == 'window-distance':
                    pnt = Part.Vertex(input[i].Base)
                    vals[i] = pnt.distToShape(obj.Stencil.Shape)[0]
            
            valFrom = obj.WindowFrom
            valTo = obj.WindowTo
            
            for i in xrange(0,len(input)):
                if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert:
                    output.append(input[i])
        else:
            raise ValueError('Filter mode not implemented:'+obj.FilterType)
                            
        return output
Example #8
0
def getPlacementsList(documentObject, context=None, suppressWarning=False):
    """getPlacementsList(documentObject, context = None): extract list of placements 
    from an array object. Context is an object to report as context, when displaying 
    a warning if the documentObject happens to be a non-lattice."""
    if not isObjectLattice(documentObject):
        if not suppressWarning:
            lattice2Executer.warning(
                context,
                documentObject.Name + " is not a placement or an array of placements. Results may be unexpected.",
            )
    leaves = LCE.AllLeaves(documentObject.Shape)
    return [leaf.Placement for leaf in leaves]
Example #9
0
def getPlacementsList(documentObject, context=None, suppressWarning=False):
    '''getPlacementsList(documentObject, context = None): extract list of placements 
    from an array object. Context is an object to report as context, when displaying 
    a warning if the documentObject happens to be a non-lattice.'''
    if not isObjectLattice(documentObject):
        if not suppressWarning:
            lattice2Executer.warning(
                context, documentObject.Name +
                " is not a placement or an array of placements. Results may be unexpected."
            )
    if documentObject.isDerivedFrom(
            'App::Placement') or documentObject.isDerivedFrom(
                'PartDesign::CoordinateSystem'):
        return [documentObject.Placement]
    leaves = LCE.AllLeaves(documentObject.Shape)
    return [leaf.Placement for leaf in leaves]
Example #10
0
    def execute(self,selfobj):
        #validity check
        if isObjectLattice(selfobj.Object):
            import lattice2Executer
            lattice2Executer.warning(selfobj,"A generic shape is expected, but a placement/array was supplied. It will be treated as a generic shape.")

        rst = [] #variable to receive the final list of shapes
        lnkobj = selfobj.Object
        for subname in selfobj.SubNames:
            subname = subname.strip()
            if len(subname)==0:
                raise ValueError("Empty subname! Not allowed.")
            if 'Face' in subname:
                index = int(subname.replace('Face',''))-1
                rst.append(lnkobj.Shape.Faces[index])
            elif 'Edge' in subname:
                index = int(subname.replace('Edge',''))-1
                rst.append(lnkobj.Shape.Edges[index])
            elif 'Vertex' in subname:
                index = int(subname.replace('Vertex',''))-1
                rst.append(lnkobj.Shape.Vertexes[index])
            else:
                lattice2Executer.warning(selfobj,"Unexpected subelement name: "+subname+". Trying to extract it with .Shape.getElement()...")
                rst.append(linkobj.Shape.getElement(subname))
        if len(rst) == 0:
            scale = 1.0
            try:
                if selfobj.Object:
                    scale = selfobj.Object[0].Shape.BoundBox.DiagonalLength/math.sqrt(3)
            except Exception as err:
                App.Console.PrintError(selfobj.Name+": Failed to estimate size of marker shape")
            if scale < DistConfusion * 100:
                scale = 1.0
            selfobj.Shape = markers.getNullShapeShape(scale)
            raise ValueError('Nothing is linked, apparently!') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.
        
        if len(rst) > 1:
            selfobj.Shape = Part.makeCompound(rst)
        else: # don't make compound of one shape, output it directly
            sh = rst[0]
            # absorb placement of original shape
            sh = ShapeCopy.transformCopy(sh)
            # apply Placement that is filled into feature's Placement property (not necessary)
            sh.Placement = selfobj.Placement
            selfobj.Shape = sh
Example #11
0
    def derivedExecute(self, obj):
        # cache stuff
        base = screen(obj.Base).Shape
        if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)):
            lattice2Executer.warning(
                obj,
                "Base is not a lattice, but lattice is expected. Results may be unexpected.\n"
            )
        baseChildren = LCE.AllLeaves(base)

        #cache mode comparisons, for speed
        posIsInvert = obj.TranslateMode == 'invert'
        posIsKeep = obj.TranslateMode == 'keep'
        posIsReset = obj.TranslateMode == 'reset'

        oriIsInvert = obj.OrientMode == 'invert'
        oriIsKeep = obj.OrientMode == 'keep'
        oriIsReset = obj.OrientMode == 'reset'

        # initialize output containers and loop variables
        outputPlms = []  #list of placements

        # the essence
        for child in baseChildren:
            pos = App.Vector()
            ori = App.Rotation()
            inverted = child.Placement.inverse()
            if posIsInvert:
                pos = inverted.Base
            elif posIsKeep:
                pos = child.Placement.Base
            elif posIsReset:
                pass

            if oriIsInvert:
                ori = inverted.Rotation
            elif oriIsKeep:
                ori = child.Placement.Rotation
            elif oriIsReset:
                pass

            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #12
0
    def derivedExecute(self, obj):
        # cache stuff
        base = obj.Base.Shape
        if not lattice2BaseFeature.isObjectLattice(obj.Base):
            lattice2Executer.warning(
                obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n"
            )
        baseChildren = LCE.AllLeaves(base)

        # cache mode comparisons, for speed
        posIsInvert = obj.TranslateMode == "invert"
        posIsKeep = obj.TranslateMode == "keep"
        posIsReset = obj.TranslateMode == "reset"

        oriIsInvert = obj.OrientMode == "invert"
        oriIsKeep = obj.OrientMode == "keep"
        oriIsReset = obj.OrientMode == "reset"

        # initialize output containers and loop variables
        outputPlms = []  # list of placements

        # the essence
        for child in baseChildren:
            pos = App.Vector()
            ori = App.Rotation()
            inverted = child.Placement.inverse()
            if posIsInvert:
                pos = inverted.Base
            elif posIsKeep:
                pos = child.Placement.Base
            elif posIsReset:
                pass

            if oriIsInvert:
                ori = inverted.Rotation
            elif oriIsKeep:
                ori = child.Placement.Rotation
            elif oriIsReset:
                pass

            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #13
0
    def derivedExecute(self, obj):
        # validity check
        nonLattices = []
        for iArr in range(0, len(obj.Links)):
            link = obj.Links[iArr]
            if not lattice2BaseFeature.isObjectLattice(link):
                nonLattices.append(link.Label)
        if len(nonLattices) > 0:
            lattice2Executer.warning(
                obj,
                "Only lattice objects are expected to be linked as arrays in JoinArrays. There are "
                + len(nonLattices)
                + " objects which are not lattice objects. Results may me unexpected.",
            )

        # extract placements
        listlistPlms = []
        lengths = []
        for link in obj.Links:
            leaves = LCE.AllLeaves(link.Shape)
            listlistPlms.append([child.Placement for child in leaves])
            lengths.append(len(leaves))

        # processing
        output = []  # list of placements
        if obj.Interleave:
            for l in lengths[1:]:
                if l != lengths[0]:
                    lattice2Executer.warning(
                        obj, "Array lengths are unequal: " + repr(lengths) + ". Interleaving will be inconsistent."
                    )
                    break

            for iItem in range(0, max(lengths)):
                for list in listlistPlms:
                    if iItem < len(list):
                        output.append(list[iItem])
        else:
            for list in listlistPlms:
                output.extend(list)
        return output
Example #14
0
    def derivedExecute(self, obj):
        #validity check
        nonLattices = []
        for iArr in range(0, len(obj.Links)):
            link = obj.Links[iArr]
            if not lattice2BaseFeature.isObjectLattice(link):
                nonLattices.append(link.Label)
        if len(nonLattices) > 0:
            lattice2Executer.warning(
                obj,
                "Only lattice objects are expected to be linked as arrays in JoinArrays. There are "
                + len(nonLattices) +
                " objects which are not lattice objects. Results may me unexpected."
            )

        #extract placements
        listlistPlms = []
        lengths = []
        for link in obj.Links:
            leaves = LCE.AllLeaves(link.Shape)
            listlistPlms.append([child.Placement for child in leaves])
            lengths.append(len(leaves))

        #processing
        output = []  #list of placements
        if obj.Interleave:
            for l in lengths[1:]:
                if l != lengths[0]:
                    lattice2Executer.warning(
                        obj, "Array lengths are unequal: " + repr(lengths) +
                        ". Interleaving will be inconsistent.")
                    break

            for iItem in range(0, max(lengths)):
                for list in listlistPlms:
                    if iItem < len(list):
                        output.append(list[iItem])
        else:
            for list in listlistPlms:
                output.extend(list)
        return output
Example #15
0
 def Activated(self):
     try:
         if len(FreeCADGui.Selection.getSelection())==0:
             infoMessage("Make compound",
                 "Make compound command. Combines several shapes into one. The shapes are kept as-is. They are not fused together, and can be extracted unchanged.\n\n"+
                 "Compounds can contain combination of shapes of any topology: one can compound some edges with some solids. Compound is effectively another kind of group. But unlike normal FreeCAD group, compound only accepts OCC geometry. Compound cannot include meshes, dimensions, labels, or other objects that provide no Shape property.\n\n"+
                 "Note that compounds that have objects that touch or intersect are considered invalid by Part CheckGeometry. Such invalid compounds cannot be used for Part Cut/Common/Fuse.")
             return
         
         oldVal = lattice2Executer.globalIsCreatingLatticeFeature
         lattice2Executer.globalIsCreatingLatticeFeature = True
         
         sel = FreeCADGui.Selection.getSelectionEx()
         for s in sel:
             if isObjectLattice(s.Object):
                 lattice2Executer.warning(None,"For making a compound, generic shapes are expected, but some of the selected objects are placements/arrays of placements. These will be treated as generic shapes; results may be unexpected.")
                 break
         FreeCADGui.runCommand("Part_Compound")
     except Exception as err:
         msgError(err)
     finally:
         lattice2Executer.globalIsCreatingLatticeFeature = oldVal
Example #16
0
def Subsequence_LinkSubList(linksublist,
                            traversal=TRAVERSAL_MODES[0],
                            loop=LOOP_MODES[0],
                            object_filter=None,
                            index_filter=None):
    """Subsequence_LinkSubList(linksublist, traversal = TRAVERSAL_MODES[0], loop = 'Till end', object_filter = None):
    form a list of values for iterating over elements in App::PropertyLinkSubList.
    
    linksublist: the value of property (either of type [(object, sub), ..], or 
    [(object, [sub1, sub2, ...]), ...]
    
    traversal: how to unpack compounds
    
    loop: sets which children to process.
    
    object_filter: list or set of objects that should be considered being arrays. If 
    omitted, all links to subelements are attempted to be enumerated as arrays.
    
    index_list: list or set of ints, that sets which parts of link are to be subsequenced. 
    If None, all elements are attempted to be subsequenced, and only if none can be, an error 
    is raised. If index_list is specified, it is treated as strict, and if any 
    corresponding sublink can't be subsequenced, an error is raised.
    
    return: list of values that can be assigned to the link in a loop.
    """

    linksublist = linkSubList_convertToOldStyle(linksublist)
    if object_filter is None:
        object_filter = [
            obj for (obj, sub) in linksublist
        ]  #number of links is likely quite low, so using sets might give more of a penalty than gain

    loops = []  #list to receive subsequences for made for pieces of the link
    n_seq = None
    i = -1
    for object, sub in linksublist:
        i += 1
        if (sub and (object in object_filter)
                and (index_filter is None or i in index_filter)
                and hasattr(object, "Shape")):
            try:
                seq = Subsequence_basic((object, sub), traversal, loop)
                if len(seq) < 2:
                    from lattice2Executer import warning
                    warning(
                        None,
                        u"Subsequencing link index {i} to {sub} of '{obj}' yielded only one item."
                        .format(i=i, obj=object.Label, sub=sub))
            except TraversalError:
                if index_filter is not None:
                    raise  # re-raise. When index list is given, treat it as that it must be subsequenced.
                loops.append((object, sub))
                continue
            loops.append(seq)
            if n_seq is None:
                n_seq = len(seq)
            else:
                n_seq = min(n_seq, len(seq))
        else:
            if index_filter and i in index_filter:
                if not sub:
                    raise SubsequencingError_LinkValue(
                        "Sublink part {index} can't be subsequenced, because it's a link to whole object, not to subelement."
                        .format(index=i))
            loops.append((object, sub))
    assert (len(loops) == len(linksublist))
    if n_seq is None:
        raise SubsequencingError_LinkValue(
            "In supplied link, nothing to loop over compounds was found.")

    # expand non-subsequenced parts of linksublist
    for i_loop in range(len(loops)):
        if type(loops[i_loop]) is not list:
            loops[i_loop] = [loops[i_loop]] * n_seq

    # form the result
    ret = []
    for i_seq in range(n_seq):
        ret.append([loop[i_seq] for loop in loops])
    return ret
Example #17
0
    def derivedExecute(self,obj):
        # cache stuff
        base = obj.Base.Shape
        if not lattice2BaseFeature.isObjectLattice(obj.Base):
            lattice2Executer.warning(obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n")
        input = [leaf.Placement for leaf in LCE.AllLeaves(base)]
        
        if len(input) < 2:
            raise ValueError("At least 2 placements ar needed to interpolate; there are just "+str(len(input))+" in base array.")
        if obj.NumberSamples < 2:
            raise ValueError("Can output no less than 2 samples; "+str(obj.NumberSamples)+" was requested.")
                        
        #cache mode comparisons, for speed
        posIsInterpolate = obj.TranslateMode == 'interpolate'
        posIsReset = obj.TranslateMode == 'reset'
        
        oriIsInterpolate = obj.OrientMode == 'interpolate'
        oriIsReset = obj.OrientMode == 'reset'        
        
        # construct interpolation functions
        #  prepare lists of input samples
        IArray = [float(i) for i in range(0,len(input))]
        XArray = [plm.Base.x for plm in input]
        YArray = [plm.Base.y for plm in input]
        ZArray = [plm.Base.z for plm in input]
        QArrays = [[],[],[],[]]
        prevQ = [0.0]*4
        for plm in input:
            Q = plm.Rotation.Q
            #test if quaernion has changed sign compared to previous one. 
            # Quaternions of opposite sign are equivalent in terms of rotation, 
            # but sign changes confuse interpolation, so we are detecting sign 
            # changes and discarding them
            if dotProduct(Q,prevQ) < -ParaConfusion: 
                Q = [-v for v in Q] 
            for iQ in [0,1,2,3]:
                QArrays[iQ].append( Q[iQ] ) 
            prevQ = Q
        
        #  constuct function objects
        if posIsInterpolate:
            FX = LIU.InterpolateF(IArray,XArray)
            FY = LIU.InterpolateF(IArray,YArray)
            FZ = LIU.InterpolateF(IArray,ZArray)
        if oriIsInterpolate:
            FQs = []
            for iQ in [0,1,2,3]:
                FQs.append(LIU.InterpolateF(IArray,QArrays[iQ]))
                
        # initialize output containers and loop variables
        outputPlms = [] #list of placements

        for i_output in range(0,math.trunc(obj.NumberSamples+ParaConfusion)):
            i_input = float(i_output) / (obj.NumberSamples-1) * (len(input)-1)
            pos = App.Vector()
            ori = App.Rotation()
            if posIsInterpolate:
                pos = App.Vector(FX.value(i_input), FY.value(i_input), FZ.value(i_input))
            
            if oriIsInterpolate:
                ori = App.Rotation(FQs[0].value(i_input),
                                   FQs[1].value(i_input),
                                   FQs[2].value(i_input),
                                   FQs[3].value(i_input))                
            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #18
0
    def execute(self, obj):
        base = screen(obj.ShapeLink).Shape
        if obj.CompoundTraversal == "Use as a whole":
            baseChildren = [base]
        else:
            if base.ShapeType != 'Compound':
                base = Part.makeCompound([base])
            if obj.CompoundTraversal == "Recursive":
                baseChildren = LCE.AllLeaves(base)
            else:
                baseChildren = base.childShapes()

        N = len(baseChildren)

        orients = []
        if obj.OrientMode == "global":
            orients = [App.Placement()] * N
        elif obj.OrientMode == "local of compound":
            orients = [screen(obj.ShapeLink).Placement] * N
        elif obj.OrientMode == "local of child":
            orients = [child.Placement for child in baseChildren]
        elif obj.OrientMode == "use OrientLink":
            orients = LBF.getPlacementsList(screen(obj.OrientLink),
                                            context=obj)
            if len(orients) == N:
                pass
            elif len(orients) > N:
                Executer.warning(
                    obj,
                    "Array of placements linked in OrientLink has more placements ("
                    + str(len(orients)) +
                    ") than bounding boxes to be constructed (" +
                    str(len(baseChildren)) +
                    "). Extra placements will be dropped.")
            elif len(orients) == 1:
                orients = [orients[0]] * N
            else:
                raise ValueError(
                    obj.Name +
                    ": Array of placements linked in OrientLink has not enough placements ("
                    + str(len(orients)) +
                    ") than bounding boxes to be constructed (" +
                    str(len(baseChildren)) + ").")
        else:
            raise ValueError(obj.Name + ": OrientMode " + obj.OrientMode +
                             " not implemented =(")

        # mark placements with no rotation
        for i in range(N):
            Q = orients[i].Rotation.Q
            # Quaternions for zero rotation are either (0,0,0,1) or (0,0,0,-1). For non-zero
            # rotations, some of first three values will be nonzero, and fourth value will
            # not be equal to 1. While it's enough to compare absolute value of fourth value
            # to 1, precision is seriously lost in such comparison, so we are checking if
            # first three values are zero instead.
            if abs(Q[0]) + abs(Q[1]) + abs(Q[2]) < ParaConfusion:
                orients[i] = None

        from lattice2ShapeCopy import shallowCopy
        boxes_shapes = []
        for i in range(N):
            child = baseChildren[i]
            if orients[i] is not None:
                child = shallowCopy(child)
                child.Placement = orients[i].inverse().multiply(
                    child.Placement)

            if obj.Precision:
                bb = getPrecisionBoundBox(child)
            else:
                bb = child.BoundBox

            bb = scaledBoundBox(bb, obj.ScaleFactor)
            bb.enlarge(obj.Padding)

            bb_shape = boundBox2RealBox(bb)
            if orients[i] is not None:
                bb_shape.transformShape(orients[i].toMatrix(), True)
            boxes_shapes.append(bb_shape)

        #Fill in read-only properties
        if N == 1:
            obj.Size = App.Vector(bb.XLength, bb.YLength, bb.ZLength)

            cnt = bb.Center
            if orients[0] is not None:
                cnt = orients[0].multVec(cnt)
            obj.Center = cnt
        else:
            obj.Size = App.Vector()
            obj.Center = App.Vector()

        if obj.CompoundTraversal == "Use as a whole":
            assert (N == 1)
            obj.Shape = boxes_shapes[0]
        else:
            obj.Shape = Part.makeCompound(boxes_shapes)
Example #19
0
    def derivedExecute(self, obj):
        #validity check
        if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)):
            lattice2Executer.warning(
                obj,
                "A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected."
            )

        toolShape = screen(obj.Tool).Shape
        if lattice2BaseFeature.isObjectLattice(screen(obj.Tool)):
            lattice2Executer.warning(
                obj,
                "A lattice object was provided as Tool. It will be converted into points; orientations will be ignored."
            )
            leaves = LCE.AllLeaves(toolShape)
            points = [Part.Vertex(leaf.Placement.Base) for leaf in leaves]
            toolShape = Part.makeCompound(points)

        leaves = LCE.AllLeaves(screen(obj.Base).Shape)
        input = [leaf.Placement for leaf in leaves]

        output = []  #variable to receive the final list of placements

        #cache settings
        elev = float(obj.PosElevation)
        posIsKeep = obj.TranslateMode == 'keep'
        posIsProjected = obj.TranslateMode == 'projected'
        posIsMixed = obj.TranslateMode == 'mixed'
        mixF = float(obj.PosMixFraction)
        oriIsKeep = obj.OrientMode == 'keep'
        oriIsAlongGap = obj.OrientMode == 'along gap'
        oriIsTangentPlane = obj.OrientMode == 'tangent plane'
        oriIsAlongU = obj.OrientMode == 'along u'
        oriIsAlongV = obj.OrientMode == 'along v'

        isMultiSol = obj.Multisolution == 'use all'

        for plm in input:
            v = Part.Vertex(plm.Base)
            projection = v.distToShape(toolShape)
            (dist, gaps, infos) = projection
            for iSol in range(0, len(gaps)):
                (posKeep, posPrj) = gaps[iSol]
                (dummy, dummy, dummy, el_topo, el_index,
                 el_params) = infos[iSol]

                # Fetch all possible parameters (some may not be required, depending on modes)
                normal = posKeep - posPrj
                if normal.Length < DistConfusion:
                    normal = None

                tangU = None
                tangV = None
                if el_topo == 'Face':
                    face = toolShape.Faces[el_index]
                    if normal is None:
                        normal = face.normalAt(*el_params)
                    (tangU, tangV) = face.tangentAt(*el_params)
                elif el_topo == "Edge":
                    edge = toolShape.Edges[el_index]
                    tangU = edge.tangentAt(el_params)

                if normal is not None:
                    normal.normalize()

                #mode logic - compute new placement
                if posIsKeep:
                    pos = plm.Base
                elif posIsProjected:
                    pos = posPrj
                elif posIsMixed:
                    pos = posKeep * mixF + posPrj * (1 - mixF)
                else:
                    raise ValueError("Positioning mode not implemented: " +
                                     obj.TranslateMode)

                if abs(elev) > DistConfusion:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for nonzero position elevation."
                        )
                    pos += normal * elev

                if oriIsKeep:
                    ori = plm.Rotation
                elif oriIsAlongGap:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("X",
                                                                XAx=normal *
                                                                (-1.0))
                elif oriIsTangentPlane:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("Z",
                                                                ZAx=normal)
                elif oriIsAlongU:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    if tangU is None:
                        raise ValueError(
                            "TangentU vector not available for point on " +
                            el_topo +
                            ". TangentU vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",
                                                                ZAx=normal,
                                                                XAx=tangU)
                elif oriIsAlongV:
                    if normal is None:
                        raise ValueError(
                            "Normal vector not available for a placement resting on "
                            + el_topo +
                            ". Normal vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    if tangV is None:
                        raise ValueError(
                            "TangentV vector not available for point on " +
                            el_topo +
                            ". TangentV vector is required for orientation mode '"
                            + obj.OrientMode + "'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",
                                                                ZAx=normal,
                                                                XAx=tangV)
                else:
                    raise ValueError("Orientation mode not implemented: " +
                                     obj.OrientMode)

                output.append(App.Placement(pos, ori))

                if not isMultiSol:
                    break

        return output
Example #20
0
    def execute(self, selfobj):
        self.assureProperties(selfobj)

        #validity check
        if isObjectLattice(screen(selfobj.Object)):
            import lattice2Executer
            lattice2Executer.warning(
                selfobj,
                "A generic shape is expected, but a placement/array was supplied. It will be treated as a generic shape."
            )

        lnkobj = screen(selfobj.Object)
        sh = lnkobj.Shape

        # subsequencing
        full_link = (lnkobj, selfobj.SubNames)
        if selfobj.Looping == 'Single':
            lnkseq = [full_link]
        else:
            lnkseq = LSS.Subsequence_auto(full_link, selfobj.CompoundTraversal,
                                          selfobj.Looping)

        # main code
        seq_packs = [
        ]  #pack = single item of subsequence. Pack contains list of elements that were selected.
        shape_count = 0
        for lnk in lnkseq:  # loop over subsequence (if Looping == 'Single', this loop will only loop once)
            # extract the pack
            assert (
                lnk[0] is lnkobj
            )  # all links should point to elements of one object anyway
            subnames = lnk[1]
            pack = [
            ]  #acculumator, to eventually become a compound of shapes for this subsequence item
            for subname in subnames:
                subname = subname.strip()
                if len(subname) == 0:
                    raise ValueError("Empty subname! Not allowed.")
                if 'Face' in subname:  # manual handling of standard cases, because support for negative indexing is needed
                    index = int(subname.replace('Face', '')) - 1
                    pack.append(sh.Faces[index])
                elif 'Edge' in subname:
                    index = int(subname.replace('Edge', '')) - 1
                    pack.append(sh.Edges[index])
                elif 'Vertex' in subname:
                    index = int(subname.replace('Vertex', '')) - 1
                    pack.append(sh.Vertexes[index])
                else:  #fail-safe. non-standard sublink.
                    import lattice2Executer
                    lattice2Executer.warning(
                        selfobj, "Unexpected subelement name: " + subname +
                        ". Trying to extract it with .Shape.getElement()...")
                    pack.append(sh.getElement(subname))

            shape_count += len(pack)

            # convert list into compound
            if len(pack) == 1:
                pack = ShapeCopy.transformCopy(pack[0])
            else:
                pack = Part.makeCompound(pack)

            # accumulate
            seq_packs.append(pack)

        # convert list into compound
        if len(seq_packs) == 1:
            seq_packs = seq_packs[0]
        else:
            seq_packs = Part.makeCompound(seq_packs)

        if shape_count == 0:
            # no shapes collected, FAIL!
            scale = 1.0
            try:
                if screen(selfobj.Object):
                    scale = screen(
                        selfobj.Object
                    ).Shape.BoundBox.DiagonalLength / math.sqrt(3)
            except Exception as err:
                App.Console.PrintError(
                    selfobj.Name + ": Failed to estimate size of marker shape")
            if scale < DistConfusion * 100:
                scale = 1.0
            selfobj.Shape = markers.getNullShapeShape(scale)
            raise ValueError(
                'Nothing is linked, apparently!'
            )  #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.

        # done!
        selfobj.Shape = seq_packs
Example #21
0
    def derivedExecute(self,obj):
        
        self.initNewProperties(obj)
        
        outputIsLattice = lattice2BaseFeature.isObjectLattice(screen(obj.Object))
        
        if not lattice2BaseFeature.isObjectLattice(screen(obj.Object)):
            if obj.ObjectTraversal == "Direct children only":
                objectShapes = screen(obj.Object).Shape.childShapes()
                if screen(obj.Object).Shape.ShapeType != "Compound":
                    lattice2Executer.warning(obj,"shape supplied as object is not a compound. It is going to be downgraded one level down (e.g, if it is a wire, the edges are going to be enumerated as children).")
            elif obj.ObjectTraversal == "Recursive":
                objectShapes = LCE.AllLeaves(screen(obj.Object).Shape)
            else:
                raise ValueError("Traversal mode not implemented: "+obj.ObjectTraversal)
        else:
            objectPlms = lattice2BaseFeature.getPlacementsList(screen(obj.Object), obj)
        placements = lattice2BaseFeature.getPlacementsList(screen(obj.PlacementsTo), obj)

        
        # Precompute referencing
        placements = DereferenceArray(obj, placements, screen(obj.PlacementsFrom), obj.Referencing)
                
        # initialize output containers and loop variables
        outputShapes = [] #output list of shapes
        outputPlms = [] #list of placements
        iChild = 0
        numChildren = len(objectPlms) if outputIsLattice else len(objectShapes) 
        copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying)
        
        # the essence
        for iPlm in range(len(placements)):
            if iChild == numChildren:
                if obj.LoopObjectSequence:
                    iChild = 0
                else:
                    break
            
            plm = placements[iPlm]
             
            if outputIsLattice:
                objectPlm = objectPlms[iChild]
                outputPlms.append(plm.multiply(objectPlm))
            else:
                outputShape = ShapeCopy.copyShape(objectShapes[iChild], copy_method_index, plm)
                # outputShape.Placement = plm.multiply(outputShape.Placement) #now done by shape copy routine
                outputShapes.append(outputShape)
            
            iChild += 1
            
        if len(placements) > numChildren and not obj.LoopObjectSequence:
            lattice2Executer.warning(obj,"There are fewer children to populate, than placements to be populated (%1, %2). Extra placements will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements))))
            
        if len(placements) < numChildren:
            lattice2Executer.warning(obj,"There are more children to populate, than placements to be populated (%1, %2). Extra children will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements))))
            
        if outputIsLattice:
            return outputPlms
        else:
            obj.Shape = Part.makeCompound(outputShapes)
            return None
Example #22
0
    def execute(self,obj):
        nOfStrings = len(obj.Strings)
        lattice = screen(obj.ArrayLink)
        if lattice is None:
            plms = [App.Placement() for i in range(0,nOfStrings)]
        else:
            if not lattice2BaseFeature.isObjectLattice(lattice):
                lattice2Executer.warning(obj,"ShapeString's link to array must point to a lattice. It points to a generic shape. Results may be unexpected.")
            leaves = LCE.AllLeaves(lattice.Shape)
            plms = [leaf.Placement for leaf in leaves]
        
        #update foolObj's properties
        self.makeFoolObj(obj) #make sure we have one - fixes defunct Lattice ShapeString after save-load
        for (proptype, propname, group, hint) in self.foolObj.properties:
            if propname != "String": #ignore "String", that will be taken care of in the following loop
                setattr(self.foolObj, propname, getattr(obj, propname))
        self.foolObj.FontFile = findFont(obj.FontFile)
        obj.FullPathToFont = self.foolObj.FontFile
        
        shapes = []
        for i in range(  0 ,  min(len(plms),len(obj.Strings))  ):
            if len(obj.Strings[i]) > 0:
                #generate shapestring using Draft
                self.foolObj.String = obj.Strings[i]
                self.foolObj.Shape = None
                self.draft_shape_string.execute(self.foolObj)
                shape = self.foolObj.Shape
                
                #calculate alignment point
                if obj.XAlign == 'None' and obj.YAlign == 'None':
                    pass #need not calculate boundbox
                else:
                    if obj.AlignPrecisionBoundBox:
                        bb = getPrecisionBoundBox(shape)
                    else:
                        bb = shape.BoundBox

                alignPnt = App.Vector()
                
                if obj.XAlign == 'Left':
                    alignPnt.x = bb.XMin
                elif obj.XAlign == 'Right':
                    alignPnt.x = bb.XMax
                elif obj.XAlign == 'Middle':
                    alignPnt.x = bb.Center.x

                if obj.YAlign == 'Bottom':
                    alignPnt.y = bb.YMin
                elif obj.YAlign == 'Top':
                    alignPnt.y = bb.YMax
                elif obj.YAlign == 'Middle':
                    alignPnt.y = bb.Center.y
                
                #Apply alignment
                shape.Placement = App.Placement(alignPnt*(-1.0), App.Rotation()).multiply(shape.Placement)
                
                #Apply placement from array
                shape.Placement = plms[i].multiply(shape.Placement)
                
                shapes.append(shape.copy())
        
        if len(shapes) == 0:
            scale = 1.0
            if lattice is not None:
                scale = lattice.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError('No strings were converted into shapes') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.
        result = Part.makeCompound(shapes)
        result.Placement = obj.Placement
        obj.Shape = result
Example #23
0
    def execute(self, obj):
        # please, don't override. Override derivedExecute instead.

        plms = self.derivedExecute(obj)

        if plms is not None:
            if plms == "suppress":
                return
            obj.NumElements = len(plms)
            shapes = []
            markerSize = obj.MarkerSize
            if markerSize < DistConfusion:
                markerSize = getMarkerSizeEstimate(plms, obj)
            marker = lattice2Markers.getPlacementMarker(
                scale=markerSize, markerID=obj.MarkerShape)

            bExposing = False
            if obj.ExposePlacement:
                if len(plms) == 1:
                    bExposing = True
                else:
                    lattice2Executer.warning(
                        obj,
                        "Multiple placements are being fed, can't expose placements. Placement property will be forced to zero."
                    )
                    obj.Placement = App.Placement()

            if bExposing:
                obj.Shape = shallowCopy(marker)
                obj.Placement = plms[0]
            else:
                for plm in plms:
                    sh = shallowCopy(marker)
                    sh.Placement = plm
                    shapes.append(sh)

                if len(shapes) == 0:
                    obj.Shape = lattice2Markers.getNullShapeShape(markerSize)
                    raise ValueError('Lattice object is null')

                sh = Part.makeCompound(shapes)
                sh.Placement = obj.Placement
                obj.Shape = sh

            if obj.isLattice == 'Auto-Off':
                obj.isLattice = 'Auto-On'

        else:
            # DerivedExecute didn't return anything. Thus we assume it
            # has assigned the shape, and thus we don't do anything.
            # Moreover, we assume that it is no longer a lattice object, so:
            if obj.isLattice == 'Auto-On':
                obj.isLattice = 'Auto-Off'

            if obj.ExposePlacement:
                if obj.Shape.ShapeType == "Compound":
                    children = obj.Shape.childShapes()
                    if len(children) == 1:
                        obj.Placement = children[0].Placement
                        obj.Shape = children[0]
                    else:
                        obj.Placement = App.Placement()
                else:
                    #nothing to do - FreeCAD will take care to make obj.Placement and obj.Shape.Placement synchronized.
                    pass
        return
Example #24
0
    def derivedExecute(self,obj):
        # cache stuff
        base = screen(obj.Base).Shape
        if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)):
            lattice2Executer.warning(obj, "Base is not a lattice, but lattice is expected. Results may be unexpected.\n")
        input = [leaf.Placement for leaf in LCE.AllLeaves(base)]
        
        if len(input) < 2:
            raise ValueError("At least 2 placements ar needed to interpolate; there are just "+str(len(input))+" in base array.")
        if obj.NumberSamples < 2:
            raise ValueError("Can output no less than 2 samples; "+str(obj.NumberSamples)+" was requested.")
                        
        #cache mode comparisons, for speed
        posIsInterpolate = obj.TranslateMode == 'interpolate'
        posIsReset = obj.TranslateMode == 'reset'
        
        oriIsInterpolate = obj.OrientMode == 'interpolate'
        oriIsReset = obj.OrientMode == 'reset'        
        
        # construct interpolation functions
        #  prepare lists of input samples
        IArray = [float(i) for i in range(0,len(input))]
        XArray = [plm.Base.x for plm in input]
        YArray = [plm.Base.y for plm in input]
        ZArray = [plm.Base.z for plm in input]
        QArrays = [[],[],[],[]]
        prevQ = [0.0]*4
        for plm in input:
            Q = plm.Rotation.Q
            #test if quaernion has changed sign compared to previous one. 
            # Quaternions of opposite sign are equivalent in terms of rotation, 
            # but sign changes confuse interpolation, so we are detecting sign 
            # changes and discarding them
            if dotProduct(Q,prevQ) < -ParaConfusion: 
                Q = [-v for v in Q] 
            for iQ in [0,1,2,3]:
                QArrays[iQ].append( Q[iQ] ) 
            prevQ = Q
        
        #  construct function objects
        if posIsInterpolate:
            FX = LIU.InterpolateF(IArray,XArray)
            FY = LIU.InterpolateF(IArray,YArray)
            FZ = LIU.InterpolateF(IArray,ZArray)
        if oriIsInterpolate:
            FQs = []
            for iQ in [0,1,2,3]:
                FQs.append(LIU.InterpolateF(IArray,QArrays[iQ]))
                
        # initialize output containers and loop variables
        outputPlms = [] #list of placements

        for i_output in range(0,math.trunc(obj.NumberSamples+ParaConfusion)):
            i_input = float(i_output) / (obj.NumberSamples-1) * (len(input)-1)
            pos = App.Vector()
            ori = App.Rotation()
            if posIsInterpolate:
                pos = App.Vector(FX.value(i_input), FY.value(i_input), FZ.value(i_input))
            
            if oriIsInterpolate:
                ori = App.Rotation(FQs[0].value(i_input),
                                   FQs[1].value(i_input),
                                   FQs[2].value(i_input),
                                   FQs[3].value(i_input))                
            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #25
0
    def execute(self, obj):
        #validity check
        if isObjectLattice(screen(obj.Base)):
            import lattice2Executer
            lattice2Executer.warning(
                obj,
                "A generic shape is expected, but an array of placements was supplied. It will be treated as a generic shape."
            )

        rst = []  #variable to receive the final list of shapes
        shps = screen(obj.Base).Shape.childShapes()
        if obj.FilterType == 'bypass':
            rst = shps
        elif obj.FilterType == 'specific items':
            rst = []
            flags = [False] * len(shps)
            ranges = obj.items.split(';')
            for r in ranges:
                r_v = r.split(':')
                if len(r_v) == 1:
                    i = int(r_v[0])
                    rst.append(shps[i])
                    flags[i] = True
                elif len(r_v) == 2 or len(r_v) == 3:
                    if len(r_v) == 2:
                        r_v.append(
                            ""
                        )  # fix issue #1: instead of checking length here and there, simply add the missing field =)
                    ifrom = None if len(r_v[0].strip()) == 0 else int(r_v[0])
                    ito = None if len(r_v[1].strip()) == 0 else int(r_v[1])
                    istep = None if len(r_v[2].strip()) == 0 else int(r_v[2])
                    rst = rst + shps[ifrom:ito:istep]
                    for b in flags[ifrom:ito:istep]:
                        b = True
                else:
                    raise ValueError('index range cannot be parsed:' + r)
            if obj.Invert:
                rst = []
                for i in range(0, len(shps)):
                    if not flags[i]:
                        rst.append(shps[i])
        elif obj.FilterType == 'collision-pass':
            stencil = screen(obj.Stencil).Shape
            for s in shps:
                d = s.distToShape(stencil)
                if bool(d[0] < DistConfusion) ^ bool(obj.Invert):
                    rst.append(s)
        elif obj.FilterType == 'window-volume' or obj.FilterType == 'window-area' or obj.FilterType == 'window-length' or obj.FilterType == 'window-distance':
            vals = [0.0] * len(shps)
            for i in range(0, len(shps)):
                if obj.FilterType == 'window-volume':
                    vals[i] = shps[i].Volume
                elif obj.FilterType == 'window-area':
                    vals[i] = shps[i].Area
                elif obj.FilterType == 'window-length':
                    vals[i] = shps[i].Length
                elif obj.FilterType == 'window-distance':
                    vals[i] = shps[i].distToShape(obj.Stencil.Shape)[0]

            maxval = max(vals)
            if obj.Stencil:
                if obj.FilterType == 'window-volume':
                    maxval = obj.Stencil.Shape.Volume
                elif obj.FilterType == 'window-area':
                    maxval = obj.Stencil.Shape.Area
                elif obj.FilterType == 'window-length':
                    maxval = obj.Stencil.Shape.Length
            if obj.OverrideMaxVal:
                maxval = obj.OverrideMaxVal

            valFrom = obj.WindowFrom / 100.0 * maxval
            valTo = obj.WindowTo / 100.0 * maxval

            for i in range(0, len(shps)):
                if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert:
                    rst.append(shps[i])
        else:
            raise ValueError('Filter mode not implemented:' + obj.FilterType)

        if len(rst) == 0:
            scale = 1.0
            if not screen(obj.Base).Shape.isNull():
                scale = screen(
                    obj.Base).Shape.BoundBox.DiagonalLength / math.sqrt(
                        3) / math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError(
                'Nothing passes through the filter'
            )  #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.

        if len(rst) > 1:
            obj.Shape = Part.makeCompound(rst)
        else:  # don't make compound of one shape, output it directly
            sh = rst[0]
            sh = ShapeCopy.transformCopy(sh)
            sh.Placement = obj.Placement
            obj.Shape = sh

        return
Example #26
0
    def derivedExecute(self, selfobj):
        # values generator should be functional even if recomputing is disabled, so do it first
        self.assureGenerator(selfobj)
        self.generator.updateReadonlyness()
        self.generator.execute()

        if selfobj.Recomputing == "Disabled":
            raise ValueError(
                selfobj.Name +
                ": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it."
            )
        try:
            #test parameter references and read out their current values
            refstr = selfobj.ParameterRef  #dict(selfobj.ExpressionEngine)["ParameterRef"]
            refstrs = refstr.replace(";", "\t").split("\t")
            defvalues = []
            for refstr in refstrs:
                refstr = refstr.strip()
                val = None
                try:
                    val = getParameter(selfobj.Document, refstr)
                except Exception as err:
                    App.Console.PrintError(
                        "{obj}: failed to read out parameter '{param}': {err}\n"
                        .format(obj=selfobj.Name, param=refstr, err=str(err)))
                defvalues.append(val)
            N_params = len(defvalues)
            if N_params == 0:
                raise ValueError(selfobj.Name +
                                 ": ParameterRef is not set. It is required.")

            #parse values
            values = []
            for strrow in selfobj.Values:
                if len(strrow) == 0:
                    break
                row = strrow.split(";")
                row = [
                    (strv.strip() if len(strv.strip()) > 0 else None)
                    for strv in row
                ]  # clean out spaces and replace empty strings with None
                if len(row) < N_params:
                    row += [None] * (N_params - len(row))
                values.append(row)

            # convert values to type, filling in defaults where values are missing
            for row in values:
                for icol in range(N_params):
                    strv = row[icol]
                    val = None
                    if strv is None:
                        val = defvalues[icol]
                    elif selfobj.ParameterType == 'float' or selfobj.ParameterType == 'int':
                        val = float(strv.replace(",", "."))
                        if selfobj.ParameterType == 'int':
                            val = int(round(val))
                    elif selfobj.ParameterType == 'string':
                        val = strv.strip()
                    else:
                        raise ValueError(
                            selfobj.Name +
                            ": ParameterType option not implemented: " +
                            selfobj.ParameterType)
                    row[icol] = val

            if len(values) == 0:
                scale = 1.0
                try:
                    if not screen(selfobj.Object).Shape.isNull():
                        scale = screen(
                            selfobj.Object
                        ).Shape.BoundBox.DiagonalLength / math.sqrt(3)
                except Exception:
                    pass
                if scale < DistConfusion * 100:
                    scale = 1.0
                selfobj.Shape = markers.getNullShapeShape(scale)
                raise ValueError(selfobj.Name + ": list of values is empty.")

            bGui = False  #bool(App.GuiUp) #disabled temporarily, because it causes a crash if property edits are approved by hitting Enter
            if bGui:
                import PySide
                progress = PySide.QtGui.QProgressDialog(
                    u"Recomputing " + selfobj.Label, u"Abort", 0,
                    len(values) + 1)
                progress.setModal(True)
                progress.show()

            doc1 = selfobj.Document
            doc2 = App.newDocument(
            )  #create temporary doc to do the computations

            # assign doc's filename before copying objects, otherwise we get errors with xlinks
            try:
                doc2.FileName = doc1.FileName
            except Exception as err:
                pass  #in old FreeCADs, FileName property is read-only, we can safely ignore that

            object_in_doc2 = None  # define the variable, to prevent del() in finally block from raising another error
            try:
                doc2.copyObject(screen(selfobj.Object), True)

                #if there are nested paraseries in the dependencies, make sure to enable them
                for objd2 in doc2.Objects:
                    if hasattr(objd2, "Recomputing"):
                        try:
                            objd2.Recomputing = "Enabled"
                            objd2.purgeTouched()
                        except exception:
                            lattice2Executer.warning(
                                selfobj, "Failed to enable recomputing of " +
                                objd2.Name)

                object_in_doc2 = doc2.getObject(screen(selfobj.Object).Name)
                if bGui:
                    progress.setValue(1)
                output_shapes = []
                for row in values:
                    for icol in range(len(row)):
                        setParameter(doc2, refstrs[icol].strip(), row[icol])

                    #recompute
                    doc2.recompute()

                    #get shape
                    shape = None
                    for obj in doc2.Objects:
                        if 'Invalid' in obj.State:
                            lattice2Executer.error(
                                obj,
                                "Recomputing shape for parameter value of " +
                                repr(row) + " failed.")

                            scale = 1.0
                            try:
                                if not screen(selfobj.Object).Shape.isNull():
                                    scale = screen(
                                        selfobj.Object
                                    ).Shape.BoundBox.DiagonalLength / math.sqrt(
                                        3)
                            except Exception:
                                pass
                            if scale < DistConfusion * 100:
                                scale = 1.0
                            shape = markers.getNullShapeShape(scale)
                    if shape is None:
                        shape = object_in_doc2.Shape.copy()
                    output_shapes.append(shape)

                    #update progress
                    if bGui:
                        progress.setValue(progress.value() + 1)
                        if progress.wasCanceled():
                            raise lattice2Executer.CancelError()

            finally:
                #delete all references, before destroying the document. Probably not required, but to be sure...
                del (object_in_doc2)
                doc2_name = doc2.Name
                del (doc2)
                App.closeDocument(doc2_name)
                if bGui:
                    progress.setValue(len(values) + 1)

            selfobj.Shape = Part.makeCompound(output_shapes)

            output_is_lattice = lattice2BaseFeature.isObjectLattice(
                screen(selfobj.Object))
            if 'Auto' in selfobj.isLattice:
                new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off'
                if selfobj.isLattice != new_isLattice:  #check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color)
                    selfobj.isLattice = new_isLattice
        finally:
            if selfobj.Recomputing == "Recompute Once":
                selfobj.Recomputing = "Disabled"
        return "suppress"  # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
Example #27
0
    def derivedExecute(self, obj):
        self.assureGenerator(obj)
        self.assureProperties(obj)
        self.updateReadonlyness(obj)

        # Apply links
        if screen(obj.AxisLink):
            if lattice2BaseFeature.isObjectLattice(screen(obj.AxisLink)):
                lattice2Executer.warning(
                    obj,
                    "For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape."
                )

            #resolve the link
            if len(obj.AxisLinkSubelement) > 0:
                linkedShape = screen(obj.AxisLink).Shape.getElement(
                    obj.AxisLinkSubelement)
            else:
                linkedShape = screen(obj.AxisLink).Shape

            #Type check
            if linkedShape.ShapeType != 'Edge':
                raise ValueError('Axis link must be an edge; it is ' +
                                 linkedShape.ShapeType + ' instead.')

            #prepare
            dir = App.Vector()
            point = App.Vector()
            if isinstance(linkedShape.Curve, Part.Line):
                start_point = linkedShape.valueAt(linkedShape.FirstParameter)
                end_point = linkedShape.valueAt(linkedShape.LastParameter)
                dir = end_point - start_point
                point = start_point
            elif isinstance(linkedShape.Curve, Part.Circle):
                dir = linkedShape.Curve.Axis
                point = linkedShape.Curve.Center
            else:
                raise ValueError(
                    "Edge " + repr(linkedShape) +
                    " can't be used to derive an axis. It must be either a line or a circle/arc."
                )

            #apply
            if obj.AxisDirIsDriven:
                obj.AxisDir = dir
            if obj.AxisPointIsDriven:
                obj.AxisPoint = point

        self.generator.execute()

        # cache properties into variables
        radius = float(obj.Radius)
        values = [float(strv) for strv in obj.Values]

        # compute initial vector. It is to be perpendicular to Axis
        rot_ini = lattice2GeomUtils.makeOrientationFromLocalAxes(
            ZAx=obj.AxisDir)
        overallPlacement = App.Placement(obj.AxisPoint, rot_ini)

        # Make the array
        output = []  # list of placements
        for ang in values:
            p = Part.Vertex()
            localrot = App.Rotation(App.Vector(0, 0, 1), ang)
            localtransl = localrot.multVec(App.Vector(radius, 0, 0))
            localplm = App.Placement(localtransl, localrot)
            resultplm = overallPlacement.multiply(localplm)
            if obj.OrientMode == 'None':
                resultplm.Rotation = App.Rotation()
            output.append(resultplm)

        return output
Example #28
0
    def derivedExecute(self,selfobj):
        # values generator should be functional even if recomputing is disabled, so do it first
        self.assureGenerator(selfobj)
        self.generator.updateReadonlyness()
        self.generator.execute()
        
        if selfobj.Recomputing == "Disabled":
            raise ValueError(selfobj.Name+": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it.")
        try:            
            #test parameter references and read out their current values
            refstr = selfobj.ParameterRef #dict(selfobj.ExpressionEngine)["ParameterRef"]
            refstrs = refstr.replace(";","\t").split("\t")
            defvalues = []
            for refstr in refstrs:
                refstr = refstr.strip();
                val = None;
                try:
                    val = getParameter(selfobj.Document,refstr)
                except Exception as err:
                    App.Console.PrintError("{obj}: failed to read out parameter '{param}': {err}\n"
                                            .format(obj= selfobj.Name,
                                                    param= refstr,
                                                    err= err.message))
                defvalues.append(val)
            N_params = len(defvalues)
            if N_params == 0:
                raise ValueError(selfobj.Name+": ParameterRef is not set. It is required.")
            
            #parse values
            values = []
            for strrow in selfobj.Values:
                if len(strrow) == 0:
                    break;
                row = strrow.split(";")
                row = [(strv.strip() if len(strv.strip())>0 else None) for strv in row] # clean out spaces and replace empty strings with None
                if len(row) < N_params:
                    row += [None]*(N_params - len(row))
                values.append(row)
            
            # convert values to type, filling in defaults where values are missing
            for row in values:
                for icol in range(N_params):
                    strv = row[icol]
                    val = None
                    if strv is None:
                        val = defvalues[icol]
                    elif selfobj.ParameterType == 'float' or selfobj.ParameterType == 'int':
                        val = float(strv.replace(",","."))
                        if selfobj.ParameterType == 'int':
                            val = int(round(val))
                    elif selfobj.ParameterType == 'string':
                        val = strv.strip()
                    else:
                        raise ValueError(selfobj.Name + ": ParameterType option not implemented: "+selfobj.ParameterType)
                    row[icol] = val
            
            if len(values) == 0:
                scale = 1.0
                try:
                    if not selfobj.Object.Shape.isNull():
                        scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3)
                except Exception:
                    pass
                if scale < DistConfusion * 100:
                    scale = 1.0
                selfobj.Shape = markers.getNullShapeShape(scale)
                raise ValueError(selfobj.Name + ": list of values is empty.") 
            
            bGui = False #bool(App.GuiUp) #disabled temporarily, because it causes a crash if property edits are approved by hitting Enter
            if bGui:
                import PySide
                progress = PySide.QtGui.QProgressDialog(u"Recomputing "+selfobj.Label, u"Abort", 0, len(values)+1)
                progress.setModal(True)
                progress.show()
            
            doc1 = selfobj.Document
            doc2 = App.newDocument()
            object_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error
            try:
                doc2.copyObject(selfobj.Object, True)
                
                #if there are nested paraseries in the dependencies, make sure to enable them
                for objd2 in doc2.Objects:
                    if hasattr(objd2,"Recomputing"):
                        try:
                            objd2.Recomputing = "Enabled"
                            objd2.purgeTouched()
                        except exception:
                            lattice2Executer.warning(selfobj,"Failed to enable recomputing of "+objd2.Name)
                
                object_in_doc2 = doc2.getObject(selfobj.Object.Name)
                if bGui:
                    progress.setValue(1)
                output_shapes = []
                for row in values:
                    for icol in range(len(row)):
                        setParameter(doc2, refstrs[icol].strip(), row[icol])
                    
                    #recompute
                    doc2.recompute()
                    
                    #get shape
                    shape = None
                    for obj in doc2.Objects:
                        if 'Invalid' in obj.State:
                            lattice2Executer.error(obj,"Recomputing shape for parameter value of "+repr(row)+" failed.")
                            
                            scale = 1.0
                            try:
                                if not selfobj.Object.Shape.isNull():
                                    scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3)
                            except Exception:
                                pass
                            if scale < DistConfusion * 100:
                                scale = 1.0
                            shape = markers.getNullShapeShape(scale)
                    if shape is None:
                        shape = object_in_doc2.Shape.copy()
                    output_shapes.append(shape)
                    
                    #update progress
                    if bGui:
                        progress.setValue(progress.value()+1)
                        if progress.wasCanceled():
                            raise lattice2Executer.CancelError()
                    
            finally:
                #delete all references, before destroying the document. Probably not required, but to be sure...
                del(object_in_doc2)
                doc2_name = doc2.Name
                del(doc2)
                App.closeDocument(doc2_name)
                if bGui:
                    progress.setValue(len(values)+1)

                
            selfobj.Shape = Part.makeCompound(output_shapes)

            output_is_lattice = lattice2BaseFeature.isObjectLattice(selfobj.Object)
            if 'Auto' in selfobj.isLattice:
                new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off'
                if selfobj.isLattice != new_isLattice:#check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color)
                    selfobj.isLattice = new_isLattice                    
        finally:
            if selfobj.Recomputing == "Recompute Once":
                selfobj.Recomputing = "Disabled"
        return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
Example #29
0
    def execute(self,obj):
        nOfStrings = len(obj.Strings)
        lattice = obj.ArrayLink
        if lattice is None:
            plms = [App.Placement() for i in range(0,nOfStrings)]
        else:
            if not lattice2BaseFeature.isObjectLattice(lattice):
                lattice2Executer.warning(obj,"ShapeString's link to array must point to a lattice. It points to a generic shape. Results may be unexpected.")
            leaves = LCE.AllLeaves(lattice.Shape)
            plms = [leaf.Placement for leaf in leaves]
        
        #update foolObj's properties
        self.makeFoolObj(obj) #make sure we have one - fixes defunct Lattice ShapeString after save-load
        for (proptype, propname, group, hint) in self.foolObj.properties:
            if propname != "String": #ignore "String", that will be taken care of in the following loop
                setattr(self.foolObj, propname, getattr(obj, propname))
        self.foolObj.FontFile = findFont(obj.FontFile)
        obj.FullPathToFont = self.foolObj.FontFile
        
        shapes = []
        for i in range(  0 ,  min(len(plms),len(obj.Strings))  ):
            if len(obj.Strings[i]) > 0:
                #generate shapestring using Draft
                self.foolObj.String = obj.Strings[i]
                self.foolObj.Shape = None
                self.draft_shape_string.execute(self.foolObj)
                shape = self.foolObj.Shape
                
                #calculate alignment point
                if obj.XAlign == 'None' and obj.YAlign == 'None':
                    pass #need not calculate boundbox
                else:
                    if obj.AlignPrecisionBoundBox:
                        bb = getPrecisionBoundBox(shape)
                    else:
                        bb = shape.BoundBox

                alignPnt = App.Vector()
                
                if obj.XAlign == 'Left':
                    alignPnt.x = bb.XMin
                elif obj.XAlign == 'Right':
                    alignPnt.x = bb.XMax
                elif obj.XAlign == 'Middle':
                    alignPnt.x = bb.Center.x

                if obj.YAlign == 'Bottom':
                    alignPnt.y = bb.YMin
                elif obj.YAlign == 'Top':
                    alignPnt.y = bb.YMax
                elif obj.YAlign == 'Middle':
                    alignPnt.y = bb.Center.y
                
                #Apply alignment
                shape.Placement = App.Placement(alignPnt*(-1.0), App.Rotation()).multiply(shape.Placement)
                
                #Apply placement from array
                shape.Placement = plms[i].multiply(shape.Placement)
                
                shapes.append(shape.copy())
        
        if len(shapes) == 0:
            scale = 1.0
            if lattice is not None:
                scale = lattice.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError('No strings were converted into shapes') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.

        obj.Shape = Part.makeCompound(shapes)
Example #30
0
    def derivedExecute(self,obj):
        self.assureGenerator(obj)
        self.updateReadonlyness(obj)

        # Apply links
        if obj.Link:
            if lattice2BaseFeature.isObjectLattice(obj.Link):
                lattice2Executer.warning(obj,"For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape.")
            
            #resolve the link
            if len(obj.LinkSubelement) > 0:
                linkedShape = obj.Link.Shape.getElement(obj.LinkSubelement)
            else:
                linkedShape = obj.Link.Shape
            
            #Type check
            if linkedShape.ShapeType != 'Edge':
                raise ValueError('Axis link must be an edge; it is '+linkedShape.ShapeType+' instead.')
            if type(linkedShape.Curve) is not Part.Line:
                raise ValueError('Axis link must be a line; it is '+type(linkedShape.Curve)+' instead.')
            
            #obtain
            dir = linkedShape.Curve.EndPoint - linkedShape.Curve.StartPoint
            point = linkedShape.Curve.StartPoint if not obj.Reverse else linkedShape.Curve.EndPoint
            
            if obj.DirIsDriven:
                obj.Dir = dir
            if obj.PointIsDriven:
                obj.Point = point
            if obj.DrivenProperty != 'None':
                if obj.DrivenProperty == 'Span':
                    propname = "SpanEnd"
                    obj.SpanEnd = obj.SpanStart + App.Units.Quantity('mm')*dir.Length
                else:
                    propname = obj.DrivenProperty
                    setattr(obj, propname, dir.Length)
                if self.generator.isPropertyControlledByGenerator(propname):
                    lattice2Executer.warning(obj, "Property "+propname+" is driven by both generator and link. Generator has priority.")

        
        # Generate series of values
        self.generator.execute()
        values = [float(strv) for strv in obj.Values]
        
        #Apply reversal
        if obj.Reverse:
            obj.Dir = obj.Dir*(-1.0)
            if not(obj.DirIsDriven and obj.Link):
                obj.Reverse = False

        # precompute orientation
        if obj.OrientMode == 'Along axis':
            ori = lattice2GeomUtils.makeOrientationFromLocalAxes(ZAx= obj.Dir).multiply(
                    lattice2GeomUtils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0), XAx= App.Vector(0,0,1)) )
        else:
            ori = App.Rotation()
        
        dir = obj.Dir
        dir.normalize()
        
        # Make the array
        output = [] # list of placements
        for v in values:
            output.append( App.Placement(obj.Point + obj.Dir*v, ori) )
            
        return output
Example #31
0
    def execute(self,obj):
        #validity check
        if isObjectLattice(obj.Base):
            import lattice2Executer
            lattice2Executer.warning(obj,"A generic shape is expected, but an array of placements was supplied. It will be treated as a generic shape.")

        rst = [] #variable to receive the final list of shapes
        shps = obj.Base.Shape.childShapes()
        if obj.FilterType == 'bypass':
            rst = shps
        elif obj.FilterType == 'specific items':
            rst = []
            flags = [False] * len(shps)
            ranges = obj.items.split(';')
            for r in ranges:
                r_v = r.split(':')
                if len(r_v) == 1:
                    i = int(r_v[0])
                    rst.append(shps[i])
                    flags[i] = True
                elif len(r_v) == 2 or len(r_v) == 3:
                    if len(r_v) == 2:
                        r_v.append("") # fix issue #1: instead of checking length here and there, simply add the missing field =)
                    ifrom = None   if len(r_v[0].strip()) == 0 else   int(r_v[0])                    
                    ito = None     if len(r_v[1].strip()) == 0 else   int(r_v[1])
                    istep = None   if len(r_v[2].strip()) == 0 else   int(r_v[2])
                    rst=rst+shps[ifrom:ito:istep]
                    for b in flags[ifrom:ito:istep]:
                        b = True
                else:
                    raise ValueError('index range cannot be parsed:'+r)
            if obj.Invert :
                rst = []
                for i in xrange(0,len(shps)):
                    if not flags[i]:
                        rst.append(shps[i])
        elif obj.FilterType == 'collision-pass':
            stencil = obj.Stencil.Shape
            for s in shps:
                d = s.distToShape(stencil)
                if bool(d[0] < DistConfusion) ^ bool(obj.Invert):
                    rst.append(s)
        elif obj.FilterType == 'window-volume' or obj.FilterType == 'window-area' or obj.FilterType == 'window-length' or obj.FilterType == 'window-distance':
            vals = [0.0] * len(shps)
            for i in xrange(0,len(shps)):
                if obj.FilterType == 'window-volume':
                    vals[i] = shps[i].Volume
                elif obj.FilterType == 'window-area':
                    vals[i] = shps[i].Area
                elif obj.FilterType == 'window-length':
                    vals[i] = shps[i].Length
                elif obj.FilterType == 'window-distance':
                    vals[i] = shps[i].distToShape(obj.Stencil.Shape)[0]
            
            maxval = max(vals)
            if obj.Stencil:
                if obj.FilterType == 'window-volume':
                    maxval = obj.Stencil.Shape.Volume
                elif obj.FilterType == 'window-area':
                    maxval = obj.Stencil.Shape.Area
                elif obj.FilterType == 'window-length':
                    maxval = obj.Stencil.Shape.Length
            if obj.OverrideMaxVal:
                maxval = obj.OverrideMaxVal
            
            valFrom = obj.WindowFrom / 100.0 * maxval
            valTo = obj.WindowTo / 100.0 * maxval
            
            for i in xrange(0,len(shps)):
                if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert:
                    rst.append(shps[i])
        else:
            raise ValueError('Filter mode not implemented:'+obj.FilterType)
        
        if len(rst) == 0:
            scale = 1.0
            if not obj.Base.Shape.isNull():
                scale = obj.Base.Shape.BoundBox.DiagonalLength/math.sqrt(3)/math.sqrt(len(shps))
            if scale < DistConfusion * 100:
                scale = 1.0
            print scale
            obj.Shape = markers.getNullShapeShape(scale)
            raise ValueError('Nothing passes through the filter') #Feeding empty compounds to FreeCAD seems to cause rendering issues, otherwise it would have been a good idea to output nothing.
        
        if len(rst) > 1:
            obj.Shape = Part.makeCompound(rst)
        else: # don't make compound of one shape, output it directly
            sh = rst[0]
            sh = ShapeCopy.transformCopy(sh)
            sh.Placement = obj.Placement
            obj.Shape = sh
            
        return
Example #32
0
    def derivedExecute(self, obj):
        base_is_lattice = LBF.isObjectLattice(obj.Object)
        pivot_is_lattice = LBF.isObjectLattice(
            obj.Pivot[0]) if obj.Pivot else True
        flipX = obj.FlipX
        flipY = obj.FlipY
        flipZ = obj.FlipZ

        # collect mirror pivot placements
        pivots = None
        em = 0  #editormode of PivotPlacement property. 0 = editable, 1 = read-only, 2 = hidden
        if obj.Pivot:
            em = 1  #read-only
            if pivot_is_lattice:
                pivots = LBF.getPlacementsList(obj.Pivot[0])
            else:
                pivot_shape = resolveSingleSublink(obj.Pivot)
                if pivot_shape.ShapeType == 'Edge' and type(
                        pivot_shape.Curve) is Part.Line:
                    dir = pivot_shape.Curve.Direction
                    base = pivot_shape.CenterOfMass
                    if flipX != flipY:
                        raise ValueError(
                            "Unsupported combination of flips for mirroring against line. FlipX and FlipY must either be both on or both off."
                        )
                    rot = makeOrientationFromLocalAxes(dir)
                    pivots = [App.Placement(base, rot)]
                elif pivot_shape.ShapeType == 'Face' and type(
                        pivot_shape.Surface) is Part.Plane:
                    dir = pivot_shape.Surface.Axis
                    base = pivot_shape.CenterOfMass
                    if flipX != flipY:
                        raise ValueError(
                            "Unsupported combination of flips for mirroring against line. FlipX and FlipY must either be both on or both off."
                        )
                    rot = makeOrientationFromLocalAxes(dir)
                    pivots = [App.Placement(base, rot)]
                elif pivot_shape.ShapeType == 'Vertex':
                    base = pivot_shape.Point
                    pivots = [App.Placement(base, obj.PivotPlacement.Rotation)]
                    em = 0  #editable
                else:
                    raise TypeError("Unsupported geometry for use as mirror")
            if len(pivots) == 1:
                obj.PivotPlacement = pivots[0]
            else:
                em = 2  #hidden
        else:
            pivots = [obj.PivotPlacement]
            em = 0
        obj.setEditorMode('PivotPlacement', em)

        # collect objects to be mirrored
        loop = False
        whole = obj.ObjectTraversal == 'Use whole'
        children = []
        if base_is_lattice:
            children = LBF.getPlacementsList(obj.Object)
        else:
            if obj.ObjectTraversal == 'Use whole':
                children = [obj.Object.Shape]
                loop = True
            elif obj.ObjectTraversal == 'Direct children only':
                children = obj.Object.Shape.childShapes()
            elif obj.ObjectTraversal == 'Use whole':
                children = LCE.AllLeaves(obj.Object.Shape)
            else:
                raise ValueError(
                    "Traversal mode not implemented: {mode}".format(
                        mode=obj.ObjectTraversal))

        if len(pivots) != len(children) and not loop and not whole:
            lattice2Executer.warning(
                obj,
                "{label}: Number of children ({nch}) doesn't match the number of pivot placements ({npiv})"
                .format(label=obj.Label, nch=len(children), npiv=len(pivots)))
            n = min(len(pivots), len(children))
        else:
            n = len(pivots)

        # actual mirroring!
        result = []
        for i in range(n):
            piv = pivots[i]
            ichild = i % len(children)
            if base_is_lattice:
                if whole:
                    for plm in children:
                        result.append(
                            mirrorPlacement(plm, piv, flipX, flipY, flipZ))
                else:
                    result.append(
                        mirrorPlacement(children[ichild], piv, flipX, flipY,
                                        flipZ))
            else:
                result.append(
                    mirrorShape(children[ichild], piv, flipX, flipY, flipZ))

        # write out the result
        if base_is_lattice:
            return result
        else:
            if n == 1:
                result = ShapeCopy.transformCopy(result[0])
            else:
                result = Part.Compound(result)
            obj.Shape = result
            return None
Example #33
0
    def derivedExecute(self,obj):
        #validity check
        if not lattice2BaseFeature.isObjectLattice(obj.Base):
            lattice2Executer.warning(obj,"A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected.")
        
        toolShape = obj.Tool.Shape
        if lattice2BaseFeature.isObjectLattice(obj.Tool):
            lattice2Executer.warning(obj,"A lattice object was provided as Tool. It will be converted into points; orientations will be ignored.")
            leaves = LCE.AllLeaves(toolShape)
            points = [Part.Vertex(leaf.Placement.Base) for leaf in leaves]
            toolShape = Part.makeCompound(points)

        leaves = LCE.AllLeaves(obj.Base.Shape)
        input = [leaf.Placement for leaf in leaves]

        output = [] #variable to receive the final list of placements
        
        #cache settings
        elev = float(obj.PosElevation)
        posIsKeep = obj.TranslateMode == 'keep'
        posIsProjected = obj.TranslateMode == 'projected'
        posIsMixed = obj.TranslateMode == 'mixed'
        mixF = float(obj.PosMixFraction)
        oriIsKeep = obj.OrientMode == 'keep'
        oriIsAlongGap = obj.OrientMode == 'along gap'
        oriIsTangentPlane = obj.OrientMode == 'tangent plane'
        oriIsAlongU = obj.OrientMode == 'along u'
        oriIsAlongV = obj.OrientMode == 'along v'
        
        isMultiSol = obj.Multisolution == 'use all'
        
        for plm in input:
            v = Part.Vertex(plm.Base)
            projection = v.distToShape(toolShape)
            (dist, gaps, infos) = projection
            for iSol in range(0,len(gaps)):
                (posKeep, posPrj) = gaps[iSol]
                (dummy, dummy, dummy, el_topo, el_index, el_params) = infos[iSol]

                # Fetch all possible parameters (some may not be required, depending on modes)
                normal = posKeep - posPrj
                if normal.Length < DistConfusion:
                    normal = None
                
                tangU = None
                tangV = None
                if el_topo == 'Face':
                    face = toolShape.Faces[el_index]
                    if normal is None:
                        normal = face.normalAt(*el_params)
                    (tangU, tangV) = face.tangentAt(*el_params)
                elif el_topo == "Edge":
                    edge = toolShape.Edges[el_index]
                    tangU = edge.tangentAt(el_params)
                
                if normal is not None:
                    normal.normalize()
                
                #mode logic - compute new placement
                if posIsKeep:
                    pos = plm.Base
                elif posIsProjected:
                    pos = posPrj
                elif posIsMixed:
                    pos = posKeep*mixF + posPrj*(1-mixF)
                else:
                    raise ValueError("Positioning mode not implemented: " + obj.TranslateMode )
                
                if abs(elev) > DistConfusion:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for nonzero position elevation.")
                    pos += normal * elev
                    
                    
                if oriIsKeep:
                    ori = plm.Rotation
                elif oriIsAlongGap:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("X",XAx= normal*(-1.0))
                elif oriIsTangentPlane:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("Z",ZAx= normal)
                elif oriIsAlongU:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    if tangU is None:
                        raise ValueError("TangentU vector not available for point on " + el_topo +". TangentU vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",ZAx= normal, XAx= tangU)
                elif oriIsAlongV:
                    if normal is None:
                        raise ValueError("Normal vector not available for a placement resting on " + el_topo +". Normal vector is required for orientation mode '"+obj.OrientMode+"'")
                    if tangV is None:
                        raise ValueError("TangentV vector not available for point on " + el_topo +". TangentV vector is required for orientation mode '"+obj.OrientMode+"'")
                    ori = Utils.makeOrientationFromLocalAxesUni("ZX",ZAx= normal, XAx= tangV)
                else:
                    raise ValueError("Orientation mode not implemented: " + obj.OrientMode )
                
                output.append(App.Placement(pos,ori))
                
                if not isMultiSol:
                    break
        
        return output
    def derivedExecute(self,obj):
        
        self.initNewProperties(obj)
        
        outputIsLattice = lattice2BaseFeature.isObjectLattice(obj.Object)
        
        if not lattice2BaseFeature.isObjectLattice(obj.Object):
            if obj.ObjectTraversal == "Direct children only":
                objectShapes = obj.Object.Shape.childShapes()
                if obj.Object.Shape.ShapeType != "Compound":
                    lattice2Executer.warning(obj,"shape supplied as object is not a compound. It is going to be downgraded one level down (e.g, if it is a wire, the edges are going to be enumerated as children).")
            elif obj.ObjectTraversal == "Recursive":
                objectShapes = LCE.AllLeaves(obj.Object.Shape)
            else:
                raise ValueError("Traversal mode not implemented: "+obj.ObjectTraversal)
        else:
            objectPlms = lattice2BaseFeature.getPlacementsList(obj.Object, obj)
        placements = lattice2BaseFeature.getPlacementsList(obj.PlacementsTo, obj)

        
        # Precompute referencing
        placements = DereferenceArray(obj, placements, obj.PlacementsFrom, obj.Referencing)
                
        # initialize output containers and loop variables
        outputShapes = [] #output list of shapes
        outputPlms = [] #list of placements
        iChild = 0
        numChildren = len(objectPlms) if outputIsLattice else len(objectShapes) 
        copy_method_index = ShapeCopy.getCopyTypeIndex(obj.Copying)
        
        # the essence
        for iPlm in range(len(placements)):
            if iChild == numChildren:
                if obj.LoopObjectSequence:
                    iChild = 0
                else:
                    break
            
            plm = placements[iPlm]
             
            if outputIsLattice:
                objectPlm = objectPlms[iChild]
                outputPlms.append(plm.multiply(objectPlm))
            else:
                outputShape = ShapeCopy.copyShape(objectShapes[iChild], copy_method_index, plm)
                # outputShape.Placement = plm.multiply(outputShape.Placement) #now done by shape copy routine
                outputShapes.append(outputShape)
            
            iChild += 1
            
        if len(placements) > numChildren and not obj.LoopObjectSequence:
            lattice2Executer.warning(obj,"There are fewer children to populate, than placements to be populated (%1, %2). Extra placements will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements))))
            
        if len(placements) < numChildren:
            lattice2Executer.warning(obj,"There are more children to populate, than placements to be populated (%1, %2). Extra children will be dropped.".replace("%1", str(numChildren)).replace("%2",str(len(placements))))
            
        if outputIsLattice:
            return outputPlms
        else:
            obj.Shape = Part.makeCompound(outputShapes)
            return None
Example #35
0
    def execute(self,obj):
        base = obj.ShapeLink.Shape
        if obj.CompoundTraversal == "Use as a whole":
            baseChildren = [base]
        else:
            if base.ShapeType != 'Compound':
                base = Part.makeCompound([base])
            if obj.CompoundTraversal == "Recursive":
                baseChildren = LCE.AllLeaves(base)
            else:
                baseChildren = base.childShapes()
        
        N = len(baseChildren)
        
        orients = []
        if obj.OrientMode == "global":
            orients = [App.Placement()]*N
        elif obj.OrientMode == "local of compound":
            orients = [obj.ShapeLink.Placement]*N
        elif obj.OrientMode == "local of child":
            orients = [child.Placement for child in baseChildren]
        elif obj.OrientMode == "use OrientLink":
            orients = LBF.getPlacementsList(obj.OrientLink, context= obj)
            if len(orients) == N:
                pass
            elif len(orients)>N:
                Executer.warning(obj, "Array of placements linked in OrientLink has more placements ("+str(len(orients))+") than bounding boxes to be constructed ("+str(len(baseChildren))+"). Extra placements will be dropped.")
            elif len(orients)==1:
                orients = [orients[0]]*N
            else:
                raise ValueError(obj.Name+": Array of placements linked in OrientLink has not enough placements ("+str(len(orients))+") than bounding boxes to be constructed ("+str(len(baseChildren))+").")
        else:
            raise ValueError(obj.Name+": OrientMode "+obj.OrientMode+" not implemented =(")
        
        # mark placements with no rotation
        for i in range(N):
            Q = orients[i].Rotation.Q
            # Quaternions for zero rotation are either (0,0,0,1) or (0,0,0,-1). For non-zero
            # rotations, some of first three values will be nonzero, and fourth value will 
            # not be equal to 1. While it's enough to compare absolute value of fourth value
            # to 1, precision is seriously lost in such comparison, so we are checking if 
            # fisrt three values are zero instead.
            if abs(Q[0])+abs(Q[1])+abs(Q[2]) < ParaConfusion:
                orients[i] = None
        
        boxes_shapes = []
        for i in range(N):
            child = baseChildren[i]
            if orients[i] is not None:
                child = child.copy()
                child.transformShape(orients[i].inverse().toMatrix())

            if obj.Precision:
                bb = getPrecisionBoundBox(child)
            else:
                bb = child.BoundBox
                
            bb = scaledBoundBox(bb, obj.ScaleFactor)
            bb.enlarge(obj.Padding)

            bb_shape = boundBox2RealBox(bb)
            if orients[i] is not None:
                bb_shape.transformShape(orients[i].toMatrix())
            boxes_shapes.append(bb_shape)
            
        #Fill in read-only properties
        if N == 1:
            obj.Size = App.Vector(bb.XLength,bb.YLength,bb.ZLength)
        
            cnt = bb.Center
            if orients[0] is not None:
                cnt = orients[0].multVec(cnt)
            obj.Center = cnt
        else:
            obj.Size = App.Vector()
            obj.Center = App.Vector()
        
        if obj.CompoundTraversal == "Use as a whole":
            assert(N==1)
            obj.Shape = boxes_shapes[0]
        else:
            obj.Shape = Part.makeCompound(boxes_shapes)
Example #36
0
    def derivedExecute(self, obj):
        self.assureGenerator(obj)
        self.assureProperties(obj)
        self.updateReadonlyness(obj)

        # Apply links
        if screen(obj.Link):
            if lattice2BaseFeature.isObjectLattice(screen(obj.Link)):
                lattice2Executer.warning(
                    obj,
                    "For polar array, axis link is expected to be a regular shape. Lattice objct was supplied instead, it's going to be treated as a generic shape."
                )

            #resolve the link
            if len(obj.LinkSubelement) > 0:
                linkedShape = screen(obj.Link).Shape.getElement(
                    obj.LinkSubelement)
            else:
                linkedShape = screen(obj.Link).Shape

            #Type check
            if linkedShape.ShapeType != 'Edge':
                raise ValueError('Axis link must be an edge; it is ' +
                                 linkedShape.ShapeType + ' instead.')
            if type(linkedShape.Curve) is not Part.Line:
                raise ValueError('Axis link must be a line; it is ' +
                                 type(linkedShape.Curve) + ' instead.')

            #obtain
            start_point = linkedShape.valueAt(linkedShape.FirstParameter)
            end_point = linkedShape.valueAt(linkedShape.LastParameter)
            dir = end_point - start_point
            point = start_point if not obj.Reverse else end_point

            if obj.DirIsDriven:
                obj.Dir = dir
            if obj.PointIsDriven:
                obj.Point = point
            if obj.DrivenProperty != 'None':
                if obj.DrivenProperty == 'Span':
                    propname = "SpanEnd"
                    obj.SpanEnd = obj.SpanStart + App.Units.Quantity(
                        'mm') * dir.Length
                else:
                    propname = obj.DrivenProperty
                    setattr(obj, propname, dir.Length)
                if self.generator.isPropertyControlledByGenerator(propname):
                    lattice2Executer.warning(
                        obj, "Property " + propname +
                        " is driven by both generator and link. Generator has priority."
                    )

        # Generate series of values
        self.generator.execute()
        values = [float(strv) for strv in obj.Values]

        #Apply reversal
        if obj.Reverse:
            obj.Dir = obj.Dir * (-1.0)
            if not (obj.DirIsDriven and screen(obj.Link)):
                obj.Reverse = False

        # precompute orientation
        if obj.OrientMode == 'Along axis':
            ori = lattice2GeomUtils.makeOrientationFromLocalAxes(
                ZAx=obj.Dir).multiply(
                    lattice2GeomUtils.makeOrientationFromLocalAxes(
                        ZAx=App.Vector(1, 0, 0), XAx=App.Vector(0, 0, 1)))
        else:
            ori = App.Rotation()

        dir = obj.Dir
        dir.normalize()

        # Make the array
        output = []  # list of placements
        for v in values:
            output.append(App.Placement(obj.Point + obj.Dir * v, ori))

        return output
    def derivedExecute(self,obj):
        # cache stuff
        if lattice2BaseFeature.isObjectLattice(obj.ShapeLink):
            lattice2Executer.warning(obj,"ShapeLink points to a placement/array of placements. The placement/array will be reinterpreted as a generic shape; the results may be unexpected.")

        base = obj.ShapeLink.Shape
        if obj.CompoundTraversal == "Use as a whole":
            baseChildren = [base]
        else:
            if base.ShapeType != 'Compound':
                base = Part.makeCompound([base])
            if obj.CompoundTraversal == "Recursive":
                baseChildren = LCE.AllLeaves(base)
            else:
                baseChildren = base.childShapes()
        
                        
        #cache mode comparisons, for speed
        posIsNone = obj.TranslateMode == '(none)'
        posIsParent = obj.TranslateMode == 'parent'
        posIsChild = obj.TranslateMode == 'child'
        posIsCenterM = obj.TranslateMode == 'child.CenterOfMass'
        posIsCenterBB = obj.TranslateMode == 'child.CenterOfBoundBox'
        posIsVertex = obj.TranslateMode == 'child.Vertex'
        
        oriIsNone = obj.OrientMode == '(none)'
        oriIsParent = obj.OrientMode == 'parent'
        oriIsChild = obj.OrientMode == 'child'
        oriIsInertial = obj.OrientMode == 'child.InertiaAxes'
        oriIsEdge = obj.OrientMode == 'child.Edge'
        oriIsFace = obj.OrientMode == 'child.FaceAxis'
        
        # initialize output containers and loop variables
        outputPlms = [] #list of placements
        
        # the essence
        for child in baseChildren:
            pos = App.Vector()
            ori = App.Rotation()
            if posIsNone:
                pass
            elif posIsParent:
                pos = base.Placement.Base
            elif posIsChild:
                pos = child.Placement.Base
            elif posIsCenterM:
                leaves = LCE.AllLeaves(child)
                totalW = 0
                weightAttrib = {"Vertex":"",
                             "Edge":"Length",
                             "Wire":"Length",
                             "Face":"Area",
                             "Shell":"Area",
                             "Solid":"Volume",
                             "CompSolid":""}[leaves[0].ShapeType]
                #Center of mass of a compound is a weghted average of centers
                # of mass of individual objects.
                for leaf in leaves:
                    w = 1.0 if not weightAttrib else (getattr(leaf, weightAttrib))
                    if leaf.ShapeType == 'Vertex':
                        leafCM = leaf.Point
                    #elif child.ShapeType == 'CompSolid':
                        #todo
                    else: 
                        leafCM = leaf.CenterOfMass
                    pos += leafCM * w
                    totalW += w
                pos = pos * (1.0/totalW)
            elif posIsCenterBB:
                import lattice2BoundBox
                bb = lattice2BoundBox.getPrecisionBoundBox(child)
                pos = bb.Center
            elif posIsVertex:
                v = child.Vertexes[obj.TranslateElementIndex - 1]
                pos = v.Point
            else:
                raise ValueError(obj.Name + ": translation mode not implemented: "+obj.TranslateMode)
            
            if oriIsNone:
                pass
            elif oriIsParent:
                ori = base.Placement.Rotation
            elif oriIsChild:
                ori = child.Placement.Rotation
            elif oriIsInertial:
                leaves = LCE.AllLeaves(child)
                if len(leaves)>1:
                    raise ValueError(obj.Name + ": calculation of principal axes of compounds is not supported yet")
                props = leaves[0].PrincipalProperties
                XAx = props['FirstAxisOfInertia']
                ZAx = props['ThirdAxisOfInertia']
                ori = Utils.makeOrientationFromLocalAxes(ZAx, XAx)
            elif oriIsEdge:
                edge = child.Edges[obj.OrientElementIndex - 1]
                XAx = edge.Curve.tangent(edge.Curve.FirstParameter)[0]
                ori1 = Utils.makeOrientationFromLocalAxes(ZAx= XAx)
                ori2 = Utils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0),XAx= App.Vector(0,0,1))
                ori = ori1.multiply(ori2)
            elif oriIsFace:
                face = child.Faces[obj.OrientElementIndex - 1]
                ZAx = face.Surface.Axis
            else:
                raise ValueError(obj.Name + ": orientation mode not implemented: "+obj.OrientMode)

            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms
Example #38
0
    def derivedExecute(self, selfobj):

        if selfobj.Recomputing == "Disabled":
            raise ValueError(
                selfobj.Name +
                ": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it."
            )
        try:

            # do the subsequencing in this document first, to verify stuff is set up correctly, and to obtain sequence length
            if self.isVerbose():
                print("In-place pre-subsequencing, for early check")
            n_seq, subs_linkdict = self.makeSubsequence(
                selfobj, screen(selfobj.ObjectToLoopOver))

            bGui = bool(
                App.GuiUp
            ) and Executer.globalIsCreatingLatticeFeature  #disabled for most recomputes, because it causes a crash if property edits are approved by hitting Enter
            if bGui:
                import PySide
                progress = PySide.QtGui.QProgressDialog(
                    u"Recomputing " + selfobj.Label, u"Abort", 0, n_seq + 1)
                progress.setModal(True)
                progress.show()

            doc1 = selfobj.Document
            doc2 = App.newDocument()
            object_to_take_in_doc2 = None  # define the variable, to prevent del() in finally block from raising another error
            object_to_loop_in_doc2 = None
            try:
                if self.isVerbose():
                    print(
                        "Copying object with dependencies to a temporary document..."
                    )

                doc2.copyObject(screen(selfobj.ObjectToTake), True)

                if self.isVerbose():
                    print("Enabling nested para/toposeries, if any...")
                #if there are nested para/toposeries in the dependencies, make sure to enable them
                for objd2 in doc2.Objects:
                    if hasattr(objd2, "Recomputing"):
                        try:
                            objd2.Recomputing = "Enabled"
                            objd2.purgeTouched()
                        except exception:
                            Executer.warning(
                                selfobj, "Failed to enable recomputing of " +
                                objd2.Name)

                object_to_take_in_doc2 = doc2.getObject(
                    screen(selfobj.ObjectToTake).Name)
                object_to_loop_in_doc2 = doc2.getObject(
                    screen(selfobj.ObjectToLoopOver).Name)
                if bGui:
                    progress.setValue(1)

                if self.isVerbose():
                    print("Repeating subsequencing in temporary document...")
                n_seq, subs_linkdict = self.makeSubsequence(
                    selfobj, object_to_loop_in_doc2)

                output_shapes = []
                for i in range(n_seq):
                    if self.isVerbose():
                        print("Computing {x}/{y}".format(x=i + 1, y=n_seq))

                    for key in subs_linkdict:
                        writeProperty(doc2, key[0], key[1],
                                      subs_linkdict[key][i])

                    #recompute
                    doc2.recompute()

                    #get shape
                    shape = None
                    for obj in doc2.Objects:
                        if 'Invalid' in obj.State:
                            Executer.error(
                                obj,
                                "Recomputing shape for subsequence index " +
                                repr(i) + " failed.")

                            scale = 1.0
                            try:
                                if not screen(
                                        selfobj.ObjectToTake).Shape.isNull():
                                    scale = screen(
                                        selfobj.ObjectToTake
                                    ).Shape.BoundBox.DiagonalLength / math.sqrt(
                                        3)
                            except Exception:
                                pass
                            if scale < DistConfusion * 100:
                                scale = 1.0
                            shape = markers.getNullShapeShape(scale)
                    if shape is None:
                        shape = object_to_take_in_doc2.Shape.copy()
                    output_shapes.append(shape)

                    #update progress
                    if bGui:
                        progress.setValue(progress.value() + 1)
                        if progress.wasCanceled():
                            raise Executer.CancelError()

            finally:
                #delete all references, before destroying the document. Probably not required, but to be sure...
                if self.isVerbose():
                    print("Cleanup...")

                del (object_to_take_in_doc2)
                del (object_to_loop_in_doc2)
                doc2_name = doc2.Name
                del (doc2)
                App.closeDocument(doc2_name)
                if bGui:
                    progress.setValue(n_seq + 1)

            selfobj.Shape = Part.makeCompound(output_shapes)

            output_is_lattice = lattice2BaseFeature.isObjectLattice(
                screen(selfobj.ObjectToTake))
            if 'Auto' in selfobj.isLattice:
                new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off'
                if selfobj.isLattice != new_isLattice:  #check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color)
                    selfobj.isLattice = new_isLattice
        finally:
            if selfobj.Recomputing == "Recompute Once":
                selfobj.Recomputing = "Disabled"
        return "suppress"  # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
Example #39
0
    def execute(self, obj):
        # please, don't override. Override derivedExecute instead.

        plms = self.derivedExecute(obj)

        if plms is not None:
            if plms == "suppress":
                return
            obj.NumElements = len(plms)
            shapes = []
            markerSize = obj.MarkerSize
            if markerSize < DistConfusion:
                markerSize = getMarkerSizeEstimate(plms)
            marker = lattice2Markers.getPlacementMarker(scale=markerSize, markerID=obj.MarkerShape)

            bExposing = False
            if obj.ExposePlacement:
                if len(plms) == 1:
                    bExposing = True
                else:
                    lattice2Executer.warning(
                        obj,
                        "Multiple placements are being fed, can't expose placements. Placement property will be forced to zero.",
                    )
                    obj.Placement = App.Placement()

            if bExposing:
                obj.Shape = shallowCopy(marker)
                obj.Placement = plms[0]
            else:
                for plm in plms:
                    sh = shallowCopy(marker)
                    sh.Placement = plm
                    shapes.append(sh)

                if len(shapes) == 0:
                    obj.Shape = lattice2Markers.getNullShapeShape(markerSize)
                    raise ValueError("Lattice object is null")

                sh = Part.makeCompound(shapes)
                obj.Shape = sh

            if obj.isLattice == "Auto-Off":
                obj.isLattice = "Auto-On"

        else:
            # DerivedExecute didn't return anything. Thus we assume it
            # has assigned the shape, and thus we don't do anything.
            # Moreover, we assume that it is no longer a lattice object, so:
            if obj.isLattice == "Auto-On":
                obj.isLattice = "Auto-Off"

            if obj.ExposePlacement:
                if obj.Shape.ShapeType == "Compound":
                    children = obj.Shape.childShapes()
                    if len(children) == 1:
                        obj.Placement = children[0].Placement
                        obj.Shape = children[0]
                    else:
                        obj.Placement = App.Placement()
                else:
                    # nothing to do - FreeCAD will take care to make obj.Placement and obj.Shape.Placement synchronized.
                    pass
        return
Example #40
0
    def derivedExecute(self, obj):
        #validity check
        if not lattice2BaseFeature.isObjectLattice(screen(obj.Base)):
            lattice2Executer.warning(
                obj,
                "A lattice object is expected as Base, but a generic shape was provided. It will be treated as a lattice object; results may be unexpected."
            )

        output = []  #variable to receive the final list of placements
        leaves = LCE.AllLeaves(screen(obj.Base).Shape)
        input = [leaf.Placement for leaf in leaves]
        if obj.FilterType == 'bypass':
            output = input
        elif obj.FilterType == 'specific items':
            flags = [False] * len(input)
            ranges = obj.items.split(';')
            for r in ranges:
                r_v = r.split(':')
                if len(r_v) == 1:
                    i = int(r_v[0])
                    output.append(input[i])
                    flags[i] = True
                elif len(r_v) == 2 or len(r_v) == 3:
                    if len(r_v) == 2:
                        r_v.append(
                            ""
                        )  # fix issue #1: instead of checking length here and there, simply add the missing field =)
                    ifrom = None if len(r_v[0].strip()) == 0 else int(r_v[0])
                    ito = None if len(r_v[1].strip()) == 0 else int(r_v[1])
                    istep = None if len(r_v[2].strip()) == 0 else int(r_v[2])
                    output = output + input[ifrom:ito:istep]
                    for b in flags[ifrom:ito:istep]:
                        b = True
                else:
                    raise ValueError('index range cannot be parsed:' + r)
            if obj.Invert:
                output = []
                for i in range(0, len(input)):
                    if not flags[i]:
                        output.append(input[i])
        elif obj.FilterType == 'collision-pass':
            stencil = screen(obj.Stencil).Shape
            for plm in input:
                pnt = Part.Vertex(plm.Base)
                d = pnt.distToShape(stencil)
                if bool(d[0] < DistConfusion) ^ bool(obj.Invert):
                    output.append(plm)
        elif obj.FilterType == 'window-distance':
            vals = [0.0] * len(input)
            for i in range(0, len(input)):
                if obj.FilterType == 'window-distance':
                    pnt = Part.Vertex(input[i].Base)
                    vals[i] = pnt.distToShape(screen(obj.Stencil).Shape)[0]

            valFrom = obj.WindowFrom
            valTo = obj.WindowTo

            for i in range(0, len(input)):
                if bool(vals[i] >= valFrom and vals[i] <= valTo) ^ obj.Invert:
                    output.append(input[i])
        else:
            raise ValueError('Filter mode not implemented:' + obj.FilterType)

        return output
Example #41
0
    def derivedExecute(self,obj):
        # cache stuff
        if lattice2BaseFeature.isObjectLattice(screen(obj.ShapeLink)):
            lattice2Executer.warning(obj,"ShapeLink points to a placement/array of placements. The placement/array will be reinterpreted as a generic shape; the results may be unexpected.")

        base = screen(obj.ShapeLink).Shape
        if obj.CompoundTraversal == "Use as a whole":
            baseChildren = [base]
        else:
            if base.ShapeType != 'Compound':
                base = Part.makeCompound([base])
            if obj.CompoundTraversal == "Recursive":
                baseChildren = LCE.AllLeaves(base)
            else:
                baseChildren = base.childShapes()
        
                        
        #cache mode comparisons, for speed
        posIsNone = obj.TranslateMode == '(none)'
        posIsParent = obj.TranslateMode == 'parent'
        posIsChild = obj.TranslateMode == 'child'
        posIsCenterM = obj.TranslateMode == 'child.CenterOfMass'
        posIsCenterBB = obj.TranslateMode == 'child.CenterOfBoundBox'
        posIsVertex = obj.TranslateMode == 'child.Vertex'
        
        oriIsNone = obj.OrientMode == '(none)'
        oriIsParent = obj.OrientMode == 'parent'
        oriIsChild = obj.OrientMode == 'child'
        oriIsInertial = obj.OrientMode == 'child.InertiaAxes'
        oriIsEdge = obj.OrientMode == 'child.Edge'
        oriIsFace = obj.OrientMode == 'child.FaceAxis'
        
        # initialize output containers and loop variables
        outputPlms = [] #list of placements
        
        # the essence
        for child in baseChildren:
            pos = App.Vector()
            ori = App.Rotation()
            if posIsNone:
                pass
            elif posIsParent:
                pos = base.Placement.Base
            elif posIsChild:
                pos = child.Placement.Base
            elif posIsCenterM:
                leaves = LCE.AllLeaves(child)
                totalW = 0
                weightAttrib = {"Vertex":"",
                             "Edge":"Length",
                             "Wire":"Length",
                             "Face":"Area",
                             "Shell":"Area",
                             "Solid":"Volume",
                             "CompSolid":""}[leaves[0].ShapeType]
                #Center of mass of a compound is a weghted average of centers
                # of mass of individual objects.
                for leaf in leaves:
                    w = 1.0 if not weightAttrib else (getattr(leaf, weightAttrib))
                    if leaf.ShapeType == 'Vertex':
                        leafCM = leaf.Point
                    #elif child.ShapeType == 'CompSolid':
                        #todo
                    else: 
                        leafCM = leaf.CenterOfMass
                    pos += leafCM * w
                    totalW += w
                pos = pos * (1.0/totalW)
            elif posIsCenterBB:
                import lattice2BoundBox
                bb = lattice2BoundBox.getPrecisionBoundBox(child)
                pos = bb.Center
            elif posIsVertex:
                v = child.Vertexes[obj.TranslateElementIndex - 1]
                pos = v.Point
            else:
                raise ValueError(obj.Name + ": translation mode not implemented: "+obj.TranslateMode)
            
            if oriIsNone:
                pass
            elif oriIsParent:
                ori = base.Placement.Rotation
            elif oriIsChild:
                ori = child.Placement.Rotation
            elif oriIsInertial:
                leaves = LCE.AllLeaves(child)
                if len(leaves)>1:
                    raise ValueError(obj.Name + ": calculation of principal axes of compounds is not supported yet")
                props = leaves[0].PrincipalProperties
                XAx = props['FirstAxisOfInertia']
                ZAx = props['ThirdAxisOfInertia']
                ori = Utils.makeOrientationFromLocalAxes(ZAx, XAx)
            elif oriIsEdge:
                edge = child.Edges[obj.OrientElementIndex - 1]
                XAx = edge.Curve.tangent(edge.Curve.FirstParameter)[0]
                ori1 = Utils.makeOrientationFromLocalAxes(ZAx= XAx)
                ori2 = Utils.makeOrientationFromLocalAxes(ZAx= App.Vector(1,0,0),XAx= App.Vector(0,0,1))
                ori = ori1.multiply(ori2)
            elif oriIsFace:
                face = child.Faces[obj.OrientElementIndex - 1]
                ZAx = face.Surface.Axis
            else:
                raise ValueError(obj.Name + ": orientation mode not implemented: "+obj.OrientMode)

            plm = App.Placement(pos, ori)
            outputPlms.append(plm)
        return outputPlms