Last active
October 19, 2023 22:29
-
-
Save mwganson/0aedd5e9057650d0a1f0483f3cc2fa6c to your computer and use it in GitHub Desktop.
wirefilter macro, allows to use only some wires of a sketch, also offsets and scales and makes faces
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
__version__ = "0.2023.10.19" | |
#__version__ = "0.2023.10.19" | |
##WireFilter, 2021, by <TheMarkster> LGPL 2.1 or later | |
#import FreeCAD, FreeCADGui, Part, Draft | |
#from PySide import QtGui, QtCore, QtWidgets | |
#from PySide.QtGui import QColor | |
#import time | |
#COLORS = ["red","green","blue","cyan","magenta","yellow","orange","maroon","purple","white",\ | |
#"chartreuse", "navy","teal","pink","black","silver"] | |
#class WireFilter: | |
# def __init__(self, obj): | |
# obj.addExtension("Part::AttachExtensionPython") | |
# obj.addProperty("App::PropertyInteger","MaxWires","WireFilter","Maximum number of wires to use (prevents hanging on complex sources)").MaxWires = 50 | |
# obj.addProperty("App::PropertyLinkSub","Source","WireFilter","Source of the wires, typically a sketch or a compound of sketches") | |
# obj.addProperty("App::PropertyIntegerList","WireOrder","WireOrder","Order of wires (0 = disable wire) There is a special editor for this if Edit Wire Order is toggled to True.") | |
# obj.addProperty("App::PropertyBool","ClaimChildren","WireFilter","Whether to claim children in tree view").ClaimChildren=True | |
# obj.addProperty("App::PropertyBool","Colorize","WireFilter","Whether to colorize wires").Colorize=True | |
# obj.addProperty("App::PropertyBool","UsePreselection","WireFilter","Applicable only when subobjects were selected when creating WireFilter object. If True, only those preselected subojects are used, else if False, all wires are used").UsePreselection = True | |
# obj.addProperty("App::PropertyEnumeration","FaceMaker","WireFilter","FaceMaker class to use to make face or No Face if no face") | |
# obj.FaceMaker = ["No Face","Part::FaceMakerBullseye", "Part::FaceMakerCheese","Part::FaceMakerSimple","Part::FaceMakerExtrusion","InvertedFace", "FilledFace"] | |
# obj.addProperty("App::PropertyFloat","InvertedFaceScale","WireFilter","(InvertedFace Face Maker only) 2D scaling used for creating outer face from which original face is cut").InvertedFaceScale = 5 | |
# obj.addProperty("App::PropertyFloat","Offset","Offsetting","2D offset in mm, < 0.0 = offset inwards, 0.0 = no offset, > 0.0 = offset outwards") | |
# obj.addProperty("App::PropertyEnumeration","OffsetJoin","Offsetting","Join method -- Arcs, Tangent, Intersection") | |
# obj.OffsetJoin = ["Arcs","Tangent","Intersection"] | |
# obj.addProperty("App::PropertyBool","OffsetFill","Offsetting", "Whether to fill offset space (makes a face if true)").OffsetFill = False | |
# obj.addProperty("App::PropertyBool","OffsetOpenResult","Offsetting","Whether to allow open result or to close result").OffsetOpenResult = False | |
# obj.addProperty("App::PropertyBool","OffsetIntersection","Offsetting","Whether to offset child wires/faces independently or collectively").OffsetIntersection = False | |
# obj.addProperty("App::PropertyVector","Scale","Scaling","Scale Factor: if 1,1,1, no scaling is done").Scale = FreeCAD.Vector(1,1,1) | |
# obj.addProperty("App::PropertyString","Version","WireFilter","Version of WireFilter used to create this object.").Version = __version__ | |
# obj.addProperty("App::PropertyVector","Normal","WireFilter","Normal of Face1 or Wire1 if no face").Normal = FreeCAD.Vector(0,0,1) | |
# obj.addProperty("App::PropertyBool","FollowSource","WireFilter","If False, source's placement is ignored.").FollowSource = True | |
# obj.addProperty("App::PropertyBool","UseDefault","WireOrder","[Trigger] sets wire order to default [1,2,3,4...] and resets itself to False").UseDefault = False | |
# obj.addProperty("App::PropertyBool","EditWireOrder","WireOrder","[Trigger] opens wire order editor, resets self to False").EditWireOrder = False | |
# obj.addProperty("App::PropertyLink","CompareObject","WireOrder","Object to compare to this wire order in editor") | |
# obj.addProperty("App::PropertyLink","Ghost","WireOrder","Temporary object created while wire order editor is open") | |
# obj.addProperty("App::PropertyBool","HideGhost","WireOrder","Internal use: hide the ghost if comparing wirefilter to itself in wire order dialog").HideGhost = False | |
# obj.addProperty("App::PropertyBool","ReUseGhost","WireOrder","Internal use: whether to reuse same compare object instead of asking. This is for when using Apply or Accept in dialog").ReUseGhost = False | |
# obj.setEditorMode("Ghost",2) #hidden | |
# obj.setEditorMode("HideGhost",2) | |
# obj.setEditorMode("ReUseGhost",2) | |
# obj.addProperty("App::PropertyBool","FixNormal","WireFilter","[Trigger] Fix Pad or Extrude's custom direction if it can't find the normal correctly.").FixNormal = False | |
# obj.addProperty("App::PropertyFloat","UniformScale","Scaling","Scale the WireFilter uniformly. Keeps it centered, too.").UniformScale = 1.0 | |
# obj.Proxy = self | |
# self.fpName = obj.Name | |
# self.editingMode = False #wire order editor | |
# | |
# def onChanged(self,fp,prop): | |
# if prop == "WireOrder": | |
# self.setWireOrder(fp) | |
# elif prop == "UseDefault" and fp.UseDefault and hasattr(fp,"WireOrder"): | |
# fp.WireOrder = [] #will get set to defaults | |
# elif prop == "FixNormal" and fp.FixNormal: | |
# fp.FixNormal = False | |
# self.fixNormal(fp) | |
# elif prop == "Source" and hasattr(fp,"WireOrder"): | |
# fp.WireOrder = [] | |
# elif prop == "EditWireOrder" and fp.EditWireOrder: | |
# t = QtCore.QTimer() | |
# t.singleShot(50, self.showWires) | |
# elif prop == "MaxWires": | |
# fp.WireOrder = [] | |
# elif prop == "UsePreselection": | |
# fp.WireOrder = [] | |
# | |
# def clearColorKeys(self,fp): | |
# for p in fp.PropertiesList: | |
# if "Wire" in p and "Color" in p: | |
# fp.removeProperty(p) | |
# | |
# def setColorKeys(self,fp): | |
# self.clearColorKeys(fp) | |
# if not hasattr(fp,"ColorKeyCount"): | |
# return #object made with earlier version of WireFilter macro | |
# for ii in range (1,fp.ColorKeyCount+1): | |
# if not hasattr(fp,"Wire"+format(ii,"03")+"Color"): | |
# fp.addProperty("App::PropertyColor","Wire"+format(ii,"03")+"Color","WireColors","Wire color keys, (some edges may belong to more than one wire\nin which case the last wire color set takes precedence)\nHint: select vertex in 3D view so edge colors are viewable.") | |
# setattr(fp,"Wire"+format(ii,"03")+"Color",self.colorize(fp,Part.Shape(),True,ii)) | |
# fp.setEditorMode("Wire"+format(ii,"03")+"Color",1) # readonly | |
# | |
# def fixNormal(self,fp): | |
# '''fixes a Pad or Extrude if it cannot find the correct normal direction for extruding''' | |
# inlist = fp.InList | |
# pads = [obj for obj in inlist if hasattr(obj,"UseCustomVector") and bool(obj.TypeId == "PartDesign::Pad" or obj.TypeId == "PartDesign::Pocket") and obj.Profile and obj.Profile[0] == fp] | |
# if len(pads) != 0: | |
# for pad in pads: | |
# pad.UseCustomVector = True | |
# pad.Direction = fp.Normal | |
# if pad.TypeId == "PartDesign::Pocket": | |
# pad.Direction = fp.Normal * -1 | |
# extrudes = [obj for obj in inlist if obj.TypeId == "Part::Extrusion" and obj.Base and obj.Base == fp] | |
# if len(extrudes) != 0: | |
# for ext in extrudes: | |
# ext.DirMode = "Custom" | |
# ext.Dir = fp.Normal | |
# | |
# #borrowed from Draft.Clone scaling code | |
# def scale(self,fp,shp): | |
# m = FreeCAD.Matrix() | |
# if not shp.isNull(): | |
# sx,sy,sz = fp.Scale | |
# if not fp.Scale.isEqual(FreeCAD.Vector(1, 1, 1),1e-7): | |
# op = shp.Placement | |
# shp.Placement = FreeCAD.Placement() | |
# m.scale(fp.Scale) | |
# if sx == sy == sz: | |
# shp.transformShape(m) | |
# else: | |
# shp = shp.transformGeometry(m) | |
# shp.Placement = op | |
# if not fp.UniformScale == 1.0: | |
# shp.scale(fp.UniformScale,shp.BoundBox.Center) | |
# return shp | |
# | |
# def offset(self,fp,shp): | |
# if fp.Offset == 0.0: | |
# return shp | |
# mapped = {"Arcs":0,"Tangent":1,"Intersection":2} | |
# join = mapped[fp.OffsetJoin] | |
# return(shp.makeOffset2D(fp.Offset,join,fp.OffsetFill,fp.OffsetOpenResult,fp.OffsetIntersection)) | |
# | |
# def setWireOrder(self,fp): | |
# '''user has adjusted WireOrder property. Pad with 0's if some are missing''' | |
# if fp.Source: | |
# if len(fp.WireOrder) < len(fp.Source[0].Shape.Wires) and len(fp.WireOrder) != 0: | |
# needed = len(fp.Source[0].Shape.Wires) - len(fp.WireOrder) | |
# if needed != len(fp.Source[0].Shape.Wires): | |
# fp.WireOrder += [0] * needed | |
# else: | |
# fp.WireOrder = [] | |
# | |
# def fixWireOrder(self,fp,shp): | |
# '''if length of wires in shape has changed, reset wire order to default''' | |
# if len(fp.WireOrder) != len(shp.Wires): | |
# fp.WireOrder = range(1,len(shp.Wires)+1) | |
# if hasattr(fp,"MaxWires") and len(fp.WireOrder) > fp.MaxWires: | |
# fp.WireOrder = fp.WireOrder[:fp.MaxWires] | |
# | |
# def isInWire(self,edge,wire): | |
# '''return True if edge is in wire''' | |
# edges = wire.Edges | |
# for e in edges: | |
# if e.isSame(edge) or e.isEqual(edge) or e.isPartner(edge): | |
# return True | |
# return False | |
# | |
# def getWireDict(self,shape,maxWires): | |
# '''returns a dictionary of form wire:[list of edge names]''' | |
# edgenames = {} | |
# wire_dict = {} | |
# for ee in range (0,len(shape.Edges)): | |
# edgenames[shape.Edges[ee]] = "Edge"+str(ee+1) | |
# wire_count = len(shape.Wires) | |
# if wire_count > maxWires: | |
# wire_count = maxWires | |
# for ww in range(0,wire_count): | |
# wire_dict[ww+1] = [] | |
# for k,v in edgenames.items(): | |
# if self.isInWire(k, shape.Wires[ww]): | |
# wire_dict[ww+1] += [v] | |
# return wire_dict | |
# | |
# def makeFilledFace(self, fp, shape): | |
# return Part.makeFilledFace(shape.Edges) | |
# | |
# def makeInvertedFace(self,fp,wire): | |
# faceOriginal = Part.makeFace(wire,"Part::FaceMakerBullseye") | |
# faceOuter = Part.makeFace(faceOriginal.OuterWire,"Part::FaceMakerCheese") | |
# offset = faceOuter.scale(fp.InvertedFaceScale, faceOuter.CenterOfGravity) | |
# cut = offset.cut(faceOriginal) | |
# return cut | |
# | |
# def wireIsPlanar(self, wire): | |
# '''check if the wire is planar and return true/false''' | |
# import DraftGeomUtils as dgu | |
# return dgu.is_planar(wire) | |
# | |
# def color(self, s): | |
# return QColor(s).getRgbF() | |
# | |
# def getCL(self): | |
# colors = COLORS | |
# colorNames = QColor.colorNames() #color name strings supported by Qt | |
# colorNames.remove("black") # will be default color for edges if we run out of colors | |
# for c in colorNames: | |
# if not c in colors: | |
# colors.append(c) | |
# cL = [] #color list of tuples, eg. red is (1.0,0.0,0.0,1.0) rgba | |
# colorDuplicates = [] #sometimes the name is different, but the color is the same | |
# for cn in colors: | |
# if not self.color(cn) in cL: | |
# cL.append(self.color(cn)) | |
# else: | |
# colorDuplicates.append(cn) | |
# for cd in colorDuplicates: | |
# colors.remove(cd) | |
# return cL | |
# | |
# def colorize(self,fp,shape,getColor = False,idx = -1): | |
# '''assign colors to individual wires if Colorize = True''' | |
# cL = self.getCL() | |
# if getColor: | |
# return cL[idx -1] | |
# if not fp.Colorize: | |
# fp.ViewObject.LineColorArray = [self.color("black")] | |
# return | |
# if not len(shape.Wires): | |
# return #no wires | |
# cLArray = self.getColorArray(fp, shape, cL) | |
# fp.ViewObject.LineColorArray = cLArray | |
# | |
# def flashAWire(self, fp, idx, reason=None): | |
# red = COLORS.index("red") | |
# blue = COLORS.index("blue") | |
# green = COLORS.index("green") | |
# white = COLORS.index("white") | |
# black = COLORS.index("black") | |
# silver = COLORS.index("silver") | |
# teal = COLORS.index("teal") | |
# yellow = COLORS.index("yellow") | |
# orange = COLORS.index("orange") | |
# | |
# num_wires = len(fp.WireOrder) | |
# multiplier = 15 if num_wires < 5 else 5 if num_wires < 10 else 2 | |
# | |
# if not reason: | |
# colorIndices = [] | |
# elif reason == "selected": | |
# colorIndices = [black,white,silver]*multiplier | |
# elif reason == "disabled": | |
# colorIndices = [red,orange,yellow]*multiplier | |
# elif reason == "enabled": | |
# colorIndices = [blue,green,teal]*multiplier | |
# | |
# for ii in colorIndices: | |
# self.colorAWire(fp, idx, ii) | |
# time.sleep(.01) | |
# FreeCADGui.updateGui() | |
# self.colorize(fp, fp.Shape) | |
# | |
# def colorAWire(self, fp, idx, colorIdx): | |
# cL = self.getCL() | |
# shape = fp.Shape | |
# colors = COLORS | |
# #print(f"in colorAWire, idx = {idx}, colorIdx = {colorIdx}") | |
# cLArray = self.getColorArray(fp, shape, cL) | |
# wireDict = self.getWireDict(shape, len(shape.Edges)) | |
# for k,vals in enumerate(wireDict.values()): | |
# numbers = [int(val.replace("Edge",""))-1 for val in vals] | |
# #FreeCAD.Console.PrintMessage(f"Wire{k+1} set to {colors[k]}\n") | |
# if k == idx: | |
# for num in numbers: | |
# cLArray[num] = cL[colorIdx] | |
# #FreeCAD.Console.PrintMessage(f"num = {num} cLArray[num] = {cLArray[num]}, cL[colorIdx] = {cL[colorIdx]}\n") | |
# fp.ViewObject.LineColorArray = cLArray | |
# | |
# def getColorArray(self, fp, shape, cL): | |
# edgeCount = len(shape.Edges) | |
# if edgeCount > len(cL): | |
# cL = cL+[self.color("black")]*(edgeCount - len(cL)) #extend cL with extra black colors | |
# FreeCAD.Console.PrintWarning("Too many wires for the number of colors in the color list, extra wires will all be black\n") | |
# wireDict = self.getWireDict(shape,edgeCount) | |
# colors = COLORS | |
# cLArray = [None]*edgeCount | |
# for k,vals in enumerate(wireDict.values()): | |
# numbers = [int(val.replace("Edge",""))-1 for val in vals] | |
# # FreeCAD.Console.PrintMessage(f"Wire{k+1} set to {colors[k]}\n") | |
# for num in numbers: | |
# cLArray[num] = cL[k] | |
# return cLArray | |
# | |
# def execute(self,fp): | |
# if not fp.Source: | |
# return | |
# self.setColorKeys(fp) | |
# shape = fp.Source[0].Shape.copy() | |
# | |
# if fp.FollowSource and not shape.isNull(): | |
# shape = shape.transformShape(shape.Placement.toMatrix(),True) | |
# | |
# wire_count = len(shape.Wires) | |
# if hasattr(fp,"MaxWires") and wire_count > fp.MaxWires: | |
# wire_count = fp.MaxWires | |
# | |
# if fp.Source[1] and bool(hasattr(fp,"UsePreselection") and fp.UsePreselection): | |
# faces = [name for name in fp.Source[1] if "Face" in name or "Wire" in name] | |
# if len(faces) != 0: | |
# shapes = [getattr(shape,f) for f in faces] | |
# shape = Part.makeCompound(shapes) | |
# else: | |
# #handle edges | |
# edges = [name for name in fp.Source[1] if "Edge" in name] | |
# if len(edges) != 0: | |
# wire_dict = self.getWireDict(shape,fp.MaxWires) | |
# wire_order = [ww for ww in range(1,wire_count+1)] | |
# for k in range(1,wire_count+1): | |
# found = False | |
# for en in wire_dict[k]: #en = edge names in this wire | |
# for un in edges: #un = user name, edges user selected | |
# if en == un: | |
# found = True | |
# break | |
# if found: | |
# break | |
# if not found: | |
# wire_order[k-1] = 0 | |
# else: | |
# wire_order[k-1] = k | |
# fp.WireOrder = wire_order | |
# wires = shape.Wires | |
# fp.WireOrder = fp.WireOrder[:len(wires)] | |
# if hasattr(fp,"MaxWires") and len(wires) > fp.MaxWires: | |
# wires = wires[:fp.MaxWires] | |
# FreeCAD.Console.PrintWarning("MaxWires ("+str(fp.MaxWires)+") exceeded. Source has "+str(len(shape.Wires))+" wires. Set MaxWires property higher if you want to use all the wires.\n") | |
# self.fixWireOrder(fp,shape) | |
# reordered = [wires[ii-1] for ii in fp.WireOrder if ii != 0] | |
# if len(reordered) == 0: | |
# new_shape = Part.Shape() | |
# elif len(reordered)==1: | |
# new_shape = Part.makeCompound(reordered) | |
# elif len(reordered)>1: | |
# new_shape = Part.makeCompound(reordered) | |
# | |
# if fp.FaceMaker != "No Face": | |
# if fp.FaceMaker == "InvertedFace": | |
# face = self.makeInvertedFace(fp,new_shape) | |
# elif fp.FaceMaker == "FilledFace": | |
# face = self.makeFilledFace(fp,new_shape) | |
# else: | |
# face = Part.makeFace(new_shape,fp.FaceMaker) | |
# new_shape = face | |
# | |
# fp.positionBySupport() | |
# if fp.FollowSource and not new_shape.isNull(): | |
# shape = new_shape.transformShape(shape.Placement.inverse().toMatrix(),True) | |
# elif not fp.FollowSource and not new_shape.isNull(): | |
# shape = new_shape.transformShape(shape.Placement.inverse().toMatrix(),True) | |
# shape.Placement = FreeCAD.Placement() | |
# | |
# if hasattr(shape,"Face1"): | |
# fp.Normal = shape.Face1.normalAt(0,0) | |
## FreeCAD.Console.PrintMessage("Normal found at: "+str(fp.Normal)+"\n") | |
# elif hasattr(shape,"Wire1") and self.wireIsPlanar(shape.Wire1) and shape.Wire1.isClosed(): | |
# fp.Normal = Part.makeFace(shape.Wire1,"Part::FaceMakerBullseye").normalAt(0,0) | |
## FreeCAD.Console.PrintMessage("Normal found at: "+str(fp.Normal)+"\n") | |
# elif len(shape.Wires) != 0: | |
# FreeCAD.Console.PrintWarning("Unable to find Normal of Wire1\n") | |
# | |
# fp.Shape = self.offset(fp,shape) | |
# fp.Shape = self.scale(fp, fp.Shape.copy()) | |
# | |
# if not hasattr(fp,"Colorize"): #object made with earlier version of WireFilter macro | |
# return | |
# else: | |
# self.colorize(fp,fp.Shape) | |
# | |
# def getCompareObject(self,fp): | |
# #put objects with same wire count as fp at the top of the list | |
# objs = [o.Label for o in fp.Document.Objects if hasattr(o,"Shape")\ | |
#and len(o.Shape.Wires) == len(fp.Shape.Wires)\ | |
#and o != fp and o != fp.Source[0]] | |
# items = objs | |
# items += [o.Label for o in fp.Document.Objects\ | |
#if not o.isDerivedFrom("PartDesign::Body")\ | |
#and (o.isDerivedFrom("Part::Feature")\ | |
#and o != fp and o != fp.Source[0]\ | |
#and o.Label not in objs)] | |
# items += [f"Self (uses {fp.Source[0].Label})"] | |
# fp.HideGhost = False | |
# if len(items) == 2: | |
# return fp.Document.getObjectsByLabel(items[0])[0] | |
# elif len(items) == 1: | |
# fp.HideGhost = True | |
# return fp.Source[0] | |
# item,ok = QtGui.QInputDialog.getItem(FreeCADGui.getMainWindow(), "WireFilter","Pick an item from the list to compare the wires to.", items, 0) | |
# if ok: | |
# fp.HideGhost = False | |
# if item == f"Self (uses {fp.Source[0].Label})": | |
# fp.HideGhost = True | |
# return fp.Source[0] | |
# return fp.Document.getObjectsByLabel(item)[0] | |
# else: | |
# return False | |
# | |
# def showWires(self): | |
# '''Show the wire order task panel''' | |
# fp = FreeCAD.ActiveDocument.getObject(self.fpName) | |
# if bool(hasattr(fp,"UsePreselection") and fp.UsePreselection) and fp.Source[1]: | |
# FreeCAD.Console.PrintError("WireFilter created using subelements of source object. Create a new WireFilter of this WireFilter to \ | |
#edit the wire order\n") | |
# return | |
# if len(fp.Shape.Wires) == 0: #no wires to edit | |
# FreeCAD.Console.PrintMessage("WireFilter: no wires to edit wire order for\n") | |
# return | |
# if not fp.ReUseGhost: | |
# co = self.getCompareObject(fp) | |
# else: | |
# fp.ReUseGhost = False# ask next time unless this is set in dialog | |
# co = fp.CompareObject | |
# if not co: | |
# return | |
# else: | |
# fp.CompareObject = co | |
# wf = fp.Document.addObject("Part::FeaturePython","WF_Ghost") | |
# wf.Label2 = "(Temporary object that should be automatically deleted when dialog closes)" | |
# self.putInBody(fp, wf) | |
# WireFilter(wf) | |
# WireFilterVP(wf.ViewObject) | |
# wf.Source = fp.CompareObject | |
# fp.CompareObject.ViewObject.Visibility = False | |
# fp.Ghost = wf | |
# if fp.HideGhost: | |
# fp.Ghost.ViewObject.Visibility = False | |
# fp.Document.recompute() | |
# | |
# if not FreeCADGui.Control.activeDialog(): | |
# panel = TaskEditWireOrdersPanel(fp) | |
# FreeCADGui.Control.showDialog(panel) | |
# self.editingMode = True | |
# else: | |
# self.editingMode=False | |
# FreeCAD.Console.PrintError("Another task dialog is active. Close that one and try again.\n") | |
# return | |
# | |
# def cleanUpWires(self): | |
# '''remove temporary representation of wires being manipulated''' | |
# fp = FreeCAD.ActiveDocument.getObject(self.fpName) | |
# if fp.Ghost: | |
# fp.Ghost.Source[0].ViewObject.Visibility = True if not fp.HideGhost else False | |
# fp.Document.removeObject(fp.Ghost.Name) | |
# fp.Document.recompute() | |
# | |
# def putInBody(self, fp, ghost): | |
# '''put the ghost into the same body as its source''' | |
# bodies = [obj for obj in fp.Document.Objects if obj.isDerivedFrom("PartDesign::Body") and fp.CompareObject in obj.Group] | |
# if len(bodies) == 1: | |
# bodies[0].Group += [ghost] | |
# else: | |
# parts = [obj for obj in fp.Document.Objects if obj.TypeId == "App::Part" and fp.CompareObject in obj.Group] | |
# if len(parts) == 1: | |
# parts[0].Group += [ghost] | |
# | |
#class TaskEditWireOrdersPanel: #simple editor for wire orders | |
# def __init__(self, fp): | |
# FreeCADGui.Selection.clearSelection() | |
# self.blockSignals = True | |
# self.fp = fp | |
# self.wireOrder = self.fp.WireOrder | |
# self.form = QtWidgets.QWidget() | |
# self.layout=QtWidgets.QVBoxLayout() | |
# self.form.setLayout(self.layout) | |
# self.form.setWindowTitle('Edit Wire Order') | |
# self.form.setObjectName("TaskEditWireOrdersPanel") | |
# wireBox = QtWidgets.QHBoxLayout() | |
# self.layout.addLayout(wireBox) | |
# self.wireOrderEdit = QtWidgets.QLineEdit() | |
# self.wireOrderEdit.setPlaceholderText(f"(Optional) Enter wire order and press OK or Apply. Example: {self.fp.WireOrder}") | |
# self.wireOrderEdit.setToolTip("\ | |
#You can use this to directly enter a new wire order rather than using \n\ | |
#the radio buttons. Enter as a comma separated list of integers.\n\ | |
#Enter 0 to disable a wire.") | |
# self.wireOrderLabel = QtWidgets.QLabel(f"Current Wire Order: {fp.WireOrder}") | |
# wireBox.addWidget(self.wireOrderLabel) | |
# wireBox.addWidget(self.wireOrderEdit) | |
# self.hboxes = [] | |
# self.buttonlist = [] | |
# self.buttongroups = [] | |
# self.enableds = [] #checkboxes to enable/disable wires | |
# self.hboxArea = QtWidgets.QVBoxLayout() | |
# self.scrollArea = QtWidgets.QScrollArea() # Create a scroll area | |
# scrollWidget = QtWidgets.QWidget() | |
# scrollWidget.setStyleSheet("background-color: lightblue;") | |
# scrollWidget.setLayout(self.hboxArea) | |
# self.scrollArea.setWidgetResizable(True) # Make the widget inside the scrollable | |
# self.scrollArea.setWidget(scrollWidget) # Set hboxArea as the widget inside the scroll area | |
# self.layout.addWidget(self.scrollArea) # Add the scroll area to the main layout | |
# self.makeHBoxes() | |
# hbox = QtWidgets.QHBoxLayout() | |
# self.layout.addLayout(hbox) | |
# self.showWireFilterCheckBox = QtWidgets.QCheckBox("Show WireFilter") | |
# self.showWireFilterCheckBox.setChecked(True) | |
# self.showWireFilterCheckBox.clicked.connect(self.onShowWireFilterCheckBoxClicked) | |
# hbox.addWidget(self.showWireFilterCheckBox) | |
# self.showGhostCheckBox = QtWidgets.QCheckBox("Show Ghost") | |
# self.showGhostCheckBox.setChecked(True) | |
# self.showGhostCheckBox.clicked.connect(self.onShowGhostCheckBoxClicked) | |
# hbox.addWidget(self.showGhostCheckBox) | |
# self.helpRequestedCheckBox = QtWidgets.QCheckBox("Show help") | |
# hbox.addWidget(self.helpRequestedCheckBox) | |
# self.helpRequestedCheckBox.clicked.connect(self.helpRequested) | |
# if len(self.fp.Shape.Wires) < 7 and not 0 in self.wireOrder: | |
# permsTxt = self.getPermsText(bPrev = True) | |
# self.btnPrevPerms = QtWidgets.QPushButton(permsTxt) | |
# self.btnPrevPerms.clicked.connect(self.previousPermutation) | |
# if permsTxt: | |
# hbox.addWidget(self.btnPrevPerms) | |
# permsTxt = self.getPermsText() | |
# self.btnPerms = QtWidgets.QPushButton(permsTxt) | |
# self.btnPerms.clicked.connect(self.nextPermutation) | |
# if permsTxt: | |
# hbox.addWidget(self.btnPerms) | |
# self.helpLabel = QtWidgets.QLabel() | |
# self.setupHelp() | |
# | |
# self.fp.Proxy.editingMode = True | |
# self.blockSignals = False | |
# | |
# def checkLuminance (self, bg): | |
# """check the luminance and return either black or white for foreground color""" | |
# bgcol = self.fp.Proxy.color(bg)# takes a string and returns a QColor | |
# red, green, blue, alpha = bgcol | |
# | |
# # Convert the color components to the sRGB color space | |
# red_srgb = red if red <= 0.04045 else ((red + 0.055) / 1.055) ** 2.4 | |
# green_srgb = green if green <= 0.04045 else ((green + 0.055) / 1.055) ** 2.4 | |
# blue_srgb = blue if blue <= 0.04045 else ((blue + 0.055) / 1.055) ** 2.4 | |
# | |
# # Calculate the luminance | |
# lum = 0.2126 * red_srgb + 0.7152 * green_srgb + 0.0722 * blue_srgb | |
# if lum > 0.5: | |
# return "black" | |
# else: | |
# return "white" | |
# | |
# def setupHelp(self): | |
# """add a label at the bottom of the panel to provide helpful information.""" | |
# txt = """ | |
# | |
#With this dialog you can edit the wire order in the WireFilter object and you can | |
#enable or disable individual wires. For example, you can make Wire1 of the source | |
#object Wire3 of the WireFilter object or you can disable Wire1 in the WireFilter | |
#object, in which case Wire2 of the source becomes Wire1 in the WireFilter and so on. | |
# | |
#Let's suppose you have applied WireFilter to Sketch, which contains 3 wires: Wire1, | |
#Wire2, and Wire3. But you want them reordered in the WireFilter object so that Wire1 | |
#becomes Wire3 and Wire3 becomes Wire1. In this example the wire order needs to be | |
#[3,2,1] since Wire3 becomes Wire1, Wire2 remains the same, and Wire1 becomes Wire3. | |
# | |
#The motivation for reording the wires is to make Part Design Loft and Part Design | |
#Sweep with multisections work the way you want them to work if the wires in the sketches | |
#are getting crossed or just not connecting to each other in the desired manner. | |
# | |
#The motivation for disabling some wires could be where you want to reuse the same | |
#sketch, but you don't want to use all the wires. Or you might be making a WireFilter | |
#of a face and don't want all the wires in the filter. | |
# | |
#Push Buttons -- In the left column there are a number of push buttons, one for each | |
#wire in the object you are comparing the WireFilter to. Typically, this object would | |
#be the other sketch to be used in a Loft. If you click one of these push buttons the | |
#only effect is that wire gets flashed in the 3D view. The flashing colors are black, | |
#gray, and white, and then after it finishes flashing it reverts back to its original color. | |
#That's the only thing the left column buttons do -- they flash the wire that you need | |
#to select one of the radio buttons to connect to in the Loft or Sweep. | |
# | |
#Enabled Checkboxes -- In each row in the 2nd column there is an Enabled checkbox. This | |
#is used to enable or disable that wire in the WireFilter object. When you disable a | |
#wire it will flash through red, yellow, and orange, to indicate to you that it is being | |
#disabled. It's Wire Order value is set to 0. If that was not the wire you wanted to | |
#disable, all you need to do is check the box again to re-enable it. When being re-enabled, | |
#the wire will flash blue, teal, and green. | |
# | |
#Radio Buttons -- The remainder of the widgets in each row are radio buttons. These are | |
#used to select the mapping for this wire in the compared item, e.g. sketch, to connect to | |
#in a Part Design Loft. When you select one of these radio buttons the indicated wire will | |
#flash white, gray, and black. So, by clicking the push button and the radio button and watching | |
#to see which wires flash, then you can know which ones will be connected in the Loft. Note: | |
#if you see asterisks in the labels of the radio buttons it's an indicator that none of the radio | |
#buttons in that column have yet been connected to a wire in the compare object. | |
# | |
#Current Wire Order Label -- This label is at the top left of the form. It shows the current | |
#working wire order. If it turns red, that indicates there are duplicates in the wire order. | |
#For example: [1,1,3] would be red because the 1 is duplicated. This is normal because you will | |
#necessarily have duplicates while you are fiddling with the dialog. You should not use Apply | |
#or OK while this label is red because it will cause some wires to be disabled and you won't be | |
#able to re-enabled them without first resetting everything back to default and starting all over | |
#again. In the above example [1,1,3] Wire2 will be disabled. | |
# | |
#Show ghost checkbox | |
#The ghost is the colorized version of the object the wirefilter is being compared with. It's a | |
#temporary object that will be deleted when the editor dialog is closed. | |
# | |
#Previous Permutation, Next Permutation buttons | |
# | |
#If the WireFilter object has fewer than 7 wires you will get additional permutation buttons. | |
#You can use these to try all the various possible wire orders, but only if you haven't disabled | |
#any of the wires. Permutation is a fancy way of saying how the wires are ordered. The fewer | |
#wires you have the fewer possible permutations there are. For example, if you only have 2 wires, | |
#then the permutations are: [1,2] and [2,1]. If you have 3 wires, then you have 6 permutations: | |
#[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1]. The Permutation buttons just allow you | |
#to cycle through all the possibilities. When you get to 7 or more wires, there are too many | |
#permutations to reasonably cycle through in this manner, so these buttons are not shown. | |
# | |
#Reset -- resets the wire order to what it was when you first opened this editor or | |
#since the last time you clicked Apply or Restore Defaults. | |
# | |
#Restore Defaults -- Resets the wire order back to default: [1,2,3...] (Can be undone | |
#using FreeCAD's Undo/Redo system. | |
# | |
#OK -- Accept the current wire order configured in this dialog (or manually entered | |
#in the wire order line edit widget) and closes the dialog. (Can be undone.) | |
# | |
#Cancel -- Cancel any changes made in this dialog since last time you clicked Apply | |
#or Restore Defaults and closes dialog. | |
# | |
#Apply -- Apply changes, but leave the dialog open. (Can be undone, but you should | |
#press Cancel, then Undo, and then open the dialog again if you want to undo.) | |
#Note: This function actually clicks OK, and then reopens the dialog for you. | |
# | |
#Show help -- Brings up this help text when checked. | |
# | |
#""" | |
# | |
# | |
# self.helpLabel.setText(txt) | |
# self.layout.addWidget(self.helpLabel) | |
# self.helpLabel.setVisible(False) | |
# | |
# def helpRequested(self): | |
# checked = self.helpRequestedCheckBox.isChecked() | |
# self.helpLabel.setVisible(checked) | |
# | |
# def onShowGhostCheckBoxClicked(self): | |
# checked = self.showGhostCheckBox.isChecked() | |
# self.fp.Ghost.ViewObject.Visibility = checked | |
# | |
# def onShowWireFilterCheckBoxClicked(self): | |
# checked = self.showWireFilterCheckBox.isChecked() | |
# self.fp.ViewObject.Visibility = checked | |
# | |
# def makeHBoxes(self): | |
# def makeTrigger(x,y): return lambda : self.wireBtnClicked(x,y) | |
# def makeEnabled(x): return lambda : self.enabledClicked(x) | |
# buttons = [] | |
# for idx,wire in enumerate(self.fp.Shape.Wires): | |
# idx += 1 | |
# hbox = QtGui.QHBoxLayout() | |
# btn = QtGui.QPushButton(self.fp.CompareObject.Label+" "+str(idx)) | |
# btn.setStyleSheet(f"background-color: {COLORS[idx-1]};color:{self.checkLuminance(COLORS[idx-1])};font-weight:bold;") | |
# btn.clicked.connect(makeTrigger(idx,self.fp)) | |
# btn.setObjectName(btn.text()) | |
# buttons.append(btn) | |
# hbox.addWidget(btn) | |
# cb = QtWidgets.QCheckBox("Enabled") | |
# cb.setChecked(True) | |
# cb.setObjectName(f"Enabled {idx}") | |
# self.enableds.append(cb) | |
# hbox.addWidget(cb) | |
# cb.clicked.connect(makeEnabled(idx-1)) | |
# | |
# group = QtGui.QButtonGroup(hbox) | |
# self.buttongroups.append(group) | |
# for idx2,wire2 in enumerate(self.fp.CompareObject.Shape.Wires): | |
# idx2 += 1 | |
# btn2 = QtGui.QRadioButton("WF "+str(idx2)) | |
# if self.wireOrder[idx -1] == idx2: | |
# btn2.setChecked(True) | |
# btn2.setStyleSheet(f"background-color: {COLORS[idx2-1]};color:{self.checkLuminance(COLORS[idx2-1])};font-weight:bold;") | |
# btn2.clicked.connect(makeTrigger(idx,idx2)) | |
# btn2.setObjectName(f"{idx}_{idx2}") | |
# hbox.addWidget(btn2) | |
# buttons.append(btn2) | |
# group.addButton(btn2) | |
# self.buttonlist.append(buttons) | |
# buttons = [] | |
# self.hboxArea.addLayout(hbox) | |
# if idx % 3 == 0: | |
# divider = QtWidgets.QFrame() | |
# divider.setFrameShape(QtWidgets.QFrame.HLine) | |
# divider.setFrameShadow(QtWidgets.QFrame.Sunken) | |
# self.hboxArea.addWidget(divider) | |
# | |
# def nextPermutation(self): | |
# """Try the next permutation in the wire order list""" | |
# if 0 in self.wireOrder: | |
# FreeCAD.Console.PrintMessage("WireFilter: 0 in wire order, skipping permutations\n") | |
# return | |
# from itertools import permutations | |
# unique_integers = sorted(self.wireOrder) | |
## print(f"self.wireOrder = {self.wireOrder}") | |
# # Generate all permutations | |
# all_permutations = list(permutations(unique_integers)) | |
# if tuple(self.wireOrder) in all_permutations: | |
# idx = all_permutations.index(tuple(self.wireOrder)) | |
## print(f"idx = {idx}") | |
# if idx >= len(all_permutations) -1: | |
# idx = -1 #return the first permutation | |
# self.wireOrder = list(all_permutations[idx+1]) | |
# self.clicked(QtGui.QDialogButtonBox.Apply) | |
# else: | |
# FreeCAD.Console.PrintMessage("WireFilter: current wire order not a valid permutation\n") | |
# return | |
# | |
# def previousPermutation(self): | |
# """Try the previous permutation in the wire order list""" | |
# if 0 in self.wireOrder: | |
# FreeCAD.Console.PrintMessage("WireFilter: 0 in wire order, skipping permutations\n") | |
# return | |
# from itertools import permutations | |
# unique_integers = sorted(self.wireOrder) | |
## print(f"self.wireOrder = {self.wireOrder}") | |
# # Generate all permutations | |
# all_permutations = list(permutations(unique_integers)) | |
# if tuple(self.wireOrder) in all_permutations: | |
# idx = all_permutations.index(tuple(self.wireOrder)) | |
## print(f"idx = {idx}") | |
# if idx == 0: | |
# idx = len(all_permutations) | |
# self.wireOrder = list(all_permutations[idx-1]) | |
# self.clicked(QtGui.QDialogButtonBox.Apply) | |
# else: | |
# FreeCAD.Console.PrintMessage("WireFilter: current wire order not a valid permutation\n") | |
# return | |
# def getPermsText(self, bPrev = False): | |
# from itertools import permutations | |
# unique_integers = sorted(self.wireOrder) | |
# # Generate all permutations | |
# all_permutations = list(permutations(unique_integers)) | |
# if tuple(self.wireOrder) in all_permutations: | |
# idx = all_permutations.index(tuple(self.wireOrder)) + 1 | |
# if idx == len(all_permutations): | |
# idx = 0 #return the first permutation | |
# idx += 1 | |
# if not bPrev: | |
# return f"Next Permutation {idx}/{len(all_permutations)}" | |
# else: | |
# idx -= 2 | |
# if idx == 0: | |
# idx = len(all_permutations) | |
# elif idx == -1: | |
# idx = len(all_permutations)-1 | |
# return f"Previous Permutation {idx}/{len(all_permutations)}" | |
# else: | |
# return "" | |
# | |
# def enabledClicked(self, idx): | |
# btn = self.enableds[idx] | |
# checked = btn.isChecked() | |
## print(f"enabled clicked {idx}, btn = {btn}, checked = {checked}") | |
# radios = self.buttonlist[idx] | |
# current = 0 | |
# for rr,radio in enumerate(radios): | |
# if radio.isChecked(): | |
# current = rr | |
# self.wireOrder[idx] = 0 if not checked else current | |
# self.wireOrderLabel.setText(f"Current Wire Order: {self.wireOrder}") | |
# reason = "enabled" if checked else "disabled" | |
# self.fp.Proxy.flashAWire(self.fp,idx,reason) | |
# | |
# def wireBtnClicked(self, row, col): | |
# if col != self.fp: #radio button clicked | |
# btn = self.buttonlist[row-1][col] | |
# pushBtn = self.buttonlist[row-1][0] #left column push button for this row | |
## print(btn.objectName()+" found") | |
## print(f"row,col = {row},{col}") | |
## print(f"incoming wire order: {self.wireOrder}") | |
# old = self.wireOrder[row-1] | |
# new = col | |
## print(f"old = {old}") | |
## print(f"new = {new}") | |
# self.fp.Proxy.flashAWire(self.fp,new-1,reason="selected") | |
# self.wireOrder[row-1] = new | |
# self.wireOrderLabel.setText(f"Current Wire Order: {self.wireOrder}") | |
# wo_stripped = [x for x in self.wireOrder if x != 0] | |
# if self.hasDuplicates(): | |
# self.wireOrderLabel.setStyleSheet("color:red;") | |
# self.wireOrderLabel.setText(f"Current Wire Order: {self.wireOrder}") | |
# self.markDuplicates() | |
# else: | |
# self.unMarkAll() | |
# self.wireOrderLabel.setStyleSheet("color:black;") | |
# self.fp.Proxy.colorize(self.fp, self.fp.Shape) | |
# else: #one of the left column buttons was clicked | |
# self.fp.Ghost.Proxy.flashAWire(self.fp.Ghost, row-1,reason="selected") | |
# | |
# def findDuplicates(self, my_list): | |
# """find the subsequent duplicates in a list and return their indices""" | |
# seen = {} # A dictionary to store the last seen index of each element | |
# duplicates = [] # List to store the indices of subsequent duplicates | |
# for index, item in enumerate(my_list): | |
# if item == 0: | |
# continue | |
# if item in seen: | |
# # If the item is already in the 'seen' dictionary, it's a duplicate | |
# if seen[item] is not None: | |
# # Append the index of the subsequent duplicate | |
# duplicates.append(index) | |
# else: | |
# # If the item is not in 'seen', add it with the current index | |
# seen[item] = index | |
# return duplicates | |
# | |
# def unMarkAll(self): | |
# """Unmark all pushbuttons as duplicates""" | |
# btns = [item for sublist in self.buttonlist for item in sublist] #flatten list | |
# for btn in btns: | |
# if "QRadioButton" in str(type(btn)): | |
# btn.setText(btn.text().replace("*","")) | |
# | |
# def parseRadioButtonObjectName(self, name): | |
# parts = name.split("_") | |
# if len(parts) == 2: | |
# x = int(parts[0]) | |
# y = int(parts[1]) | |
# return x, y | |
# else: | |
# raise ValueError("Input string does not have the expected format") | |
# | |
# def markDuplicates(self): | |
# self.unMarkAll() | |
# duplicates = self.findDuplicates(self.wireOrder) | |
# all_btns = [item for sublist in self.buttonlist for item in sublist] #flatten list | |
# radio_btns = [b for b in all_btns if "QRadioButton" in str(type(b))] | |
## print (f"radio_btns = {radio_btns}") | |
# cols = len(self.buttonlist[0])-1 | |
# uncheckedColumns = list(range(1,cols+1)) | |
## print(f"uncheckedColumns = {uncheckedColumns}") | |
# for rb in radio_btns: | |
# r,c = self.parseRadioButtonObjectName(rb.objectName()) | |
## print(f"r,c,rb.text(),rb.isChecked() = {r},{c},{rb.text()},{rb.isChecked()}") | |
# if rb.isChecked() and c in uncheckedColumns: | |
# uncheckedColumns.remove(c) | |
## print(f"After parsing: uncheckedColumns = {uncheckedColumns}") | |
# uncheckedRadios = [] | |
# for rb in radio_btns: | |
# r,c = self.parseRadioButtonObjectName(rb.objectName()) | |
# if c in uncheckedColumns: | |
# uncheckedRadios.append(rb) | |
# for btn in uncheckedRadios: | |
# txt = btn.text() | |
# if not "*" in txt: | |
# new_txt = "*" + txt + "*" | |
# btn.setText(new_txt) | |
# | |
# | |
# def hasDuplicates(self): | |
# """Checks if self.wireOrder has duplicate non-zero values""" | |
# wo_stripped = [x for x in self.wireOrder if x != 0] | |
# if len(wo_stripped) != len(list(set(wo_stripped))): | |
# return True | |
# else: | |
# return False | |
# | |
# def reject(self): | |
# FreeCADGui.Control.closeDialog() | |
# FreeCADGui.ActiveDocument.resetEdit() | |
# self.fp.Proxy.editingMode = False #self.fp.Proxy is the fp class object (self in that class) | |
# self.fp.ViewObject.Visibility = True | |
# self.fp.Proxy.cleanUpWires() | |
# FreeCAD.ActiveDocument.recompute() | |
# | |
# def accept(self): | |
# if self.hasDuplicates(): | |
# FreeCAD.Console.PrintWarning("WireFilter: Wire order has duplicate non-zero values\n") | |
# self.fp.Document.openTransaction("WireFilter:Accept Wire Order Changes") | |
# wo = self.parseWireOrderEdit() | |
# if wo: | |
# self.fp.WireOrder = wo | |
# else: | |
# self.fp.WireOrder = self.wireOrder | |
# self.fp.Document.commitTransaction() | |
# FreeCADGui.Control.closeDialog() | |
# FreeCADGui.ActiveDocument.resetEdit() | |
# self.fp.Proxy.editingMode = False | |
# if not self.fp: #user deleted or closed document perhaps | |
# return | |
# self.fp.ViewObject.Visibility = True | |
# self.fp.Proxy.cleanUpWires() | |
# FreeCAD.ActiveDocument.recompute() | |
# | |
# def parseWireOrderEdit(self): | |
# """returns a python list of integers if valid, else None""" | |
# input_string = self.wireOrderEdit.text() | |
# if not input_string: | |
# return None | |
# else: | |
# if not input_string[0] == "[": | |
# input_string = "[" + input_string | |
# if not input_string[-1] == "]": | |
# input_string = input_string + "]" | |
# import ast | |
# try: | |
# # Attempt to parse the input string as a Python literal (list) | |
# parsed_list = ast.literal_eval(input_string) | |
# # Check if the parsed result is a list | |
# if isinstance(parsed_list, list): | |
# # Check if all elements in the list are integers | |
# if all(isinstance(item, int) for item in parsed_list): | |
# return parsed_list | |
# else: | |
# FreeCAD.Console.PrintError("WireFilter: enter only integers in the list\n") | |
# return None | |
# else: | |
# FreeCAD.Console.PrintError("WireFilter: not a valid list in wire order line edit.") | |
# except (ValueError, SyntaxError): | |
# FreeCAD.Console.PrintError("WireFilter: unable to parse list for new wire order.") | |
# return None | |
# | |
# def clicked(self, button): | |
# if button == QtGui.QDialogButtonBox.Reset: | |
# self.reject() | |
# self.fp.ReUseGhost = True | |
# self.fp.ViewObject.Proxy.setEdit(self.fp.ViewObject, 5) #essentially closes and restarts editor | |
# elif button == QtGui.QDialogButtonBox.Apply: | |
# self.fp.Document.openTransaction("WireFilter:ApplyChanges") | |
# self.accept() | |
# self.fp.ReUseGhost = True | |
# self.fp.ViewObject.Proxy.setEdit(self.fp.ViewObject, 5) #essentially closes and restarts editor | |
# self.fp.Document.commitTransaction() | |
# elif button == QtGui.QDialogButtonBox.RestoreDefaults: | |
# self.fp.Document.openTransaction("WireFilter:RestoreDefaults") | |
# self.fp.UseDefault=True | |
# self.reject() | |
# self.fp.ReUseGhost = True | |
# self.fp.ViewObject.Proxy.setEdit(self.fp.ViewObject, 5) #essentially closes and restarts editor | |
# self.fp.Document.commitTransaction() | |
# | |
# def getStandardButtons(self): | |
# return int(QtGui.QDialogButtonBox.Ok) | int(QtGui.QDialogButtonBox.Apply)\ | |
# | int(QtGui.QDialogButtonBox.Cancel) | int(QtGui.QDialogButtonBox.Reset)\ | |
# | int(QtGui.QDialogButtonBox.RestoreDefaults) | |
# | |
# | |
#class WireFilterVP: | |
# def __init__(self, obj): | |
# '''Set this object to the proxy object of the actual view provider''' | |
# obj.Proxy = self | |
# self.Object = obj.Proxy.Object | |
# | |
# def attach(self,vobj): | |
# self.Object = vobj.Object | |
# | |
# def dropObject(self, vp, dropped): | |
# vp.Object.Source = dropped | |
# | |
# def canDropObject(self, dropped): | |
# return hasattr(dropped, "ViewObject") | |
# | |
# def onDelete(self, vobj, subelements): | |
# if vobj.Object.Source: | |
# vobj.Object.Source[0].ViewObject.Visibility = True | |
# return True | |
# | |
# def updateData(self, fp, prop): | |
# '''If a property of the handled feature has changed we have the chance to handle this here''' | |
# pass | |
# | |
# def getDisplayModes(self,obj): | |
# '''Return a list of display modes.''' | |
# modes=[] | |
# modes.append("Flat Lines") | |
# modes.append("Shaded") | |
# modes.append("Wireframe") | |
# return modes | |
# | |
# def claimChildren(self): | |
# fp = self.Object | |
# if fp.Source and fp.ClaimChildren and not fp.Source[0].isDerivedFrom("PartDesign::Feature"): | |
# return [fp.Source[0]] | |
# else: | |
# return[] | |
# | |
# def getDefaultDisplayMode(self): | |
# '''Return the name of the default display mode. It must be defined in getDisplayModes.''' | |
# return "Flat Lines" | |
# | |
# def setDisplayMode(self,mode): | |
# '''Map the display mode defined in attach with those defined in getDisplayModes.\ | |
# Since they have the same names nothing needs to be done. This method is optional''' | |
# return mode | |
# | |
# def setupContextMenu(self, vobj, menu): | |
# if vobj.Object.Proxy.editingMode == False: | |
# text = "Edit Wire Orders" | |
# mode = 5 | |
# else: | |
# text = "Accept Wire Orders" | |
# mode = 6 | |
# if len(vobj.Object.Shape.Wires) == 0: | |
# text = "No wires to edit" | |
# mode = 5 | |
# action = menu.addAction(text) | |
# action.triggered.connect(lambda: self.setEdit(vobj,mode)) | |
# | |
# def setEdit(self,vp,modNum): | |
# if modNum == 3: | |
# FreeCADGui.runCommand('Part_ColorPerFace',0) | |
# elif modNum == 5: #begin edit mode | |
# vp.Object.Proxy.showWires() | |
# elif modNum == 6: #accept changes and end edit mode | |
# vp.Object.Proxy.editingMode = False | |
# vp.Object.Proxy.cleanUpWires() | |
# FreeCADGui.Control.closeDialog() | |
# return False | |
# | |
# def onChanged(self, vp, prop): | |
# '''Here we can do something when a single property got changed''' | |
# #FreeCAD.Console.PrintMessage("Change property: " + str(prop) + "\n") | |
# pass | |
# | |
# def getIcon(self): | |
# '''Return the icon in XPM format which will appear in the tree view. This method is\ | |
# optional and if not defined a default icon is shown.''' | |
# return """ | |
#/* XPM */ | |
#static char *a57ib_u2pr2[] = { | |
#/* columns rows colors chars-per-pixel */ | |
#"64 64 158 2 ", | |
#" c #AD0E0E", | |
#". c #B30C0C", | |
#"X c #BC0E0E", | |
#"o c #AF1212", | |
#"O c #AE1A1A", | |
#"+ c #B41212", | |
#"@ c #BB1212", | |
#"# c #B41A1A", | |
#"$ c #B91C1C", | |
#"% c #A92B2B", | |
#"& c #B32525", | |
#"* c #B82323", | |
#"= c #B52C2C", | |
#"- c #BB2C2C", | |
#"; c #B53030", | |
#": c #BC3535", | |
#"> c #B53E3E", | |
#", c #BB3C3C", | |
#"< c #C51414", | |
#"1 c #CB1616", | |
#"2 c #C41B1B", | |
#"3 c #CD1A1A", | |
#"4 c #D41B1B", | |
#"5 c #DA1E1E", | |
#"6 c #C52525", | |
#"7 c #CB2323", | |
#"8 c #C72C2C", | |
#"9 c #CB2B2B", | |
#"0 c #D12222", | |
#"q c #DD2121", | |
#"w c #D12A2A", | |
#"e c #C03535", | |
#"r c #CC3333", | |
#"t c #C23D3D", | |
#"y c #CB3C3C", | |
#"u c #D13333", | |
#"i c #D03D3D", | |
#"p c #E22424", | |
#"a c #E12A2A", | |
#"s c #935858", | |
#"d c #AA4646", | |
#"f c #A04B4B", | |
#"g c #AA4D4D", | |
#"h c #B54343", | |
#"j c #BA4242", | |
#"k c #B34E4E", | |
#"l c #B94A4A", | |
#"z c #AB5252", | |
#"x c #A45D5D", | |
#"c c #BD5656", | |
#"v c #8C7373", | |
#"b c #897979", | |
#"n c #917272", | |
#"m c #9E7B7B", | |
#"M c #A76161", | |
#"N c #BC6262", | |
#"B c #AB7171", | |
#"V c #B07878", | |
#"C c #C54343", | |
#"Z c #CB4343", | |
#"A c #C44C4C", | |
#"S c #CA4B4B", | |
#"D c #D14646", | |
#"F c #D04C4C", | |
#"G c #C45454", | |
#"H c #CC5454", | |
#"J c #C65A5A", | |
#"K c #CB5C5C", | |
#"L c #D05353", | |
#"P c #D05B5B", | |
#"I c #D95D5D", | |
#"U c #C66363", | |
#"Y c #CB6464", | |
#"T c #CD6B6B", | |
#"R c #D16363", | |
#"E c #D06A6A", | |
#"W c #C37575", | |
#"Q c #CE7373", | |
#"! c #D27575", | |
#"~ c #D27B7B", | |
#"^ c #838383", | |
#"/ c #898989", | |
#"( c #938484", | |
#") c #969696", | |
#"_ c #9D9D9D", | |
#"` c #A58585", | |
#"' c #AC8080", | |
#"] c #A38C8C", | |
#"[ c #AD8C8C", | |
#"{ c #B08585", | |
#"} c #BF8282", | |
#"| c #A09292", | |
#" . c #A89292", | |
#".. c #A29D9D", | |
#"X. c #AB9D9D", | |
#"o. c #B59797", | |
#"O. c #A5A4A4", | |
#"+. c #AAA6A6", | |
#"@. c #AAAAAA", | |
#"#. c #B6A3A3", | |
#"$. c #BBA3A3", | |
#"%. c #B1ADAD", | |
#"&. c #BFABAB", | |
#"*. c #B4B4B4", | |
#"=. c #BDB1B1", | |
#"-. c #BDBDBD", | |
#";. c #CF8E8E", | |
#":. c #D58484", | |
#">. c #D88787", | |
#",. c #D98C8C", | |
#"<. c #C59393", | |
#"1. c #D59393", | |
#"2. c #DA9494", | |
#"3. c #DC9C9C", | |
#"4. c #C8A7A7", | |
#"5. c #D7A7A7", | |
#"6. c #DEA3A3", | |
#"7. c #D0A9A9", | |
#"8. c #DEBCBC", | |
#"9. c #E0A6A6", | |
#"0. c #E1AAAA", | |
#"q. c #E2ACAC", | |
#"w. c #E2ADAD", | |
#"e. c #E3B0B0", | |
#"r. c #E5B4B4", | |
#"t. c #E7BBBB", | |
#"y. c #E8BFBF", | |
#"u. c #C2C2C2", | |
#"i. c #CDCDCD", | |
#"p. c #DEC3C3", | |
#"a. c #D9CCCC", | |
#"s. c #DFDBDB", | |
#"d. c #EBC4C4", | |
#"f. c #ECC9C9", | |
#"g. c #EDCBCB", | |
#"h. c #EED0D0", | |
#"j. c #E0D9D9", | |
#"k. c #EADBDB", | |
#"l. c #F0D5D5", | |
#"z. c #F0D7D7", | |
#"x. c #F2DADA", | |
#"c. c #F2DCDC", | |
#"v. c #F3DDDD", | |
#"b. c #E2E1E1", | |
#"n. c #E8E0E0", | |
#"m. c #EDEDED", | |
#"M. c #F4E0E0", | |
#"N. c #F5E3E3", | |
#"B. c #F7EBEB", | |
#"V. c #F8EBEB", | |
#"C. c #F9EFEF", | |
#"Z. c #F1F1F1", | |
#"A. c #FAF3F3", | |
#"S. c #FBF4F4", | |
#"D. c #FBF6F6", | |
#"F. c #FCF6F6", | |
#"G. c #FDF9F9", | |
#"H. c white", | |
#/* pixels */ | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.A.V.M.c.z.h.h.g.g.g.g.h.h.z.c.M.V.A.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.B.z.y.6.>.Q T Y K J J J Y T ~ ,.6.t.z.C.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.S.B.x.d.0.>.T A : * # + . . + # * : A T >.9.g.M.V.F.G.H.H.H.H.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.A.h.0.>.T H S i u w w 0 3 3 3 3 0 w w u y S H T :.q.h.B.H.H.H.H.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.B.x.9.T t # X < 3 5 q p p p q p p p p q 4 3 < @ $ : Q 9.g.S.G.H.H.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.M.e.~ K Z 9 3 1 1 < < < 7 9 9 r r r 9 3 2 < 1 1 3 3 9 Z J >.e.z.G.H.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.M.d.Q & $ 1 4 p q 3 < X + & ; , j j , = O + @ < 3 5 q 5 < @ t T e.F.G.H.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.M.0.T S 6 1 3 3 3 9 Z H E ~ >.2.3.3.3.3.2.>.~ E H y 9 3 1 1 3 8 C ! q.h.G.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.M.g.Q # 2 q 4 4 2 # : T 3.d.M.C.F.F.F.S.S.S.A.c.d.3.Y , $ X 4 q 3 < t Y 0.A.H.H.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.r.~ S 2 1 4 7 6 Z Y >.0.g.M.F.G.H.H.H.H.H.H.H.A.M.g.0.>.Y C 9 3 3 3 9 t ~ d.M.G.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.c.:.= 6 4 4 1 6 : T r.c.V.S.G.H.H.H.H.H.H.H.H.H.H.G.S.C.z.0.~ t X 3 q 3 @ H 2.d.A.H.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.x.r.Y # 3 5 3 2 C Q 6.x.F.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.z.q.Q t 9 4 1 1 y K 3.M.G.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.r.:.S < 3 4 6 - T y.M.S.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.S.c.q.~ C X 1 q 6 & Q h.F.H.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.M.2.G r 3 1 1 y J 3.M.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.z.q.Y $ 7 q 1 + J r.c.S.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.z.Q - 7 q 1 X H 6.g.A.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.x.>., 9 4 3 < S ,.y.C.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.A.d.K + 3 p 3 # Q g.B.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.0.T Z 1 1 1 y T 6.M.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.x.0.H @ 3 q 9 : ,.M.F.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.g.3.H @ 1 4 r A >.c.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.d.2.S < 1 4 y G 6.A.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.M.g.E . < q w - Q z.H.", | |
#"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.M.r.:.Z < 1 3 S Q r.S.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.M.~ o 1 p 0 # Y h.H.", | |
#"H.H.S.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.k.5.Q y 1 1 2 H ;.8.m.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.S.H.H.H.H.H.H.H.S.>.O 3 p 4 . J g.H.", | |
#"H.Z.i.*.*.*.*.*.*.*.*.*.*.*.*.*.%. .B , 1 < < k ` X.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.i.m.H.H.H.H.H.H.F.,.& 7 p 3 . G d.G.", | |
#"H.b.@.^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ n h 4 < @ f ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ O.b.H.H.H.H.H.H.F.2.; 9 p 1 . G y.A.", | |
#"H.b.@.^ ) O.O.O.O.O.O.O.O.O.O.O.O.O.[ A 5 4 7 N O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.) ^ O.b.H.H.H.H.H.H.S.1.= 6 p 3 . G d.F.", | |
#"G.b.+./ @.u.u.u.u.u.u.u.u.u.u.u.u.u.4.I p p a ! u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.@./ O.b.H.H.H.H.H.H.S.,.& 7 p 3 . G d.G.", | |
#"S.j.+./ @.u.*.O.O.O.O.O.O.O.O.O.O.O.[ A 5 5 q N O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.*.u.@./ O.b.H.H.H.H.H.G.C.>.O 3 p 4 + J g.H.", | |
#"S.j.O./ @.-.O.^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ n > 4 4 0 z ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ _ -.@./ O.b.H.H.H.H.H.F.M.~ < p 0 # U h.H.", | |
#"S.j.O./ @.-._ ^ ) @.*.*.*.*.*.*.@.X.' j < 3 0 > s m X.@.%.*.*.*.*.*.*.*.*.*.*.*.) ^ _ -.@./ O.b.H.H.H.H.G.M.d.T . < q w : Q z.H.", | |
#"S.j.O./ @.-._ ^ *.m.m.m.m.m.m.m.n.p.1.C X 1 q 6 % W p.k.m.m.m.m.m.m.m.m.m.m.m.m.-.^ _ -.@./ O.b.H.H.H.H.F.g.3.K @ 1 4 r A >.c.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.S.M.r.J # 3 q 3 # Y r.x.F.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.C.0.T Z 1 1 1 y T 6.M.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.F.h.Q - 7 q 3 @ H 2.d.A.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.G.M.>.: 9 5 1 X S ,.d.C.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.M.2.G r 4 1 1 y Y 3.c.S.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.S.x.r.Y # 3 q 3 # K q.x.S.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.C.y.>.S X 1 5 6 - T t.c.F.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.M.6.T y 2 3 5 6 - ~ h.A.G.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.F.c.r.Y # 3 q 3 @ C >.r.x.C.G.H.H.H.H.H.H.H.u.^ _ -.@./ O.s.C.c.r.! t 9 3 3 3 r G 3.B.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.A.z.>.C 8 3 3 3 9 t T 3.d.B.H.H.H.H.H.H.H.u.^ _ -.@./ ..a.g.2.Y C 6 0 5 1 @ H 3.d.A.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.A.e.Q S 2 3 5 3 @ : Y 2.d.c.M.C.A.S.S.A.-.^ _ -.+.( ` 7.3.J - 2 1 4 q 6 & ~ g.V.G.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.G.x.t.! e 6 3 3 3 7 y A T :.3.r.d.z.g.d.#.^ _ -.o.x g c A r 7 3 4 1 2 S ! q.M.F.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.A.6.J t 2 3 q 4 1 @ $ : J ~ 2.0.3.2.{ b | =.} > # + X 1 4 4 4 2 & Q y.c.G.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.G.z.q.~ A r 3 1 1 3 7 9 y S L P H A l d c Y H 9 3 1 1 1 1 2 8 H ~ q.c.C.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.y.:.A # @ 3 5 p q 4 1 < X X X < 1 4 q q 5 5 p q 3 < * , >.h.B.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.c.y.3.! H y 9 3 1 1 < < < < < 1 4 5 q 5 1 1 3 9 Z K ! 2.d.B.S.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.M.g.3.Y , # . X < 1 1 4 4 4 5 q p 4 X + # , T 3.g.C.F.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.B.g.0.,.! Y K H F D y r t h G R H : C T ,.q.g.M.G.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.F.C.M.c.g.q.2.>.~ Y A M v ] &.<.U ~ d.V.A.S.G.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.C.x.d.y.e.6.1.[ ^ _ -.$.V <.k.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.S.A.C.V.M.M.=.^ _ -.@.( +.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ -.S.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.-.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ _ -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-._ ^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.-._ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.u.@._ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ @.-.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"S.j.O./ @.u.u.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.u.u.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"G.j.+.^ ) @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.) ^ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"H.b.@.^ ^ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / ^ ^ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"H.m.u.@.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.+.@.@.u.m.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"H.G.m.b.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.b.b.m.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
#"H.H.H.H.G.S.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.S.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H." | |
#}; | |
# """ | |
# | |
# def __getstate__(self): | |
# '''When saving the document this object gets stored using Python's json module.\ | |
# Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\ | |
# to return a tuple of all serializable objects or None.''' | |
# return None | |
# | |
# def __setstate__(self,state): | |
# '''When restoring the serialized object from document we have the chance to set some internals here.\ | |
# Since no data were serialized nothing needs to be done here.''' | |
# return None | |
######################################## | |
#CODE_ENDS_HERE | |
BASENAME = 'wirefilter' | |
def makeObject(WF): | |
doc = FreeCAD.ActiveDocument if FreeCAD.ActiveDocument else FreeCAD.newDocument() | |
selobj = FreeCADGui.Selection.getSelectionEx() | |
wf = doc.addObject("Part::Part2DObjectPython","WireFilter") | |
WF.WireFilter(wf) | |
WF.WireFilterVP(wf.ViewObject) | |
if selobj: | |
wf.Source = (selobj[0].Object,selobj[0].SubElementNames) | |
if selobj[0].SubElementNames: | |
FreeCAD.Console.PrintMessage("WireFilter created using selected subelements. You should make a WireFilter of this WireFilter if you \ | |
wish to edit the wire order. This is because the numer of elements in the wire order is based on the number \ | |
of wires in the original source shape.\n") | |
selobj[0].Object.ViewObject.Visibility = False | |
bodies = [obj for obj in doc.Objects if obj.isDerivedFrom("PartDesign::Body") and selobj[0].Object in obj.Group] | |
if len(bodies) == 1: | |
bodies[0].Group += [wf] | |
else: | |
parts = [obj for obj in doc.Objects if obj.TypeId == "App::Part" and selobj[0].Object in obj.Group] | |
if len(parts) == 1: | |
parts[0].Group += [wf] | |
else: | |
FreeCAD.Console.PrintMessage("WireFilter: No selected object. Edit the Source property or drop an object on to it.\n") | |
################# | |
def writeFile(): | |
with open(py_file,"w") as outfile: | |
for line in code.splitlines(): | |
if "#CODE_ENDS_HERE" in line: | |
break | |
if line.startswith('#'): | |
if line == "# -*- coding: utf-8 -*-": | |
line = "#" + line | |
outfile.write(line[1:]+"\n") #skip first character (#) | |
############ | |
#if __name__ == "__main__": | |
# makeObject(None) | |
# raise Exception("quick exit for testing/debugging") | |
############ | |
if __name__ == "__main__": | |
import os | |
fin = open(__file__, 'r') | |
code = fin.read() | |
fin.close() | |
version = code.splitlines()[1][16:] | |
real_path = os.path.realpath(__file__) | |
dir_path = os.path.dirname(real_path) | |
py_file = real_path.replace(".FCMacro",".py").replace('WireFilter','wirefilter').replace('Wirefilter','wirefilter') | |
bHasFile = os.path.exists(py_file) | |
noImport = False #user elects not to save import file | |
if not bHasFile: | |
from PySide import QtGui | |
window = QtGui.QApplication.activeWindow() | |
mb = QtGui.QMessageBox() | |
mb.setWindowTitle(BASENAME+" setup") | |
mb.setIcon(mb.Information) | |
mb.setStandardButtons(mb.Ok) | |
mb.addButton(mb.Cancel) | |
mb.setDefaultButton(mb.Cancel) | |
okBtn = mb.button(QtGui.QMessageBox.StandardButton.Ok) | |
cancelBtn = mb.button(QtGui.QMessageBox.StandardButton.Cancel) | |
okBtn.setText("Create file") | |
cancelBtn.setText("Do not create file") | |
caption = "In order for "+BASENAME+" objects to be parametric after saving and reloading file\n\ | |
we need to create another file on this computer. File to be created will be: \n\n"+py_file+"\n\n\ | |
This makes it available to the system upon restarting FreeCAD and loading documents containing the \ | |
"+BASENAME+" feature python objects. Create file now?\n\n" | |
mb.setText(caption) | |
ret = mb.exec_() | |
if ret == mb.Ok: | |
writeFile() | |
QtGui.QMessageBox.information(window,"Success","File successfully created. Please note: if you uninstall "+BASENAME+" macro you need to manually remove "+py_file+", too.\n") | |
else: | |
new_lines = [] | |
for line in code.splitlines(): | |
if line.startswith('#'): | |
if "CODE_ENDS_HERE" in line: | |
break | |
if line == "# -*- coding: utf-8 -*-": | |
new_lines.append(line+"\n") | |
continue | |
new_lines.append(line[1:]+"\n") | |
code = "".join(new_lines) | |
#credit to Mila Nautikus for his answer to a question on stackoverflow, which I modified here | |
#in this example the filename is pdwrapper.py | |
#https://stackoverflow.com/questions/5362771/how-to-load-a-module-from-code-in-a-string | |
########## | |
import sys, importlib | |
my_name = 'wirefilter' #filename = wirefilter.py, so this must be 'wirefilter' | |
my_spec = importlib.util.spec_from_loader(my_name, loader=None) | |
wirefilter = importlib.util.module_from_spec(my_spec) | |
exec(code, wirefilter.__dict__) | |
sys.modules['wirefilter'] = wirefilter | |
makeObject(wirefilter) | |
noImport = True | |
if not noImport: #don't never use no double negatives | |
import addonmanager_utilities as utils | |
import wirefilter as WF | |
if WF.__version__ != __version__: | |
writeFile() | |
from PySide import QtCore,QtGui | |
window = QtGui.QApplication.activeWindow() | |
mbox = QtGui.QMessageBox() | |
mbox.setWindowTitle(BASENAME+" updated") | |
mbox.setText(BASENAME+".py has been updated to version "+__version__+". \ | |
You must restart FreeCAD for the new changes to take effect and to use the macro.") | |
mbox.setIcon(mbox.Warning) | |
mbox.setStandardButtons(mbox.Ok | mbox.Cancel) | |
mbox.setDefaultButton(mbox.Cancel) | |
okBtn = mbox.button(QtGui.QMessageBox.StandardButton.Ok) | |
cancelBtn = mbox.button(QtGui.QMessageBox.StandardButton.Cancel) | |
okBtn.setText("Restart now") | |
cancelBtn.setText("Restart later") | |
ret = mbox.exec_() | |
if ret == mbox.Ok: | |
QtCore.QTimer.singleShot(1000, utils.restart_freecad) | |
else: | |
makeObject(WF) | |
# icon for addonmanager auto toolbar installation | |
__xpm__ = """/* XPM */ | |
static char *a57ib_u2pr2[] = { | |
"64 64 158 2 ", | |
" c #AD0E0E", | |
". c #B30C0C", | |
"X c #BC0E0E", | |
"o c #AF1212", | |
"O c #AE1A1A", | |
"+ c #B41212", | |
"@ c #BB1212", | |
"# c #B41A1A", | |
"$ c #B91C1C", | |
"% c #A92B2B", | |
"& c #B32525", | |
"* c #B82323", | |
"= c #B52C2C", | |
"- c #BB2C2C", | |
"; c #B53030", | |
": c #BC3535", | |
"> c #B53E3E", | |
", c #BB3C3C", | |
"< c #C51414", | |
"1 c #CB1616", | |
"2 c #C41B1B", | |
"3 c #CD1A1A", | |
"4 c #D41B1B", | |
"5 c #DA1E1E", | |
"6 c #C52525", | |
"7 c #CB2323", | |
"8 c #C72C2C", | |
"9 c #CB2B2B", | |
"0 c #D12222", | |
"q c #DD2121", | |
"w c #D12A2A", | |
"e c #C03535", | |
"r c #CC3333", | |
"t c #C23D3D", | |
"y c #CB3C3C", | |
"u c #D13333", | |
"i c #D03D3D", | |
"p c #E22424", | |
"a c #E12A2A", | |
"s c #935858", | |
"d c #AA4646", | |
"f c #A04B4B", | |
"g c #AA4D4D", | |
"h c #B54343", | |
"j c #BA4242", | |
"k c #B34E4E", | |
"l c #B94A4A", | |
"z c #AB5252", | |
"x c #A45D5D", | |
"c c #BD5656", | |
"v c #8C7373", | |
"b c #897979", | |
"n c #917272", | |
"m c #9E7B7B", | |
"M c #A76161", | |
"N c #BC6262", | |
"B c #AB7171", | |
"V c #B07878", | |
"C c #C54343", | |
"Z c #CB4343", | |
"A c #C44C4C", | |
"S c #CA4B4B", | |
"D c #D14646", | |
"F c #D04C4C", | |
"G c #C45454", | |
"H c #CC5454", | |
"J c #C65A5A", | |
"K c #CB5C5C", | |
"L c #D05353", | |
"P c #D05B5B", | |
"I c #D95D5D", | |
"U c #C66363", | |
"Y c #CB6464", | |
"T c #CD6B6B", | |
"R c #D16363", | |
"E c #D06A6A", | |
"W c #C37575", | |
"Q c #CE7373", | |
"! c #D27575", | |
"~ c #D27B7B", | |
"^ c #838383", | |
"/ c #898989", | |
"( c #938484", | |
") c #969696", | |
"_ c #9D9D9D", | |
"` c #A58585", | |
"' c #AC8080", | |
"] c #A38C8C", | |
"[ c #AD8C8C", | |
"{ c #B08585", | |
"} c #BF8282", | |
"| c #A09292", | |
" . c #A89292", | |
".. c #A29D9D", | |
"X. c #AB9D9D", | |
"o. c #B59797", | |
"O. c #A5A4A4", | |
"+. c #AAA6A6", | |
"@. c #AAAAAA", | |
"#. c #B6A3A3", | |
"$. c #BBA3A3", | |
"%. c #B1ADAD", | |
"&. c #BFABAB", | |
"*. c #B4B4B4", | |
"=. c #BDB1B1", | |
"-. c #BDBDBD", | |
";. c #CF8E8E", | |
":. c #D58484", | |
">. c #D88787", | |
",. c #D98C8C", | |
"<. c #C59393", | |
"1. c #D59393", | |
"2. c #DA9494", | |
"3. c #DC9C9C", | |
"4. c #C8A7A7", | |
"5. c #D7A7A7", | |
"6. c #DEA3A3", | |
"7. c #D0A9A9", | |
"8. c #DEBCBC", | |
"9. c #E0A6A6", | |
"0. c #E1AAAA", | |
"q. c #E2ACAC", | |
"w. c #E2ADAD", | |
"e. c #E3B0B0", | |
"r. c #E5B4B4", | |
"t. c #E7BBBB", | |
"y. c #E8BFBF", | |
"u. c #C2C2C2", | |
"i. c #CDCDCD", | |
"p. c #DEC3C3", | |
"a. c #D9CCCC", | |
"s. c #DFDBDB", | |
"d. c #EBC4C4", | |
"f. c #ECC9C9", | |
"g. c #EDCBCB", | |
"h. c #EED0D0", | |
"j. c #E0D9D9", | |
"k. c #EADBDB", | |
"l. c #F0D5D5", | |
"z. c #F0D7D7", | |
"x. c #F2DADA", | |
"c. c #F2DCDC", | |
"v. c #F3DDDD", | |
"b. c #E2E1E1", | |
"n. c #E8E0E0", | |
"m. c #EDEDED", | |
"M. c #F4E0E0", | |
"N. c #F5E3E3", | |
"B. c #F7EBEB", | |
"V. c #F8EBEB", | |
"C. c #F9EFEF", | |
"Z. c #F1F1F1", | |
"A. c #FAF3F3", | |
"S. c #FBF4F4", | |
"D. c #FBF6F6", | |
"F. c #FCF6F6", | |
"G. c #FDF9F9", | |
"H. c white", | |
/* pixels */ | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.A.V.M.c.z.h.h.g.g.g.g.h.h.z.c.M.V.A.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.B.z.y.6.>.Q T Y K J J J Y T ~ ,.6.t.z.C.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.S.B.x.d.0.>.T A : * # + . . + # * : A T >.9.g.M.V.F.G.H.H.H.H.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.A.h.0.>.T H S i u w w 0 3 3 3 3 0 w w u y S H T :.q.h.B.H.H.H.H.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.B.x.9.T t # X < 3 5 q p p p q p p p p q 4 3 < @ $ : Q 9.g.S.G.H.H.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.M.e.~ K Z 9 3 1 1 < < < 7 9 9 r r r 9 3 2 < 1 1 3 3 9 Z J >.e.z.G.H.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.M.d.Q & $ 1 4 p q 3 < X + & ; , j j , = O + @ < 3 5 q 5 < @ t T e.F.G.H.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.M.0.T S 6 1 3 3 3 9 Z H E ~ >.2.3.3.3.3.2.>.~ E H y 9 3 1 1 3 8 C ! q.h.G.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.M.g.Q # 2 q 4 4 2 # : T 3.d.M.C.F.F.F.S.S.S.A.c.d.3.Y , $ X 4 q 3 < t Y 0.A.H.H.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.r.~ S 2 1 4 7 6 Z Y >.0.g.M.F.G.H.H.H.H.H.H.H.A.M.g.0.>.Y C 9 3 3 3 9 t ~ d.M.G.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.c.:.= 6 4 4 1 6 : T r.c.V.S.G.H.H.H.H.H.H.H.H.H.H.G.S.C.z.0.~ t X 3 q 3 @ H 2.d.A.H.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.x.r.Y # 3 5 3 2 C Q 6.x.F.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.z.q.Q t 9 4 1 1 y K 3.M.G.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.r.:.S < 3 4 6 - T y.M.S.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.S.c.q.~ C X 1 q 6 & Q h.F.H.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.M.2.G r 3 1 1 y J 3.M.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.z.q.Y $ 7 q 1 + J r.c.S.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.z.Q - 7 q 1 X H 6.g.A.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.x.>., 9 4 3 < S ,.y.C.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.A.d.K + 3 p 3 # Q g.B.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.0.T Z 1 1 1 y T 6.M.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.x.0.H @ 3 q 9 : ,.M.F.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.g.3.H @ 1 4 r A >.c.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.C.d.2.S < 1 4 y G 6.A.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.M.g.E . < q w - Q z.H.", | |
"H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.M.r.:.Z < 1 3 S Q r.S.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.A.M.~ o 1 p 0 # Y h.H.", | |
"H.H.S.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.k.5.Q y 1 1 2 H ;.8.m.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.Z.S.H.H.H.H.H.H.H.S.>.O 3 p 4 . J g.H.", | |
"H.Z.i.*.*.*.*.*.*.*.*.*.*.*.*.*.%. .B , 1 < < k ` X.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.*.i.m.H.H.H.H.H.H.F.,.& 7 p 3 . G d.G.", | |
"H.b.@.^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ n h 4 < @ f ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ O.b.H.H.H.H.H.H.F.2.; 9 p 1 . G y.A.", | |
"H.b.@.^ ) O.O.O.O.O.O.O.O.O.O.O.O.O.[ A 5 4 7 N O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.) ^ O.b.H.H.H.H.H.H.S.1.= 6 p 3 . G d.F.", | |
"G.b.+./ @.u.u.u.u.u.u.u.u.u.u.u.u.u.4.I p p a ! u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.u.@./ O.b.H.H.H.H.H.H.S.,.& 7 p 3 . G d.G.", | |
"S.j.+./ @.u.*.O.O.O.O.O.O.O.O.O.O.O.[ A 5 5 q N O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.*.u.@./ O.b.H.H.H.H.H.G.C.>.O 3 p 4 + J g.H.", | |
"S.j.O./ @.-.O.^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ n > 4 4 0 z ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ _ -.@./ O.b.H.H.H.H.H.F.M.~ < p 0 # U h.H.", | |
"S.j.O./ @.-._ ^ ) @.*.*.*.*.*.*.@.X.' j < 3 0 > s m X.@.%.*.*.*.*.*.*.*.*.*.*.*.) ^ _ -.@./ O.b.H.H.H.H.G.M.d.T . < q w : Q z.H.", | |
"S.j.O./ @.-._ ^ *.m.m.m.m.m.m.m.n.p.1.C X 1 q 6 % W p.k.m.m.m.m.m.m.m.m.m.m.m.m.-.^ _ -.@./ O.b.H.H.H.H.F.g.3.K @ 1 4 r A >.c.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.S.M.r.J # 3 q 3 # Y r.x.F.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.C.0.T Z 1 1 1 y T 6.M.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.F.h.Q - 7 q 3 @ H 2.d.A.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.G.M.>.: 9 5 1 X S ,.d.C.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.M.2.G r 4 1 1 y Y 3.c.S.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.S.x.r.Y # 3 q 3 # K q.x.S.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.C.y.>.S X 1 5 6 - T t.c.F.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.M.6.T y 2 3 5 6 - ~ h.A.G.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.F.c.r.Y # 3 q 3 @ C >.r.x.C.G.H.H.H.H.H.H.H.u.^ _ -.@./ O.s.C.c.r.! t 9 3 3 3 r G 3.B.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.A.z.>.C 8 3 3 3 9 t T 3.d.B.H.H.H.H.H.H.H.u.^ _ -.@./ ..a.g.2.Y C 6 0 5 1 @ H 3.d.A.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.A.e.Q S 2 3 5 3 @ : Y 2.d.c.M.C.A.S.S.A.-.^ _ -.+.( ` 7.3.J - 2 1 4 q 6 & ~ g.V.G.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.G.x.t.! e 6 3 3 3 7 y A T :.3.r.d.z.g.d.#.^ _ -.o.x g c A r 7 3 4 1 2 S ! q.M.F.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.A.6.J t 2 3 q 4 1 @ $ : J ~ 2.0.3.2.{ b | =.} > # + X 1 4 4 4 2 & Q y.c.G.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.G.z.q.~ A r 3 1 1 3 7 9 y S L P H A l d c Y H 9 3 1 1 1 1 2 8 H ~ q.c.C.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.y.:.A # @ 3 5 p q 4 1 < X X X < 1 4 q q 5 5 p q 3 < * , >.h.B.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.c.y.3.! H y 9 3 1 1 < < < < < 1 4 5 q 5 1 1 3 9 Z K ! 2.d.B.S.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.M.g.3.Y , # . X < 1 1 4 4 4 5 q p 4 X + # , T 3.g.C.F.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.F.B.g.0.,.! Y K H F D y r t h G R H : C T ,.q.g.M.G.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.F.C.M.c.g.q.2.>.~ Y A M v ] &.<.U ~ d.V.A.S.G.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.C.x.d.y.e.6.1.[ ^ _ -.$.V <.k.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.G.S.A.C.V.M.M.=.^ _ -.@.( +.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.u.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ -.S.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.G.-.^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ _ -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-._ ^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.-._ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ _ -.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.u.@._ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ @.-.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"S.j.O./ @.u.u.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.u.u.@./ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"G.j.+.^ ) @.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.@.) ^ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"H.b.@.^ ^ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / ^ ^ O.b.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"H.m.u.@.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.O.+.@.@.u.m.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"H.G.m.b.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.j.b.b.m.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.", | |
"H.H.H.H.G.S.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.A.S.G.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H.H." | |
};""" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment