Draft Bezier Curves: Add functionality to mockup
- Split poles to multiple bezier segments - set degree of bezier curve - Close Draft bezier curves - set Degree at creation - implement addPoint for piecewise Bezier curves Draft BezCurve bugfixes - fix Close button for BexCurve Edit - fix tracker behaviour in for BezCurve.undoLast Draft Bezier Curve: use circle trackers for control points Draft: prepare functions to make bezier knots tangent or symmetric
This commit is contained in:
parent
2d43d61b92
commit
69b09448d9
|
@ -831,7 +831,7 @@ def makeBSpline(pointslist,closed=False,placement=None,face=True,support=None):
|
|||
FreeCAD.ActiveDocument.recompute()
|
||||
return obj
|
||||
#######################################
|
||||
def makeBezCurve(pointslist,placement=None,support=None):
|
||||
def makeBezCurve(pointslist,closed=False,placement=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 isinstance(pointslist,list):
|
||||
|
@ -845,7 +845,13 @@ def makeBezCurve(pointslist,placement=None,support=None):
|
|||
obj = FreeCAD.ActiveDocument.addObject("Part::Part2DObjectPython",fname)
|
||||
_BezCurve(obj)
|
||||
obj.Points = pointslist
|
||||
# obj.Closed = closed
|
||||
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 placement: obj.Placement = placement
|
||||
if gui:
|
||||
|
@ -3963,12 +3969,10 @@ class _BezCurve(_DraftObject):
|
|||
_DraftObject.__init__(self,obj,"BezCurve")
|
||||
obj.addProperty("App::PropertyVectorList","Points","Draft",
|
||||
"The points of the Bezier curve")
|
||||
# obj.addProperty("App::PropertyBool","Closed","Draft",
|
||||
# "If the Bezier curve is closed or not")
|
||||
obj.addProperty("App::PropertyInteger","Degree","Draft",
|
||||
"The degree of the Bezier function")
|
||||
obj.addProperty("App::PropertyBool","Closed","Draft",
|
||||
"If the Bezier curve is closed or not(??)")
|
||||
"If the Bezier curve should be closed or not")
|
||||
obj.Closed = False
|
||||
obj.Degree = 3
|
||||
|
||||
|
@ -3976,28 +3980,51 @@ class _BezCurve(_DraftObject):
|
|||
self.createGeometry(fp)
|
||||
|
||||
def onChanged(self, fp, prop):
|
||||
if prop in ["Points","Degree"]:
|
||||
if prop in ["Points","Degree", "Closed"]:
|
||||
self.createGeometry(fp)
|
||||
|
||||
def createGeometry(self,fp):
|
||||
import Part
|
||||
plm = fp.Placement
|
||||
if fp.Points:
|
||||
# if fp.Points[0] == fp.Points[-1]:
|
||||
# if not fp.Closed: fp.Closed = True
|
||||
# fp.Points.pop()
|
||||
# if fp.Closed and (len(fp.Points) > 2):
|
||||
c = Part.BezierCurve()
|
||||
c.setPoles(fp.Points)
|
||||
e = Part.Edge(c)
|
||||
w = Part.Wire(e)
|
||||
startpoint=fp.Points[0]
|
||||
poles=fp.Points[1:]
|
||||
if fp.Closed and (len(fp.Points) > 2):
|
||||
poles.append(fp.Points[0])
|
||||
segpoleslst=[poles[x:x+fp.Degree] for x in \
|
||||
xrange(0, len(poles), fp.Degree)]
|
||||
edges = []
|
||||
for segpoles in segpoleslst:
|
||||
# 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)
|
||||
fp.Shape = w
|
||||
# else:
|
||||
# spline = Part.BezCurveCurve()
|
||||
# spline.interpolate(fp.Points, False)
|
||||
# fp.Shape = spline.toShape()
|
||||
fp.Placement = plm
|
||||
|
||||
@staticmethod
|
||||
def symmetricpoles(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+p1-p2 , knot+p2-p1)
|
||||
|
||||
@staticmethod
|
||||
def tangentpoles(knot, p1, p2):
|
||||
"""make two poles have the same tangent at knot"""
|
||||
p12n=p2.sub(p1)
|
||||
p12n.normalize()
|
||||
p1k=knot-p1
|
||||
p1k_= FreeCAD.Vector(p12n)
|
||||
p1k_.multiply(p1k*p12n)
|
||||
pk_k=knot-p1-p1k_
|
||||
return (p1+pk_k,p2+pk_k)
|
||||
|
||||
# for compatibility with older versions ???????
|
||||
_ViewProviderBezCurve = _ViewProviderWire
|
||||
#######################################
|
||||
|
|
|
@ -193,6 +193,8 @@ def geomType(edge):
|
|||
return "Circle"
|
||||
elif isinstance(edge.Curve,Part.BSplineCurve):
|
||||
return "BSplineCurve"
|
||||
elif isinstance(edge.Curve,Part.BezierCurve):
|
||||
return "BezierCurve"
|
||||
elif isinstance(edge.Curve,Part.Ellipse):
|
||||
return "Ellipse"
|
||||
else:
|
||||
|
@ -610,7 +612,8 @@ def sortEdges(lEdges, aVertex=None):
|
|||
elif geomType(result[3]) == "Circle":
|
||||
mp = findMidpoint(result[3])
|
||||
return [Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape()]
|
||||
elif geomType(result[3]) == "BSplineCurve":
|
||||
elif geomType(result[3]) == "BSplineCurve" or\
|
||||
geomType(result[3]) == "BezierCurve":
|
||||
if isLine(result[3].Curve):
|
||||
return [Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()]
|
||||
else:
|
||||
|
@ -648,7 +651,8 @@ def sortEdges(lEdges, aVertex=None):
|
|||
mp = findMidpoint(result[3])
|
||||
newedge = Part.Arc(aVertex.Point,mp,result[3].Vertexes[0].Point).toShape()
|
||||
olEdges += [newedge] + next
|
||||
elif geomType(result[3]) == "BSplineCurve":
|
||||
elif geomType(result[3]) == "BSplineCurve" or \
|
||||
geomType(result[3]) == "BezierCurve":
|
||||
if isLine(result[3].Curve):
|
||||
newedge = Part.Line(aVertex.Point,result[3].Vertexes[0].Point).toShape()
|
||||
olEdges += [newedge] + next
|
||||
|
@ -1099,7 +1103,8 @@ def findDistance(point,edge,strict=False):
|
|||
return None
|
||||
else:
|
||||
return dist
|
||||
elif geomType(edge) == "BSplineCurve":
|
||||
elif geomType(edge) == "BSplineCurve" or \
|
||||
geomType(edge) == "BezierCurve":
|
||||
try:
|
||||
pr = edge.Curve.parameter(point)
|
||||
np = edge.Curve.value(pr)
|
||||
|
@ -1214,7 +1219,8 @@ def getTangent(edge,frompoint=None):
|
|||
'''
|
||||
if geomType(edge) == "Line":
|
||||
return vec(edge)
|
||||
elif geomType(edge) == "BSplineCurve":
|
||||
elif geomType(edge) == "BSplineCurve" or \
|
||||
geomType(edge) == "BezierCurve":
|
||||
if not frompoint:
|
||||
return None
|
||||
cp = edge.Curve.parameter(frompoint)
|
||||
|
@ -1817,7 +1823,8 @@ def cleanProjection(shape,tessellate=False):
|
|||
newedges.append(a)
|
||||
else:
|
||||
newedges.append(e.Curve.toShape())
|
||||
elif geomType(e) == "BSplineCurve":
|
||||
elif geomType(e) == "BSplineCurve" or \
|
||||
geomType(e) == "BezierCurve":
|
||||
if tessellate:
|
||||
newedges.append(Part.Wire(curvetowire(e,e.Curve.NbPoles)))
|
||||
else:
|
||||
|
|
|
@ -665,7 +665,7 @@ class BSpline(Line):
|
|||
if self.ui:
|
||||
if self.ui.continueMode:
|
||||
self.Activated()
|
||||
#######################################
|
||||
|
||||
class BezCurve(Line):
|
||||
"a FreeCAD command for creating a Bezier Curve"
|
||||
|
||||
|
@ -718,20 +718,10 @@ class BezCurve(Line):
|
|||
|
||||
def undolast(self):
|
||||
"undoes last line segment"
|
||||
### this won't work exactly the same way for Bcurve????
|
||||
if (len(self.node) > 1):
|
||||
self.node.pop()
|
||||
self.bezcurvetrack.update(self.node)
|
||||
### self.obj.Shape.Edge[0].Curve.removePole(???)
|
||||
# c = Part.BezierCurve()
|
||||
# c.setPoles(self.Points)
|
||||
# e = Part.Edge(c)
|
||||
# w = Part.Wire(e)
|
||||
## spline = Part.bSplineCurve()
|
||||
## spline.interpolate(self.node, False)
|
||||
## self.obj.Shape = spline.toShape()
|
||||
# self.obj.Shape = w
|
||||
msg(translate("draft", "BezCurve sb undoing last segment\n"))
|
||||
self.obj.Shape = self.updateShape(self.node)
|
||||
msg(translate("draft", "Last point has been removed\n"))
|
||||
|
||||
def drawUpdate(self,point):
|
||||
|
@ -742,13 +732,18 @@ class BezCurve(Line):
|
|||
self.planetrack.set(self.node[0])
|
||||
msg(translate("draft", "Pick next point:\n"))
|
||||
else:
|
||||
c = Part.BezierCurve()
|
||||
c.setPoles(self.node)
|
||||
e = Part.Edge(c)
|
||||
w = Part.Wire(e)
|
||||
self.obj.Shape = w
|
||||
self.obj.Shape = self.updateShape(self.node)
|
||||
msg(translate("draft", "Pick next point, or (F)inish or (C)lose:\n"))
|
||||
|
||||
|
||||
def updateShape(self, pts):
|
||||
'''creates shape for display during creation process.'''
|
||||
# not quite right. draws 1 big bez. sb segmented
|
||||
c = Part.BezierCurve()
|
||||
c.setPoles(pts)
|
||||
e = Part.Edge(c)
|
||||
w = Part.Wire(e)
|
||||
return(w)
|
||||
|
||||
def finish(self,closed=False,cont=False):
|
||||
"terminates the operation and closes the poly if asked"
|
||||
if self.ui:
|
||||
|
@ -764,14 +759,13 @@ class BezCurve(Line):
|
|||
self.commit(translate("draft","Create BezCurve"),
|
||||
['import Draft',
|
||||
'points='+pts,
|
||||
'Draft.makeBezCurve(points,support='+sup+')'])
|
||||
'Draft.makeBezCurve(points,closed='+str(closed)+',support='+sup+')'])
|
||||
except:
|
||||
print "Draft: error delaying commit"
|
||||
Creator.finish(self)
|
||||
if self.ui:
|
||||
if self.ui.continueMode:
|
||||
self.Activated()
|
||||
#######################################
|
||||
|
||||
class FinishLine:
|
||||
"a FreeCAD command to finish any running Line drawing operation"
|
||||
|
@ -3210,10 +3204,19 @@ class Edit(Modifier):
|
|||
if "Placement" in self.obj.PropertiesList:
|
||||
self.pl = self.obj.Placement
|
||||
self.invpl = self.pl.inverse()
|
||||
if Draft.getType(self.obj) in ["Wire","BSpline","BezCurve"]:
|
||||
if Draft.getType(self.obj) in ["Wire","BSpline"]:
|
||||
for p in self.obj.Points:
|
||||
if self.pl: p = self.pl.multVec(p)
|
||||
self.editpoints.append(p)
|
||||
elif Draft.getType(self.obj) == "BezCurve":
|
||||
self.resetTrackersBezier()
|
||||
self.call = self.view.addEventCallback("SoEvent",self.action)
|
||||
self.running = True
|
||||
plane.save()
|
||||
if "Shape" in self.obj.PropertiesList:
|
||||
plane.alignToFace(self.obj.Shape)
|
||||
if self.planetrack:
|
||||
self.planetrack.set(self.editpoints[0])
|
||||
elif Draft.getType(self.obj) == "Circle":
|
||||
self.editpoints.append(self.obj.Placement.Base)
|
||||
if self.obj.FirstAngle == self.obj.LastAngle:
|
||||
|
@ -3237,21 +3240,22 @@ class Edit(Modifier):
|
|||
self.editpoints.append(self.obj.End)
|
||||
self.editpoints.append(self.obj.Dimline)
|
||||
self.editpoints.append(Vector(p[0],p[1],p[2]))
|
||||
self.trackers = []
|
||||
if self.editpoints:
|
||||
for ep in range(len(self.editpoints)):
|
||||
self.trackers.append(editTracker(self.editpoints[ep],self.obj.Name,
|
||||
ep,self.obj.ViewObject.LineColor))
|
||||
self.call = self.view.addEventCallback("SoEvent",self.action)
|
||||
self.running = True
|
||||
plane.save()
|
||||
if "Shape" in self.obj.PropertiesList:
|
||||
plane.alignToFace(self.obj.Shape)
|
||||
if self.planetrack:
|
||||
self.planetrack.set(self.editpoints[0])
|
||||
else:
|
||||
msg(translate("draft", "This object type is not editable\n"),'warning')
|
||||
self.finish()
|
||||
if Draft.getType(self.obj) != "BezCurve":
|
||||
self.trackers = []
|
||||
if self.editpoints:
|
||||
for ep in range(len(self.editpoints)):
|
||||
self.trackers.append(editTracker(self.editpoints[ep],self.obj.Name,
|
||||
ep,self.obj.ViewObject.LineColor))
|
||||
self.call = self.view.addEventCallback("SoEvent",self.action)
|
||||
self.running = True
|
||||
plane.save()
|
||||
if "Shape" in self.obj.PropertiesList:
|
||||
plane.alignToFace(self.obj.Shape)
|
||||
if self.planetrack:
|
||||
self.planetrack.set(self.editpoints[0])
|
||||
else:
|
||||
msg(translate("draft", "This object type is not editable\n"),'warning')
|
||||
self.finish()
|
||||
else:
|
||||
self.finish()
|
||||
|
||||
|
@ -3302,7 +3306,7 @@ class Edit(Modifier):
|
|||
if self.ui.addButton.isChecked():
|
||||
if self.point:
|
||||
self.pos = arg["Position"]
|
||||
self.addPoint(self.point)
|
||||
self.addPoint(self.point,info)
|
||||
elif self.ui.delButton.isChecked():
|
||||
if 'EditNode' in info["Component"]:
|
||||
self.delPoint(int(info["Component"][8:]))
|
||||
|
@ -3405,41 +3409,65 @@ class Edit(Modifier):
|
|||
self.ui.editUi()
|
||||
self.node = []
|
||||
|
||||
def addPoint(self,point):
|
||||
def addPoint(self,point,info=None):
|
||||
if not (Draft.getType(self.obj) in ["Wire","BSpline","BezCurve"]): return
|
||||
pts = self.obj.Points
|
||||
if ( Draft.getType(self.obj) == "Wire" ):
|
||||
if (self.obj.Closed == True):
|
||||
# DNC: work around.... seems there is a
|
||||
# bug in approximate method for closed wires...
|
||||
edges = self.obj.Shape.Wires[0].Edges
|
||||
e1 = edges[-1] # last edge
|
||||
v1 = e1.Vertexes[0].Point
|
||||
v2 = e1.Vertexes[1].Point
|
||||
v2.multiply(0.9999)
|
||||
edges[-1] = Part.makeLine(v1,v2)
|
||||
edges.reverse()
|
||||
wire = Part.Wire(edges)
|
||||
curve = wire.approximate(0.0001,0.0001,100,25)
|
||||
else:
|
||||
# DNC: this version is much more reliable near sharp edges!
|
||||
curve = self.obj.Shape.Wires[0].approximate(0.0001,0.0001,100,25)
|
||||
elif ( Draft.getType(self.obj) in ["BSpline","BezCurve"]):
|
||||
if (self.obj.Closed == True):
|
||||
curve = self.obj.Shape.Edges[0].Curve
|
||||
else:
|
||||
curve = self.obj.Shape.Curve
|
||||
uNewPoint = curve.parameter(point)
|
||||
uPoints = []
|
||||
for p in self.obj.Points:
|
||||
uPoints.append(curve.parameter(p))
|
||||
for i in range(len(uPoints)-1):
|
||||
if ( uNewPoint > uPoints[i] ) and ( uNewPoint < uPoints[i+1] ):
|
||||
pts.insert(i+1, self.invpl.multVec(point))
|
||||
break
|
||||
# DNC: fix: add points to last segment if curve is closed
|
||||
if ( self.obj.Closed ) and ( uNewPoint > uPoints[-1] ) :
|
||||
pts.append(self.invpl.multVec(point))
|
||||
if Draft.getType(self.obj) == "BezCurve":
|
||||
if not info['Component'].startswith('Edge'):
|
||||
return # clicked control point
|
||||
edgeindex = int(info['Component'].lstrip('Edge'))-1
|
||||
wire=self.obj.Shape.Wires[0]
|
||||
bz=wire.Edges[edgeindex].Curve
|
||||
param=bz.parameter(point)
|
||||
seg1=wire.Edges[edgeindex].copy().Curve
|
||||
seg2=wire.Edges[edgeindex].copy().Curve
|
||||
seg1.segment(seg1.FirstParameter,param)
|
||||
seg2.segment(param,seg2.LastParameter)
|
||||
if edgeindex == len(wire.Edges):
|
||||
#we hit the last segment, we need to fix the degree
|
||||
degree=wire.Edges[0].Curve.Degree
|
||||
seg1.increase(degree)
|
||||
seg2.increase(degree)
|
||||
edges=wire.Edges[0:edgeindex]+[Part.Edge(seg1),Part.Edge(seg2)]\
|
||||
+ wire.Edges[edgeindex+1:]
|
||||
pts = edges[0].Curve.getPoles()[0:1]
|
||||
for edge in edges:
|
||||
pts.extend(edge.Curve.getPoles()[1:])
|
||||
if self.obj.Closed:
|
||||
pts.pop()
|
||||
else:
|
||||
if ( Draft.getType(self.obj) == "Wire" ):
|
||||
if (self.obj.Closed == True):
|
||||
# DNC: work around.... seems there is a
|
||||
# bug in approximate method for closed wires...
|
||||
edges = self.obj.Shape.Wires[0].Edges
|
||||
e1 = edges[-1] # last edge
|
||||
v1 = e1.Vertexes[0].Point
|
||||
v2 = e1.Vertexes[1].Point
|
||||
v2.multiply(0.9999)
|
||||
edges[-1] = Part.makeLine(v1,v2)
|
||||
edges.reverse()
|
||||
wire = Part.Wire(edges)
|
||||
curve = wire.approximate(0.0001,0.0001,100,25)
|
||||
else:
|
||||
# DNC: this version is much more reliable near sharp edges!
|
||||
curve = self.obj.Shape.Wires[0].approximate(0.0001,0.0001,100,25)
|
||||
elif ( Draft.getType(self.obj) in ["BSpline"]):
|
||||
if (self.obj.Closed == True):
|
||||
curve = self.obj.Shape.Edges[0].Curve
|
||||
else:
|
||||
curve = self.obj.Shape.Curve
|
||||
uNewPoint = curve.parameter(point)
|
||||
uPoints = []
|
||||
for p in self.obj.Points:
|
||||
uPoints.append(curve.parameter(p))
|
||||
for i in range(len(uPoints)-1):
|
||||
if ( uNewPoint > uPoints[i] ) and ( uNewPoint < uPoints[i+1] ):
|
||||
pts.insert(i+1, self.invpl.multVec(point))
|
||||
break
|
||||
# DNC: fix: add points to last segment if curve is closed
|
||||
if ( self.obj.Closed ) and ( uNewPoint > uPoints[-1] ) :
|
||||
pts.append(self.invpl.multVec(point))
|
||||
self.doc.openTransaction("Edit "+self.obj.Name)
|
||||
self.obj.Points = pts
|
||||
self.doc.commitTransaction()
|
||||
|
@ -3457,16 +3485,37 @@ class Edit(Modifier):
|
|||
self.doc.commitTransaction()
|
||||
self.resetTrackers()
|
||||
|
||||
def resetTrackersBezier(self):
|
||||
knotmarker = coin.SoMarkerSet.SQUARE_FILLED_9_9
|
||||
polemarker = coin.SoMarkerSet.CIRCLE_FILLED_9_9
|
||||
self.trackers=[]
|
||||
pointswithmarkers=[(self.obj.Shape.Edges[0].Curve.\
|
||||
getPole(1),knotmarker)]
|
||||
for edgeindex, edge in enumerate(self.obj.Shape.Edges):
|
||||
poles=edge.Curve.getPoles()
|
||||
pointswithmarkers.extend([(point,polemarker) for \
|
||||
point in poles[1:-1]])
|
||||
if not self.obj.Closed or len(self.obj.Shape.Edges) > edgeindex +1:
|
||||
pointswithmarkers.append((poles[-1],knotmarker))
|
||||
for index,pwm in enumerate(pointswithmarkers):
|
||||
p,marker=pwm
|
||||
if self.pl: p = self.pl.multVec(p)
|
||||
self.trackers.append(editTracker(p,self.obj.Name,\
|
||||
index,self.obj.ViewObject.LineColor,\
|
||||
marker=marker))
|
||||
|
||||
def resetTrackers(self):
|
||||
for t in self.trackers:
|
||||
t.finalize()
|
||||
self.trackers = []
|
||||
for ep in range(len(self.obj.Points)):
|
||||
objPoints = self.obj.Points[ep]
|
||||
if self.pl: objPoints = self.pl.multVec(objPoints)
|
||||
self.trackers.append(editTracker(objPoints,self.obj.Name,ep,self.obj.ViewObject.LineColor))
|
||||
if Draft.getType(self.obj) == "BezCurve":
|
||||
self.resetTrackersBezier()
|
||||
else:
|
||||
for ep in range(len(self.obj.Points)):
|
||||
objPoints = self.obj.Points[ep]
|
||||
if self.pl: objPoints = self.pl.multVec(objPoints)
|
||||
self.trackers.append(editTracker(objPoints,self.obj.Name,ep,self.obj.ViewObject.LineColor))
|
||||
|
||||
|
||||
class AddToGroup():
|
||||
"The AddToGroup FreeCAD command definition"
|
||||
|
||||
|
|
|
@ -605,14 +605,15 @@ class ghostTracker(Tracker):
|
|||
|
||||
class editTracker(Tracker):
|
||||
"A node edit tracker"
|
||||
def __init__(self,pos=Vector(0,0,0),name="None",idx=0,objcol=None):
|
||||
def __init__(self,pos=Vector(0,0,0),name="None",idx=0,objcol=None,\
|
||||
marker=coin.SoMarkerSet.SQUARE_FILLED_9_9):
|
||||
color = coin.SoBaseColor()
|
||||
if objcol:
|
||||
color.rgb = objcol[:3]
|
||||
else:
|
||||
color.rgb = FreeCADGui.draftToolBar.getDefaultColor("snap")
|
||||
self.marker = coin.SoMarkerSet() # this is the marker symbol
|
||||
self.marker.markerIndex = coin.SoMarkerSet.SQUARE_FILLED_9_9
|
||||
self.marker.markerIndex = marker
|
||||
self.coords = coin.SoCoordinate3() # this is the coordinate
|
||||
self.coords.point.setValue((pos.x,pos.y,pos.z))
|
||||
selnode = coin.SoType.fromName("SoFCSelection").createInstance()
|
||||
|
|
Loading…
Reference in New Issue
Block a user