def MoveCallback(self,point,snapinfo): self.currentpoint = point if len(self.points) == 1: # we have the base point already self.Length.setText(FreeCAD.Units.Quantity(str(self.points[-1].sub(point).Length)+"mm").UserString) self.Length.selectAll() self.Length.setFocus() elif len(self.points) == 2: # now we already have our base line, we update the 1st rectangle p = point v1 = point.sub(self.points[1]) v4 = v1.cross(self.points[1].sub(self.points[0])) if v4 and v4.Length: n = (self.points[1].sub(self.points[0])).cross(v4) if n and n.Length: n = DraftVecUtils.project(v1,n) p = self.points[1].add(n) self.cubetracker[0].p3(p) self.Width.setText(FreeCAD.Units.Quantity(str(self.cubetracker[0].getSize()[1])+"mm").UserString) self.Width.selectAll() self.Width.setFocus() elif len(self.points) == 3: # we must first find our height point by projecting on the normal w = DraftVecUtils.project(point.sub(self.cubetracker[0].p3()),self.normal) # then we update all rectangles self.cubetracker[1].p3((self.cubetracker[0].p2()).add(w)) self.cubetracker[2].p3((self.cubetracker[0].p4()).add(w)) self.cubetracker[3].p1((self.cubetracker[0].p1()).add(w)) self.cubetracker[3].p3((self.cubetracker[0].p3()).add(w)) self.Height.setText(FreeCAD.Units.Quantity(str(w.Length)+"mm").UserString) self.Height.selectAll() self.Height.setFocus()
def makeFlatFace(mobile=[], fixed=[], vert=False): import Part import DraftVecUtils import DraftGeomUtils if not fixed: pol = Part.makePolygon(mobile + [mobile[0]]) pol = DraftGeomUtils.flattenWire(pol) return Part.Face(pol) elif len(fixed) == 3: tempf = Part.Face(Part.makePolygon(fixed + [fixed[0]])) v4 = mobile[0].add( DraftVecUtils.project(tempf.CenterOfMass.sub(mobile[0]), tempf.normalAt(0, 0))) pol = Part.makePolygon([fixed[0], fixed[1], v4, fixed[2], fixed[0]]) pol = DraftGeomUtils.flattenWire(pol) return Part.Face(pol) elif len(fixed) == 2: tp = DraftGeomUtils.findMidpoint( Part.LineSegment(mobile[0], mobile[1]).toShape()) tempf = Part.Face(Part.makePolygon(fixed + [tp, fixed[0]])) v4 = mobile[0].add( DraftVecUtils.project(tempf.CenterOfMass.sub(mobile[0]), tempf.normalAt(0, 0))) v5 = mobile[1].add( DraftVecUtils.project(tempf.CenterOfMass.sub(mobile[1]), tempf.normalAt(0, 0))) if vert: pol = Part.makePolygon([fixed[0], v4, v5, fixed[1], fixed[0]]) else: pol = Part.makePolygon(fixed + [v4, v5, fixed[0]]) pol = DraftGeomUtils.flattenWire(pol) return Part.Face(pol)
def PointCallback(self,point,snapinfo): import FreeCADGui import Image if not point: # cancelled self.tracker.off() return elif not self.basepoint: # this is our first clicked point, nothing to do just yet self.basepoint = point self.tracker.setorigin(point) self.tracker.on() FreeCADGui.Snapper.getPoint(last=point,callback=self.PointCallback,movecallback=self.MoveCallback) else: # this is our second point self.tracker.off() midpoint = self.basepoint.add(self.opposite.sub(self.basepoint).multiply(0.5)) rotation = FreeCAD.DraftWorkingPlane.getRotation().Rotation diagonal = self.opposite.sub(self.basepoint) length = DraftVecUtils.project(diagonal,FreeCAD.DraftWorkingPlane.u).Length height = DraftVecUtils.project(diagonal,FreeCAD.DraftWorkingPlane.v).Length FreeCAD.ActiveDocument.openTransaction("Create image plane") image = FreeCAD.activeDocument().addObject('Image::ImagePlane','ImagePlane') image.Label = os.path.splitext(os.path.basename(self.filename))[0] image.ImageFile = self.filename image.Placement = FreeCAD.Placement(midpoint,rotation) image.XSize = length image.YSize = height FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute()
def isPlanar(shape): """Return True if the given shape or list of points is planar.""" n = getNormal(shape) if not n: return False if isinstance(shape, list): if len(shape) <= 3: return True else: for v in shape[3:]: pv = v.sub(shape[0]) rv = DraftVecUtils.project(pv, n) if not DraftVecUtils.isNull(rv): return False else: if len(shape.Vertexes) <= 3: return True for p in shape.Vertexes[1:]: pv = p.Point.sub(shape.Vertexes[0].Point) rv = DraftVecUtils.project(pv, n) if not DraftVecUtils.isNull(rv): return False return True
def _get_proj(vec, plane=None): if not plane: return vec nx = DraftVecUtils.project(vec, plane.u) ny = DraftVecUtils.project(vec, plane.v) return App.Vector(nx.Length, ny.Length, 0)
def getSize(self): """returns (length,width) of the rectangle""" p1 = Vector(self.coords.point.getValues()[0].getValue()) p2 = Vector(self.coords.point.getValues()[2].getValue()) diag = p2.sub(p1) return ((DraftVecUtils.project(diag, self.u)).Length, (DraftVecUtils.project(diag, self.v)).Length)
def updateRectangle(obj, nodeIndex, v): if nodeIndex == 0: obj.Placement.Base = obj.Placement.multVec(v) elif nodeIndex == 1: obj.Length = DraftVecUtils.project(v, App.Vector(1, 0, 0)).Length elif nodeIndex == 2: obj.Height = DraftVecUtils.project(v, App.Vector(0, 1, 0)).Length
def get_proj(vec, plane=None): """Get a projection of the vector in the plane's u and v directions. TODO: check if the same function for SVG and DXF projection can be used so that this function is not just duplicated code. This function may also be present elsewhere, like `WorkingPlane` or `DraftGeomUtils`, so we should avoid code duplication. Parameters ---------- vec: Base::Vector3 An arbitrary vector that will be projected on the U and V directions. plane: WorkingPlane.Plane An object of type `WorkingPlane`. """ if not plane: return vec nx = DraftVecUtils.project(vec, plane.u) lx = nx.Length if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx ny = DraftVecUtils.project(vec, plane.v) ly = ny.Length if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly # if techdraw: buggy - we now simply do it at the end # ly = -ly return App.Vector(lx, ly, 0)
def createObject(self): """Create the actual object in the current document.""" plane = App.DraftWorkingPlane p1 = self.node[0] p3 = self.node[-1] diagonal = p3.sub(p1) halfdiag = App.Vector(diagonal).multiply(0.5) center = p1.add(halfdiag) p2 = p1.add(DraftVecUtils.project(diagonal, plane.v)) p4 = p1.add(DraftVecUtils.project(diagonal, plane.u)) r1 = (p4.sub(p1).Length) / 2 r2 = (p2.sub(p1).Length) / 2 try: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. rot, sup, pts, fil = self.getStrings() if r2 > r1: r1, r2 = r2, r1 m = App.Matrix() m.rotateZ(math.pi / 2) rot1 = App.Rotation() rot1.Q = eval(rot) rot2 = App.Placement(m) rot2 = rot2.Rotation rot = str((rot1.multiply(rot2)).Q) if utils.getParam("UsePartPrimitives", False): # Insert a Part::Primitive object Gui.addModule("Part") _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Ellipse", "Ellipse")' _cmd_list = [ 'ellipse = ' + _cmd, 'ellipse.MajorRadius = ' + str(r1), 'ellipse.MinorRadius = ' + str(r2), 'pl = FreeCAD.Placement()', 'pl.Rotation.Q= ' + rot, 'pl.Base = ' + DraftVecUtils.toString(center), 'ellipse.Placement = pl', 'Draft.autogroup(ellipse)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Ellipse"), _cmd_list) else: # Insert a Draft ellipse Gui.addModule("Draft") _cmd = 'Draft.makeEllipse' _cmd += '(' _cmd += str(r1) + ', ' + str(r2) + ', ' _cmd += 'placement=pl, ' _cmd += 'face=' + fil + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = [ 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + DraftVecUtils.toString(center), 'ellipse = ' + _cmd, 'Draft.autogroup(ellipse)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Ellipse"), _cmd_list) except Exception: _err("Draft: Error: Unable to create object.") self.finish(cont=True)
def update(self,point): "sets the opposite (diagonal) point of the rectangle" diagonal = point.sub(self.origin) inpoint1 = self.origin.add(DraftVecUtils.project(diagonal,self.v)) inpoint2 = self.origin.add(DraftVecUtils.project(diagonal,self.u)) self.coords.point.set1Value(1,inpoint1.x,inpoint1.y,inpoint1.z) self.coords.point.set1Value(2,point.x,point.y,point.z) self.coords.point.set1Value(3,inpoint2.x,inpoint2.y,inpoint2.z)
def createObject(self): """Create the final object in the current document.""" plane = App.DraftWorkingPlane p1 = self.node[0] p3 = self.node[-1] diagonal = p3.sub(p1) p2 = p1.add(DraftVecUtils.project(diagonal, plane.v)) p4 = p1.add(DraftVecUtils.project(diagonal, plane.u)) length = p4.sub(p1).Length if abs(DraftVecUtils.angle(p4.sub(p1), plane.u, plane.axis)) > 1: length = -length height = p2.sub(p1).Length if abs(DraftVecUtils.angle(p2.sub(p1), plane.v, plane.axis)) > 1: height = -height try: # The command to run is built as a series of text strings # to be committed through the `draftutils.todo.ToDo` class. rot, sup, pts, fil = self.getStrings() base = p1 if length < 0: length = -length base = base.add((p1.sub(p4)).negative()) if height < 0: height = -height base = base.add((p1.sub(p2)).negative()) Gui.addModule("Draft") if utils.getParam("UsePartPrimitives", False): # Insert a Part::Primitive object _cmd = 'FreeCAD.ActiveDocument.' _cmd += 'addObject("Part::Plane", "Plane")' _cmd_list = [ 'plane = ' + _cmd, 'plane.Length = ' + str(length), 'plane.Width = ' + str(height), 'pl = FreeCAD.Placement()', 'pl.Rotation.Q=' + rot, 'pl.Base = ' + DraftVecUtils.toString(base), 'plane.Placement = pl', 'Draft.autogroup(plane)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Plane"), _cmd_list) else: _cmd = 'Draft.makeRectangle' _cmd += '(' _cmd += 'length=' + str(length) + ', ' _cmd += 'height=' + str(height) + ', ' _cmd += 'placement=pl, ' _cmd += 'face=' + fil + ', ' _cmd += 'support=' + sup _cmd += ')' _cmd_list = [ 'pl = FreeCAD.Placement()', 'pl.Rotation.Q = ' + rot, 'pl.Base = ' + DraftVecUtils.toString(base), 'rec = ' + _cmd, 'Draft.autogroup(rec)', 'FreeCAD.ActiveDocument.recompute()' ] self.commit(translate("draft", "Create Rectangle"), _cmd_list) except Exception: _err("Draft: error delaying commit") self.finish(cont=True)
def update(self, point): """sets the opposite (diagonal) point of the rectangle""" if DEBUG_T: App.Console.PrintMessage("rectangle tracker : update \n") diagonal = point.sub(self.origin) inpoint1 = self.origin.add(DraftVecUtils.project(diagonal, self.v)) inpoint2 = self.origin.add(DraftVecUtils.project(diagonal, self.u)) self.coords.point.set1Value(1, inpoint1.x, inpoint1.y, inpoint1.z) self.coords.point.set1Value(2, point.x, point.y, point.z) self.coords.point.set1Value(3, inpoint2.x, inpoint2.y, inpoint2.z)
def update_object_from_edit_points(self, obj, node_idx, v, alt_edit_mode=0): if node_idx == 0: obj.Placement.Base = obj.Placement.multVec(v) elif node_idx == 1: obj.Length = DraftVecUtils.project(v, App.Vector(1, 0, 0)).Length elif node_idx == 2: obj.Height = DraftVecUtils.project(v, App.Vector(0, 1, 0)).Length
def getProj(vec, plane): if not plane: return vec nx = DraftVecUtils.project(vec,plane.u) lx = nx.Length if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx ny = DraftVecUtils.project(vec,plane.v) ly = ny.Length if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly #if techdraw: buggy - we now simply do it at the end # ly = -ly return Vector(lx,ly,0)
def getProj(vec, plane): if not plane: return vec nx = DraftVecUtils.project(vec, plane.u) lx = nx.Length if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx ny = DraftVecUtils.project(vec, plane.v) ly = ny.Length if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly #if techdraw: buggy - we now simply do it at the end # ly = -ly return Vector(lx, ly, 0)
def updatePartBox(obj, nodeIndex, v): if nodeIndex == 0: obj.Placement.Base = obj.Placement.Base + v elif nodeIndex == 1: _vector = DraftVecUtils.project(v, App.Vector(1, 0, 0)) obj.Length = _vector.Length elif nodeIndex == 2: _vector = DraftVecUtils.project(v, App.Vector(0, 1, 0)) obj.Width = _vector.Length elif nodeIndex == 3: _vector = DraftVecUtils.project(v, App.Vector(0, 0, 1)) obj.Height = _vector.Length
def isInside(self,point): "returns True if the given point is inside the rectangle" vp = point.sub(self.p1()) uv = self.p2().sub(self.p1()) vv = self.p4().sub(self.p1()) uvp = DraftVecUtils.project(vp,uv) vvp = DraftVecUtils.project(vp,vv) if uvp.getAngle(uv) < 1: if vvp.getAngle(vv) < 1: if uvp.Length <= uv.Length: if vvp.Length <= vv.Length: return True return False
def getProjectionToSVGPlane(vec, plane): """getProjectionToSVGPlane(Vector, Plane): Returns projection of vector on plane. """ nx = DraftVecUtils.project(vec, plane.u) lx = nx.Length if abs(nx.getAngle(plane.u)) > 0.1: lx = -lx ny = DraftVecUtils.project(vec, plane.v) ly = ny.Length if abs(ny.getAngle(plane.v)) > 0.1: ly = -ly return FreeCAD.Vector(lx, ly, 0)
def getRect(p, obj): """returns length,height,placement""" pl = obj.Placement.copy() pl.Base = p[0] diag = p[2].sub(p[0]) bb = p[1].sub(p[0]) bh = p[3].sub(p[0]) nb = DraftVecUtils.project(diag, bb) nh = DraftVecUtils.project(diag, bh) if obj.Length.Value < 0: l = -nb.Length else: l = nb.Length if obj.Height.Value < 0: h = -nh.Length else: h = nh.Length return l, h, pl
def _get_proj(vec, plane=None): """Get a projection of the vector in the plane's u and v directions. TODO: check if the same function for SVG and DXF projection can be used so that this function is not just duplicated code. This function may also be present elsewhere, like `WorkingPlane` or `DraftGeomUtils`, so we should avoid code duplication. """ if not plane: return vec nx = DraftVecUtils.project(vec, plane.u) ny = DraftVecUtils.project(vec, plane.v) return App.Vector(nx.Length, ny.Length, 0)
def setString(self,text=None): "sets the dim string to the given value or auto value" self.dimnode.param1.setValue(.5) p1 = Vector(self.dimnode.pnts.getValues()[0].getValue()) p2 = Vector(self.dimnode.pnts.getValues()[-1].getValue()) m = self.dimnode.datumtype.getValue() if m == 2: self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(1,0,0))).Length elif m == 3: self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(0,1,0))).Length else: self.Distance = (p2.sub(p1)).Length text = FreeCAD.Units.Quantity(self.Distance,FreeCAD.Units.Length).UserString self.dimnode.string.setValue(text)
def getCutVolume(cutplane, shapes): """getCutVolume(cutplane,shapes): returns a cut face and a cut volume from the given shapes and the given cutting plane""" import Part placement = FreeCAD.Placement(cutplane.Placement) # building boundbox bb = shapes[0].BoundBox for sh in shapes[1:]: bb.add(sh.BoundBox) bb.enlarge(1) um = vm = wm = 0 ax = placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) u = placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) v = placement.Rotation.multVec(FreeCAD.Vector(0, 1, 0)) if not bb.isCutPlane(placement.Base, ax): FreeCAD.Console.PrintMessage(str(translate("Arch", "No objects are cut by the plane"))) return None, None, None else: corners = [ FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMax), ] for c in corners: dv = c.sub(placement.Base) um1 = DraftVecUtils.project(dv, u).Length um = max(um, um1) vm1 = DraftVecUtils.project(dv, v).Length vm = max(vm, vm1) wm1 = DraftVecUtils.project(dv, ax).Length wm = max(wm, wm1) p1 = FreeCAD.Vector(-um, vm, 0) p2 = FreeCAD.Vector(um, vm, 0) p3 = FreeCAD.Vector(um, -vm, 0) p4 = FreeCAD.Vector(-um, -vm, 0) cutface = Part.makePolygon([p1, p2, p3, p4, p1]) cutface = Part.Face(cutface) cutface.Placement = placement cutnormal = DraftVecUtils.scaleTo(ax, wm) cutvolume = cutface.extrude(cutnormal) cutnormal = DraftVecUtils.neg(cutnormal) invcutvolume = cutface.extrude(cutnormal) return cutface, cutvolume, invcutvolume
def getCutVolume(cutplane, shapes): """getCutVolume(cutplane,shapes): returns a cut face and a cut volume from the given shapes and the given cutting plane""" import Part placement = FreeCAD.Placement(cutplane.Placement) # building boundbox bb = shapes[0].BoundBox for sh in shapes[1:]: bb.add(sh.BoundBox) bb.enlarge(1) um = vm = wm = 0 ax = placement.Rotation.multVec(FreeCAD.Vector(0, 0, 1)) u = placement.Rotation.multVec(FreeCAD.Vector(1, 0, 0)) v = placement.Rotation.multVec(FreeCAD.Vector(0, 1, 0)) if not bb.isCutPlane(placement.Base, ax): FreeCAD.Console.PrintMessage( str(translate("Arch", "No objects are cut by the plane"))) return None, None, None else: corners = [ FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMax) ] for c in corners: dv = c.sub(placement.Base) um1 = DraftVecUtils.project(dv, u).Length um = max(um, um1) vm1 = DraftVecUtils.project(dv, v).Length vm = max(vm, vm1) wm1 = DraftVecUtils.project(dv, ax).Length wm = max(wm, wm1) p1 = FreeCAD.Vector(-um, vm, 0) p2 = FreeCAD.Vector(um, vm, 0) p3 = FreeCAD.Vector(um, -vm, 0) p4 = FreeCAD.Vector(-um, -vm, 0) cutface = Part.makePolygon([p1, p2, p3, p4, p1]) cutface = Part.Face(cutface) cutface.Placement = placement cutnormal = DraftVecUtils.scaleTo(ax, wm) cutvolume = cutface.extrude(cutnormal) cutnormal = DraftVecUtils.neg(cutnormal) invcutvolume = cutface.extrude(cutnormal) return cutface, cutvolume, invcutvolume
def getLocalRot(self,point): "Same as getLocalCoords, but discards the WP position" xv = DraftVecUtils.project(point,self.u) x = xv.Length if xv.getAngle(self.u) > 1: x = -x yv = DraftVecUtils.project(point,self.v) y = yv.Length if yv.getAngle(self.v) > 1: y = -y zv = DraftVecUtils.project(point,self.axis) z = zv.Length if zv.getAngle(self.axis) > 1: z = -z return Vector(x,y,z)
def getLocalRot(self, point): "Same as getLocalCoords, but discards the WP position" xv = DraftVecUtils.project(point, self.u) x = xv.Length if xv.getAngle(self.u) > 1: x = -x yv = DraftVecUtils.project(point, self.v) y = yv.Length if yv.getAngle(self.v) > 1: y = -y zv = DraftVecUtils.project(point, self.axis) z = zv.Length if zv.getAngle(self.axis) > 1: z = -z return Vector(x, y, z)
def update_object_from_edit_points(self, obj, node_idx, v, alt_edit_mode=0): if node_idx == 0: obj.Placement.Base = obj.Placement.Base + v elif node_idx == 1: _vector = DraftVecUtils.project(v, App.Vector(1, 0, 0)) obj.Length = _vector.Length elif node_idx == 2: _vector = DraftVecUtils.project(v, App.Vector(0, 1, 0)) obj.Width = _vector.Length elif node_idx == 3: _vector = DraftVecUtils.project(v, App.Vector(0, 0, 1)) obj.Height = _vector.Length
def setup_clipping_plane(self, vobj): """Set-up the clipping plane of the 3dView. This method is called when the property CutView or the object Placement changes. """ sg = Gui.ActiveDocument.ActiveView.getSceneGraph() if self.clip: sg.removeChild(self.clip) self.clip = None '''for o in Draft.getGroupContents(vobj.Object.Objects,walls=True): if hasattr(o.ViewObject,"Lighting"): o.ViewObject.Lighting = "One side"''' # prefer keeping interior lighty self.clip = coin.SoClipPlane() self.clip.on.setValue(True) norm = vobj.Object.Proxy.getNormal(vobj.Object) mp = vobj.Object.Shape.CenterOfMass mp = DraftVecUtils.project(mp, norm) dist = mp.Length #- 0.1 # to not clip exactly on the section object norm = norm.negative() marg = 1 if hasattr(vobj, "CutMargin"): marg = vobj.CutMargin.Value if mp.getAngle(norm) > 1: dist += marg dist = -dist else: dist -= marg plane = coin.SbPlane(coin.SbVec3f(norm.x, norm.y, norm.z), dist) self.clip.plane.setValue(plane) sg.insertChild(self.clip, 0)
def updateWire(self,v): pts = self.obj.Points editPnt = v#self.invpl.multVec(v) # DNC: allows to close the curve by placing ends close to each other tol = 0.001 if ( ( self.editing == 0 ) and ( (editPnt - pts[-1]).Length < tol) ) or ( self.editing == len(pts) - 1 ) and ( (editPnt - pts[0]).Length < tol): self.obj.Closed = True # DNC: fix error message if edited point coincides with one of the existing points if ( editPnt in pts ) == True: # checks if point enter is equal to other, this could cause a OCC problem FreeCAD.Console.PrintMessage(translate("draft", "Is not possible to have two coincident points in this object, please try again.")+"\n") if Draft.getType(self.obj) in ["BezCurve"]: self.resetTrackers() else: self.trackers[self.editing].set(self.pl.multVec(self.obj.Points[self.editing])) return if Draft.getType(self.obj) in ["BezCurve"]: pts = self.recomputePointsBezier(pts,self.editing,v,self.obj.Degree,moveTrackers=False) # check that the new point lies on the plane of the wire import DraftGeomUtils, DraftVecUtils if self.obj.Closed: n = DraftGeomUtils.getNormal(self.obj.Shape) dv = editPnt.sub(pts[self.editing]) rn = DraftVecUtils.project(dv,n) if dv.Length: editPnt = editPnt.add(rn.negative()) pts[self.editing] = editPnt self.obj.Points = pts self.trackers[self.editing].set(self.pl.multVec(v))
def getLocalCoords(self, point): "returns the coordinates of a given point on the working plane" pt = point.sub(self.position) xv = DraftVecUtils.project(pt, self.u) x = xv.Length if xv.getAngle(self.u) > 1: x = -x yv = DraftVecUtils.project(pt, self.v) y = yv.Length if yv.getAngle(self.v) > 1: y = -y zv = DraftVecUtils.project(pt, self.axis) z = zv.Length if zv.getAngle(self.axis) > 1: z = -z return Vector(x, y, z)
def getLocalCoords(self,point): "returns the coordinates of a given point on the working plane" pt = point.sub(self.position) xv = DraftVecUtils.project(pt,self.u) x = xv.Length if xv.getAngle(self.u) > 1: x = -x yv = DraftVecUtils.project(pt,self.v) y = yv.Length if yv.getAngle(self.v) > 1: y = -y zv = DraftVecUtils.project(pt,self.axis) z = zv.Length if zv.getAngle(self.axis) > 1: z = -z return Vector(x,y,z)
def align_axis_to_edge(self, wall, sub_link): """Align the wall Placement in LCS xy plane to a given edge. If the linked subobject changes, the wall is not notified, so I was thinking to modify the Axis system object to do that. TODO: Take into account global placement. """ if sub_link is None: return linked_object = sub_link[0] linked_subobject_names = sub_link[1] for name in linked_subobject_names: subobject = linked_object.getSubObject(name) if hasattr(subobject, "ShapeType") and subobject.ShapeType == 'Edge': break import DraftVecUtils v1 = subobject.Vertexes[0].Point v2 = subobject.Vertexes[1].Point p = wall.Placement.Base.sub(v1) point_on_edge = DraftVecUtils.project(p, v2.sub(v1)) + v1 angle = Draft.DraftVecUtils.angle(App.Vector(1,0,0), v2.sub(v1)) print(angle) wall.Placement.Base.x = point_on_edge.x wall.Placement.Base.y = point_on_edge.y wall.Placement.Rotation.Angle = angle
def sortedge(edge): vdir = FreeCAD.Vector(1,0,0) proj = DraftVecUtils.project(edge.CenterOfMass,vdir) if proj.getAngle(vdir) < 1: return proj.Length else: return -proj.Length
def setString(self,text=None): "sets the dim string to the given value or auto value" self.dimnode.param1.setValue(.5) p1 = Vector(self.dimnode.pnts.getValues()[0].getValue()) p2 = Vector(self.dimnode.pnts.getValues()[-1].getValue()) m = self.dimnode.datumtype.getValue() if m == 2: self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(1,0,0))).Length elif m == 3: self.Distance = (DraftVecUtils.project(p2.sub(p1),Vector(0,1,0))).Length else: self.Distance = (p2.sub(p1)).Length if not text: text = Draft.getParam("dimPrecision",2) text = "%."+str(text)+"f" text = (text % self.Distance) self.dimnode.string.setValue(text)
def getProjectedLength(self, v, ref): """gets a signed length from projecting a vector on another""" proj = DraftVecUtils.project(v, ref) if proj.getAngle(ref) < 1: return proj.Length else: return -proj.Length
def order(self,face,right=False): """order(face,[right]): returns a list of vertices ordered clockwise. The first vertex will be the lefmost one, unless right is True, in which case the first vertex will be the rightmost one""" verts = [v.Point for v in face.OuterWire.OrderedVertexes] # flatten the polygon on the XY plane wp = WorkingPlane.plane() wp.alignToPointAndAxis(face.CenterOfMass,face.normalAt(0,0)) pverts = [] for v in verts: vx = DraftVecUtils.project(v,wp.u) lx = vx.Length if vx.getAngle(wp.u) > 1: lx = -lx vy = DraftVecUtils.project(v,wp.v) ly = vy.Length if vy.getAngle(wp.v) > 1: ly = -ly pverts.append(FreeCAD.Vector(lx,ly,0)) pverts.append(pverts[0]) # https://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order s = 0 for i in range(len(pverts)-1): s += (pverts[i+1].x-pverts[i].x)*(pverts[i+1].y+pverts[i].y) if s < 0: verts.reverse() elif s == 0: print("error computing winding direction") return return verts
def projectToVector(shape, vector): """projectToVector(shape,vector): projects the given shape on the given vector""" projpoints = [] minl = 10000000000 maxl = -10000000000 for v in shape.Vertexes: p = DraftVecUtils.project(v.Point, vector) projpoints.append(p) l = p.Length if p.getAngle(vector) > 1: l = -l if l > maxl: maxl = l if l < minl: minl = l return DraftVecUtils.scaleTo(vector, maxl - minl)
def onChanged(self,vobj,prop): if prop == "LineColor": l = vobj.LineColor self.mat1.diffuseColor.setValue([l[0],l[1],l[2]]) self.mat2.diffuseColor.setValue([l[0],l[1],l[2]]) elif prop == "Transparency": if hasattr(vobj,"Transparency"): self.mat2.transparency.setValue(vobj.Transparency/100.0) elif prop in ["DisplayLength","DisplayHeight","ArrowSize"]: if hasattr(vobj,"DisplayLength"): ld = vobj.DisplayLength.Value/2 hd = vobj.DisplayHeight.Value/2 elif hasattr(vobj,"DisplaySize"): # old objects ld = vobj.DisplaySize.Value/2 hd = vobj.DisplaySize.Value/2 else: ld = 1 hd = 1 verts = [] fverts = [] for v in [[-ld,-hd],[ld,-hd],[ld,hd],[-ld,hd]]: if hasattr(vobj,"ArrowSize"): l1 = vobj.ArrowSize.Value if vobj.ArrowSize.Value > 0 else 0.1 else: l1 = 0.1 l2 = l1/3 pl = FreeCAD.Placement(vobj.Object.Placement) p1 = pl.multVec(Vector(v[0],v[1],0)) p2 = pl.multVec(Vector(v[0],v[1],-l1)) p3 = pl.multVec(Vector(v[0]-l2,v[1],-l1+l2)) p4 = pl.multVec(Vector(v[0]+l2,v[1],-l1+l2)) p5 = pl.multVec(Vector(v[0],v[1]-l2,-l1+l2)) p6 = pl.multVec(Vector(v[0],v[1]+l2,-l1+l2)) verts.extend([[p1.x,p1.y,p1.z],[p2.x,p2.y,p2.z]]) fverts.append([p1.x,p1.y,p1.z]) verts.extend([[p2.x,p2.y,p2.z],[p3.x,p3.y,p3.z],[p4.x,p4.y,p4.z],[p2.x,p2.y,p2.z]]) verts.extend([[p2.x,p2.y,p2.z],[p5.x,p5.y,p5.z],[p6.x,p6.y,p6.z],[p2.x,p2.y,p2.z]]) verts.extend(fverts+[fverts[0]]) self.lcoords.point.setValues(verts) self.fcoords.point.setValues(fverts) elif prop == "LineWidth": self.drawstyle.lineWidth = vobj.LineWidth elif prop == "CutView": if hasattr(vobj,"CutView") and FreeCADGui.ActiveDocument.ActiveView: sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph() if vobj.CutView: if self.clip: sg.removeChild(self.clip) self.clip = None for o in Draft.getGroupContents(vobj.Object.Objects,walls=True): if hasattr(o.ViewObject,"Lighting"): o.ViewObject.Lighting = "One side" self.clip = coin.SoClipPlane() self.clip.on.setValue(True) norm = vobj.Object.Proxy.getNormal(vobj.Object) mp = vobj.Object.Shape.CenterOfMass mp = DraftVecUtils.project(mp,norm) dist = mp.Length + 0.1 # to not clip exactly on the section object norm = norm.negative() plane = coin.SbPlane(coin.SbVec3f(norm.x,norm.y,norm.z),-dist) self.clip.plane.setValue(plane) sg.insertChild(self.clip,0) else: if self.clip: sg.removeChild(self.clip) self.clip = None return
def getCutVolume(cutplane, shapes): """getCutVolume(cutplane,shapes): returns a cut face and a cut volume from the given shapes and the given cutting plane""" if not shapes: return None, None, None import Part if not isinstance(shapes, list): shapes = [shapes] # building boundbox bb = shapes[0].BoundBox for sh in shapes[1:]: bb.add(sh.BoundBox) bb.enlarge(1) # building cutplane space placement = None um = vm = wm = 0 try: if hasattr(cutplane, "Shape"): p = cutplane.Shape.copy().Faces[0] else: p = cutplane.copy().Faces[0] except: FreeCAD.Console.PrintMessage(translate("Arch", "Invalid cutplane")) return None, None, None ce = p.CenterOfMass ax = p.normalAt(0, 0) u = p.Vertexes[1].Point.sub(p.Vertexes[0].Point).normalize() v = u.cross(ax) if not bb.isCutPlane(ce, ax): FreeCAD.Console.PrintMessage(translate("Arch", "No objects are cut by the plane")) return None, None, None else: corners = [ FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMin), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMin), FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMin, bb.YMax, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMin, bb.ZMax), FreeCAD.Vector(bb.XMax, bb.YMax, bb.ZMax), ] for c in corners: dv = c.sub(ce) um1 = DraftVecUtils.project(dv, u).Length um = max(um, um1) vm1 = DraftVecUtils.project(dv, v).Length vm = max(vm, vm1) wm1 = DraftVecUtils.project(dv, ax).Length wm = max(wm, wm1) vu = DraftVecUtils.scaleTo(u, um) vui = vu.negative() vv = DraftVecUtils.scaleTo(v, vm) vvi = vv.negative() p1 = ce.add(vu.add(vvi)) p2 = ce.add(vu.add(vv)) p3 = ce.add(vui.add(vv)) p4 = ce.add(vui.add(vvi)) cutface = Part.makePolygon([p1, p2, p3, p4, p1]) cutface = Part.Face(cutface) cutnormal = DraftVecUtils.scaleTo(ax, wm) cutvolume = cutface.extrude(cutnormal) cutnormal = cutnormal.negative() invcutvolume = cutface.extrude(cutnormal) return cutface, cutvolume, invcutvolume
def constrain(self,point,basepoint=None,axis=None): '''constrain(point,basepoint=None,axis=None: Returns a constrained point. Axis can be "x","y" or "z" or a custom vector. If None, the closest working plane axis will be picked. Basepoint is the base point used to figure out from where the point must be constrained. If no basepoint is given, the current point is used as basepoint.''' # without the Draft module fully loaded, no axes system!" if not hasattr(FreeCAD,"DraftWorkingPlane"): return point point = Vector(point) # setup trackers if needed if not self.constrainLine: self.constrainLine = DraftTrackers.lineTracker(dotted=True) # setting basepoint if not basepoint: if not self.basepoint: self.basepoint = point else: self.basepoint = basepoint delta = point.sub(self.basepoint) # setting constraint axis if self.mask: self.affinity = self.mask if not self.affinity: self.affinity = FreeCAD.DraftWorkingPlane.getClosestAxis(delta) if isinstance(axis,FreeCAD.Vector): self.constraintAxis = axis elif axis == "x": self.constraintAxis = FreeCAD.DraftWorkingPlane.u elif axis == "y": self.constraintAxis = FreeCAD.DraftWorkingPlane.v elif axis == "z": self.constraintAxis = FreeCAD.DraftWorkingPlane.axis else: if self.affinity == "x": self.constraintAxis = FreeCAD.DraftWorkingPlane.u elif self.affinity == "y": self.constraintAxis = FreeCAD.DraftWorkingPlane.v else: self.constraintAxis = FreeCAD.DraftWorkingPlane.axis # calculating constrained point cdelta = DraftVecUtils.project(delta,self.constraintAxis) npoint = self.basepoint.add(cdelta) # setting constrain line if self.constrainLine: if point != npoint: self.constrainLine.p1(point) self.constrainLine.p2(npoint) self.constrainLine.on() else: self.constrainLine.off() return npoint
def getPerpendicular(self,edge,pt): "returns a point on an edge, perpendicular to the given point" dv = pt.sub(edge.Vertexes[0].Point) nv = DraftVecUtils.project(dv,DraftGeomUtils.vec(edge)) np = (edge.Vertexes[0].Point).add(nv) return np
def getSize(self): "returns (length,width) of the rectangle" p1 = Vector(self.coords.point.getValues()[0].getValue()) p2 = Vector(self.coords.point.getValues()[2].getValue()) diag = p2.sub(p1) return ((DraftVecUtils.project(diag,self.u)).Length,(DraftVecUtils.project(diag,self.v)).Length)
def compare(self,face1,face2): "zsorts two faces. Returns 1 if face1 is closer, 2 if face2 is closer, 0 otherwise" #print face1,face2 if not face1: if DEBUG: print "Warning, undefined face!" return 31 elif not face2: if DEBUG: print "Warning, undefined face!" return 32 # theory from # http://www.siggraph.org/education/materials/HyperGraph/scanline/visibility/painter.htm # and practical application http://vrm.ao2.it/ (blender vector renderer) b1 = face1[0].BoundBox b2 = face2[0].BoundBox # test 1: if faces don't overlap, no comparison possible if DEBUG: print "doing test 1" if b1.XMax < b2.XMin: return 0 if b1.XMin > b2.XMax: return 0 if b1.YMax < b2.YMin: return 0 if b1.YMin > b2.YMax: return 0 if DEBUG: print "failed, faces bboxes are not distinct" # test 2: if Z bounds dont overlap, it's easy to know the closest if DEBUG: print "doing test 2" if b1.ZMax < b2.ZMin: return 2 if b2.ZMax < b1.ZMin: return 1 if DEBUG: print "failed, faces Z are not distinct" # test 3: all verts of face1 are in front or behind the plane of face2 if DEBUG: print "doing test 3" norm = face2[0].normalAt(0,0) behind = 0 front = 0 for v in face1[0].Vertexes: dv = v.Point.sub(face2[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv,norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print "front: ",front," behind: ",behind if behind == len(face1[0].Vertexes): return 2 elif front == len(face1[0].Vertexes): return 1 if DEBUG: print "failed, cannot say if face 1 is in front or behind" # test 4: all verts of face2 are in front or behind the plane of face1 if DEBUG: print "doing test 4" norm = face1[0].normalAt(0,0) behind = 0 front = 0 for v in face2[0].Vertexes: dv = v.Point.sub(face1[0].Vertexes[0].Point) dv = DraftVecUtils.project(dv,norm) if DraftVecUtils.isNull(dv): behind += 1 front += 1 else: if dv.getAngle(norm) > 1: behind += 1 else: front += 1 if DEBUG: print "front: ",front," behind: ",behind if behind == len(face2[0].Vertexes): return 1 elif front == len(face2[0].Vertexes): return 2 if DEBUG: print "failed, cannot say if face 2 is in front or behind" # test 5: see if faces projections don't overlap, vertexwise if DEBUG: print "doing test 5" if not self.zOverlaps(face1,face2): return 0 elif not self.zOverlaps(face2,face1): return 0 if DEBUG: print "failed, faces are overlapping" if DEBUG: print "Houston, all tests passed, and still no results" return 0
def cut(self,cutplane): "Cuts through the shapes with a given cut plane and builds section faces" if DEBUG: print "\n\n======> Starting cut\n\n" if self.iscut: return if not self.shapes: if DEBUG: print "No objects to make sections" else: fill = (1.0,1.0,1.0,1.0) placement = FreeCAD.Placement(cutplane.Placement) # building boundbox bb = self.shapes[0][0].BoundBox for sh in self.shapes[1:]: bb.add(sh[0].BoundBox) bb.enlarge(1) um = vm = wm = 0 if not bb.isCutPlane(placement.Base,self.wp.axis): if DEBUG: print "No objects are cut by the plane" else: corners = [FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMin), FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMin), FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMin), FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMin), FreeCAD.Vector(bb.XMin,bb.YMin,bb.ZMax), FreeCAD.Vector(bb.XMin,bb.YMax,bb.ZMax), FreeCAD.Vector(bb.XMax,bb.YMin,bb.ZMax), FreeCAD.Vector(bb.XMax,bb.YMax,bb.ZMax)] for c in corners: dv = c.sub(placement.Base) um1 = DraftVecUtils.project(dv,self.wp.u).Length um = max(um,um1) vm1 = DraftVecUtils.project(dv,self.wp.v).Length vm = max(vm,vm1) wm1 = DraftVecUtils.project(dv,self.wp.axis).Length wm = max(wm,wm1) p1 = FreeCAD.Vector(-um,vm,0) p2 = FreeCAD.Vector(um,vm,0) p3 = FreeCAD.Vector(um,-vm,0) p4 = FreeCAD.Vector(-um,-vm,0) cutface = Part.makePolygon([p1,p2,p3,p4,p1]) cutface = Part.Face(cutface) cutface.Placement = placement cutnormal = DraftVecUtils.scaleTo(self.wp.axis,wm) cutvolume = cutface.extrude(cutnormal) shapes = [] faces = [] sections = [] for sh in self.shapes: for sol in sh[0].Solids: c = sol.cut(cutvolume) shapes.append([c]+sh[1:]) for f in c.Faces: faces.append([f]+sh[1:]) print "iscoplanar:",f.Vertexes[0].Point,f.normalAt(0,0),cutface.Vertexes[0].Point,cutface.normalAt(0,0) if DraftGeomUtils.isCoplanar([f,cutface]): print "COPLANAR" sections.append([f,fill]) self.shapes = shapes self.faces = faces self.sections = sections if DEBUG: print "Built ",len(self.sections)," sections, ", len(self.faces), " faces retained" self.iscut = True self.oriented = False self.trimmed = False self.sorted = False self.joined = False if DEBUG: print "\n\n======> Finished cut\n\n"