def onChanged(self, vp, prop): self.updateCam() if prop == 'Total': if float(vp.Total) >= float(vp.Subdivision): self.xy.mainDim = float(vp.Total) self.xz.mainDim = float(vp.Total) self.yz.mainDim = float(vp.Total) else: vp.Total = vp.Subdivision if prop == 'Subdivision': if float(vp.Total) >= float(vp.Subdivision): self.xy.subDim = float(vp.Subdivision) self.xz.subDim = float(vp.Subdivision) self.yz.subDim = float(vp.Subdivision) else: vp.Subdivision = vp.Total if prop == 'XY_Attenuation': if vp.XY_Attenuation < 0.1: vp.XY_Attenuation = 0.1 elif vp.XY_Attenuation > 100: vp.XY_Attenuation = 100 self.xy.factor = vp.XY_Attenuation if prop == 'XZ_Attenuation': if vp.XZ_Attenuation < 0.1: vp.XZ_Attenuation = 0.1 elif vp.XZ_Attenuation > 100: vp.XZ_Attenuation = 100 self.xz.factor = vp.XZ_Attenuation if prop == 'YZ_Attenuation': if vp.YZ_Attenuation < 0.1: vp.YZ_Attenuation = 0.1 elif vp.YZ_Attenuation > 100: vp.YZ_Attenuation = 100 self.yz.factor = vp.YZ_Attenuation if prop == 'XY_Visibility': if vp.XY_Visibility < 0.0: vp.XY_Visibility = 0.0 elif vp.XY_Visibility > 1.0: vp.XY_Visibility = 1.0 self.xy.maxviz = vp.XY_Visibility if prop == 'XZ_Visibility': if vp.XZ_Visibility < 0.0: vp.XZ_Visibility = 0.0 elif vp.XZ_Visibility > 1.0: vp.XZ_Visibility = 1.0 self.xz.maxviz = vp.XZ_Visibility if prop == 'YZ_Visibility': if vp.YZ_Visibility < 0.0: vp.YZ_Visibility = 0.0 elif vp.YZ_Visibility > 1.0: vp.YZ_Visibility = 1.0 self.yz.maxviz = vp.YZ_Visibility if prop == 'GridColor': self.xy.gridcolor = vp.GridColor self.xz.gridcolor = vp.GridColor self.yz.gridcolor = vp.GridColor if prop == 'Placement': FreeCAD.Console.PrintMessage('Placement update\n') tr = vp.Object.Placement.Base ro = vp.Object.Placement.Rotation.Q self.trans.translation = coin.SbVec3f(tr.x, tr.y, tr.z) self.trans.rotation = coin.SbRotation(ro[0], ro[1], ro[2], ro[3]) self.xy.updateTransformedNormal() self.xz.updateTransformedNormal() self.yz.updateTransformedNormal()
def render(outputfile,scene=None,camera=None,zoom=False,width=400,height=300,background=(1.0,1.0,1.0),lightdir=None): """render(outputfile,scene=None,camera=None,zoom=False,width=400,height=300,background=(1.0,1.0,1.0),lightdir=None): Renders a PNG image of given width and height and background color from the given coin scene, using the given coin camera (ortho or perspective). If zoom is True the camera will be resized to fit all objects. The outputfile must be a file path to save a png image. Optionally a light direction as a (x,y,z) tuple can be given. In this case, a directional light will be added and shadows will be turned on. This might not work with some 3D drivers.""" # On Linux, the X server must have indirect rendering enabled in order to be able to do offline # PNG rendering. Unfortunately, this is turned off by default on most recent distros. The easiest # way I found is to edit (or create if inexistent) /etc/X11/xorg.conf and add this: # # Section "ServerFlags" # Option "AllowIndirectGLX" "on" # Option "IndirectGLX" "on" # EndSection # # But there are other ways, google of GLX indirect rendering if isinstance(camera,str): camera = getCoinCamera(camera) print("Starting offline renderer") # build an offline scene root separator root = coin.SoSeparator() if not lightdir: # add one light (mandatory) light = coin.SoDirectionalLight() root.addChild(light) if not camera: # create a default camera if none was given camera = coin.SoPerspectiveCamera() cameraRotation = coin.SbRotation.identity() cameraRotation *= coin.SbRotation(coin.SbVec3f(1,0,0),-0.4) cameraRotation *= coin.SbRotation(coin.SbVec3f(0,1,0), 0.4) camera.orientation = cameraRotation # make sure all objects get in the view later zoom = True root.addChild(camera) if scene: root.addChild(scene) else: # no scene was given, add a simple cube cube = coin.SoCube() root.addChild(cube) if lightdir: root = embedLight(root,lightdir) vpRegion = coin.SbViewportRegion(width,height) if zoom: camera.viewAll(root,vpRegion) print("Creating viewport") offscreenRenderer = coin.SoOffscreenRenderer(vpRegion) offscreenRenderer.setBackgroundColor(coin.SbColor(background[0],background[1],background[2])) print("Ready to render") # ref ensures that the node will not be garbage-collected during rendering root.ref() ok = offscreenRenderer.render(root) root.unref() if ok: offscreenRenderer.writeToFile(outputfile,"PNG") print("Rendering",outputfile,"done") else: print("Error rendering image")
def updateData(self, obj, prop): """Execute when a property from the Proxy class is changed. It only runs if `Start`, `End`, `Dimline`, or `Direction` changed. """ if prop not in ("Start", "End", "Dimline", "Direction"): return if obj.Start == obj.End: return if not hasattr(self, "node"): return vobj = obj.ViewObject # Calculate the 4 points # # | d | # ---p2-------------c----p3---- c # | | # | | # p1 p4 # # - `c` is the `Dimline`, a point that lies on the dimension line # or on its extension. # - The line itself between `p2` to `p3` is the `base`. # - The distance between `p2` (`base`) to `p1` is `proj`, an extension # line from the dimension to the measured object. # - If the `proj` distance is zero, `p1` and `p2` are the same point, # and same with `p3` and `p4`. # self.p1 = obj.Start self.p4 = obj.End base = None if (hasattr(obj, "Direction") and not DraftVecUtils.isNull(obj.Direction)): v2 = self.p1 - obj.Dimline v3 = self.p4 - obj.Dimline v2 = DraftVecUtils.project(v2, obj.Direction) v3 = DraftVecUtils.project(v3, obj.Direction) self.p2 = obj.Dimline + v2 self.p3 = obj.Dimline + 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 + proj.negative() self.p3 = self.p4 + proj.negative() else: self.p2 = self.p1 self.p3 = self.p4 if proj: if hasattr(vobj, "ExtLines") and hasattr(vobj, "ScaleMultiplier"): # The scale multiplier also affects the value # of the extension line; this makes sure a maximum length # is used if the calculated value is larger than it. dmax = vobj.ExtLines.Value * vobj.ScaleMultiplier if dmax and proj.Length > dmax: if dmax > 0: self.p1 = self.p2 + DraftVecUtils.scaleTo(proj, dmax) self.p4 = self.p3 + DraftVecUtils.scaleTo(proj, dmax) else: rest = proj.Length + dmax self.p1 = self.p2 + DraftVecUtils.scaleTo(proj, rest) self.p4 = self.p3 + DraftVecUtils.scaleTo(proj, rest) else: proj = (self.p3 - self.p2).cross(App.Vector(0, 0, 1)) # Calculate the arrow positions p2 = (self.p2.x, self.p2.y, self.p2.z) p3 = (self.p3.x, self.p3.y, self.p3.z) self.trans1.translation.setValue(p2) self.coord1.point.setValue(p2) self.trans2.translation.setValue(p3) self.coord2.point.setValue(p3) # Calculate dimension and extension lines overshoots positions self.transDimOvershoot1.translation.setValue(p2) self.transDimOvershoot2.translation.setValue(p3) self.transExtOvershoot1.translation.setValue(p2) self.transExtOvershoot2.translation.setValue(p3) # Determine the orientation of the text by using a normal direction. # By default the value of +Z will be used, or a calculated value # from p2 and p3. So the text will lie on the XY plane # or a plane coplanar with p2 and p3. u = self.p3 - self.p2 u.normalize() if proj: _norm = u.cross(proj) norm = _norm.negative() else: norm = App.Vector(0, 0, 1) # If `Normal` exists and is different from the default `(0,0,0)`, # it will be used. if hasattr(obj, "Normal") and not DraftVecUtils.isNull(obj.Normal): norm = App.Vector(obj.Normal) if not DraftVecUtils.isNull(norm): norm.normalize() # Calculate the position of the arrows and extension lines v1 = norm.cross(u) _plane_rot = DraftVecUtils.getPlaneRotation(u, v1, norm) rot1 = App.Placement(_plane_rot).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(vobj, "FlipArrows") and vobj.FlipArrows: u = u.negative() v2 = norm.cross(u) _plane_rot = DraftVecUtils.getPlaneRotation(u, v2, norm) rot2 = App.Placement(_plane_rot).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 - self.p2 u3.normalize() v3 = norm.cross(u3) _plane_rot = DraftVecUtils.getPlaneRotation(u3, v3, norm) rot3 = App.Placement(_plane_rot).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])) # Offset is the distance from the dimension line to the textual # element that displays the value of the measurement if hasattr(vobj, "TextSpacing") and hasattr(vobj, "ScaleMultiplier"): ts = vobj.TextSpacing.Value * vobj.ScaleMultiplier offset = DraftVecUtils.scaleTo(v1, ts) else: offset = DraftVecUtils.scaleTo(v1, 0.05) rott = rot1 if hasattr(vobj, "FlipText") and vobj.FlipText: _rott = App.Rotation(rott[0], rott[1], rott[2], rott[3]) rott = _rott.multiply(App.Rotation(norm, 180)).Q offset = offset.negative() # On first run the `DisplayMode` enumeration is not set, so we trap # the exception and set the display mode using the value # in the parameter database try: m = vobj.DisplayMode except AssertionError: m = ["2D", "3D"][utils.get_param("dimstyle", 0)] if m == "3D": offset = offset.negative() # The position of the text element in the dimension is provided # in absolute coordinates by the value of `TextPosition`, # if it is different from the default `(0,0,0)` if (hasattr(vobj, "TextPosition") and not DraftVecUtils.isNull(vobj.TextPosition)): self.tbase = vobj.TextPosition else: # Otherwise the position is calculated from the end points # of the dimension line, and the offset that depends # on `TextSpacing` center = self.p2 + (self.p3 - self.p2).multiply(0.5) self.tbase = center + offset 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]) show_unit = True if hasattr(vobj, "ShowUnit"): show_unit = vobj.ShowUnit # Set text element showing the value of the dimension length = (self.p3 - self.p2).Length unit = None if hasattr(vobj, "UnitOverride"): unit = vobj.UnitOverride # Special representation if we use 'Building US' scheme u_params = App.ParamGet("User parameter:BaseApp/Preferences/Units") if u_params.GetInt("UserSchema", 0) == 5: s = App.Units.Quantity(length, App.Units.Length).UserString self.string = s.replace("' ", "'- ") # feet self.string = s.replace("+", " ") elif hasattr(vobj, "Decimals"): self.string = units.display_external(length, vobj.Decimals, 'Length', show_unit, unit) else: self.string = units.display_external(length, None, 'Length', show_unit, unit) if hasattr(vobj, "Override") and vobj.Override: self.string = vobj.Override.replace("$dim", self.string) self.text.string = utils.string_encode_coin(self.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) * vobj.FontSize.Value / 4.0 spacing = (self.p3 - self.p2).Length / 2.0 - textsize self.p2a = self.p2 + DraftVecUtils.scaleTo(self.p3 - self.p2, spacing) self.p2b = self.p3 + DraftVecUtils.scaleTo(self.p2 - 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 updateData(self, obj, prop): """Execute when a property from the Proxy class is changed.""" if not hasattr(self, "arc"): return arcsegs = 24 vobj = obj.ViewObject # Determine the orientation of the text by using a normal direction. # Also calculate the arc data. if DraftVecUtils.isNull(obj.Normal): norm = App.Vector(0, 0, 1) else: norm = obj.Normal radius = (obj.Dimline - obj.Center).Length self.circle = Part.makeCircle(radius, obj.Center, norm, obj.FirstAngle.Value, obj.LastAngle.Value) self.p2 = self.circle.Vertexes[0].Point self.p3 = self.circle.Vertexes[-1].Point midp = DraftGeomUtils.findMidpoint(self.circle.Edges[0]) ray = midp - obj.Center # Set text value if obj.LastAngle.Value > obj.FirstAngle.Value: angle = obj.LastAngle.Value - obj.FirstAngle.Value else: angle = (360 - obj.FirstAngle.Value) + obj.LastAngle.Value show_unit = True if hasattr(vobj, "ShowUnit"): show_unit = vobj.ShowUnit if hasattr(vobj, "Decimals"): self.string = units.display_external(angle, vobj.Decimals, 'Angle', show_unit) else: self.string = units.display_external(angle, None, 'Angle', show_unit) if vobj.Override: self.string = vobj.Override.replace("$dim", self.string) self.text.string = utils.string_encode_coin(self.string) self.text3d.string = utils.string_encode_coin(self.string) # On first run the `DisplayMode` enumeration is not set, so we trap # the exception and set the display mode using the value # in the parameter database try: m = vobj.DisplayMode except AssertionError: m = ["2D", "3D"][utils.get_param("dimstyle", 0)] # Set the arc first = self.circle.FirstParameter last = self.circle.LastParameter if m == "3D": # Calculate the spacing of the text spacing = len(self.string) * vobj.FontSize.Value / 8.0 pts1 = [] cut = None pts2 = [] for i in range(arcsegs + 1): p = self.circle.valueAt(first + (last - first) / arcsegs * i) if (p - midp).Length <= spacing: if cut is None: cut = i else: if cut is None: pts1.append([p.x, p.y, p.z]) else: pts2.append([p.x, p.y, p.z]) self.coords.point.setValues(pts1 + pts2) pts1_num = len(pts1) pts2_num = len(pts2) i1 = pts1_num i2 = i1 + pts2_num self.arc.coordIndex.setValues( 0, pts1_num + pts2_num + 1, list(range(pts1_num)) + [-1] + list(range(i1, i2))) if pts1_num >= 3 and pts2_num >= 3: self.circle1 = Part.Arc( App.Vector(pts1[0][0], pts1[0][1], pts1[0][2]), App.Vector(pts1[1][0], pts1[1][1], pts1[1][2]), App.Vector(pts1[-1][0], pts1[-1][1], pts1[-1][2])).toShape() self.circle2 = Part.Arc( App.Vector(pts2[0][0], pts2[0][1], pts2[0][2]), App.Vector(pts2[1][0], pts2[1][1], pts2[1][2]), App.Vector(pts2[-1][0], pts2[-1][1], pts2[-1][2])).toShape() else: pts = [] for i in range(arcsegs + 1): p = self.circle.valueAt(first + (last - first) / arcsegs * i) pts.append([p.x, p.y, p.z]) self.coords.point.setValues(pts) self.arc.coordIndex.setValues(0, arcsegs + 1, list(range(arcsegs + 1))) # Set the arrow coords and rotation p2 = (self.p2.x, self.p2.y, self.p2.z) p3 = (self.p3.x, self.p3.y, self.p3.z) self.trans1.translation.setValue(p2) self.coord1.point.setValue(p2) self.trans2.translation.setValue(p3) self.coord2.point.setValue(p3) # Calculate small chords to make arrows look better arrowlength = 4 * vobj.ArrowSize.Value u1 = (self.circle.valueAt(first + arrowlength) - self.circle.valueAt(first)).normalize() u2 = (self.circle.valueAt(last) - self.circle.valueAt(last - arrowlength)).normalize() if hasattr(vobj, "FlipArrows") and vobj.FlipArrows: u1 = u1.negative() u2 = u2.negative() w2 = self.circle.Curve.Axis w1 = w2.negative() v1 = w1.cross(u1) v2 = w2.cross(u2) _plane_rot_1 = DraftVecUtils.getPlaneRotation(u1, v1, w1) _plane_rot_2 = DraftVecUtils.getPlaneRotation(u2, v2, w2) q1 = App.Placement(_plane_rot_1).Rotation.Q q2 = App.Placement(_plane_rot_2).Rotation.Q self.trans1.rotation.setValue((q1[0], q1[1], q1[2], q1[3])) self.trans2.rotation.setValue((q2[0], q2[1], q2[2], q2[3])) # Set text position and rotation self.tbase = midp if (hasattr(vobj, "TextPosition") and not DraftVecUtils.isNull(vobj.TextPosition)): self.tbase = vobj.TextPosition u3 = ray.cross(norm).normalize() v3 = norm.cross(u3) _plane_rot_3 = DraftVecUtils.getPlaneRotation(u3, v3, norm) r = App.Placement(_plane_rot_3).Rotation offset = r.multVec(App.Vector(0, 1, 0)) if hasattr(vobj, "TextSpacing"): offset = DraftVecUtils.scaleTo(offset, vobj.TextSpacing.Value) else: offset = DraftVecUtils.scaleTo(offset, 0.05) if m == "3D": offset = offset.negative() self.tbase = self.tbase.add(offset) q = r.Q self.textpos.translation.setValue( [self.tbase.x, self.tbase.y, self.tbase.z]) self.textpos.rotation = coin.SbRotation(q[0], q[1], q[2], q[3]) # Set the angle property _round_1 = round(obj.Angle, utils.precision()) _round_2 = round(angle, utils.precision()) if _round_1 != _round_2: obj.Angle = angle
def drag_rotation(self, vector, modify): """ Manage rotation during dragging """ datum = self.datums['rotation'] #center is the first selected node _ctr = datum['center'] #ref_vec is the refernce vector from the previous mouse position #used to calculate the change in rotation _ref_vec = datum['ref_vec'] #angle is the cumulative angle of rotation _angle = datum['angle'] #non-continuous rotation case if not _ctr: _ctr = self.datums['drag_start'] datum['center'] = _ctr _ref_vec = vector.sub(_ctr) if _ref_vec != Vector(): _ref_vec.normalize() datum['ref_vec'] = _ref_vec self.nodes['transform'].center = coin.SbVec3f(tuple(_ctr)) _ctr = _ctr.add( Vector(self.nodes['transform'].translation.getValue()) ) #scale the rotation by one-tenth if shift is depressed _scale = 1.0 if modify: _scale = 0.1 #calculate the direction of rotation between the current mouse position #vector and the previous. Normalize and reverse sign on direction.z _vec = vector.sub(_ctr) if _vec != Vector(): _vec.normalize() _dir = _vec.cross(_ref_vec).z if _dir != 0: _dir = -_dir / abs(_dir) #calculate the cumulatibe rotation _rot = _angle + _vec.getAngle(_ref_vec) * _dir * _scale #store the updated values datum['ref_vec'] = _vec datum['angle'] = _rot #return the +z axis rotation for the transformation return coin.SbRotation(coin.SbVec3f(0.0, 0.0, 1.0), _rot)
def makeFrame(self, frame_labels): """ Method which makes a Coin3D frame to show a current pose in a RobRotation. A frame is made from 3 red, green and blue arrows representing X, Y and Z. Arrows are each constructed from a shaft and an arrowhead. Their dimensions and other attributes are unassigned as they are extracted from appropriate `RobRotation` properties. Returns: A SoSeparator with the frame shown in the FreeCAD View. """ # make a generic shaft from 0 in Y direction shaft_vertices = coin.SoVertexProperty() shaft_vertices.vertex.setNum(2) shaft_vertices.vertex.set1Value(0, 0, 0, 0) self.frame_shaft = coin.SoLineSet() self.frame_shaft.vertexProperty.setValue(shaft_vertices) self.frame_shaft.numVertices.setNum(1) self.frame_shaft.numVertices.setValue(2) # make a generic conic arrowhead oriented in Y axis direction and # move it at the end of the shaft self.frame_arrowhead_translation = coin.SoTranslation() self.frame_arrowhead_cone = coin.SoCone() self.frame_arrowhead = coin.SoSwitch() self.frame_arrowhead.addChild(self.frame_arrowhead_translation) self.frame_arrowhead.addChild(self.frame_arrowhead_cone) # make rotations to rotate prepared shaft and arrowhead for Y axis # direction also to X and Z rot_y2x = coin.SoRotation() rot_y2x.rotation.setValue(coin.SbRotation(coin.SbVec3f(0, 1, 0), coin.SbVec3f(1, 0, 0))) rot_y2z = coin.SoRotation() rot_y2z.rotation.setValue(coin.SbRotation(coin.SbVec3f(0, 1, 0), coin.SbVec3f(0, 0, 1))) # prepare colors for X,Y,Z which will correspond to R,G,B as customary self.frame_color_x = coin.SoPackedColor() self.frame_color_y = coin.SoPackedColor() self.frame_color_z = coin.SoPackedColor() # make complete colored and rotated arrows x_arrow = coin.SoSeparator() x_arrow.addChild(rot_y2x) x_arrow.addChild(self.frame_color_x) x_arrow.addChild(self.frame_shaft) x_arrow.addChild(self.frame_arrowhead) x_arrow.addChild(frame_labels[0]) y_arrow = coin.SoSeparator() y_arrow.addChild(self.frame_color_y) y_arrow.addChild(self.frame_shaft) y_arrow.addChild(self.frame_arrowhead) y_arrow.addChild(frame_labels[1]) z_arrow = coin.SoSeparator() z_arrow.addChild(rot_y2z) z_arrow.addChild(self.frame_color_z) z_arrow.addChild(self.frame_shaft) z_arrow.addChild(self.frame_arrowhead) z_arrow.addChild(frame_labels[2]) # prepare draw style to control shaft width self.frame_drawstyle = coin.SoDrawStyle() # make complete frame and it to shaded display mode separated_frame = coin.SoSeparator() separated_frame.addChild(self.frame_drawstyle) separated_frame.addChild(x_arrow) separated_frame.addChild(y_arrow) separated_frame.addChild(z_arrow) return separated_frame
def updateData(self, obj, prop): if hasattr(self,"arc"): from pivy import coin import Part, DraftGeomUtils import DraftGui arcsegs = 24 # calculate the arc data if DraftVecUtils.isNull(obj.Normal): norm = App.Vector(0,0,1) else: norm = obj.Normal radius = (obj.Dimline.sub(obj.Center)).Length self.circle = Part.makeCircle(radius,obj.Center,norm,obj.FirstAngle.Value,obj.LastAngle.Value) self.p2 = self.circle.Vertexes[0].Point self.p3 = self.circle.Vertexes[-1].Point mp = DraftGeomUtils.findMidpoint(self.circle.Edges[0]) ray = mp.sub(obj.Center) # set text value if obj.LastAngle.Value > obj.FirstAngle.Value: a = obj.LastAngle.Value - obj.FirstAngle.Value else: a = (360 - obj.FirstAngle.Value) + obj.LastAngle.Value su = True if hasattr(obj.ViewObject,"ShowUnit"): su = obj.ViewObject.ShowUnit if hasattr(obj.ViewObject,"Decimals"): self.string = DraftGui.displayExternal(a,obj.ViewObject.Decimals,'Angle',su) else: self.string = DraftGui.displayExternal(a,None,'Angle',su) 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) # check display mode 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)] # set the arc if m == "3D": # calculate the spacing of the text spacing = (len(self.string)*obj.ViewObject.FontSize.Value)/8.0 pts1 = [] cut = None pts2 = [] for i in range(arcsegs+1): p = self.circle.valueAt(self.circle.FirstParameter+((self.circle.LastParameter-self.circle.FirstParameter)/arcsegs)*i) if (p.sub(mp)).Length <= spacing: if cut is None: cut = i else: if cut is None: pts1.append([p.x,p.y,p.z]) else: pts2.append([p.x,p.y,p.z]) self.coords.point.setValues(pts1+pts2) i1 = len(pts1) i2 = i1+len(pts2) self.arc.coordIndex.setValues(0,len(pts1)+len(pts2)+1,list(range(len(pts1)))+[-1]+list(range(i1,i2))) if (len(pts1) >= 3) and (len(pts2) >= 3): self.circle1 = Part.Arc(App.Vector(pts1[0][0],pts1[0][1],pts1[0][2]),App.Vector(pts1[1][0],pts1[1][1],pts1[1][2]),App.Vector(pts1[-1][0],pts1[-1][1],pts1[-1][2])).toShape() self.circle2 = Part.Arc(App.Vector(pts2[0][0],pts2[0][1],pts2[0][2]),App.Vector(pts2[1][0],pts2[1][1],pts2[1][2]),App.Vector(pts2[-1][0],pts2[-1][1],pts2[-1][2])).toShape() else: pts = [] for i in range(arcsegs+1): p = self.circle.valueAt(self.circle.FirstParameter+((self.circle.LastParameter-self.circle.FirstParameter)/arcsegs)*i) pts.append([p.x,p.y,p.z]) self.coords.point.setValues(pts) self.arc.coordIndex.setValues(0,arcsegs+1,list(range(arcsegs+1))) # set the arrow coords and rotation 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 small chords to make arrows look better arrowlength = 4*obj.ViewObject.ArrowSize.Value u1 = (self.circle.valueAt(self.circle.FirstParameter+arrowlength)).sub(self.circle.valueAt(self.circle.FirstParameter)).normalize() u2 = (self.circle.valueAt(self.circle.LastParameter)).sub(self.circle.valueAt(self.circle.LastParameter-arrowlength)).normalize() if hasattr(obj.ViewObject,"FlipArrows"): if obj.ViewObject.FlipArrows: u1 = u1.negative() u2 = u2.negative() w2 = self.circle.Curve.Axis w1 = w2.negative() v1 = w1.cross(u1) v2 = w2.cross(u2) q1 = App.Placement(DraftVecUtils.getPlaneRotation(u1,v1,w1)).Rotation.Q q2 = App.Placement(DraftVecUtils.getPlaneRotation(u2,v2,w2)).Rotation.Q self.trans1.rotation.setValue((q1[0],q1[1],q1[2],q1[3])) self.trans2.rotation.setValue((q2[0],q2[1],q2[2],q2[3])) # setting text pos & rot self.tbase = mp if hasattr(obj.ViewObject,"TextPosition"): if not DraftVecUtils.isNull(obj.ViewObject.TextPosition): self.tbase = obj.ViewObject.TextPosition u3 = ray.cross(norm).normalize() v3 = norm.cross(u3) r = App.Placement(DraftVecUtils.getPlaneRotation(u3,v3,norm)).Rotation offset = r.multVec(App.Vector(0,1,0)) if hasattr(obj.ViewObject,"TextSpacing"): offset = DraftVecUtils.scaleTo(offset,obj.ViewObject.TextSpacing.Value) else: offset = DraftVecUtils.scaleTo(offset,0.05) if m == "3D": offset = offset.negative() self.tbase = self.tbase.add(offset) q = r.Q self.textpos.translation.setValue([self.tbase.x,self.tbase.y,self.tbase.z]) self.textpos.rotation = coin.SbRotation(q[0],q[1],q[2],q[3]) # set the angle property if round(obj.Angle,utils.precision()) != round(a,utils.precision()): obj.Angle = a
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 makeFrame(self, frame_labels): # make a generic shaft from 0 in Y direction shaft_vertices = coin.SoVertexProperty() shaft_vertices.vertex.setNum(2) shaft_vertices.vertex.set1Value(0, 0, 0, 0) self.frame_shaft = coin.SoLineSet() self.frame_shaft.vertexProperty.setValue(shaft_vertices) self.frame_shaft.numVertices.setNum(1) self.frame_shaft.numVertices.setValue(2) # make a generic conic arrowhead oriented in Y axis direction and # move it at the end of the shaft self.frame_arrowhead_translation = coin.SoTranslation() self.frame_arrowhead_cone = coin.SoCone() self.frame_arrowhead = coin.SoSwitch() self.frame_arrowhead.addChild(self.frame_arrowhead_translation) self.frame_arrowhead.addChild(self.frame_arrowhead_cone) # make rotations to rotate prepared shaft and arrowhead for Y axis # direction also to X and Z rot_y2x = coin.SoRotation() rot_y2x.rotation.setValue( coin.SbRotation(coin.SbVec3f(0, 1, 0), coin.SbVec3f(1, 0, 0))) rot_y2z = coin.SoRotation() rot_y2z.rotation.setValue( coin.SbRotation(coin.SbVec3f(0, 1, 0), coin.SbVec3f(0, 0, 1))) # prepare colors for X,Y,Z which will correspond to R,G,B as customary self.frame_color_x = coin.SoPackedColor() self.frame_color_y = coin.SoPackedColor() self.frame_color_z = coin.SoPackedColor() # make complete colored and rotated arrows x_arrow = coin.SoSeparator() x_arrow.addChild(rot_y2x) x_arrow.addChild(self.frame_color_x) x_arrow.addChild(self.frame_shaft) x_arrow.addChild(self.frame_arrowhead) x_arrow.addChild(frame_labels[0]) y_arrow = coin.SoSeparator() y_arrow.addChild(self.frame_color_y) y_arrow.addChild(self.frame_shaft) y_arrow.addChild(self.frame_arrowhead) y_arrow.addChild(frame_labels[1]) z_arrow = coin.SoSeparator() z_arrow.addChild(rot_y2z) z_arrow.addChild(self.frame_color_z) z_arrow.addChild(self.frame_shaft) z_arrow.addChild(self.frame_arrowhead) z_arrow.addChild(frame_labels[2]) # prepare draw style to control shaft width self.frame_drawstyle = coin.SoDrawStyle() # make complete frame and it to shaded display mode separated_frame = coin.SoSeparator() separated_frame.addChild(self.frame_drawstyle) separated_frame.addChild(x_arrow) separated_frame.addChild(y_arrow) separated_frame.addChild(z_arrow) return separated_frame