diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 9e9e22fa8d..c85f37a7ec 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -251,79 +251,14 @@ def makeRectangle(length, height, placement=None, face=None, support=None): return obj -def makeDimension(p1,p2,p3=None,p4=None): - """makeDimension(p1,p2,[p3]) or makeDimension(object,i1,i2,p3) - or makeDimension(objlist,indices,p3): Creates a Dimension object with - the dimension line passign through p3.The current line width and color - will be used. There are multiple ways to create a dimension, depending on - the arguments you pass to it: - - (p1,p2,p3): creates a standard dimension from p1 to p2 - - (object,i1,i2,p3): creates a linked dimension to the given object, - measuring the distance between its vertices indexed i1 and i2 - - (object,i1,mode,p3): creates a linked dimension - to the given object, i1 is the index of the (curved) edge to measure, - and mode is either "radius" or "diameter". - """ - if not FreeCAD.ActiveDocument: - FreeCAD.Console.PrintError("No active document. Aborting\n") - return - obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython","Dimension") - _Dimension(obj) - if gui: - _ViewProviderDimension(obj.ViewObject) - if isinstance(p1,Vector) and isinstance(p2,Vector): - obj.Start = p1 - obj.End = p2 - if not p3: - p3 = p2.sub(p1) - p3.multiply(0.5) - p3 = p1.add(p3) - elif isinstance(p2,int) and isinstance(p3,int): - l = [] - idx = (p2,p3) - l.append((p1,"Vertex"+str(p2+1))) - l.append((p1,"Vertex"+str(p3+1))) - obj.LinkedGeometry = l - obj.Support = p1 - p3 = p4 - if not p3: - v1 = obj.Base.Shape.Vertexes[idx[0]].Point - v2 = obj.Base.Shape.Vertexes[idx[1]].Point - p3 = v2.sub(v1) - p3.multiply(0.5) - p3 = v1.add(p3) - elif isinstance(p3,str): - l = [] - l.append((p1,"Edge"+str(p2+1))) - if p3 == "radius": - #l.append((p1,"Center")) - if FreeCAD.GuiUp: - obj.ViewObject.Override = "R $dim" - obj.Diameter = False - elif p3 == "diameter": - #l.append((p1,"Diameter")) - if FreeCAD.GuiUp: - obj.ViewObject.Override = "Ø $dim" - obj.Diameter = True - obj.LinkedGeometry = l - obj.Support = p1 - p3 = p4 - if not p3: - p3 = p1.Shape.Edges[p2].Curve.Center.add(Vector(1,0,0)) - obj.Dimline = p3 - if hasattr(FreeCAD,"DraftWorkingPlane"): - normal = FreeCAD.DraftWorkingPlane.axis - else: - normal = FreeCAD.Vector(0,0,1) - if gui: - # invert the normal if we are viewing it from the back - vnorm = get3DView().getViewDirection() - if vnorm.getAngle(normal) < math.pi/2: - normal = normal.negative() - obj.Normal = normal - if gui: - formatObject(obj) - select(obj) +from draftobjects.dimension import make_dimension +makeDimension = make_dimension + +from draftobjects.dimension import LinearDimension +_Dimension = LinearDimension + +from draftviewproviders.view_dimension import ViewProviderLinearDimension +_ViewProviderDimension = ViewProviderLinearDimension return obj @@ -3368,722 +3303,6 @@ class _ViewProviderDraftLink: else: return obj.ElementList -class _Dimension(_DraftObject): - """The Draft Dimension object""" - def __init__(self, obj): - _DraftObject.__init__(self,obj,"Dimension") - - # Annotation - obj.addProperty("App::PropertyLink","DimensionStyle", - "Annotation", - QT_TRANSLATE_NOOP("App::Property", - "Link dimension style")) - - # Draft - obj.addProperty("App::PropertyVectorDistance","Start", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "Startpoint of dimension")) - - obj.addProperty("App::PropertyVectorDistance","End", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "Endpoint of dimension")) - - obj.addProperty("App::PropertyVector","Normal", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "The normal direction of this dimension")) - - obj.addProperty("App::PropertyVector","Direction", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "The normal direction of this dimension")) - - obj.addProperty("App::PropertyVectorDistance","Dimline", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "Point through which the dimension line passes")) - - obj.addProperty("App::PropertyLink","Support", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "The object measured by this dimension")) - - obj.addProperty("App::PropertyLinkSubList","LinkedGeometry", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "The geometry this dimension is linked to")) - - obj.addProperty("App::PropertyLength","Distance", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "The measurement of this dimension")) - - obj.addProperty("App::PropertyBool","Diameter", - "Draft", - QT_TRANSLATE_NOOP("App::Property", - "For arc/circle measurements, false = radius, true = diameter")) - obj.Start = FreeCAD.Vector(0,0,0) - obj.End = FreeCAD.Vector(1,0,0) - obj.Dimline = FreeCAD.Vector(0,1,0) - obj.Normal = FreeCAD.Vector(0,0,1) - - def onChanged(self,obj,prop): - if hasattr(obj, "Distance"): - obj.setEditorMode('Distance', 1) - #if hasattr(obj,"Normal"): - # obj.setEditorMode('Normal', 2) - if hasattr(obj, "Support"): - obj.setEditorMode('Support', 2) - if prop == "DimensionStyle": - if hasattr(obj, "DimensionStyle"): - from draftutils import gui_utils - gui_utils.format_object(target = obj, origin = obj.DimensionStyle) - - - def execute(self, obj): - import DraftGeomUtils - # set start point and end point according to the linked geometry - if obj.LinkedGeometry: - if len(obj.LinkedGeometry) == 1: - lobj = obj.LinkedGeometry[0][0] - lsub = obj.LinkedGeometry[0][1] - if len(lsub) == 1: - if "Edge" in lsub[0]: - n = int(lsub[0][4:])-1 - edge = lobj.Shape.Edges[n] - if DraftGeomUtils.geomType(edge) == "Line": - obj.Start = edge.Vertexes[0].Point - obj.End = edge.Vertexes[-1].Point - elif DraftGeomUtils.geomType(edge) == "Circle": - c = edge.Curve.Center - r = edge.Curve.Radius - a = edge.Curve.Axis - ray = obj.Dimline.sub(c).projectToPlane(Vector(0,0,0),a) - if (ray.Length == 0): - ray = a.cross(Vector(1,0,0)) - if (ray.Length == 0): - ray = a.cross(Vector(0,1,0)) - ray = DraftVecUtils.scaleTo(ray,r) - if hasattr(obj,"Diameter"): - if obj.Diameter: - obj.Start = c.add(ray.negative()) - obj.End = c.add(ray) - else: - obj.Start = c - obj.End = c.add(ray) - elif len(lsub) == 2: - if ("Vertex" in lsub[0]) and ("Vertex" in lsub[1]): - n1 = int(lsub[0][6:])-1 - n2 = int(lsub[1][6:])-1 - obj.Start = lobj.Shape.Vertexes[n1].Point - obj.End = lobj.Shape.Vertexes[n2].Point - elif len(obj.LinkedGeometry) == 2: - lobj1 = obj.LinkedGeometry[0][0] - lobj2 = obj.LinkedGeometry[1][0] - lsub1 = obj.LinkedGeometry[0][1] - lsub2 = obj.LinkedGeometry[1][1] - if (len(lsub1) == 1) and (len(lsub2) == 1): - if ("Vertex" in lsub1[0]) and ("Vertex" in lsub2[1]): - n1 = int(lsub1[0][6:])-1 - n2 = int(lsub2[0][6:])-1 - obj.Start = lobj1.Shape.Vertexes[n1].Point - obj.End = lobj2.Shape.Vertexes[n2].Point - # set the distance property - total_len = (obj.Start.sub(obj.End)).Length - if round(obj.Distance.Value, precision()) != round(total_len, precision()): - obj.Distance = total_len - if gui: - if obj.ViewObject: - obj.ViewObject.update() - - -class _ViewProviderDimension(_ViewProviderDraft): - """ - A View Provider for the Draft Dimension object - - DIMENSION VIEW PROVIDER: - - | txt | e - ----o--------------------------------o----- - | | - | | d - | | - - a b c b a - - a = DimOvershoot (vobj) - b = Arrows (vobj) - c = Dimline (obj) - d = ExtLines (vobj) - e = ExtOvershoot (vobj) - txt = label (vobj) - - STRUCTURE: - vobj.node.color - .drawstyle - .lineswitch1.coords - .line - .marks - .marksDimOvershoot - .marksExtOvershoot - .label.textpos - .color - .font - .text - - vobj.node3d.color - .drawstyle - .lineswitch3.coords - .line - .marks - .marksDimOvershoot - .marksExtOvershoot - .label3d.textpos - .color - .font3d - .text3d - - """ - def __init__(self, obj): - # annotation properties - obj.addProperty("App::PropertyFloat","ScaleMultiplier", - "Annotation",QT_TRANSLATE_NOOP("App::Property", - "Dimension size overall multiplier")) - - # text properties - obj.addProperty("App::PropertyFont","FontName", - "Text",QT_TRANSLATE_NOOP("App::Property","Font name")) - obj.addProperty("App::PropertyLength","FontSize", - "Text",QT_TRANSLATE_NOOP("App::Property","Font size")) - obj.addProperty("App::PropertyLength","TextSpacing", - "Text",QT_TRANSLATE_NOOP("App::Property", - "The spacing between the text and the dimension line")) - obj.addProperty("App::PropertyBool","FlipText", - "Text",QT_TRANSLATE_NOOP("App::Property", - "Rotate the dimension text 180 degrees")) - obj.addProperty("App::PropertyVectorDistance","TextPosition", - "Text",QT_TRANSLATE_NOOP("App::Property", - "The position of the text. Leave (0,0,0) for automatic position")) - obj.addProperty("App::PropertyString","Override", - "Text",QT_TRANSLATE_NOOP("App::Property", - "Text override. Use $dim to insert the dimension length")) - # units properties - obj.addProperty("App::PropertyInteger","Decimals", - "Units",QT_TRANSLATE_NOOP("App::Property", - "The number of decimals to show")) - obj.addProperty("App::PropertyBool","ShowUnit", - "Units",QT_TRANSLATE_NOOP("App::Property", - "Show the unit suffix")) - obj.addProperty("App::PropertyString","UnitOverride", - "Units",QT_TRANSLATE_NOOP("App::Property", - "A unit to express the measurement. Leave blank for system default")) - # graphics properties - obj.addProperty("App::PropertyFloat","LineWidth", - "Graphics",QT_TRANSLATE_NOOP("App::Property","Line width")) - obj.addProperty("App::PropertyColor","LineColor", - "Graphics",QT_TRANSLATE_NOOP("App::Property","Line color")) - obj.addProperty("App::PropertyLength","ArrowSize", - "Graphics",QT_TRANSLATE_NOOP("App::Property","Arrow size")) - obj.addProperty("App::PropertyEnumeration","ArrowType", - "Graphics",QT_TRANSLATE_NOOP("App::Property","Arrow type")) - obj.addProperty("App::PropertyBool","FlipArrows", - "Graphics",QT_TRANSLATE_NOOP("App::Property", - "Rotate the dimension arrows 180 degrees")) - obj.addProperty("App::PropertyDistance","DimOvershoot", - "Graphics",QT_TRANSLATE_NOOP("App::Property", - "The distance the dimension line is extended past the extension lines")) - obj.addProperty("App::PropertyDistance","ExtLines", - "Graphics",QT_TRANSLATE_NOOP("App::Property", - "Length of the extension lines")) - obj.addProperty("App::PropertyDistance","ExtOvershoot", - "Graphics",QT_TRANSLATE_NOOP("App::Property", - "Length of the extension line above the dimension line")) - obj.addProperty("App::PropertyBool","ShowLine", - "Graphics",QT_TRANSLATE_NOOP("App::Property", - "Shows the dimension line and arrows")) - - param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") - annotation_scale = param.GetFloat("DraftAnnotationScale", 1.0) - obj.ScaleMultiplier = 1 / annotation_scale - obj.FontSize = getParam("textheight",0.20) - obj.TextSpacing = getParam("dimspacing",0.05) - obj.FontName = getParam("textfont","") - obj.ArrowSize = getParam("arrowsize",0.1) - obj.ArrowType = arrowtypes - obj.ArrowType = arrowtypes[getParam("dimsymbol",0)] - obj.ExtLines = getParam("extlines",0.3) - obj.DimOvershoot = getParam("dimovershoot",0) - obj.ExtOvershoot = getParam("extovershoot",0) - obj.Decimals = getParam("dimPrecision",2) - obj.ShowUnit = getParam("showUnit",True) - obj.ShowLine = True - _ViewProviderDraft.__init__(self,obj) - - def attach(self, vobj): - """called on object creation""" - from pivy import coin - self.Object = vobj.Object - self.color = coin.SoBaseColor() - self.font = coin.SoFont() - self.font3d = coin.SoFont() - self.text = coin.SoAsciiText() - self.text3d = coin.SoText2() - self.text.string = "d" # some versions of coin crash if string is not set - self.text3d.string = "d" - self.textpos = coin.SoTransform() - self.text.justification = self.text3d.justification = coin.SoAsciiText.CENTER - label = coin.SoSeparator() - label.addChild(self.textpos) - label.addChild(self.color) - label.addChild(self.font) - label.addChild(self.text) - label3d = coin.SoSeparator() - label3d.addChild(self.textpos) - label3d.addChild(self.color) - label3d.addChild(self.font3d) - label3d.addChild(self.text3d) - self.coord1 = coin.SoCoordinate3() - self.trans1 = coin.SoTransform() - self.coord2 = coin.SoCoordinate3() - self.trans2 = coin.SoTransform() - self.transDimOvershoot1 = coin.SoTransform() - self.transDimOvershoot2 = coin.SoTransform() - self.transExtOvershoot1 = coin.SoTransform() - self.transExtOvershoot2 = coin.SoTransform() - self.marks = coin.SoSeparator() - self.marksDimOvershoot = coin.SoSeparator() - self.marksExtOvershoot = coin.SoSeparator() - self.drawstyle = coin.SoDrawStyle() - self.line = coin.SoType.fromName("SoBrepEdgeSet").createInstance() - self.coords = coin.SoCoordinate3() - self.node = coin.SoGroup() - self.node.addChild(self.color) - self.node.addChild(self.drawstyle) - self.lineswitch2 = coin.SoSwitch() - self.lineswitch2.whichChild = -3 - self.node.addChild(self.lineswitch2) - self.lineswitch2.addChild(self.coords) - self.lineswitch2.addChild(self.line) - self.lineswitch2.addChild(self.marks) - self.lineswitch2.addChild(self.marksDimOvershoot) - self.lineswitch2.addChild(self.marksExtOvershoot) - self.node.addChild(label) - self.node3d = coin.SoGroup() - self.node3d.addChild(self.color) - self.node3d.addChild(self.drawstyle) - self.lineswitch3 = coin.SoSwitch() - self.lineswitch3.whichChild = -3 - self.node3d.addChild(self.lineswitch3) - self.lineswitch3.addChild(self.coords) - self.lineswitch3.addChild(self.line) - self.lineswitch3.addChild(self.marks) - self.lineswitch3.addChild(self.marksDimOvershoot) - self.lineswitch3.addChild(self.marksExtOvershoot) - self.node3d.addChild(label3d) - vobj.addDisplayMode(self.node,"2D") - vobj.addDisplayMode(self.node3d,"3D") - self.updateData(vobj.Object,"Start") - self.onChanged(vobj,"FontSize") - self.onChanged(vobj,"FontName") - self.onChanged(vobj,"ArrowType") - self.onChanged(vobj,"LineColor") - self.onChanged(vobj,"DimOvershoot") - self.onChanged(vobj,"ExtOvershoot") - - def updateData(self, obj, prop): - """called when the base object is changed""" - import DraftGui - if prop in ["Start","End","Dimline","Direction"]: - - if obj.Start == obj.End: - return - - if not hasattr(self,"node"): - return - - import Part, DraftGeomUtils - from pivy import coin - - # calculate the 4 points - self.p1 = obj.Start - self.p4 = obj.End - base = None - if hasattr(obj,"Direction"): - if not DraftVecUtils.isNull(obj.Direction): - v2 = self.p1.sub(obj.Dimline) - v3 = self.p4.sub(obj.Dimline) - v2 = DraftVecUtils.project(v2,obj.Direction) - v3 = DraftVecUtils.project(v3,obj.Direction) - self.p2 = obj.Dimline.add(v2) - self.p3 = obj.Dimline.add(v3) - if DraftVecUtils.equals(self.p2,self.p3): - base = None - proj = None - else: - base = Part.LineSegment(self.p2,self.p3).toShape() - proj = DraftGeomUtils.findDistance(self.p1,base) - if proj: - proj = proj.negative() - if not base: - if DraftVecUtils.equals(self.p1,self.p4): - base = None - proj = None - else: - base = Part.LineSegment(self.p1,self.p4).toShape() - proj = DraftGeomUtils.findDistance(obj.Dimline,base) - if proj: - self.p2 = self.p1.add(proj.negative()) - self.p3 = self.p4.add(proj.negative()) - else: - self.p2 = self.p1 - self.p3 = self.p4 - if proj: - if hasattr(obj.ViewObject,"ExtLines") and hasattr(obj.ViewObject,"ScaleMultiplier"): - dmax = obj.ViewObject.ExtLines.Value * obj.ViewObject.ScaleMultiplier - if dmax and (proj.Length > dmax): - if (dmax > 0): - self.p1 = self.p2.add(DraftVecUtils.scaleTo(proj,dmax)) - self.p4 = self.p3.add(DraftVecUtils.scaleTo(proj,dmax)) - else: - rest = proj.Length + dmax - self.p1 = self.p2.add(DraftVecUtils.scaleTo(proj,rest)) - self.p4 = self.p3.add(DraftVecUtils.scaleTo(proj,rest)) - else: - proj = (self.p3.sub(self.p2)).cross(Vector(0,0,1)) - - # calculate the arrows positions - self.trans1.translation.setValue((self.p2.x,self.p2.y,self.p2.z)) - self.coord1.point.setValue((self.p2.x,self.p2.y,self.p2.z)) - self.trans2.translation.setValue((self.p3.x,self.p3.y,self.p3.z)) - self.coord2.point.setValue((self.p3.x,self.p3.y,self.p3.z)) - - # calculate dimension and extension lines overshoots positions - self.transDimOvershoot1.translation.setValue((self.p2.x,self.p2.y,self.p2.z)) - self.transDimOvershoot2.translation.setValue((self.p3.x,self.p3.y,self.p3.z)) - self.transExtOvershoot1.translation.setValue((self.p2.x,self.p2.y,self.p2.z)) - self.transExtOvershoot2.translation.setValue((self.p3.x,self.p3.y,self.p3.z)) - - # calculate the text position and orientation - if hasattr(obj,"Normal"): - if DraftVecUtils.isNull(obj.Normal): - if proj: - norm = (self.p3.sub(self.p2).cross(proj)).negative() - else: - norm = Vector(0,0,1) - else: - norm = FreeCAD.Vector(obj.Normal) - else: - if proj: - norm = (self.p3.sub(self.p2).cross(proj)).negative() - else: - norm = Vector(0,0,1) - if not DraftVecUtils.isNull(norm): - norm.normalize() - u = self.p3.sub(self.p2) - u.normalize() - v1 = norm.cross(u) - rot1 = FreeCAD.Placement(DraftVecUtils.getPlaneRotation(u,v1,norm)).Rotation.Q - self.transDimOvershoot1.rotation.setValue((rot1[0],rot1[1],rot1[2],rot1[3])) - self.transDimOvershoot2.rotation.setValue((rot1[0],rot1[1],rot1[2],rot1[3])) - if hasattr(obj.ViewObject,"FlipArrows"): - if obj.ViewObject.FlipArrows: - u = u.negative() - v2 = norm.cross(u) - rot2 = FreeCAD.Placement(DraftVecUtils.getPlaneRotation(u,v2,norm)).Rotation.Q - self.trans1.rotation.setValue((rot2[0],rot2[1],rot2[2],rot2[3])) - self.trans2.rotation.setValue((rot2[0],rot2[1],rot2[2],rot2[3])) - if self.p1 != self.p2: - u3 = self.p1.sub(self.p2) - u3.normalize() - v3 = norm.cross(u3) - rot3 = FreeCAD.Placement(DraftVecUtils.getPlaneRotation(u3,v3,norm)).Rotation.Q - self.transExtOvershoot1.rotation.setValue((rot3[0],rot3[1],rot3[2],rot3[3])) - self.transExtOvershoot2.rotation.setValue((rot3[0],rot3[1],rot3[2],rot3[3])) - if hasattr(obj.ViewObject,"TextSpacing") and hasattr(obj.ViewObject,"ScaleMultiplier"): - ts = obj.ViewObject.TextSpacing.Value * obj.ViewObject.ScaleMultiplier - offset = DraftVecUtils.scaleTo(v1,ts) - else: - offset = DraftVecUtils.scaleTo(v1,0.05) - rott = rot1 - if hasattr(obj.ViewObject,"FlipText"): - if obj.ViewObject.FlipText: - rott = FreeCAD.Rotation(*rott).multiply(FreeCAD.Rotation(norm,180)).Q - offset = offset.negative() - # setting text - try: - m = obj.ViewObject.DisplayMode - except: # swallow all exceptions here since it always fails on first run (Displaymode enum no set yet) - m = ["2D","3D"][getParam("dimstyle",0)] - if m == "3D": - offset = offset.negative() - self.tbase = (self.p2.add((self.p3.sub(self.p2).multiply(0.5)))).add(offset) - if hasattr(obj.ViewObject,"TextPosition"): - if not DraftVecUtils.isNull(obj.ViewObject.TextPosition): - self.tbase = obj.ViewObject.TextPosition - self.textpos.translation.setValue([self.tbase.x,self.tbase.y,self.tbase.z]) - self.textpos.rotation = coin.SbRotation(rott[0],rott[1],rott[2],rott[3]) - su = True - if hasattr(obj.ViewObject,"ShowUnit"): - su = obj.ViewObject.ShowUnit - # set text value - l = self.p3.sub(self.p2).Length - unit = None - if hasattr(obj.ViewObject,"UnitOverride"): - unit = obj.ViewObject.UnitOverride - # special representation if "Building US" scheme - if FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("UserSchema",0) == 5: - s = FreeCAD.Units.Quantity(l,FreeCAD.Units.Length).UserString - self.string = s.replace("' ","'- ") - self.string = s.replace("+"," ") - elif hasattr(obj.ViewObject,"Decimals"): - self.string = DraftGui.displayExternal(l,obj.ViewObject.Decimals,'Length',su,unit) - else: - self.string = DraftGui.displayExternal(l,None,'Length',su,unit) - if hasattr(obj.ViewObject,"Override"): - if obj.ViewObject.Override: - self.string = obj.ViewObject.Override.replace("$dim",\ - self.string) - self.text.string = self.text3d.string = stringencodecoin(self.string) - - # set the lines - if m == "3D": - # calculate the spacing of the text - textsize = (len(self.string)*obj.ViewObject.FontSize.Value)/4.0 - spacing = ((self.p3.sub(self.p2)).Length/2.0) - textsize - self.p2a = self.p2.add(DraftVecUtils.scaleTo(self.p3.sub(self.p2),spacing)) - self.p2b = self.p3.add(DraftVecUtils.scaleTo(self.p2.sub(self.p3),spacing)) - self.coords.point.setValues([[self.p1.x,self.p1.y,self.p1.z], - [self.p2.x,self.p2.y,self.p2.z], - [self.p2a.x,self.p2a.y,self.p2a.z], - [self.p2b.x,self.p2b.y,self.p2b.z], - [self.p3.x,self.p3.y,self.p3.z], - [self.p4.x,self.p4.y,self.p4.z]]) - #self.line.numVertices.setValues([3,3]) - self.line.coordIndex.setValues(0,7,(0,1,2,-1,3,4,5)) - else: - self.coords.point.setValues([[self.p1.x,self.p1.y,self.p1.z], - [self.p2.x,self.p2.y,self.p2.z], - [self.p3.x,self.p3.y,self.p3.z], - [self.p4.x,self.p4.y,self.p4.z]]) - #self.line.numVertices.setValue(4) - self.line.coordIndex.setValues(0,4,(0,1,2,3)) - - def onChanged(self, vobj, prop): - """called when a view property has changed""" - if prop == "ScaleMultiplier" and hasattr(vobj,"ScaleMultiplier"): - # update all dimension values - if hasattr(self,"font"): - self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier - if hasattr(self,"font3d"): - self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier - if hasattr(self,"node") and hasattr(self,"p2") and hasattr(vobj,"ArrowSize"): - self.remove_dim_arrows() - self.draw_dim_arrows(vobj) - if hasattr(vobj,"DimOvershoot"): - self.remove_dim_overshoot() - self.draw_dim_overshoot(vobj) - if hasattr(vobj,"ExtOvershoot"): - self.remove_ext_overshoot() - self.draw_ext_overshoot(vobj) - self.updateData(vobj.Object,"Start") - vobj.Object.touch() - - elif (prop == "FontSize") and hasattr(vobj,"FontSize"): - if hasattr(self,"font") and hasattr(vobj,"ScaleMultiplier"): - self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier - if hasattr(self,"font3d") and hasattr(vobj,"ScaleMultiplier"): - self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier - vobj.Object.touch() - - elif (prop == "FontName") and hasattr(vobj,"FontName"): - if hasattr(self,"font") and hasattr(self,"font3d"): - self.font.name = self.font3d.name = str(vobj.FontName) - vobj.Object.touch() - - elif (prop == "LineColor") and hasattr(vobj,"LineColor"): - if hasattr(self,"color"): - c = vobj.LineColor - self.color.rgb.setValue(c[0],c[1],c[2]) - - elif (prop == "LineWidth") and hasattr(vobj,"LineWidth"): - if hasattr(self,"drawstyle"): - self.drawstyle.lineWidth = vobj.LineWidth - - elif (prop in ["ArrowSize","ArrowType"]) and hasattr(vobj,"ArrowSize"): - if hasattr(self,"node") and hasattr(self,"p2"): - if hasattr(vobj,"ScaleMultiplier"): - self.remove_dim_arrows() - self.draw_dim_arrows(vobj) - vobj.Object.touch() - - elif (prop == "DimOvershoot") and hasattr(vobj,"DimOvershoot"): - if hasattr(vobj,"ScaleMultiplier"): - self.remove_dim_overshoot() - self.draw_dim_overshoot(vobj) - vobj.Object.touch() - - elif (prop == "ExtOvershoot") and hasattr(vobj,"ExtOvershoot"): - if hasattr(vobj,"ScaleMultiplier"): - self.remove_ext_overshoot() - self.draw_ext_overshoot(vobj) - vobj.Object.touch() - - elif (prop == "ShowLine") and hasattr(vobj,"ShowLine"): - if vobj.ShowLine: - self.lineswitch2.whichChild = -3 - self.lineswitch3.whichChild = -3 - else: - self.lineswitch2.whichChild = -1 - self.lineswitch3.whichChild = -1 - else: - self.updateData(vobj.Object,"Start") - - def remove_dim_arrows(self): - # remove existing nodes - self.node.removeChild(self.marks) - self.node3d.removeChild(self.marks) - - def draw_dim_arrows(self, vobj): - from pivy import coin - - if not hasattr(vobj,"ArrowType"): - return - - if self.p3.x < self.p2.x: - inv = False - else: - inv = True - - # set scale - symbol = arrowtypes.index(vobj.ArrowType) - s = vobj.ArrowSize.Value * vobj.ScaleMultiplier - self.trans1.scaleFactor.setValue((s,s,s)) - self.trans2.scaleFactor.setValue((s,s,s)) - - - # set new nodes - self.marks = coin.SoSeparator() - self.marks.addChild(self.color) - s1 = coin.SoSeparator() - if symbol == "Circle": - s1.addChild(self.coord1) - else: - s1.addChild(self.trans1) - s1.addChild(dimSymbol(symbol,invert=not(inv))) - self.marks.addChild(s1) - s2 = coin.SoSeparator() - if symbol == "Circle": - s2.addChild(self.coord2) - else: - s2.addChild(self.trans2) - s2.addChild(dimSymbol(symbol,invert=inv)) - self.marks.addChild(s2) - self.node.insertChild(self.marks,2) - self.node3d.insertChild(self.marks,2) - - def remove_dim_overshoot(self): - self.node.removeChild(self.marksDimOvershoot) - self.node3d.removeChild(self.marksDimOvershoot) - - - def draw_dim_overshoot(self, vobj): - from pivy import coin - - # set scale - s = vobj.DimOvershoot.Value * vobj.ScaleMultiplier - self.transDimOvershoot1.scaleFactor.setValue((s,s,s)) - self.transDimOvershoot2.scaleFactor.setValue((s,s,s)) - - # remove existing nodes - - # set new nodes - self.marksDimOvershoot = coin.SoSeparator() - if vobj.DimOvershoot.Value: - self.marksDimOvershoot.addChild(self.color) - s1 = coin.SoSeparator() - s1.addChild(self.transDimOvershoot1) - s1.addChild(dimDash((-1,0,0),(0,0,0))) - self.marksDimOvershoot.addChild(s1) - s2 = coin.SoSeparator() - s2.addChild(self.transDimOvershoot2) - s2.addChild(dimDash((0,0,0),(1,0,0))) - self.marksDimOvershoot.addChild(s2) - self.node.insertChild(self.marksDimOvershoot,2) - self.node3d.insertChild(self.marksDimOvershoot,2) - - - def remove_ext_overshoot(self): - self.node.removeChild(self.marksExtOvershoot) - self.node3d.removeChild(self.marksExtOvershoot) - - - def draw_ext_overshoot(self, vobj): - from pivy import coin - - # set scale - s = vobj.ExtOvershoot.Value * vobj.ScaleMultiplier - self.transExtOvershoot1.scaleFactor.setValue((s,s,s)) - self.transExtOvershoot2.scaleFactor.setValue((s,s,s)) - - # set new nodes - self.marksExtOvershoot = coin.SoSeparator() - if vobj.ExtOvershoot.Value: - self.marksExtOvershoot.addChild(self.color) - s1 = coin.SoSeparator() - s1.addChild(self.transExtOvershoot1) - s1.addChild(dimDash((0,0,0),(-1,0,0))) - self.marksExtOvershoot.addChild(s1) - s2 = coin.SoSeparator() - s2.addChild(self.transExtOvershoot2) - s2.addChild(dimDash((0,0,0),(-1,0,0))) - self.marksExtOvershoot.addChild(s2) - self.node.insertChild(self.marksExtOvershoot,2) - self.node3d.insertChild(self.marksExtOvershoot,2) - - - def doubleClicked(self,vobj): - self.setEdit(vobj) - - def getDisplayModes(self,vobj): - return ["2D","3D"] - - def getDefaultDisplayMode(self): - if hasattr(self,"defaultmode"): - return self.defaultmode - else: - return ["2D","3D"][getParam("dimstyle",0)] - - def setDisplayMode(self,mode): - return mode - - def is_linked_to_circle(self): - import DraftGeomUtils - _obj = self.Object - if _obj.LinkedGeometry and len(_obj.LinkedGeometry) == 1: - lobj = _obj.LinkedGeometry[0][0] - lsub = _obj.LinkedGeometry[0][1] - if len(lsub) == 1 and "Edge" in lsub[0]: - n = int(lsub[0][4:]) - 1 - edge = lobj.Shape.Edges[n] - if DraftGeomUtils.geomType(edge) == "Circle": - return True - return False - - def getIcon(self): - if self.is_linked_to_circle(): - return ":/icons/Draft_DimensionRadius.svg" - return ":/icons/Draft_Dimension_Tree.svg" - - def __getstate__(self): - return self.Object.ViewObject.DisplayMode - - def __setstate__(self,state): - if state: - self.defaultmode = state - self.setDisplayMode(state) - class _AngularDimension(_DraftObject): """The Draft AngularDimension object""" def __init__(self, obj): diff --git a/src/Mod/Draft/draftguitools/gui_base.py b/src/Mod/Draft/draftguitools/gui_base.py index 1a149cc8cc..40b0660ccc 100644 --- a/src/Mod/Draft/draftguitools/gui_base.py +++ b/src/Mod/Draft/draftguitools/gui_base.py @@ -32,6 +32,99 @@ import FreeCADGui as Gui import draftutils.todo as todo +class GuiCommandSimplest: + """Simplest base class for GuiCommands. + + This class only sets up the command name and the document object + to use for the command. + When it is executed, it logs the command name to the log file, + and prints the command name to the console. + + It implements the `IsActive` method, which must return `True` + when the command should be available. + It should return `True` when there is an active document, + otherwise the command (button or menu) should be disabled. + + This class is meant to be inherited by other GuiCommand classes + to quickly log the command name, and set the correct document object. + + Parameter + --------- + name: str, optional + It defaults to `'None'`. + The name of the action that is being run, + for example, `'Heal'`, `'Flip dimensions'`, + `'Line'`, `'Circle'`, etc. + + doc: App::Document, optional + It defaults to the value of `App.activeDocument()`. + The document object itself, which indicates where the actions + of the command will be executed. + + Attributes + ---------- + command_name: str + This is the command name, which is assigned by `name`. + + doc: App::Document + This is the document object itself, which is assigned by `doc`. + + This attribute should be used by functions to make sure + that the operations are performed in the correct document + and not in other documents. + To set the active document we can use + + >>> App.setActiveDocument(self.doc.Name) + """ + + def __init__(self, name="None", doc=App.activeDocument()): + self.command_name = name + self.doc = doc + + def IsActive(self): + """Return True when this command should be available. + + It is `True` when there is a document. + """ + if App.activeDocument(): + return True + else: + return False + + def Activated(self): + """Execute when the command is called. + + Log the command name to the log file and console. + Also update the `doc` attribute. + """ + self.doc = App.activeDocument() + _log("Document: {}".format(self.doc.Label)) + _log("GuiCommand: {}".format(self.command_name)) + _msg("{}".format(16*"-")) + _msg("GuiCommand: {}".format(self.command_name)) + + +class GuiCommandNeedsSelection(GuiCommandSimplest): + """Base class for GuiCommands that need a selection to be available. + + It re-implements the `IsActive` method to return `True` + when there is both an active document and an active selection. + + It inherits `GuiCommandSimplest` to set up the document + and other behavior. See this class for more information. + """ + + def IsActive(self): + """Return True when this command should be available. + + It is `True` when there is a selection. + """ + if App.activeDocument() and Gui.Selection.getSelection(): + return True + else: + return False + + class GuiCommandBase: """Generic class that is the basis of all Gui commands. diff --git a/src/Mod/Draft/draftguitools/gui_style_dimension.py b/src/Mod/Draft/draftguitools/gui_dimensionstyle.py similarity index 70% rename from src/Mod/Draft/draftguitools/gui_style_dimension.py rename to src/Mod/Draft/draftguitools/gui_dimensionstyle.py index fcba6fa0f1..ff3fec839f 100644 --- a/src/Mod/Draft/draftguitools/gui_style_dimension.py +++ b/src/Mod/Draft/draftguitools/gui_dimensionstyle.py @@ -32,18 +32,78 @@ import FreeCADGui as Gui from PySide import QtCore from . import gui_base from draftutils import utils -from draftobjects.style_dimension import make_dimension_style +from draftobjects.dimensionstyle import make_dimension_style -class GuiCommandDimensionStyle(gui_base.GuiCommandBase): + +''' +class AnnotationStylesContainer: + """The Layer Container""" + + def __init__(self, obj): + + self.Type = "AnnotationContainer" + obj.Proxy = self + + def execute(self, obj): + + g = obj.Group + g.sort(key=lambda o: o.Label) + obj.Group = g + + def __getstate__(self): + + if hasattr(self, "Type"): + return self.Type + + def __setstate__(self, state): + + if state: + self.Type = state + + +class ViewProviderAnnotationStylesContainer: + """A View Provider for the Layer Container""" + + def __init__(self, vobj): + + vobj.Proxy = self + + def getIcon(self): + + return ":/icons/Draft_Annotation_Style.svg" + + def attach(self, vobj): + + self.Object = vobj.Object + + def __getstate__(self): + + return None + + def __setstate__(self, state): + + return None + + +''' + + + +# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + +class GuiCommandDimensionStyle(gui_base.GuiCommandSimplest): """ The command creates a dimension style object """ + def __init__(self): + super().__init__() + self.command_name = "DimensionStyle" def GetResources(self): _msg = ("Creates a new dimension style.\n" "The object stores dimension preferences into the document." ) - return {'Pixmap' : 'Draft_AutoGroup', + return {'Pixmap' : 'Draft_Annotation_Style', 'MenuText': QtCore.QT_TRANSLATE_NOOP("Draft", "Dimension Style"), 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Draft", _msg)} @@ -59,10 +119,8 @@ class GuiCommandDimensionStyle(gui_base.GuiCommandBase): if len(sel) == 1: if utils.get_type(sel[0]) == 'Dimension': make_dimension_style(sel[0]) - return self.finish() make_dimension_style() - return self.finish() Gui.addCommand('Draft_DimensionStyle', GuiCommandDimensionStyle()) diff --git a/src/Mod/Draft/draftobjects/dimension.py b/src/Mod/Draft/draftobjects/dimension.py new file mode 100644 index 0000000000..e4e194aea4 --- /dev/null +++ b/src/Mod/Draft/draftobjects/dimension.py @@ -0,0 +1,235 @@ +# *************************************************************************** +# * (c) 2020 Carlo Pavan * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +"""This module provides the object code for Draft DimensionStyle. +""" +## @package style_dimension +# \ingroup DRAFT +# \brief This module provides the object code for Draft DimensionStyle. + +import FreeCAD as App +import math +from PySide.QtCore import QT_TRANSLATE_NOOP +import draftutils.gui_utils as gui_utils +import draftutils.utils as utils +from draftobjects.draft_annotation import DimensionBase +from draftviewproviders.view_dimension import ViewProviderLinearDimension + +def make_dimension(p1,p2,p3=None,p4=None): + """makeDimension(p1,p2,[p3]) or makeDimension(object,i1,i2,p3) + or makeDimension(objlist,indices,p3): Creates a Dimension object with + the dimension line passign through p3.The current line width and color + will be used. There are multiple ways to create a dimension, depending on + the arguments you pass to it: + - (p1,p2,p3): creates a standard dimension from p1 to p2 + - (object,i1,i2,p3): creates a linked dimension to the given object, + measuring the distance between its vertices indexed i1 and i2 + - (object,i1,mode,p3): creates a linked dimension + to the given object, i1 is the index of the (curved) edge to measure, + and mode is either "radius" or "diameter". + """ + if not App.ActiveDocument: + App.Console.PrintError("No active document. Aborting\n") + return + obj = App.ActiveDocument.addObject("App::FeaturePython","Dimension") + LinearDimension(obj) + if App.GuiUp: + ViewProviderLinearDimension(obj.ViewObject) + if isinstance(p1,App.Vector) and isinstance(p2,App.Vector): + obj.Start = p1 + obj.End = p2 + if not p3: + p3 = p2.sub(p1) + p3.multiply(0.5) + p3 = p1.add(p3) + elif isinstance(p2,int) and isinstance(p3,int): + l = [] + idx = (p2,p3) + l.append((p1,"Vertex"+str(p2+1))) + l.append((p1,"Vertex"+str(p3+1))) + obj.LinkedGeometry = l + obj.Support = p1 + p3 = p4 + if not p3: + v1 = obj.Base.Shape.Vertexes[idx[0]].Point + v2 = obj.Base.Shape.Vertexes[idx[1]].Point + p3 = v2.sub(v1) + p3.multiply(0.5) + p3 = v1.add(p3) + elif isinstance(p3,str): + l = [] + l.append((p1,"Edge"+str(p2+1))) + if p3 == "radius": + #l.append((p1,"Center")) + if App.GuiUp: + obj.ViewObject.Override = "R $dim" + obj.Diameter = False + elif p3 == "diameter": + #l.append((p1,"Diameter")) + if App.GuiUp: + obj.ViewObject.Override = "Ø $dim" + obj.Diameter = True + obj.LinkedGeometry = l + obj.Support = p1 + p3 = p4 + if not p3: + p3 = p1.Shape.Edges[p2].Curve.Center.add(App.Vector(1,0,0)) + obj.Dimline = p3 + if hasattr(App,"DraftWorkingPlane"): + normal = App.DraftWorkingPlane.axis + else: + normal = App.Vector(0,0,1) + if App.GuiUp: + # invert the normal if we are viewing it from the back + vnorm = gui_utils.get3DView().getViewDirection() + if vnorm.getAngle(normal) < math.pi/2: + normal = normal.negative() + obj.Normal = normal + if App.GuiUp: + gui_utils.format_object(obj) + gui_utils.select(obj) + + return obj + +class LinearDimension(DimensionBase): + """The Draft Dimension object""" + def __init__(self, obj): + DimensionBase.__init__(self,obj,"Dimension") + + # Draft + obj.addProperty("App::PropertyVectorDistance","Start", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "Startpoint of dimension")) + + obj.addProperty("App::PropertyVectorDistance","End", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "Endpoint of dimension")) + + obj.addProperty("App::PropertyVector","Normal", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "The normal direction of this dimension")) + + obj.addProperty("App::PropertyVector","Direction", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "The normal direction of this dimension")) + + obj.addProperty("App::PropertyVectorDistance","Dimline", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "Point through which the dimension line passes")) + + obj.addProperty("App::PropertyLink","Support", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "The object measured by this dimension")) + + obj.addProperty("App::PropertyLinkSubList","LinkedGeometry", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "The geometry this dimension is linked to")) + + obj.addProperty("App::PropertyLength","Distance", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "The measurement of this dimension")) + + obj.addProperty("App::PropertyBool","Diameter", + "Draft", + QT_TRANSLATE_NOOP("App::Property", + "For arc/circle measurements, false = radius, true = diameter")) + obj.Start = App.Vector(0,0,0) + obj.End = App.Vector(1,0,0) + obj.Dimline = App.Vector(0,1,0) + obj.Normal = App.Vector(0,0,1) + + def onChanged(self,obj,prop): + if hasattr(obj, "Distance"): + obj.setEditorMode('Distance', 1) + #if hasattr(obj,"Normal"): + # obj.setEditorMode('Normal', 2) + if hasattr(obj, "Support"): + obj.setEditorMode('Support', 2) + if prop == "DimensionStyle": + if hasattr(obj, "DimensionStyle"): + gui_utils.format_object(target = obj, origin = obj.DimensionStyle) + + + def execute(self, obj): + import DraftGeomUtils + # set start point and end point according to the linked geometry + if obj.LinkedGeometry: + if len(obj.LinkedGeometry) == 1: + lobj = obj.LinkedGeometry[0][0] + lsub = obj.LinkedGeometry[0][1] + if len(lsub) == 1: + if "Edge" in lsub[0]: + n = int(lsub[0][4:])-1 + edge = lobj.Shape.Edges[n] + if DraftGeomUtils.geomType(edge) == "Line": + obj.Start = edge.Vertexes[0].Point + obj.End = edge.Vertexes[-1].Point + elif DraftGeomUtils.geomType(edge) == "Circle": + c = edge.Curve.Center + r = edge.Curve.Radius + a = edge.Curve.Axis + ray = obj.Dimline.sub(c).projectToPlane(App.Vector(0,0,0),a) + if (ray.Length == 0): + ray = a.cross(App.Vector(1,0,0)) + if (ray.Length == 0): + ray = a.cross(App.Vector(0,1,0)) + ray = DraftVecUtils.scaleTo(ray,r) + if hasattr(obj,"Diameter"): + if obj.Diameter: + obj.Start = c.add(ray.negative()) + obj.End = c.add(ray) + else: + obj.Start = c + obj.End = c.add(ray) + elif len(lsub) == 2: + if ("Vertex" in lsub[0]) and ("Vertex" in lsub[1]): + n1 = int(lsub[0][6:])-1 + n2 = int(lsub[1][6:])-1 + obj.Start = lobj.Shape.Vertexes[n1].Point + obj.End = lobj.Shape.Vertexes[n2].Point + elif len(obj.LinkedGeometry) == 2: + lobj1 = obj.LinkedGeometry[0][0] + lobj2 = obj.LinkedGeometry[1][0] + lsub1 = obj.LinkedGeometry[0][1] + lsub2 = obj.LinkedGeometry[1][1] + if (len(lsub1) == 1) and (len(lsub2) == 1): + if ("Vertex" in lsub1[0]) and ("Vertex" in lsub2[1]): + n1 = int(lsub1[0][6:])-1 + n2 = int(lsub2[0][6:])-1 + obj.Start = lobj1.Shape.Vertexes[n1].Point + obj.End = lobj2.Shape.Vertexes[n2].Point + # set the distance property + total_len = (obj.Start.sub(obj.End)).Length + if round(obj.Distance.Value, utils.precision()) != round(total_len, utils.precision()): + obj.Distance = total_len + if App.GuiUp: + if obj.ViewObject: + obj.ViewObject.update() \ No newline at end of file diff --git a/src/Mod/Draft/draftobjects/style_dimension.py b/src/Mod/Draft/draftobjects/dimensionstyle.py similarity index 90% rename from src/Mod/Draft/draftobjects/style_dimension.py rename to src/Mod/Draft/draftobjects/dimensionstyle.py index c4f75d6778..8f86251725 100644 --- a/src/Mod/Draft/draftobjects/style_dimension.py +++ b/src/Mod/Draft/draftobjects/dimensionstyle.py @@ -28,13 +28,12 @@ # \brief This module provides the object code for Draft DimensionStyle. import FreeCAD as App -import Draft -from Draft import _DraftObject +from draftobjects.draft_annotation import DraftAnnotation from PySide.QtCore import QT_TRANSLATE_NOOP if App.GuiUp: import FreeCADGui as Gui - from draftviewproviders.view_style_dimension import ViewProviderDraftDimensionStyle + from draftviewproviders.view_dimensionstyle import ViewProviderDraftDimensionStyle def make_dimension_style(existing_dimension = None): """ @@ -49,6 +48,6 @@ def make_dimension_style(existing_dimension = None): ViewProviderDraftDimensionStyle(obj.ViewObject, existing_dimension) return obj -class DimensionStyle(_DraftObject): +class DimensionStyle(DraftAnnotation): def __init__(self, obj): - _DraftObject.__init__(self, obj, "DimensionStyle") \ No newline at end of file + DraftAnnotation.__init__(self, obj, "DimensionStyle") \ No newline at end of file diff --git a/src/Mod/Draft/draftobjects/draft_annotation.py b/src/Mod/Draft/draftobjects/draft_annotation.py new file mode 100644 index 0000000000..fd28c9e527 --- /dev/null +++ b/src/Mod/Draft/draftobjects/draft_annotation.py @@ -0,0 +1,78 @@ +# *************************************************************************** +# * (c) 2020 Carlo Pavan * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +"""This module provides the object code for Draft DimensionStyle. +""" +## @package style_dimension +# \ingroup DRAFT +# \brief This module provides the object code for Draft DimensionStyle. + +import FreeCAD as App +from PySide.QtCore import QT_TRANSLATE_NOOP +from draftutils import gui_utils + +class DraftAnnotation: + """The Draft Annotation Base object""" + def __init__(self,obj,tp="Unknown"): + if obj: + obj.Proxy = self + self.Type = tp + + def __getstate__(self): + return self.Type + + def __setstate__(self,state): + if state: + self.Type = state + + def execute(self,obj): + pass + + def onChanged(self, obj, prop): + pass + + + +class DimensionBase(DraftAnnotation): + """The Draft Dimension Base object""" + + def __init__(self, obj, tp = "Dimension"): + "Initialize common properties for dimension objects" + DraftAnnotation.__init__(self,obj, tp) + + # Annotation + obj.addProperty("App::PropertyLink","DimensionStyle", + "Annotation", + QT_TRANSLATE_NOOP("App::Property", + "Link dimension style")) + + def onChanged(self,obj,prop): + + if prop == "DimensionStyle": + if hasattr(obj, "DimensionStyle"): + gui_utils.format_object(target = obj, origin = obj.DimensionStyle) + + + def execute(self, obj): + + return diff --git a/src/Mod/Draft/draftviewproviders/view_dimension.py b/src/Mod/Draft/draftviewproviders/view_dimension.py new file mode 100644 index 0000000000..d09b240440 --- /dev/null +++ b/src/Mod/Draft/draftviewproviders/view_dimension.py @@ -0,0 +1,549 @@ +# *************************************************************************** +# * (c) 2019 Eliud Cabrera Castillo * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""This module provides the Draft Annotations view provider base classes +""" +## @package polararray +# \ingroup DRAFT +# \brief This module provides the view provider code for Draft PolarArray. + + +import FreeCAD as App +import FreeCADGui as Gui +import DraftVecUtils +from PySide.QtCore import QT_TRANSLATE_NOOP +import draftutils.utils as utils +import draftutils.gui_utils as gui_utils +from draftviewproviders.view_draft_annotation import ViewProviderDimensionBase + +class ViewProviderLinearDimension(ViewProviderDimensionBase): + """ + A View Provider for the Draft Dimension object + + DIMENSION VIEW PROVIDER: + + | txt | e + ----o--------------------------------o----- + | | + | | d + | | + + a b c b a + + a = DimOvershoot (vobj) + b = Arrows (vobj) + c = Dimline (obj) + d = ExtLines (vobj) + e = ExtOvershoot (vobj) + txt = label (vobj) + + STRUCTURE: + vobj.node.color + .drawstyle + .lineswitch1.coords + .line + .marks + .marksDimOvershoot + .marksExtOvershoot + .label.textpos + .color + .font + .text + + vobj.node3d.color + .drawstyle + .lineswitch3.coords + .line + .marks + .marksDimOvershoot + .marksExtOvershoot + .label3d.textpos + .color + .font3d + .text3d + + """ + def __init__(self, vobj): + ViewProviderDimensionBase.__init__(self,vobj) + + def attach(self, vobj): + """called on object creation""" + from pivy import coin + self.Object = vobj.Object + self.color = coin.SoBaseColor() + self.font = coin.SoFont() + self.font3d = coin.SoFont() + self.text = coin.SoAsciiText() + self.text3d = coin.SoText2() + self.text.string = "d" # some versions of coin crash if string is not set + self.text3d.string = "d" + self.textpos = coin.SoTransform() + self.text.justification = self.text3d.justification = coin.SoAsciiText.CENTER + label = coin.SoSeparator() + label.addChild(self.textpos) + label.addChild(self.color) + label.addChild(self.font) + label.addChild(self.text) + label3d = coin.SoSeparator() + label3d.addChild(self.textpos) + label3d.addChild(self.color) + label3d.addChild(self.font3d) + label3d.addChild(self.text3d) + self.coord1 = coin.SoCoordinate3() + self.trans1 = coin.SoTransform() + self.coord2 = coin.SoCoordinate3() + self.trans2 = coin.SoTransform() + self.transDimOvershoot1 = coin.SoTransform() + self.transDimOvershoot2 = coin.SoTransform() + self.transExtOvershoot1 = coin.SoTransform() + self.transExtOvershoot2 = coin.SoTransform() + self.marks = coin.SoSeparator() + self.marksDimOvershoot = coin.SoSeparator() + self.marksExtOvershoot = coin.SoSeparator() + self.drawstyle = coin.SoDrawStyle() + self.line = coin.SoType.fromName("SoBrepEdgeSet").createInstance() + self.coords = coin.SoCoordinate3() + self.node = coin.SoGroup() + self.node.addChild(self.color) + self.node.addChild(self.drawstyle) + self.lineswitch2 = coin.SoSwitch() + self.lineswitch2.whichChild = -3 + self.node.addChild(self.lineswitch2) + self.lineswitch2.addChild(self.coords) + self.lineswitch2.addChild(self.line) + self.lineswitch2.addChild(self.marks) + self.lineswitch2.addChild(self.marksDimOvershoot) + self.lineswitch2.addChild(self.marksExtOvershoot) + self.node.addChild(label) + self.node3d = coin.SoGroup() + self.node3d.addChild(self.color) + self.node3d.addChild(self.drawstyle) + self.lineswitch3 = coin.SoSwitch() + self.lineswitch3.whichChild = -3 + self.node3d.addChild(self.lineswitch3) + self.lineswitch3.addChild(self.coords) + self.lineswitch3.addChild(self.line) + self.lineswitch3.addChild(self.marks) + self.lineswitch3.addChild(self.marksDimOvershoot) + self.lineswitch3.addChild(self.marksExtOvershoot) + self.node3d.addChild(label3d) + vobj.addDisplayMode(self.node,"2D") + vobj.addDisplayMode(self.node3d,"3D") + self.updateData(vobj.Object,"Start") + self.onChanged(vobj,"FontSize") + self.onChanged(vobj,"FontName") + self.onChanged(vobj,"ArrowType") + self.onChanged(vobj,"LineColor") + self.onChanged(vobj,"DimOvershoot") + self.onChanged(vobj,"ExtOvershoot") + + def updateData(self, obj, prop): + """called when the base object is changed""" + import DraftGui + if prop in ["Start","End","Dimline","Direction"]: + + if obj.Start == obj.End: + return + + if not hasattr(self,"node"): + return + + import Part, DraftGeomUtils + from pivy import coin + + # calculate the 4 points + self.p1 = obj.Start + self.p4 = obj.End + base = None + if hasattr(obj,"Direction"): + if not DraftVecUtils.isNull(obj.Direction): + v2 = self.p1.sub(obj.Dimline) + v3 = self.p4.sub(obj.Dimline) + v2 = DraftVecUtils.project(v2,obj.Direction) + v3 = DraftVecUtils.project(v3,obj.Direction) + self.p2 = obj.Dimline.add(v2) + self.p3 = obj.Dimline.add(v3) + if DraftVecUtils.equals(self.p2,self.p3): + base = None + proj = None + else: + base = Part.LineSegment(self.p2,self.p3).toShape() + proj = DraftGeomUtils.findDistance(self.p1,base) + if proj: + proj = proj.negative() + if not base: + if DraftVecUtils.equals(self.p1,self.p4): + base = None + proj = None + else: + base = Part.LineSegment(self.p1,self.p4).toShape() + proj = DraftGeomUtils.findDistance(obj.Dimline,base) + if proj: + self.p2 = self.p1.add(proj.negative()) + self.p3 = self.p4.add(proj.negative()) + else: + self.p2 = self.p1 + self.p3 = self.p4 + if proj: + if hasattr(obj.ViewObject,"ExtLines") and hasattr(obj.ViewObject,"ScaleMultiplier"): + dmax = obj.ViewObject.ExtLines.Value * obj.ViewObject.ScaleMultiplier + if dmax and (proj.Length > dmax): + if (dmax > 0): + self.p1 = self.p2.add(DraftVecUtils.scaleTo(proj,dmax)) + self.p4 = self.p3.add(DraftVecUtils.scaleTo(proj,dmax)) + else: + rest = proj.Length + dmax + self.p1 = self.p2.add(DraftVecUtils.scaleTo(proj,rest)) + self.p4 = self.p3.add(DraftVecUtils.scaleTo(proj,rest)) + else: + proj = (self.p3.sub(self.p2)).cross(App.Vector(0,0,1)) + + # calculate the arrows positions + self.trans1.translation.setValue((self.p2.x,self.p2.y,self.p2.z)) + self.coord1.point.setValue((self.p2.x,self.p2.y,self.p2.z)) + self.trans2.translation.setValue((self.p3.x,self.p3.y,self.p3.z)) + self.coord2.point.setValue((self.p3.x,self.p3.y,self.p3.z)) + + # calculate dimension and extension lines overshoots positions + self.transDimOvershoot1.translation.setValue((self.p2.x,self.p2.y,self.p2.z)) + self.transDimOvershoot2.translation.setValue((self.p3.x,self.p3.y,self.p3.z)) + self.transExtOvershoot1.translation.setValue((self.p2.x,self.p2.y,self.p2.z)) + self.transExtOvershoot2.translation.setValue((self.p3.x,self.p3.y,self.p3.z)) + + # calculate the text position and orientation + if hasattr(obj,"Normal"): + if DraftVecUtils.isNull(obj.Normal): + if proj: + norm = (self.p3.sub(self.p2).cross(proj)).negative() + else: + norm = App.Vector(0,0,1) + else: + norm = App.Vector(obj.Normal) + else: + if proj: + norm = (self.p3.sub(self.p2).cross(proj)).negative() + else: + norm = App.Vector(0,0,1) + if not DraftVecUtils.isNull(norm): + norm.normalize() + u = self.p3.sub(self.p2) + u.normalize() + v1 = norm.cross(u) + rot1 = App.Placement(DraftVecUtils.getPlaneRotation(u,v1,norm)).Rotation.Q + self.transDimOvershoot1.rotation.setValue((rot1[0],rot1[1],rot1[2],rot1[3])) + self.transDimOvershoot2.rotation.setValue((rot1[0],rot1[1],rot1[2],rot1[3])) + if hasattr(obj.ViewObject,"FlipArrows"): + if obj.ViewObject.FlipArrows: + u = u.negative() + v2 = norm.cross(u) + rot2 = App.Placement(DraftVecUtils.getPlaneRotation(u,v2,norm)).Rotation.Q + self.trans1.rotation.setValue((rot2[0],rot2[1],rot2[2],rot2[3])) + self.trans2.rotation.setValue((rot2[0],rot2[1],rot2[2],rot2[3])) + if self.p1 != self.p2: + u3 = self.p1.sub(self.p2) + u3.normalize() + v3 = norm.cross(u3) + rot3 = App.Placement(DraftVecUtils.getPlaneRotation(u3,v3,norm)).Rotation.Q + self.transExtOvershoot1.rotation.setValue((rot3[0],rot3[1],rot3[2],rot3[3])) + self.transExtOvershoot2.rotation.setValue((rot3[0],rot3[1],rot3[2],rot3[3])) + if hasattr(obj.ViewObject,"TextSpacing") and hasattr(obj.ViewObject,"ScaleMultiplier"): + ts = obj.ViewObject.TextSpacing.Value * obj.ViewObject.ScaleMultiplier + offset = DraftVecUtils.scaleTo(v1,ts) + else: + offset = DraftVecUtils.scaleTo(v1,0.05) + rott = rot1 + if hasattr(obj.ViewObject,"FlipText"): + if obj.ViewObject.FlipText: + rott = App.Rotation(*rott).multiply(App.Rotation(norm,180)).Q + offset = offset.negative() + # setting text + try: + m = obj.ViewObject.DisplayMode + except: # swallow all exceptions here since it always fails on first run (Displaymode enum no set yet) + m = ["2D","3D"][utils.get_param("dimstyle",0)] + if m == "3D": + offset = offset.negative() + self.tbase = (self.p2.add((self.p3.sub(self.p2).multiply(0.5)))).add(offset) + if hasattr(obj.ViewObject,"TextPosition"): + if not DraftVecUtils.isNull(obj.ViewObject.TextPosition): + self.tbase = obj.ViewObject.TextPosition + self.textpos.translation.setValue([self.tbase.x,self.tbase.y,self.tbase.z]) + self.textpos.rotation = coin.SbRotation(rott[0],rott[1],rott[2],rott[3]) + su = True + if hasattr(obj.ViewObject,"ShowUnit"): + su = obj.ViewObject.ShowUnit + # set text value + l = self.p3.sub(self.p2).Length + unit = None + if hasattr(obj.ViewObject,"UnitOverride"): + unit = obj.ViewObject.UnitOverride + # special representation if "Building US" scheme + if App.ParamGet("User parameter:BaseApp/Preferences/Units").GetInt("UserSchema",0) == 5: + s = App.Units.Quantity(l,App.Units.Length).UserString + self.string = s.replace("' ","'- ") + self.string = s.replace("+"," ") + elif hasattr(obj.ViewObject,"Decimals"): + self.string = DraftGui.displayExternal(l,obj.ViewObject.Decimals,'Length',su,unit) + else: + self.string = DraftGui.displayExternal(l,None,'Length',su,unit) + if hasattr(obj.ViewObject,"Override"): + if obj.ViewObject.Override: + self.string = obj.ViewObject.Override.replace("$dim",\ + self.string) + self.text.string = self.text3d.string = utils.string_encode_coin(self.string) + + # set the lines + if m == "3D": + # calculate the spacing of the text + textsize = (len(self.string)*obj.ViewObject.FontSize.Value)/4.0 + spacing = ((self.p3.sub(self.p2)).Length/2.0) - textsize + self.p2a = self.p2.add(DraftVecUtils.scaleTo(self.p3.sub(self.p2),spacing)) + self.p2b = self.p3.add(DraftVecUtils.scaleTo(self.p2.sub(self.p3),spacing)) + self.coords.point.setValues([[self.p1.x,self.p1.y,self.p1.z], + [self.p2.x,self.p2.y,self.p2.z], + [self.p2a.x,self.p2a.y,self.p2a.z], + [self.p2b.x,self.p2b.y,self.p2b.z], + [self.p3.x,self.p3.y,self.p3.z], + [self.p4.x,self.p4.y,self.p4.z]]) + #self.line.numVertices.setValues([3,3]) + self.line.coordIndex.setValues(0,7,(0,1,2,-1,3,4,5)) + else: + self.coords.point.setValues([[self.p1.x,self.p1.y,self.p1.z], + [self.p2.x,self.p2.y,self.p2.z], + [self.p3.x,self.p3.y,self.p3.z], + [self.p4.x,self.p4.y,self.p4.z]]) + #self.line.numVertices.setValue(4) + self.line.coordIndex.setValues(0,4,(0,1,2,3)) + + def onChanged(self, vobj, prop): + """called when a view property has changed""" + if prop == "ScaleMultiplier" and hasattr(vobj,"ScaleMultiplier"): + # update all dimension values + if hasattr(self,"font"): + self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier + if hasattr(self,"font3d"): + self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier + if hasattr(self,"node") and hasattr(self,"p2") and hasattr(vobj,"ArrowSize"): + self.remove_dim_arrows() + self.draw_dim_arrows(vobj) + if hasattr(vobj,"DimOvershoot"): + self.remove_dim_overshoot() + self.draw_dim_overshoot(vobj) + if hasattr(vobj,"ExtOvershoot"): + self.remove_ext_overshoot() + self.draw_ext_overshoot(vobj) + self.updateData(vobj.Object,"Start") + vobj.Object.touch() + + elif (prop == "FontSize") and hasattr(vobj,"FontSize"): + if hasattr(self,"font") and hasattr(vobj,"ScaleMultiplier"): + self.font.size = vobj.FontSize.Value*vobj.ScaleMultiplier + if hasattr(self,"font3d") and hasattr(vobj,"ScaleMultiplier"): + self.font3d.size = vobj.FontSize.Value*100*vobj.ScaleMultiplier + vobj.Object.touch() + + elif (prop == "FontName") and hasattr(vobj,"FontName"): + if hasattr(self,"font") and hasattr(self,"font3d"): + self.font.name = self.font3d.name = str(vobj.FontName) + vobj.Object.touch() + + elif (prop == "LineColor") and hasattr(vobj,"LineColor"): + if hasattr(self,"color"): + c = vobj.LineColor + self.color.rgb.setValue(c[0],c[1],c[2]) + + elif (prop == "LineWidth") and hasattr(vobj,"LineWidth"): + if hasattr(self,"drawstyle"): + self.drawstyle.lineWidth = vobj.LineWidth + + elif (prop in ["ArrowSize","ArrowType"]) and hasattr(vobj,"ArrowSize"): + if hasattr(self,"node") and hasattr(self,"p2"): + if hasattr(vobj,"ScaleMultiplier"): + self.remove_dim_arrows() + self.draw_dim_arrows(vobj) + vobj.Object.touch() + + elif (prop == "DimOvershoot") and hasattr(vobj,"DimOvershoot"): + if hasattr(vobj,"ScaleMultiplier"): + self.remove_dim_overshoot() + self.draw_dim_overshoot(vobj) + vobj.Object.touch() + + elif (prop == "ExtOvershoot") and hasattr(vobj,"ExtOvershoot"): + if hasattr(vobj,"ScaleMultiplier"): + self.remove_ext_overshoot() + self.draw_ext_overshoot(vobj) + vobj.Object.touch() + + elif (prop == "ShowLine") and hasattr(vobj,"ShowLine"): + if vobj.ShowLine: + self.lineswitch2.whichChild = -3 + self.lineswitch3.whichChild = -3 + else: + self.lineswitch2.whichChild = -1 + self.lineswitch3.whichChild = -1 + else: + self.updateData(vobj.Object,"Start") + + def remove_dim_arrows(self): + # remove existing nodes + self.node.removeChild(self.marks) + self.node3d.removeChild(self.marks) + + def draw_dim_arrows(self, vobj): + from pivy import coin + + if not hasattr(vobj,"ArrowType"): + return + + if self.p3.x < self.p2.x: + inv = False + else: + inv = True + + # set scale + symbol = utils.ARROW_TYPES.index(vobj.ArrowType) + s = vobj.ArrowSize.Value * vobj.ScaleMultiplier + self.trans1.scaleFactor.setValue((s,s,s)) + self.trans2.scaleFactor.setValue((s,s,s)) + + + # set new nodes + self.marks = coin.SoSeparator() + self.marks.addChild(self.color) + s1 = coin.SoSeparator() + if symbol == "Circle": + s1.addChild(self.coord1) + else: + s1.addChild(self.trans1) + s1.addChild(gui_utils.dim_symbol(symbol,invert=not(inv))) + self.marks.addChild(s1) + s2 = coin.SoSeparator() + if symbol == "Circle": + s2.addChild(self.coord2) + else: + s2.addChild(self.trans2) + s2.addChild(gui_utils.dim_symbol(symbol,invert=inv)) + self.marks.addChild(s2) + self.node.insertChild(self.marks,2) + self.node3d.insertChild(self.marks,2) + + def remove_dim_overshoot(self): + self.node.removeChild(self.marksDimOvershoot) + self.node3d.removeChild(self.marksDimOvershoot) + + + def draw_dim_overshoot(self, vobj): + from pivy import coin + + # set scale + s = vobj.DimOvershoot.Value * vobj.ScaleMultiplier + self.transDimOvershoot1.scaleFactor.setValue((s,s,s)) + self.transDimOvershoot2.scaleFactor.setValue((s,s,s)) + + # remove existing nodes + + # set new nodes + self.marksDimOvershoot = coin.SoSeparator() + if vobj.DimOvershoot.Value: + self.marksDimOvershoot.addChild(self.color) + s1 = coin.SoSeparator() + s1.addChild(self.transDimOvershoot1) + s1.addChild(gui_utils.dimDash((-1,0,0),(0,0,0))) + self.marksDimOvershoot.addChild(s1) + s2 = coin.SoSeparator() + s2.addChild(self.transDimOvershoot2) + s2.addChild(gui_utils.dimDash((0,0,0),(1,0,0))) + self.marksDimOvershoot.addChild(s2) + self.node.insertChild(self.marksDimOvershoot,2) + self.node3d.insertChild(self.marksDimOvershoot,2) + + + def remove_ext_overshoot(self): + self.node.removeChild(self.marksExtOvershoot) + self.node3d.removeChild(self.marksExtOvershoot) + + + def draw_ext_overshoot(self, vobj): + from pivy import coin + + # set scale + s = vobj.ExtOvershoot.Value * vobj.ScaleMultiplier + self.transExtOvershoot1.scaleFactor.setValue((s,s,s)) + self.transExtOvershoot2.scaleFactor.setValue((s,s,s)) + + # set new nodes + self.marksExtOvershoot = coin.SoSeparator() + if vobj.ExtOvershoot.Value: + self.marksExtOvershoot.addChild(self.color) + s1 = coin.SoSeparator() + s1.addChild(self.transExtOvershoot1) + s1.addChild(gui_utils.dimDash((0,0,0),(-1,0,0))) + self.marksExtOvershoot.addChild(s1) + s2 = coin.SoSeparator() + s2.addChild(self.transExtOvershoot2) + s2.addChild(gui_utils.dimDash((0,0,0),(-1,0,0))) + self.marksExtOvershoot.addChild(s2) + self.node.insertChild(self.marksExtOvershoot,2) + self.node3d.insertChild(self.marksExtOvershoot,2) + + + def doubleClicked(self,vobj): + self.setEdit(vobj) + + def getDisplayModes(self,vobj): + return ["2D","3D"] + + def getDefaultDisplayMode(self): + if hasattr(self,"defaultmode"): + return self.defaultmode + else: + return ["2D","3D"][utils.get_param("dimstyle",0)] + + def setDisplayMode(self,mode): + return mode + + def is_linked_to_circle(self): + _obj = self.Object + if _obj.LinkedGeometry and len(_obj.LinkedGeometry) == 1: + lobj = _obj.LinkedGeometry[0][0] + lsub = _obj.LinkedGeometry[0][1] + if len(lsub) == 1 and "Edge" in lsub[0]: + n = int(lsub[0][4:]) - 1 + edge = lobj.Shape.Edges[n] + if DraftGeomUtils.geomType(edge) == "Circle": + return True + return False + + def getIcon(self): + if self.is_linked_to_circle(): + return ":/icons/Draft_DimensionRadius.svg" + return ":/icons/Draft_Dimension_Tree.svg" + + def __getstate__(self): + return self.Object.ViewObject.DisplayMode + + def __setstate__(self,state): + if state: + self.defaultmode = state + self.setDisplayMode(state) + diff --git a/src/Mod/Draft/draftviewproviders/view_style_dimension.py b/src/Mod/Draft/draftviewproviders/view_dimensionstyle.py similarity index 94% rename from src/Mod/Draft/draftviewproviders/view_style_dimension.py rename to src/Mod/Draft/draftviewproviders/view_dimensionstyle.py index aaae6fe778..9e2fb24f13 100644 --- a/src/Mod/Draft/draftviewproviders/view_style_dimension.py +++ b/src/Mod/Draft/draftviewproviders/view_dimensionstyle.py @@ -28,10 +28,10 @@ # \brief This module provides the view provider code for Draft DimensionStyle. import FreeCAD as App -import Draft from Draft import _ViewProviderDraft from PySide.QtCore import QT_TRANSLATE_NOOP import draftutils.utils as utils +from pivy import coin class ViewProviderDraftDimensionStyle(_ViewProviderDraft): """ @@ -190,6 +190,9 @@ class ViewProviderDraftDimensionStyle(_ViewProviderDraft): if hasattr(vobj, "AutoUpdate"): if vobj.AutoUpdate: self.update_related_dimensions(vobj) + if hasattr(vobj, "Visibility"): + if prop == "Visibility": + print(vobj.Visibility) def doubleClicked(self,vobj): self.set_current(vobj) @@ -228,3 +231,19 @@ class ViewProviderDraftDimensionStyle(_ViewProviderDraft): from draftutils import gui_utils for dim in vobj.Object.InList: gui_utils.format_object(target = dim, origin = vobj.Object) + + def getIcon(self): + import Draft_rc + return ":/icons/Draft_Dimension_Tree_Style.svg" + + def attach(self, vobj): + self.standard = coin.SoGroup() + vobj.addDisplayMode(self.standard,"Standard") + + def getDisplayModes(self,obj): + "'''Return a list of display modes.'''" + return ["Standard"] + + def getDefaultDisplayMode(self): + "'''Return the name of the default display mode. It must be defined in getDisplayModes.'''" + return "Standard" \ No newline at end of file diff --git a/src/Mod/Draft/draftviewproviders/view_draft_annotation.py b/src/Mod/Draft/draftviewproviders/view_draft_annotation.py new file mode 100644 index 0000000000..9c24b5bcd7 --- /dev/null +++ b/src/Mod/Draft/draftviewproviders/view_draft_annotation.py @@ -0,0 +1,263 @@ +# *************************************************************************** +# * (c) 2019 Eliud Cabrera Castillo * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * FreeCAD is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""This module provides the Draft Annotations view provider base classes +""" +## @package polararray +# \ingroup DRAFT +# \brief This module provides the view provider code for Draft PolarArray. + + +import FreeCAD as App +import FreeCADGui as Gui +from PySide.QtCore import QT_TRANSLATE_NOOP +import draftutils.utils as utils + + +class ViewProviderDraftAnnotation: + """The base class for Draft Annotation Viewproviders""" + + def __init__(self, vobj): + vobj.Proxy = self + self.Object = vobj.Object + + # annotation properties + vobj.addProperty("App::PropertyFloat","ScaleMultiplier", + "Annotation",QT_TRANSLATE_NOOP("App::Property", + "Dimension size overall multiplier")) + + # graphics properties + vobj.addProperty("App::PropertyFloat","LineWidth", + "Graphics",QT_TRANSLATE_NOOP("App::Property","Line width")) + vobj.addProperty("App::PropertyColor","LineColor", + "Graphics",QT_TRANSLATE_NOOP("App::Property","Line color")) + + param = App.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") + annotation_scale = param.GetFloat("DraftAnnotationScale", 1.0) + vobj.ScaleMultiplier = 1 / annotation_scale + + + def __getstate__(self): + return None + + def __setstate__(self, state): + return None + + def attach(self,vobj): + self.Object = vobj.Object + return + + def updateData(self, obj, prop): + return + + def getDisplayModes(self, vobj): + modes=[] + return modes + + def setDisplayMode(self, mode): + return mode + + def onChanged(self, vobj, prop): + return + + def execute(self,vobj): + return + + def setEdit(self,vobj,mode=0): + if mode == 0: + Gui.runCommand("Draft_Edit") + return True + return False + + def unsetEdit(self,vobj,mode=0): + if App.activeDraftCommand: + App.activeDraftCommand.finish() + Gui.Control.closeDialog() + return False + + def getIcon(self): + return ":/icons/Draft_Draft.svg" + + def claimChildren(self): + """perhaps this is not useful???""" + objs = [] + if hasattr(self.Object,"Base"): + objs.append(self.Object.Base) + if hasattr(self.Object,"Objects"): + objs.extend(self.Object.Objects) + if hasattr(self.Object,"Components"): + objs.extend(self.Object.Components) + if hasattr(self.Object,"Group"): + objs.extend(self.Object.Group) + return objs + + +class ViewProviderDimensionBase(ViewProviderDraftAnnotation): + """ + A View Provider for the Draft Dimension object + + DIMENSION VIEW PROVIDER: + + | txt | e + ----o--------------------------------o----- + | | + | | d + | | + + a b c b a + + a = DimOvershoot (vobj) + b = Arrows (vobj) + c = Dimline (obj) + d = ExtLines (vobj) + e = ExtOvershoot (vobj) + txt = label (vobj) + + STRUCTURE: + vobj.node.color + .drawstyle + .lineswitch1.coords + .line + .marks + .marksDimOvershoot + .marksExtOvershoot + .label.textpos + .color + .font + .text + + vobj.node3d.color + .drawstyle + .lineswitch3.coords + .line + .marks + .marksDimOvershoot + .marksExtOvershoot + .label3d.textpos + .color + .font3d + .text3d + + """ + def __init__(self, vobj): + # text properties + vobj.addProperty("App::PropertyFont","FontName", + "Text",QT_TRANSLATE_NOOP("App::Property","Font name")) + vobj.addProperty("App::PropertyLength","FontSize", + "Text",QT_TRANSLATE_NOOP("App::Property","Font size")) + vobj.addProperty("App::PropertyLength","TextSpacing", + "Text",QT_TRANSLATE_NOOP("App::Property", + "The spacing between the text and the dimension line")) + vobj.addProperty("App::PropertyBool","FlipText", + "Text",QT_TRANSLATE_NOOP("App::Property", + "Rotate the dimension text 180 degrees")) + vobj.addProperty("App::PropertyVectorDistance","TextPosition", + "Text",QT_TRANSLATE_NOOP("App::Property", + "The position of the text. Leave (0,0,0) for automatic position")) + vobj.addProperty("App::PropertyString","Override", + "Text",QT_TRANSLATE_NOOP("App::Property", + "Text override. Use $dim to insert the dimension length")) + # units properties + vobj.addProperty("App::PropertyInteger","Decimals", + "Units",QT_TRANSLATE_NOOP("App::Property", + "The number of decimals to show")) + vobj.addProperty("App::PropertyBool","ShowUnit", + "Units",QT_TRANSLATE_NOOP("App::Property", + "Show the unit suffix")) + vobj.addProperty("App::PropertyString","UnitOverride", + "Units",QT_TRANSLATE_NOOP("App::Property", + "A unit to express the measurement. Leave blank for system default")) + # graphics properties + vobj.addProperty("App::PropertyLength","ArrowSize", + "Graphics",QT_TRANSLATE_NOOP("App::Property","Arrow size")) + vobj.addProperty("App::PropertyEnumeration","ArrowType", + "Graphics",QT_TRANSLATE_NOOP("App::Property","Arrow type")) + vobj.addProperty("App::PropertyBool","FlipArrows", + "Graphics",QT_TRANSLATE_NOOP("App::Property", + "Rotate the dimension arrows 180 degrees")) + vobj.addProperty("App::PropertyDistance","DimOvershoot", + "Graphics",QT_TRANSLATE_NOOP("App::Property", + "The distance the dimension line is extended past the extension lines")) + vobj.addProperty("App::PropertyDistance","ExtLines", + "Graphics",QT_TRANSLATE_NOOP("App::Property", + "Length of the extension lines")) + vobj.addProperty("App::PropertyDistance","ExtOvershoot", + "Graphics",QT_TRANSLATE_NOOP("App::Property", + "Length of the extension line above the dimension line")) + vobj.addProperty("App::PropertyBool","ShowLine", + "Graphics",QT_TRANSLATE_NOOP("App::Property", + "Shows the dimension line and arrows")) + + vobj.FontSize = utils.get_param("textheight",0.20) + vobj.TextSpacing = utils.get_param("dimspacing",0.05) + vobj.FontName = utils.get_param("textfont","") + vobj.ArrowSize = utils.get_param("arrowsize",0.1) + vobj.ArrowType = utils.ARROW_TYPES + vobj.ArrowType = utils.ARROW_TYPES[utils.get_param("dimsymbol",0)] + vobj.ExtLines = utils.get_param("extlines",0.3) + vobj.DimOvershoot = utils.get_param("dimovershoot",0) + vobj.ExtOvershoot = utils.get_param("extovershoot",0) + vobj.Decimals = utils.get_param("dimPrecision",2) + vobj.ShowUnit = utils.get_param("showUnit",True) + vobj.ShowLine = True + ViewProviderDraftAnnotation.__init__(self,vobj) + + def attach(self, vobj): + """called on object creation""" + return + + def updateData(self, obj, prop): + """called when the base object is changed""" + return + + def onChanged(self, vobj, prop): + """called when a view property has changed""" + return + + def doubleClicked(self,vobj): + self.setEdit(vobj) + + def getDisplayModes(self,vobj): + return ["2D","3D"] + + def getDefaultDisplayMode(self): + if hasattr(self,"defaultmode"): + return self.defaultmode + else: + return ["2D","3D"][utils.get_param("dimstyle",0)] + + def setDisplayMode(self,mode): + return mode + + def getIcon(self): + if self.is_linked_to_circle(): + return ":/icons/Draft_DimensionRadius.svg" + return ":/icons/Draft_Dimension_Tree.svg" + + def __getstate__(self): + return self.Object.ViewObject.DisplayMode + + def __setstate__(self,state): + if state: + self.defaultmode = state + self.setDisplayMode(state) +