Draft: split wire related tools from Draft.py

Line Polyline BezCurve BSpline
.


.


.
This commit is contained in:
carlopav
2020-04-25 16:17:28 +02:00
committed by Yorik van Havre
parent 76e97c7062
commit 1a04f97d8f
10 changed files with 1185 additions and 572 deletions

View File

@@ -64,14 +64,20 @@ SET(Draft_functions
SET(Draft_make_functions
draftmake/__init__.py
draftmake/make_bezcurve.py
draftmake/make_bspline.py
draftmake/make_circle.py
draftmake/make_line.py
draftmake/make_polygon.py
draftmake/make_rectangle.py
draftmake/make_wire.py
)
SET(Draft_objects
draftobjects/__init__.py
draftobjects/base.py
draftobjects/bezcurve.py
draftobjects/bspline.py
draftobjects/circulararray.py
draftobjects/circle.py
draftobjects/orthoarray.py
@@ -83,6 +89,7 @@ SET(Draft_objects
draftobjects/polygon.py
draftobjects/rectangle.py
draftobjects/text.py
draftobjects/wire.py
draftobjects/README.md
)
@@ -97,6 +104,7 @@ SET(Draft_view_providers
draftviewproviders/view_dimension.py
draftviewproviders/view_rectangle.py
draftviewproviders/view_text.py
draftviewproviders/view_wire.py
draftviewproviders/README.md
)

View File

@@ -206,7 +206,27 @@ if FreeCAD.GuiUp:
from draftmake.make_polygon import make_polygon, makePolygon
from draftobjects.polygon import Polygon, _Polygon
# wire and line
from draftmake.make_line import make_line, makeLine
from draftmake.make_wire import make_wire, makeWire
from draftobjects.wire import Wire, _Wire
if FreeCAD.GuiUp:
from draftviewproviders.view_wire import ViewProviderWire
from draftviewproviders.view_wire import _ViewProviderWire
# bspline
from draftmake.make_bspline import make_bspline, makeBSpline
from draftobjects.bspline import BSpline, _BSpline
if FreeCAD.GuiUp:
# for compatibility with older versions
_ViewProviderBSpline = ViewProviderWire
# bezcurve
from draftmake.make_bezcurve import make_bezcurve, makeBezCurve
from draftobjects.bezcurve import BezCurve, _BezCurve
if FreeCAD.GuiUp:
# for compatibility with older versions
_ViewProviderBezCurve = ViewProviderWire
#---------------------------------------------------------------------------
# Draft annotation objects
@@ -322,105 +342,6 @@ def makeWire(pointslist,closed=False,placement=None,face=None,support=None,bs2wi
return obj
def makeLine(p1,p2=None):
"""makeLine(p1,p2): Creates a line between p1 and p2.
makeLine(LineSegment): Creates a line from a Part.LineSegment
makeLine(Shape): Creates a line from first vertex to last vertex of the given shape"""
if not p2:
if hasattr(p1,"StartPoint") and hasattr(p1,"EndPoint"):
p2 = p1.EndPoint
p1 = p1.StartPoint
elif hasattr(p1,"Vertexes"):
p2 = p1.Vertexes[-1].Point
p1 = p1.Vertexes[0].Point
else:
FreeCAD.Console.PrintError("Unable to create a line from the given data\n")
return
obj = makeWire([p1,p2])
return obj
def makeBSpline(pointslist,closed=False,placement=None,face=None,support=None):
"""makeBSpline(pointslist,[closed],[placement]): Creates a B-Spline object
from the given list of vectors. If closed is True or first
and last points are identical, the wire is closed. If face is
true (and wire is closed), the wire will appear filled. Instead of
a pointslist, you can also pass a Part Wire."""
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
if not isinstance(pointslist,list):
nlist = []
for v in pointslist.Vertexes:
nlist.append(v.Point)
pointslist = nlist
if len(pointslist) < 2:
FreeCAD.Console.PrintError(translate("draft","Draft.makeBSpline: not enough points")+"\n")
return
if (pointslist[0] == pointslist[-1]):
if len(pointslist) > 2:
closed = True
pointslist.pop()
FreeCAD.Console.PrintWarning(translate("draft","Draft.makeBSpline: Equal endpoints forced Closed")+"\n")
else: # len == 2 and first == last GIGO
FreeCAD.Console.PrintError(translate("draft","Draft.makeBSpline: Invalid pointslist")+"\n")
return
# should have sensible parms from here on
if placement: typecheck([(placement,FreeCAD.Placement)], "makeBSpline")
if len(pointslist) == 2: fname = "Line"
else: fname = "BSpline"
obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
_BSpline(obj)
obj.Closed = closed
obj.Points = pointslist
obj.Support = support
if face != None:
obj.MakeFace = face
if placement: obj.Placement = placement
if gui:
_ViewProviderWire(obj.ViewObject)
formatObject(obj)
select(obj)
return obj
def makeBezCurve(pointslist,closed=False,placement=None,face=None,support=None,degree=None):
"""makeBezCurve(pointslist,[closed],[placement]): Creates a Bezier Curve object
from the given list of vectors. Instead of a pointslist, you can also pass a Part Wire."""
if not FreeCAD.ActiveDocument:
FreeCAD.Console.PrintError("No active document. Aborting\n")
return
if not isinstance(pointslist,list):
nlist = []
for v in pointslist.Vertexes:
nlist.append(v.Point)
pointslist = nlist
if placement: typecheck([(placement,FreeCAD.Placement)], "makeBezCurve")
if len(pointslist) == 2: fname = "Line"
else: fname = "BezCurve"
obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
_BezCurve(obj)
obj.Points = pointslist
if degree:
obj.Degree = degree
else:
import Part
obj.Degree = min((len(pointslist)-(1 * (not closed))),
Part.BezierCurve().MaxDegree)
obj.Closed = closed
obj.Support = support
if face != None:
obj.MakeFace = face
obj.Proxy.resetcontinuity(obj)
if placement: obj.Placement = placement
if gui:
_ViewProviderWire(obj.ViewObject)
# if not face: obj.ViewObject.DisplayMode = "Wireframe"
# obj.ViewObject.DisplayMode = "Wireframe"
formatObject(obj)
select(obj)
return obj
def makeCopy(obj,force=None,reparent=False):
"""makeCopy(object): returns an exact copy of an object"""
if not FreeCAD.ActiveDocument:
@@ -3102,270 +3023,6 @@ class _Ellipse(_DraftObject):
obj.Placement = plm
obj.positionBySupport()
class _Wire(_DraftObject):
"""The Wire object"""
def __init__(self, obj):
_DraftObject.__init__(self,obj,"Wire")
obj.addProperty("App::PropertyVectorList","Points","Draft",QT_TRANSLATE_NOOP("App::Property","The vertices of the wire"))
obj.addProperty("App::PropertyBool","Closed","Draft",QT_TRANSLATE_NOOP("App::Property","If the wire is closed or not"))
obj.addProperty("App::PropertyLink","Base","Draft",QT_TRANSLATE_NOOP("App::Property","The base object is the wire, it's formed from 2 objects"))
obj.addProperty("App::PropertyLink","Tool","Draft",QT_TRANSLATE_NOOP("App::Property","The tool object is the wire, it's formed from 2 objects"))
obj.addProperty("App::PropertyVectorDistance","Start","Draft",QT_TRANSLATE_NOOP("App::Property","The start point of this line"))
obj.addProperty("App::PropertyVectorDistance","End","Draft",QT_TRANSLATE_NOOP("App::Property","The end point of this line"))
obj.addProperty("App::PropertyLength","Length","Draft",QT_TRANSLATE_NOOP("App::Property","The length of this line"))
obj.addProperty("App::PropertyLength","FilletRadius","Draft",QT_TRANSLATE_NOOP("App::Property","Radius to use to fillet the corners"))
obj.addProperty("App::PropertyLength","ChamferSize","Draft",QT_TRANSLATE_NOOP("App::Property","Size of the chamfer to give to the corners"))
obj.addProperty("App::PropertyBool","MakeFace","Draft",QT_TRANSLATE_NOOP("App::Property","Create a face if this object is closed"))
obj.addProperty("App::PropertyInteger","Subdivisions","Draft",QT_TRANSLATE_NOOP("App::Property","The number of subdivisions of each edge"))
obj.addProperty("App::PropertyArea","Area","Draft",QT_TRANSLATE_NOOP("App::Property","The area of this object"))
obj.MakeFace = getParam("fillmode",True)
obj.Closed = False
def execute(self, obj):
import Part, DraftGeomUtils
plm = obj.Placement
if obj.Base and (not obj.Tool):
if obj.Base.isDerivedFrom("Sketcher::SketchObject"):
shape = obj.Base.Shape.copy()
if obj.Base.Shape.isClosed():
if hasattr(obj,"MakeFace"):
if obj.MakeFace:
shape = Part.Face(shape)
else:
shape = Part.Face(shape)
obj.Shape = shape
elif obj.Base and obj.Tool:
if hasattr(obj.Base,'Shape') and hasattr(obj.Tool,'Shape'):
if (not obj.Base.Shape.isNull()) and (not obj.Tool.Shape.isNull()):
sh1 = obj.Base.Shape.copy()
sh2 = obj.Tool.Shape.copy()
shape = sh1.fuse(sh2)
if DraftGeomUtils.isCoplanar(shape.Faces):
shape = DraftGeomUtils.concatenate(shape)
obj.Shape = shape
p = []
for v in shape.Vertexes: p.append(v.Point)
if obj.Points != p: obj.Points = p
elif obj.Points:
if obj.Points[0] == obj.Points[-1]:
if not obj.Closed: obj.Closed = True
obj.Points.pop()
if obj.Closed and (len(obj.Points) > 2):
pts = obj.Points
if hasattr(obj,"Subdivisions"):
if obj.Subdivisions > 0:
npts = []
for i in range(len(pts)):
p1 = pts[i]
npts.append(pts[i])
if i == len(pts)-1:
p2 = pts[0]
else:
p2 = pts[i+1]
v = p2.sub(p1)
v = DraftVecUtils.scaleTo(v,v.Length/(obj.Subdivisions+1))
for j in range(obj.Subdivisions):
npts.append(p1.add(FreeCAD.Vector(v).multiply(j+1)))
pts = npts
shape = Part.makePolygon(pts+[pts[0]])
if "ChamferSize" in obj.PropertiesList:
if obj.ChamferSize.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.ChamferSize.Value,chamfer=True)
if w:
shape = w
if "FilletRadius" in obj.PropertiesList:
if obj.FilletRadius.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.FilletRadius.Value)
if w:
shape = w
try:
if hasattr(obj,"MakeFace"):
if obj.MakeFace:
shape = Part.Face(shape)
else:
shape = Part.Face(shape)
except Part.OCCError:
pass
else:
edges = []
pts = obj.Points[1:]
lp = obj.Points[0]
for p in pts:
if not DraftVecUtils.equals(lp,p):
if hasattr(obj,"Subdivisions"):
if obj.Subdivisions > 0:
npts = []
v = p.sub(lp)
v = DraftVecUtils.scaleTo(v,v.Length/(obj.Subdivisions+1))
edges.append(Part.LineSegment(lp,lp.add(v)).toShape())
lv = lp.add(v)
for j in range(obj.Subdivisions):
edges.append(Part.LineSegment(lv,lv.add(v)).toShape())
lv = lv.add(v)
else:
edges.append(Part.LineSegment(lp,p).toShape())
else:
edges.append(Part.LineSegment(lp,p).toShape())
lp = p
try:
shape = Part.Wire(edges)
except Part.OCCError:
print("Error wiring edges")
shape = None
if "ChamferSize" in obj.PropertiesList:
if obj.ChamferSize.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.ChamferSize.Value,chamfer=True)
if w:
shape = w
if "FilletRadius" in obj.PropertiesList:
if obj.FilletRadius.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.FilletRadius.Value)
if w:
shape = w
if shape:
obj.Shape = shape
if hasattr(obj,"Area") and hasattr(shape,"Area"):
obj.Area = shape.Area
if hasattr(obj,"Length"):
obj.Length = shape.Length
obj.Placement = plm
obj.positionBySupport()
self.onChanged(obj,"Placement")
def onChanged(self, obj, prop):
if prop == "Start":
pts = obj.Points
invpl = FreeCAD.Placement(obj.Placement).inverse()
realfpstart = invpl.multVec(obj.Start)
if pts:
if pts[0] != realfpstart:
pts[0] = realfpstart
obj.Points = pts
elif prop == "End":
pts = obj.Points
invpl = FreeCAD.Placement(obj.Placement).inverse()
realfpend = invpl.multVec(obj.End)
if len(pts) > 1:
if pts[-1] != realfpend:
pts[-1] = realfpend
obj.Points = pts
elif prop == "Length":
if obj.Shape and not obj.Shape.isNull():
if obj.Length.Value != obj.Shape.Length:
if len(obj.Points) == 2:
v = obj.Points[-1].sub(obj.Points[0])
v = DraftVecUtils.scaleTo(v,obj.Length.Value)
obj.Points = [obj.Points[0],obj.Points[0].add(v)]
elif prop == "Placement":
pl = FreeCAD.Placement(obj.Placement)
if len(obj.Points) >= 2:
displayfpstart = pl.multVec(obj.Points[0])
displayfpend = pl.multVec(obj.Points[-1])
if obj.Start != displayfpstart:
obj.Start = displayfpstart
if obj.End != displayfpend:
obj.End = displayfpend
class _ViewProviderWire(_ViewProviderDraft):
"""A View Provider for the Wire object"""
def __init__(self, obj):
_ViewProviderDraft.__init__(self,obj)
obj.addProperty("App::PropertyBool","EndArrow","Draft",QT_TRANSLATE_NOOP("App::Property","Displays a Dimension symbol at the end of the wire"))
obj.addProperty("App::PropertyLength","ArrowSize","Draft",QT_TRANSLATE_NOOP("App::Property","Arrow size"))
obj.addProperty("App::PropertyEnumeration","ArrowType","Draft",QT_TRANSLATE_NOOP("App::Property","Arrow type"))
obj.ArrowSize = getParam("arrowsize",0.1)
obj.ArrowType = arrowtypes
obj.ArrowType = arrowtypes[getParam("dimsymbol",0)]
def attach(self, obj):
from pivy import coin
self.Object = obj.Object
col = coin.SoBaseColor()
col.rgb.setValue(obj.LineColor[0],obj.LineColor[1],obj.LineColor[2])
self.coords = coin.SoTransform()
self.pt = coin.SoSeparator()
self.pt.addChild(col)
self.pt.addChild(self.coords)
self.symbol = dimSymbol()
self.pt.addChild(self.symbol)
_ViewProviderDraft.attach(self,obj)
self.onChanged(obj,"EndArrow")
def updateData(self, obj, prop):
from pivy import coin
if prop == "Points":
if obj.Points:
p = obj.Points[-1]
if hasattr(self,"coords"):
self.coords.translation.setValue((p.x,p.y,p.z))
if len(obj.Points) >= 2:
v1 = obj.Points[-2].sub(obj.Points[-1])
if not DraftVecUtils.isNull(v1):
v1.normalize()
_rot = coin.SbRotation()
_rot.setValue(coin.SbVec3f(1, 0, 0), coin.SbVec3f(v1[0], v1[1], v1[2]))
self.coords.rotation.setValue(_rot)
return
def onChanged(self, vobj, prop):
from pivy import coin
if prop in ["EndArrow","ArrowSize","ArrowType","Visibility"]:
rn = vobj.RootNode
if hasattr(self,"pt") and hasattr(vobj,"EndArrow"):
if vobj.EndArrow and vobj.Visibility:
self.pt.removeChild(self.symbol)
s = arrowtypes.index(vobj.ArrowType)
self.symbol = dimSymbol(s)
self.pt.addChild(self.symbol)
self.updateData(vobj.Object,"Points")
if hasattr(vobj,"ArrowSize"):
s = vobj.ArrowSize
else:
s = getParam("arrowsize",0.1)
self.coords.scaleFactor.setValue((s,s,s))
rn.addChild(self.pt)
else:
if self.symbol:
if self.pt.findChild(self.symbol) != -1:
self.pt.removeChild(self.symbol)
if rn.findChild(self.pt) != -1:
rn.removeChild(self.pt)
if prop in ["LineColor"]:
if hasattr(self, "pt"):
self.pt[0].rgb.setValue(vobj.LineColor[0],vobj.LineColor[1],vobj.LineColor[2])
_ViewProviderDraft.onChanged(self,vobj,prop)
return
def claimChildren(self):
if hasattr(self.Object,"Base"):
return [self.Object.Base,self.Object.Tool]
return []
def setupContextMenu(self,vobj,menu):
from PySide import QtCore,QtGui
action1 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Edit.svg"),"Flatten this wire",menu)
QtCore.QObject.connect(action1,QtCore.SIGNAL("triggered()"),self.flatten)
menu.addAction(action1)
def flatten(self):
if hasattr(self,"Object"):
if len(self.Object.Shape.Wires) == 1:
import DraftGeomUtils
fw = DraftGeomUtils.flattenWire(self.Object.Shape.Wires[0])
points = [v.Point for v in fw.Vertexes]
if len(points) == len(self.Object.Points):
if points != self.Object.Points:
FreeCAD.ActiveDocument.openTransaction("Flatten wire")
FreeCADGui.doCommand("FreeCAD.ActiveDocument."+self.Object.Name+".Points="+str(points).replace("Vector","FreeCAD.Vector").replace(" ",""))
FreeCAD.ActiveDocument.commitTransaction()
else:
from DraftTools import translate
FreeCAD.Console.PrintMessage(translate("Draft","This Wire is already flat")+"\n")
class _DrawingView(_DraftObject):
"""The Draft DrawingView object"""
@@ -3429,215 +3086,6 @@ class _DrawingView(_DraftObject):
"returns a DXF fragment"
return getDXF(obj)
class _BSpline(_DraftObject):
"""The BSpline object"""
def __init__(self, obj):
_DraftObject.__init__(self,obj,"BSpline")
obj.addProperty("App::PropertyVectorList","Points","Draft", QT_TRANSLATE_NOOP("App::Property","The points of the B-spline"))
obj.addProperty("App::PropertyBool","Closed","Draft",QT_TRANSLATE_NOOP("App::Property","If the B-spline is closed or not"))
obj.addProperty("App::PropertyBool","MakeFace","Draft",QT_TRANSLATE_NOOP("App::Property","Create a face if this spline is closed"))
obj.addProperty("App::PropertyArea","Area","Draft",QT_TRANSLATE_NOOP("App::Property","The area of this object"))
obj.MakeFace = getParam("fillmode",True)
obj.Closed = False
obj.Points = []
self.assureProperties(obj)
def assureProperties(self, obj): # for Compatibility with older versions
if not hasattr(obj, "Parameterization"):
obj.addProperty("App::PropertyFloat","Parameterization","Draft",QT_TRANSLATE_NOOP("App::Property","Parameterization factor"))
obj.Parameterization = 1.0
self.knotSeq = []
def parameterization (self, pts, a, closed):
# Computes a knot Sequence for a set of points
# fac (0-1) : parameterization factor
# fac=0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length
if closed: # we need to add the first point as the end point
pts.append(pts[0])
params = [0]
for i in range(1,len(pts)):
p = pts[i].sub(pts[i-1])
pl = pow(p.Length,a)
params.append(params[-1] + pl)
return params
def onChanged(self, fp, prop):
if prop == "Parameterization":
if fp.Parameterization < 0.:
fp.Parameterization = 0.
if fp.Parameterization > 1.0:
fp.Parameterization = 1.0
def execute(self, obj):
import Part
self.assureProperties(obj)
if obj.Points:
self.knotSeq = self.parameterization(obj.Points, obj.Parameterization, obj.Closed)
plm = obj.Placement
if obj.Closed and (len(obj.Points) > 2):
if obj.Points[0] == obj.Points[-1]: # should not occur, but OCC will crash
FreeCAD.Console.PrintError(translate('draft', "_BSpline.createGeometry: Closed with same first/last Point. Geometry not updated.")+"\n")
return
spline = Part.BSplineCurve()
spline.interpolate(obj.Points, PeriodicFlag = True, Parameters = self.knotSeq)
# DNC: bug fix: convert to face if closed
shape = Part.Wire(spline.toShape())
# Creating a face from a closed spline cannot be expected to always work
# Usually, if the spline is not flat the call of Part.Face() fails
try:
if hasattr(obj,"MakeFace"):
if obj.MakeFace:
shape = Part.Face(shape)
else:
shape = Part.Face(shape)
except Part.OCCError:
pass
obj.Shape = shape
if hasattr(obj,"Area") and hasattr(shape,"Area"):
obj.Area = shape.Area
else:
spline = Part.BSplineCurve()
spline.interpolate(obj.Points, PeriodicFlag = False, Parameters = self.knotSeq)
shape = spline.toShape()
obj.Shape = shape
if hasattr(obj,"Area") and hasattr(shape,"Area"):
obj.Area = shape.Area
obj.Placement = plm
obj.positionBySupport()
# for compatibility with older versions
_ViewProviderBSpline = _ViewProviderWire
class _BezCurve(_DraftObject):
"""The BezCurve object"""
def __init__(self, obj):
_DraftObject.__init__(self,obj,"BezCurve")
obj.addProperty("App::PropertyVectorList","Points","Draft",QT_TRANSLATE_NOOP("App::Property","The points of the Bezier curve"))
obj.addProperty("App::PropertyInteger","Degree","Draft",QT_TRANSLATE_NOOP("App::Property","The degree of the Bezier function"))
obj.addProperty("App::PropertyIntegerList","Continuity","Draft",QT_TRANSLATE_NOOP("App::Property","Continuity"))
obj.addProperty("App::PropertyBool","Closed","Draft",QT_TRANSLATE_NOOP("App::Property","If the Bezier curve should be closed or not"))
obj.addProperty("App::PropertyBool","MakeFace","Draft",QT_TRANSLATE_NOOP("App::Property","Create a face if this curve is closed"))
obj.addProperty("App::PropertyLength","Length","Draft",QT_TRANSLATE_NOOP("App::Property","The length of this object"))
obj.addProperty("App::PropertyArea","Area","Draft",QT_TRANSLATE_NOOP("App::Property","The area of this object"))
obj.MakeFace = getParam("fillmode",True)
obj.Closed = False
obj.Degree = 3
obj.Continuity = []
#obj.setEditorMode("Degree",2)#hide
obj.setEditorMode("Continuity",1)#ro
def execute(self, fp):
self.createGeometry(fp)
fp.positionBySupport()
def _segpoleslst(self,fp):
"""split the points into segments"""
if not fp.Closed and len(fp.Points) >= 2: #allow lower degree segment
poles=fp.Points[1:]
elif fp.Closed and len(fp.Points) >= fp.Degree: #drawable
#poles=fp.Points[1:(fp.Degree*(len(fp.Points)//fp.Degree))]+fp.Points[0:1]
poles=fp.Points[1:]+fp.Points[0:1]
else:
poles=[]
return [poles[x:x+fp.Degree] for x in \
range(0, len(poles), (fp.Degree or 1))]
def resetcontinuity(self,fp):
fp.Continuity = [0]*(len(self._segpoleslst(fp))-1+1*fp.Closed)
#nump= len(fp.Points)-1+fp.Closed*1
#numsegments = (nump // fp.Degree) + 1 * (nump % fp.Degree > 0) -1
#fp.Continuity = [0]*numsegments
def onChanged(self, fp, prop):
if prop == 'Closed': # if remove the last entry when curve gets opened
oldlen = len(fp.Continuity)
newlen = (len(self._segpoleslst(fp))-1+1*fp.Closed)
if oldlen > newlen:
fp.Continuity = fp.Continuity[:newlen]
if oldlen < newlen:
fp.Continuity = fp.Continuity + [0]*(newlen-oldlen)
if hasattr(fp,'Closed') and fp.Closed and prop in ['Points','Degree','Closed'] and\
len(fp.Points) % fp.Degree: # the curve editing tools can't handle extra points
fp.Points=fp.Points[:(fp.Degree*(len(fp.Points)//fp.Degree))] #for closed curves
if prop in ["Degree"] and fp.Degree >= 1: #reset Continuity
self.resetcontinuity(fp)
if prop in ["Points","Degree","Continuity","Closed"]:
self.createGeometry(fp)
def createGeometry(self,fp):
import Part
plm = fp.Placement
if fp.Points:
startpoint=fp.Points[0]
edges = []
for segpoles in self._segpoleslst(fp):
# if len(segpoles) == fp.Degree # would skip additional poles
c = Part.BezierCurve() #last segment may have lower degree
c.increase(len(segpoles))
c.setPoles([startpoint]+segpoles)
edges.append(Part.Edge(c))
startpoint = segpoles[-1]
w = Part.Wire(edges)
if fp.Closed and w.isClosed():
try:
if hasattr(fp,"MakeFace"):
if fp.MakeFace:
w = Part.Face(w)
else:
w = Part.Face(w)
except Part.OCCError:
pass
fp.Shape = w
if hasattr(fp,"Area") and hasattr(w,"Area"):
fp.Area = w.Area
if hasattr(fp,"Length") and hasattr(w,"Length"):
fp.Length = w.Length
fp.Placement = plm
@classmethod
def symmetricpoles(cls,knot, p1, p2):
"""make two poles symmetric respective to the knot"""
p1h=FreeCAD.Vector(p1)
p2h=FreeCAD.Vector(p2)
p1h.multiply(0.5)
p2h.multiply(0.5)
return ( knot+p1h-p2h , knot+p2h-p1h)
@classmethod
def tangentpoles(cls,knot, p1, p2,allowsameside=False):
"""make two poles have the same tangent at knot"""
p12n=p2.sub(p1)
p12n.normalize()
p1k=knot-p1
p2k=knot-p2
p1k_= FreeCAD.Vector(p12n)
kon12=(p1k*p12n)
if allowsameside or not (kon12 < 0 or p2k*p12n > 0):# instead of moving
p1k_.multiply(kon12)
pk_k=knot-p1-p1k_
return (p1+pk_k,p2+pk_k)
else:
return cls.symmetricpoles(knot, p1, p2)
@staticmethod
def modifysymmetricpole(knot,p1):
"""calculate the coordinates of the opposite pole
of a symmetric knot"""
return knot+knot-p1
@staticmethod
def modifytangentpole(knot,p1,oldp2):
"""calculate the coordinates of the opposite pole
of a tangent knot"""
pn=knot-p1
pn.normalize()
pn.multiply((knot-oldp2).Length)
return pn+knot
# for compatibility with older versions ???????
_ViewProviderBezCurve = _ViewProviderWire
class _Block(_DraftObject):
"""The Block object"""

View File

@@ -0,0 +1,107 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 FreeCAD Developers *
# * *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""This module provides the code for Draft make_bezcurve function.
"""
## @package make_bezcurve
# \ingroup DRAFT
# \brief This module provides the code for Draft make_bezcurve function.
import FreeCAD as App
from draftutils.gui_utils import format_object
from draftutils.gui_utils import select
from draftutils.utils import type_check
from draftutils.translate import translate
from draftobjects.bezcurve import BezCurve
if App.GuiUp:
from draftviewproviders.view_bezcurve import ViewProviderBezCurve
def make_bezcurve(pointslist, closed=False, placement=None, face=None, support=None, degree=None):
"""make_bezcurve(pointslist, [closed], [placement])
Creates a Bezier Curve object from the given list of vectors.
Parameters
----------
pointlist : [Base.Vector]
List of points to create the polyline.
Instead of a pointslist, you can also pass a Part Wire.
TODO: Change the name so!
closed : bool
If closed is True or first and last points are identical,
the created BSpline will be closed.
placement : Base.Placement
If a placement is given, it is used.
face : Bool
If face is False, the rectangle is shown as a wireframe,
otherwise as a face.
support :
TODO: Describe
degree : int
Degree of the BezCurve
"""
if not App.ActiveDocument:
App.Console.PrintError("No active document. Aborting\n")
return
if not isinstance(pointslist,list):
nlist = []
for v in pointslist.Vertexes:
nlist.append(v.Point)
pointslist = nlist
if placement: type_check([(placement,App.Placement)], "make_bezcurve")
if len(pointslist) == 2: fname = "Line"
else: fname = "BezCurve"
obj = App.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
BezCurve(obj)
obj.Points = pointslist
if degree:
obj.Degree = degree
else:
import Part
obj.Degree = min((len(pointslist)-(1 * (not closed))),
Part.BezierCurve().MaxDegree)
obj.Closed = closed
obj.Support = support
if face != None:
obj.MakeFace = face
obj.Proxy.resetcontinuity(obj)
if placement: obj.Placement = placement
if App.GuiUp:
ViewProviderBezCurve(obj.ViewObject)
# if not face: obj.ViewObject.DisplayMode = "Wireframe"
# obj.ViewObject.DisplayMode = "Wireframe"
format_object(obj)
select(obj)
return obj
makeBezCurve = make_bezcurve

View File

@@ -0,0 +1,111 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 FreeCAD Developers *
# * *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""This module provides the code for Draft make_bspline function.
"""
## @package make_bspline
# \ingroup DRAFT
# \brief This module provides the code for Draft make_bspline function.
import FreeCAD as App
from draftutils.gui_utils import format_object
from draftutils.gui_utils import select
from draftutils.utils import type_check
from draftutils.translate import translate
from draftobjects.bspline import BSpline
if App.GuiUp:
from draftviewproviders.view_bspline import ViewProviderBSpline
def make_bspline(pointslist, closed=False, placement=None, face=None, support=None):
"""make_bspline(pointslist, [closed], [placement])
Creates a B-Spline object from the given list of vectors.
Parameters
----------
pointlist : [Base.Vector]
List of points to create the polyline.
Instead of a pointslist, you can also pass a Part Wire.
TODO: Change the name so!
closed : bool
If closed is True or first and last points are identical,
the created BSpline will be closed.
placement : Base.Placement
If a placement is given, it is used.
face : Bool
If face is False, the rectangle is shown as a wireframe,
otherwise as a face.
support :
TODO: Describe
"""
if not App.ActiveDocument:
App.Console.PrintError("No active document. Aborting\n")
return
if not isinstance(pointslist,list):
nlist = []
for v in pointslist.Vertexes:
nlist.append(v.Point)
pointslist = nlist
if len(pointslist) < 2:
_err = "Draft.makeBSpline: not enough points"
App.Console.PrintError(translate("draft", _err)+"\n")
return
if (pointslist[0] == pointslist[-1]):
if len(pointslist) > 2:
closed = True
pointslist.pop()
_err = "Draft.makeBSpline: Equal endpoints forced Closed"
App.Console.PrintWarning(translate("Draft", _err) + _err + "\n")
else:
# len == 2 and first == last GIGO
_err = "Draft.makeBSpline: Invalid pointslist"
App.Console.PrintError(translate("Draft", _err)+"\n")
return
# should have sensible parms from here on
if placement: type_check([(placement,App.Placement)], "make_bspline")
if len(pointslist) == 2: fname = "Line"
else: fname = "BSpline"
obj = App.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
BSpline(obj)
obj.Closed = closed
obj.Points = pointslist
obj.Support = support
if face != None:
obj.MakeFace = face
if placement: obj.Placement = placement
if App.GuiUp:
ViewProviderBSpline(obj.ViewObject)
format_object(obj)
select(obj)
return obj
makeBSpline = make_bspline

View File

@@ -0,0 +1,73 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 FreeCAD Developers *
# * *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""This module provides the code for Draft make_line function.
"""
## @package make_line
# \ingroup DRAFT
# \brief This module provides the code for Draft make_line function.
import FreeCAD as App
from draftutils.gui_utils import format_object
from draftutils.gui_utils import select
from draftmake.make_wire import make_wire
def make_line(first_param, last_param=None):
"""makeLine(first_param, p2)
Creates a line from 2 points or from a given object.
Parameters
----------
first_param :
Base.Vector -> First point of the line (if p2 == None)
Part.LineSegment -> Line is created from the given Linesegment
Shape -> Line is created from the give Shape
last_param : Base.Vector
Second point of the line, if not set the function evaluates
the first_param to look for a Part.LineSegment or a Shape
"""
if last_param:
p1 = first_param
p2 = last_param
else:
if hasattr(first_param, "StartPoint") and hasattr(first_param, "EndPoint"):
p2 = first_param.EndPoint
p1 = first_param.StartPoint
elif hasattr(p1,"Vertexes"):
p2 = first_param.Vertexes[-1].Point
p1 = first_param.Vertexes[0].Point
else:
_err = "Unable to create a line from the given parameters"
App.Console.PrintError(_err + "\n")
return
obj = make_wire([p1,p2])
return obj
makeLine = make_line

View File

@@ -0,0 +1,123 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""This module provides the code for Draft make_wire function.
"""
## @package make_wire
# \ingroup DRAFT
# \brief This module provides the code for Draft make_wire function.
import FreeCAD as App
import DraftGeomUtils
from draftutils.gui_utils import format_object
from draftutils.gui_utils import select
from draftutils.utils import type_check
from draftobjects.wire import Wire
if App.GuiUp:
from draftviewproviders.view_wire import ViewProviderWire
def make_wire(pointslist, closed=False, placement=None, face=None, support=None, bs2wire=False):
"""makeWire(pointslist,[closed],[placement])
Creates a Wire object from the given list of vectors. If face is
true (and wire is closed), the wire will appear filled. Instead of
a pointslist, you can also pass a Part Wire.
Parameters
----------
pointlist : [Base.Vector]
List of points to create the polyline
closed : bool
If closed is True or first and last points are identical,
the created polyline will be closed.
placement : Base.Placement
If a placement is given, it is used.
face : Bool
If face is False, the rectangle is shown as a wireframe,
otherwise as a face.
support :
TODO: Describe
bs2wire : bool
TODO: Describe
"""
if not App.ActiveDocument:
App.Console.PrintError("No active document. Aborting\n")
return
import Part
if not isinstance(pointslist, list):
e = pointslist.Wires[0].Edges
pointslist = Part.Wire(Part.__sortEdges__(e))
nlist = []
for v in pointslist.Vertexes:
nlist.append(v.Point)
if DraftGeomUtils.isReallyClosed(pointslist):
closed = True
pointslist = nlist
if len(pointslist) == 0:
print("Invalid input points: ", pointslist)
#print(pointslist)
#print(closed)
if placement:
type_check([(placement, App.Placement)], "make_wire")
ipl = placement.inverse()
if not bs2wire:
pointslist = [ipl.multVec(p) for p in pointslist]
if len(pointslist) == 2:
fname = "Line"
else:
fname = "Wire"
obj = App.ActiveDocument.addObject("Part::Part2DObjectPython", fname)
Wire(obj)
obj.Points = pointslist
obj.Closed = closed
obj.Support = support
if face != None:
obj.MakeFace = face
if placement:
obj.Placement = placement
if App.GuiUp:
ViewProviderWire(obj.ViewObject)
format_object(obj)
select(obj)
return obj
makeWire = make_wire

View File

@@ -0,0 +1,196 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 FreeCAD Developers *
# * *
# * 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. *
# * *
# * This program 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 this program; 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 BezCurve.
"""
## @package bezcurve
# \ingroup DRAFT
# \brief This module provides the object code for Draft BezCurve.
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCAD as App
from draftutils.utils import get_param
from draftobjects.base import DraftObject
class BezCurve(DraftObject):
"""The BezCurve object"""
def __init__(self, obj):
super(BezCurve, self).__init__(obj, "BezCurve")
_tip = "The points of the Bezier curve"
obj.addProperty("App::PropertyVectorList", "Points",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The degree of the Bezier function"
obj.addProperty("App::PropertyInteger", "Degree",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Continuity"
obj.addProperty("App::PropertyIntegerList", "Continuity",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "If the Bezier curve should be closed or not"
obj.addProperty("App::PropertyBool", "Closed",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Create a face if this curve is closed"
obj.addProperty("App::PropertyBool", "MakeFace",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The length of this object"
obj.addProperty("App::PropertyLength", "Length",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The area of this object"
obj.addProperty("App::PropertyArea", "Area",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
obj.MakeFace = get_param("fillmode", True)
obj.Closed = False
obj.Degree = 3
obj.Continuity = []
#obj.setEditorMode("Degree",2)
obj.setEditorMode("Continuity", 1)
def execute(self, fp):
self.createGeometry(fp)
fp.positionBySupport()
def _segpoleslst(self,fp):
"""Split the points into segments."""
if not fp.Closed and len(fp.Points) >= 2: #allow lower degree segment
poles=fp.Points[1:]
elif fp.Closed and len(fp.Points) >= fp.Degree: #drawable
#poles=fp.Points[1:(fp.Degree*(len(fp.Points)//fp.Degree))]+fp.Points[0:1]
poles=fp.Points[1:]+fp.Points[0:1]
else:
poles=[]
return [poles[x:x+fp.Degree] for x in \
range(0, len(poles), (fp.Degree or 1))]
def resetcontinuity(self,fp):
fp.Continuity = [0]*(len(self._segpoleslst(fp))-1+1*fp.Closed)
#nump= len(fp.Points)-1+fp.Closed*1
#numsegments = (nump // fp.Degree) + 1 * (nump % fp.Degree > 0) -1
#fp.Continuity = [0]*numsegments
def onChanged(self, fp, prop):
if prop == 'Closed':
# if remove the last entry when curve gets opened
oldlen = len(fp.Continuity)
newlen = (len(self._segpoleslst(fp))-1+1*fp.Closed)
if oldlen > newlen:
fp.Continuity = fp.Continuity[:newlen]
if oldlen < newlen:
fp.Continuity = fp.Continuity + [0]*(newlen-oldlen)
if (hasattr(fp,'Closed') and
fp.Closed and
prop in ['Points','Degree','Closed'] and
len(fp.Points) % fp.Degree):
# the curve editing tools can't handle extra points
fp.Points=fp.Points[:(fp.Degree*(len(fp.Points)//fp.Degree))]
#for closed curves
if prop in ["Degree"] and fp.Degree >= 1:
self.resetcontinuity(fp)
if prop in ["Points","Degree","Continuity","Closed"]:
self.createGeometry(fp)
def createGeometry(self,fp):
import Part
plm = fp.Placement
if fp.Points:
startpoint=fp.Points[0]
edges = []
for segpoles in self._segpoleslst(fp):
# if len(segpoles) == fp.Degree # would skip additional poles
c = Part.BezierCurve() #last segment may have lower degree
c.increase(len(segpoles))
c.setPoles([startpoint]+segpoles)
edges.append(Part.Edge(c))
startpoint = segpoles[-1]
w = Part.Wire(edges)
if fp.Closed and w.isClosed():
try:
if hasattr(fp,"MakeFace"):
if fp.MakeFace:
w = Part.Face(w)
else:
w = Part.Face(w)
except Part.OCCError:
pass
fp.Shape = w
if hasattr(fp,"Area") and hasattr(w,"Area"):
fp.Area = w.Area
if hasattr(fp,"Length") and hasattr(w,"Length"):
fp.Length = w.Length
fp.Placement = plm
@classmethod
def symmetricpoles(cls,knot, p1, p2):
"""Make two poles symmetric respective to the knot."""
p1h = App.Vector(p1)
p2h = App.Vector(p2)
p1h.multiply(0.5)
p2h.multiply(0.5)
return ( knot+p1h-p2h , knot+p2h-p1h )
@classmethod
def tangentpoles(cls,knot, p1, p2,allowsameside=False):
"""Make two poles have the same tangent at knot."""
p12n = p2.sub(p1)
p12n.normalize()
p1k = knot-p1
p2k = knot-p2
p1k_= App.Vector(p12n)
kon12=(p1k * p12n)
if allowsameside or not (kon12 < 0 or p2k * p12n > 0):# instead of moving
p1k_.multiply(kon12)
pk_k = knot - p1 - p1k_
return (p1 + pk_k, p2 + pk_k)
else:
return cls.symmetricpoles(knot, p1, p2)
@staticmethod
def modifysymmetricpole(knot,p1):
"""calculate the coordinates of the opposite pole
of a symmetric knot"""
return knot + knot - p1
@staticmethod
def modifytangentpole(knot,p1,oldp2):
"""calculate the coordinates of the opposite pole
of a tangent knot"""
pn = knot - p1
pn.normalize()
pn.multiply((knot - oldp2).Length)
return pn + knot
_BezCurve = BezCurve

View File

@@ -0,0 +1,136 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 FreeCAD Developers *
# * *
# * 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. *
# * *
# * This program 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 this program; 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 BSpline.
"""
## @package bspline
# \ingroup DRAFT
# \brief This module provides the object code for Draft BSpline.
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCAD as App
from draftutils.utils import get_param
from draftobjects.base import DraftObject
class BSpline(DraftObject):
"""The BSpline object"""
def __init__(self, obj):
super(BSpline, self).__init__(obj, "BSpline")
_tip = "The points of the B-spline"
obj.addProperty("App::PropertyVectorList","Points",
"Draft", QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "If the B-spline is closed or not"
obj.addProperty("App::PropertyBool","Closed",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Create a face if this spline is closed"
obj.addProperty("App::PropertyBool","MakeFace",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The area of this object"
obj.addProperty("App::PropertyArea","Area",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
obj.MakeFace = get_param("fillmode",True)
obj.Closed = False
obj.Points = []
self.assureProperties(obj)
def assureProperties(self, obj): # for Compatibility with older versions
if not hasattr(obj, "Parameterization"):
obj.addProperty("App::PropertyFloat","Parameterization","Draft",QT_TRANSLATE_NOOP("App::Property","Parameterization factor"))
obj.Parameterization = 1.0
self.knotSeq = []
def parameterization (self, pts, a, closed):
"""Computes a knot Sequence for a set of points.
fac (0-1) : parameterization factor
fac = 0 -> Uniform / fac=0.5 -> Centripetal / fac=1.0 -> Chord-Length
"""
if closed: # we need to add the first point as the end point
pts.append(pts[0])
params = [0]
for i in range(1,len(pts)):
p = pts[i].sub(pts[i-1])
pl = pow(p.Length,a)
params.append(params[-1] + pl)
return params
def onChanged(self, fp, prop):
if prop == "Parameterization":
if fp.Parameterization < 0.:
fp.Parameterization = 0.
if fp.Parameterization > 1.0:
fp.Parameterization = 1.0
def execute(self, obj):
import Part
self.assureProperties(obj)
if not obj.Points:
obj.positionBySupport()
self.knotSeq = self.parameterization(obj.Points, obj.Parameterization, obj.Closed)
plm = obj.Placement
if obj.Closed and (len(obj.Points) > 2):
if obj.Points[0] == obj.Points[-1]: # should not occur, but OCC will crash
_err = "_BSpline.createGeometry: \
Closed with same first/last Point. Geometry not updated."
App.Console.PrintError(QT_TRANSLATE_NOOP('Draft', _err)+"\n")
return
spline = Part.BSplineCurve()
spline.interpolate(obj.Points, PeriodicFlag = True, Parameters = self.knotSeq)
# DNC: bug fix: convert to face if closed
shape = Part.Wire(spline.toShape())
# Creating a face from a closed spline cannot be expected to always work
# Usually, if the spline is not flat the call of Part.Face() fails
try:
if hasattr(obj,"MakeFace"):
if obj.MakeFace:
shape = Part.Face(shape)
else:
shape = Part.Face(shape)
except Part.OCCError:
pass
obj.Shape = shape
if hasattr(obj,"Area") and hasattr(shape,"Area"):
obj.Area = shape.Area
else:
spline = Part.BSplineCurve()
spline.interpolate(obj.Points, PeriodicFlag = False, Parameters = self.knotSeq)
shape = spline.toShape()
obj.Shape = shape
if hasattr(obj,"Area") and hasattr(shape,"Area"):
obj.Area = shape.Area
obj.Placement = plm
obj.positionBySupport()
_BSpline = BSpline

View File

@@ -0,0 +1,251 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2020 FreeCAD Developers *
# * *
# * 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. *
# * *
# * This program 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 this program; 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 Wire.
"""
## @package wire
# \ingroup DRAFT
# \brief This module provides the object code for Draft Wire.
import math
import FreeCAD as App
import DraftGeomUtils
import DraftVecUtils
from PySide.QtCore import QT_TRANSLATE_NOOP
from draftutils.utils import get_param
from draftobjects.base import DraftObject
class Wire(DraftObject):
"""The Wire object"""
def __init__(self, obj):
super(Wire, self).__init__(obj, "Wire")
_tip = "The vertices of the wire"
obj.addProperty("App::PropertyVectorList","Points",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "If the wire is closed or not"
obj.addProperty("App::PropertyBool","Closed",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The base object is the wire, it's formed from 2 objects"
obj.addProperty("App::PropertyLink","Base",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The tool object is the wire, it's formed from 2 objects"
obj.addProperty("App::PropertyLink","Tool",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The start point of this line"
obj.addProperty("App::PropertyVectorDistance","Start",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The end point of this line"
obj.addProperty("App::PropertyVectorDistance","End",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The length of this line"
obj.addProperty("App::PropertyLength","Length",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Radius to use to fillet the corners"
obj.addProperty("App::PropertyLength","FilletRadius",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Size of the chamfer to give to the corners"
obj.addProperty("App::PropertyLength","ChamferSize",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Create a face if this object is closed"
obj.addProperty("App::PropertyBool","MakeFace",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The number of subdivisions of each edge"
obj.addProperty("App::PropertyInteger","Subdivisions",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "The area of this object"
obj.addProperty("App::PropertyArea","Area",
"Draft",QT_TRANSLATE_NOOP("App::Property", _tip))
obj.MakeFace = get_param("fillmode",True)
obj.Closed = False
def execute(self, obj):
import Part
plm = obj.Placement
if obj.Base and (not obj.Tool):
if obj.Base.isDerivedFrom("Sketcher::SketchObject"):
shape = obj.Base.Shape.copy()
if obj.Base.Shape.isClosed():
if hasattr(obj,"MakeFace"):
if obj.MakeFace:
shape = Part.Face(shape)
else:
shape = Part.Face(shape)
obj.Shape = shape
elif obj.Base and obj.Tool:
if hasattr(obj.Base,'Shape') and hasattr(obj.Tool,'Shape'):
if (not obj.Base.Shape.isNull()) and (not obj.Tool.Shape.isNull()):
sh1 = obj.Base.Shape.copy()
sh2 = obj.Tool.Shape.copy()
shape = sh1.fuse(sh2)
if DraftGeomUtils.isCoplanar(shape.Faces):
shape = DraftGeomUtils.concatenate(shape)
obj.Shape = shape
p = []
for v in shape.Vertexes: p.append(v.Point)
if obj.Points != p: obj.Points = p
elif obj.Points:
if obj.Points[0] == obj.Points[-1]:
if not obj.Closed: obj.Closed = True
obj.Points.pop()
if obj.Closed and (len(obj.Points) > 2):
pts = obj.Points
if hasattr(obj,"Subdivisions"):
if obj.Subdivisions > 0:
npts = []
for i in range(len(pts)):
p1 = pts[i]
npts.append(pts[i])
if i == len(pts)-1:
p2 = pts[0]
else:
p2 = pts[i+1]
v = p2.sub(p1)
v = DraftVecUtils.scaleTo(v,v.Length/(obj.Subdivisions+1))
for j in range(obj.Subdivisions):
npts.append(p1.add(App.Vector(v).multiply(j+1)))
pts = npts
shape = Part.makePolygon(pts+[pts[0]])
if "ChamferSize" in obj.PropertiesList:
if obj.ChamferSize.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.ChamferSize.Value,chamfer=True)
if w:
shape = w
if "FilletRadius" in obj.PropertiesList:
if obj.FilletRadius.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.FilletRadius.Value)
if w:
shape = w
try:
if hasattr(obj,"MakeFace"):
if obj.MakeFace:
shape = Part.Face(shape)
else:
shape = Part.Face(shape)
except Part.OCCError:
pass
else:
edges = []
pts = obj.Points[1:]
lp = obj.Points[0]
for p in pts:
if not DraftVecUtils.equals(lp,p):
if hasattr(obj,"Subdivisions"):
if obj.Subdivisions > 0:
npts = []
v = p.sub(lp)
v = DraftVecUtils.scaleTo(v,v.Length/(obj.Subdivisions+1))
edges.append(Part.LineSegment(lp,lp.add(v)).toShape())
lv = lp.add(v)
for j in range(obj.Subdivisions):
edges.append(Part.LineSegment(lv,lv.add(v)).toShape())
lv = lv.add(v)
else:
edges.append(Part.LineSegment(lp,p).toShape())
else:
edges.append(Part.LineSegment(lp,p).toShape())
lp = p
try:
shape = Part.Wire(edges)
except Part.OCCError:
print("Error wiring edges")
shape = None
if "ChamferSize" in obj.PropertiesList:
if obj.ChamferSize.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.ChamferSize.Value,chamfer=True)
if w:
shape = w
if "FilletRadius" in obj.PropertiesList:
if obj.FilletRadius.Value != 0:
w = DraftGeomUtils.filletWire(shape,obj.FilletRadius.Value)
if w:
shape = w
if shape:
obj.Shape = shape
if hasattr(obj,"Area") and hasattr(shape,"Area"):
obj.Area = shape.Area
if hasattr(obj,"Length"):
obj.Length = shape.Length
obj.Placement = plm
obj.positionBySupport()
self.onChanged(obj,"Placement")
def onChanged(self, obj, prop):
if prop == "Start":
pts = obj.Points
invpl = App.Placement(obj.Placement).inverse()
realfpstart = invpl.multVec(obj.Start)
if pts:
if pts[0] != realfpstart:
pts[0] = realfpstart
obj.Points = pts
elif prop == "End":
pts = obj.Points
invpl = App.Placement(obj.Placement).inverse()
realfpend = invpl.multVec(obj.End)
if len(pts) > 1:
if pts[-1] != realfpend:
pts[-1] = realfpend
obj.Points = pts
elif prop == "Length":
if obj.Shape and not obj.Shape.isNull():
if obj.Length.Value != obj.Shape.Length:
if len(obj.Points) == 2:
v = obj.Points[-1].sub(obj.Points[0])
v = DraftVecUtils.scaleTo(v,obj.Length.Value)
obj.Points = [obj.Points[0],obj.Points[0].add(v)]
elif prop == "Placement":
pl = App.Placement(obj.Placement)
if len(obj.Points) >= 2:
displayfpstart = pl.multVec(obj.Points[0])
displayfpend = pl.multVec(obj.Points[-1])
if obj.Start != displayfpstart:
obj.Start = displayfpstart
if obj.End != displayfpend:
obj.End = displayfpend
_Wire = Wire

View File

@@ -0,0 +1,160 @@
# ***************************************************************************
# * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> *
# * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> *
# * Copyright (c) 2019 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> *
# * *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
"""This module provides the view provider code for Draft wire related objects.
"""
## @package view_base
# \ingroup DRAFT
# \brief This module provides the view provider code for Draft objects like\
# Line, Polyline, BSpline, BezCurve.
from pivy import coin
from PySide import QtCore
from PySide import QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import FreeCAD as App
import FreeCADGui as Gui
import draftutils.utils as utils
import draftutils.gui_utils as gui_utils
import DraftVecUtils
import DraftGeomUtils
from draftviewproviders.view_base import ViewProviderDraft
class ViewProviderWire(ViewProviderDraft):
"""A base View Provider for the Wire object"""
def __init__(self, vobj):
super(ViewProviderWire, self).__init__(vobj)
_tip = "Displays a Dimension symbol at the end of the wire"
vobj.addProperty("App::PropertyBool", "EndArrow",
"Draft", QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Arrow size"
vobj.addProperty("App::PropertyLength", "ArrowSize",
"Draft", QT_TRANSLATE_NOOP("App::Property", _tip))
_tip = "Arrow type"
vobj.addProperty("App::PropertyEnumeration", "ArrowType",
"Draft", QT_TRANSLATE_NOOP("App::Property", _tip))
vobj.ArrowSize = utils.get_param("arrowsize",0.1)
vobj.ArrowType = utils.ARROW_TYPES
vobj.ArrowType = utils.ARROW_TYPES[utils.get_param("dimsymbol",0)]
def attach(self, vobj):
self.Object = vobj.Object
col = coin.SoBaseColor()
col.rgb.setValue(vobj.LineColor[0],vobj.LineColor[1],vobj.LineColor[2])
self.coords = coin.SoTransform()
self.pt = coin.SoSeparator()
self.pt.addChild(col)
self.pt.addChild(self.coords)
self.symbol = gui_utils.dim_symbol()
self.pt.addChild(self.symbol)
super(ViewProviderWire, self).attach(vobj)
self.onChanged(vobj,"EndArrow")
def updateData(self, obj, prop):
if prop == "Points":
if obj.Points:
p = obj.Points[-1]
if hasattr(self,"coords"):
self.coords.translation.setValue((p.x,p.y,p.z))
if len(obj.Points) >= 2:
v1 = obj.Points[-2].sub(obj.Points[-1])
if not DraftVecUtils.isNull(v1):
v1.normalize()
_rot = coin.SbRotation()
_rot.setValue(coin.SbVec3f(1, 0, 0), coin.SbVec3f(v1[0], v1[1], v1[2]))
self.coords.rotation.setValue(_rot)
return
def onChanged(self, vobj, prop):
if prop in ["EndArrow","ArrowSize","ArrowType","Visibility"]:
rn = vobj.RootNode
if hasattr(self,"pt") and hasattr(vobj,"EndArrow"):
if vobj.EndArrow and vobj.Visibility:
self.pt.removeChild(self.symbol)
s = utils.ARROW_TYPES.index(vobj.ArrowType)
self.symbol = gui_utils.dim_symbol(s)
self.pt.addChild(self.symbol)
self.updateData(vobj.Object,"Points")
if hasattr(vobj,"ArrowSize"):
s = vobj.ArrowSize
else:
s = utils.get_param("arrowsize",0.1)
self.coords.scaleFactor.setValue((s,s,s))
rn.addChild(self.pt)
else:
if self.symbol:
if self.pt.findChild(self.symbol) != -1:
self.pt.removeChild(self.symbol)
if rn.findChild(self.pt) != -1:
rn.removeChild(self.pt)
if prop in ["LineColor"]:
if hasattr(self, "pt"):
self.pt[0].rgb.setValue(vobj.LineColor[0],vobj.LineColor[1],vobj.LineColor[2])
super(ViewProviderWire, self).onChanged(vobj, prop)
return
def claimChildren(self):
if hasattr(self.Object,"Base"):
return [self.Object.Base,self.Object.Tool]
return []
def setupContextMenu(self,vobj,menu):
action1 = QtGui.QAction(QtGui.QIcon(":/icons/Draft_Edit.svg"),
"Flatten this wire",
menu)
QtCore.QObject.connect(action1,
QtCore.SIGNAL("triggered()"),
self.flatten)
menu.addAction(action1)
def flatten(self):
if hasattr(self,"Object"):
if len(self.Object.Shape.Wires) == 1:
fw = DraftGeomUtils.flattenWire(self.Object.Shape.Wires[0])
points = [v.Point for v in fw.Vertexes]
if len(points) == len(self.Object.Points):
if points != self.Object.Points:
App.ActiveDocument.openTransaction("Flatten wire")
Gui.doCommand("FreeCAD.ActiveDocument." +
self.Object.Name +
".Points=" +
str(points).replace("Vector", "FreeCAD.Vector").replace(" " , ""))
App.ActiveDocument.commitTransaction()
else:
_msg = "This Wire is already flat"
App.Console.PrintMessage(QT_TRANSLATE_NOOP("Draft", _msg) + "\n")
_ViewProviderWire = ViewProviderWire