Sketcher: Bezier curves

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
User avatar
microelly2
Veteran
Posts: 4688
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Post by microelly2 »

microelly2 wrote: Sat Apr 21, 2018 3:11 pm
abdullah wrote: Sat Apr 21, 2018 9:59 am
1. I see there is a first sketch which what I think is a periodic B-Spline. Am I right?
2. I see a second sketch with lines, which you are somehow using to modify the sketch with the periodic B-Spline. Could you elaborate a little bit on the "how"?

Code: Select all

# -*- coding: utf-8 -*-
#-------------------------------------------------
#-- create a sketch for a closed bezier curves chain
#--
#-- microelly 2018  0.1
#--
#-- GNU Lesser General Public License (LGPL)
#-------------------------------------------------

import FreeCAD,FreeCADGui,Sketcher,Part

App = FreeCAD
Gui = FreeCADGui

import numpy as np
import time
import random

class FeaturePython:
	''' basic defs'''

	def __init__(self, obj):
		obj.Proxy = self
		self.Object = obj

	def attach(self, vobj):
		self.Object = vobj.Object

	def claimChildren(self):
		return self.Object.Group

	def __getstate__(self):
		return None

	def __setstate__(self, state):
		return None


class ViewProvider:
	''' basic defs '''

	def __init__(self, obj):
		obj.Proxy = self
		self.Object = obj

	def __getstate__(self):
		return None

	def __setstate__(self, state):
		return None


	def setupContextMenu(self, obj, menu):
		menu.clear()
		action = menu.addAction("MyMethod #1")
		action.triggered.connect(lambda:self.methodA(obj.Object))
		action = menu.addAction("MyMethod #2")
		menu.addSeparator()
		action.triggered.connect(lambda:self.methodB(obj.Object))
		action = menu.addAction("Edit Sketch")
		action.triggered.connect(lambda:self.myedit(obj.Object))


	def myedit(self,obj):
		self.methodB(None)
		Gui.activeDocument().setEdit(obj.Name)
		obj.ViewObject.show()
		self.methodA(None)

	def methodA(self,obj):
		print "my Method A"
		FreeCAD.activeDocument().recompute()

	def methodB(self,obj):
		print "my method B"
		FreeCAD.activeDocument().recompute()

	def methodC(self,obj):
		print "my method C"
		FreeCAD.activeDocument().recompute()

	def unsetEdit(self,vobj,mode=0):
		self.methodC(None)


	def doubleClicked(self,vobj):
		print "double clicked"
		self.myedit(vobj.Object)
		
		print "Ende double clicked"


def getNamedConstraint(sketch,name):
	'''get the index of a constraint name'''
	for i,c in enumerate (sketch.Constraints):
		if c.Name==name: return i
	print ('Constraint name "'+name+'" not in ' +sketch.Label)
	raise Exception ('Constraint name "'+name+'" not in ' + sketch.Label)



def clearReportView(name):
	from PySide import QtGui
	mw=Gui.getMainWindow()
	r=mw.findChild(QtGui.QTextEdit, "Report view")
	r.clear()
	import time
	now = time.ctime(int(time.time()))
	App.Console.PrintWarning("Cleared Report view " +str(now)+" by " + name+"\n")
##\endcond


class BezierSketch(FeaturePython):
	'''Sketch Object with Python for Bezier Curve''' 

	##\cond
	def __init__(self, obj, icon='/home/thomas/.FreeCAD/Mod/freecad-nurbs/icons/draw.svg'):
		obj.Proxy = self
		self.Type = self.__class__.__name__
		self.obj2 = obj
		self.aa = None
		obj.addProperty("App::PropertyInteger",'polescount',).polescount=12
		obj.addProperty("App::PropertyBool",'init')
		ViewProvider(obj.ViewObject)
	##\endcond


	def execute(proxy,obj):
		obj.Shape=run(obj)


def run(sk):

	gc=sk.GeometryCount
	ap=gc/3
	print ("geometry count, poles, constraints count",gc,ap,sk.ConstraintCount)

	try:
		if sk.init:
			if sk.ConstraintCount <= 150:
				for i in range(ap-1):
					rc=sk.addConstraint(Sketcher.Constraint('Parallel',3*i+3,3*i+2))
#					sk.setVirtualSpace(rc, True)
					sk.solve() 

			rc=sk.addConstraint(Sketcher.Constraint('Parallel',gc-1,0)) 
#			sk.setVirtualSpace(rc, True)

			sk.init=False

			for i in range(90):
				# must become parameteric, depends on the number sk.polescount
				#if i in [2,3,8,9,15,16,]: # for 3
				# if i in [0,1,6,7,13,14,19,20]: # for 5
				if i in [0,1, 6,7, 13,14, 20,21, 27,28, 34,35, 41,42, 47,48 ]: # for 7
					sk.setDriving(i,False)
					sk.setVirtualSpace(i, True)

			jj=sk.addConstraint(Sketcher.Constraint('Distance',0,10)) 
			sk.setDriving(jj,False)
			sk.setVirtualSpace(jj, True)

			jj=sk.addConstraint(Sketcher.Constraint('Distance',20,10)) 
			sk.setDriving(jj,False)
			sk.setVirtualSpace(jj, True)
			print "done"

	except: pass 

#	for c in range(sk.ConstraintCount):
#		sk.setVirtualSpace(c, True)

	try:
		#poles=[ sk.getPoint(i,1) +FreeCAD.Vector(0,0,random.random()*2000) for i in range(sk.polescount)]
		#poles=[ sk.getPoint(i,1) +FreeCAD.Vector(0,0,0) for i in range(sk.polescount*3)]

		# gleich doppelte Punkte
		poles=[]
		for i in range(sk.polescount*3):
			p=sk.getPoint(i,1)
			if i%3== 0:
				poles += [p,p,p] # corner pole -- multiplicity 3
			else:
				poles += [p] # tangent pole

		cc=Part.BSplineCurve(poles+[poles[0]])
		return cc.toShape()
	except:
		return Part.Shape()


def init_bezierring(sk,count=5,source=None):


	if source <> None:
		ptsa=source.Shape.Wires[0].discretize(count*2*10)
		ptsb=[]

		# read some exact point p for the pole and a approx tangent p2-p, pv-p for the tangent indicators
		for n in range(count):
			p=ptsa[2*n*10]
			p2=p+ (ptsa[2*n*10+1]-p).normalize()*10
			pv=p+((p-p2).normalize())*(10)
			if n==0:
				last=pv
				ptsb += [p,p2]
			else:
				ptsb += [pv,p,p2]

		# the tangent from the first pole
		ptsb += [last]

		# map from xz scan to xy sketch
		pts=[FreeCAD.Vector(p.x,p.z,0) for p in ptsb]

	else: # a generated circle with some noise
		r=100
		pts=[]
		for i in range(count):
			p=FreeCAD.Vector(r*np.cos(2*i*np.pi/count),r*np.sin(2*i*np.pi/count),0)
			p2=FreeCAD.Vector(r*np.cos((2*i+1)*np.pi/count),r*np.sin((2*i+1)*np.pi/count),0)
			p2 += FreeCAD.Vector((0.5-random.random())*0.2*r,(0.5-random.random())*0.2*r)
			pm=(p+p2)*0.5
			pts +=[p,pm,p2]

	for i in range(count):
			if i <> 0: # connect to the last segment with a connector line
				lc=sk.addGeometry(Part.LineSegment(pts[3*i-1],pts[3*i]),False)
				sk.addConstraint(Sketcher.Constraint('Coincident',lb,2,lc,1)) 

			la=sk.addGeometry(Part.LineSegment(pts[3*i],pts[3*i+1]),False)
			lb=sk.addGeometry(Part.LineSegment(pts[3*i+1],pts[3*i+2]),False)

			if 1: # avoid moving of points 
				p2=sk.getPoint(lb,1)
				cc=sk.addConstraint(Sketcher.Constraint('DistanceX',lb,1,p2.x)) 
				sk.addConstraint(Sketcher.Constraint('DistanceY',lb,1,p2.y)) 
				sk.renameConstraint(cc, u'aa ' + str(i))
				p2=sk.getPoint(la,1)
				cc=sk.addConstraint(Sketcher.Constraint('DistanceX',la,1,p2.x)) 
				sk.addConstraint(Sketcher.Constraint('DistanceY',la,1,p2.y)) 
				sk.renameConstraint(cc, u'bb ' + str(i))

			# blocking does not work (unsolvable by sketcher) why?
#			sk.addConstraint(Sketcher.Constraint('Block',lb))
			sk.addConstraint(Sketcher.Constraint('Coincident',la,2,lb,1)) 

			if i <> 0: # connect connector line to the new created segment
				sk.addConstraint(Sketcher.Constraint('Coincident',lc,2,la,1)) 

	# close the figure
	# the last connector
	la=sk.addGeometry(Part.LineSegment(pts[3*i],pts[0]),False)
	p2=sk.getPoint(la,1)
	cc=sk.addConstraint(Sketcher.Constraint('DistanceX',la,1,p2.x)) 
	sk.addConstraint(Sketcher.Constraint('DistanceY',la,1,p2.y)) 
	sk.renameConstraint(cc, u'cc ' + str(i))

	# connect head and foot
	sk.addConstraint(Sketcher.Constraint('Coincident',lb,2,la,1)) 
	sk.addConstraint(Sketcher.Constraint('Coincident',la,2,0,1)) 


def createBezierSketch(name="BezierRing",source=None):

	if source <> None:
		name="Sk_"+source.Label+'_'

	obj = FreeCAD.ActiveDocument.addObject("Sketcher::SketchObjectPython",name)
	BezierSketch(obj)
	obj.polescount=7
	init_bezierring(obj,count=obj.polescount,source=source)
	obj.init=True
	return obj

def createBezierRingSketch(name="BezierRing",source=None):
	return createBezierSketch(name,source)

if __name__ == '__main__':
	createBezierRingSketch()

Edit:
phpBB [video]
abdullah
Veteran
Posts: 4935
Joined: Sun May 04, 2014 3:16 pm
Contact:

Re: Sketcher: Bezier curves

Post by abdullah »

microelly2 wrote: Sat Apr 21, 2018 3:49 pm ...
Ach so!

So if I give you a python console, you do not actually need a lever and a fulcrum ;)

It is not the way I intend for the Sketcher, as the B-spline/Bezier is not really there, but I must admit really like how you tackled the problem. You are at a level of python+FC knowledge that you could actually write a book about it :)

EDIT: I saw your edit with the B-Spline inside the sketch. When I will consider this further I will come back here to this thread.
Jee-Bee
Veteran
Posts: 2566
Joined: Tue Jun 16, 2015 10:32 am
Location: Netherlands

Re: Sketcher: Bezier curves

Post by Jee-Bee »

I have a question about the usage of the B-spline.
Would it be possible to create the points where the B-spline is crossing(intersecting) against now the that we edit the control points.
I think in method editing the crossing points is more intuitive as editing the control points.
spline_creo.PNG
spline_creo.PNG (17.54 KiB) Viewed 1918 times
I have created a model in creo. with 4 construction (yellow dots) lines equal spaced and 5 points 4 on the lines and 1 floating above the center point.
The red lines with the points are what i called the crossing/ intersection points. the blue ..-..-..- lines are the lines between the crossing points.

from user perspective i think that the intersection points are more easy to edit than the control points. When editing the dimension (32.92) all control points are moving but the points where i want the model is still there.
User avatar
DeepSOIC
Veteran
Posts: 7896
Joined: Fri Aug 29, 2014 12:45 am
Location: used to be Saint-Petersburg, Russia

Re: Sketcher: Bezier curves

Post by DeepSOIC »

Jee-Bee wrote: Tue Nov 13, 2018 1:55 pm Would it be possible to create the points where the B-spline is crossing(intersecting) against now the that we edit the control points.
yes, it's just a matter of implementing point-on-curve constraint. It requires quite a bit of effort.
abdullah
Veteran
Posts: 4935
Joined: Sun May 04, 2014 3:16 pm
Contact:

Re: Sketcher: Bezier curves

Post by abdullah »

There is basically minimum support for B-Spline in GCS. Therefore what we can do with B-splines is very basic. No point on curve. No tangency other than endpoints... seen from another point of view, it is almost a miracle that we have the support we have with the little the solver knows about B-Splines.

At some moment we will hopefully find new momentum to do that "quite a bit of effort" DeepSOIC is talking about.
chrisb
Veteran
Posts: 53786
Joined: Tue Mar 17, 2015 9:14 am

Re: Sketcher: Bezier curves

Post by chrisb »

Based on an idea of wmayer I have a section in my sketcher document on how to simulate tangents to a B-spline. Perhaps this technique can be refined by automatically splitting the spline.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
Jee-Bee
Veteran
Posts: 2566
Joined: Tue Jun 16, 2015 10:32 am
Location: Netherlands

Re: Sketcher: Bezier curves

Post by Jee-Bee »

I hope at some point the solver is powerfull enough that this kind of functionallity is possible...
User avatar
microelly2
Veteran
Posts: 4688
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Post by microelly2 »

I use Sketcher only to hold the control points for a Bezier curve and than I calculate the Bezier curve as a shape.
Working with Bezier curves is easier than with BSplines in general

https://youtu.be/zRhb5vDPEWM
https://youtu.be/GPKJVlakQ8Y
https://youtu.be/PPJ55ghd4KE
Jee-Bee
Veteran
Posts: 2566
Joined: Tue Jun 16, 2015 10:32 am
Location: Netherlands

Re: Sketcher: Bezier curves

Post by Jee-Bee »

microelly2 wrote: Wed Nov 14, 2018 8:15 am I use Sketcher only to hold the control points for a Bezier curve and than I calculate the Bezier curve as a shape.
Working with Bezier curves is easier than with BSplines in general
But that's exactly how the spline in the sketcher also works... I want the reversed. not editing the control points(without a clue where the lines are...) but editing some points on the lines without care how and what the control points are.
User avatar
microelly2
Veteran
Posts: 4688
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Post by microelly2 »

Jee-Bee wrote: Wed Nov 14, 2018 8:34 am
microelly2 wrote: Wed Nov 14, 2018 8:15 am I use Sketcher only to hold the control points for a Bezier curve and than I calculate the Bezier curve as a shape.
Working with Bezier curves is easier than with BSplines in general
But that's exactly how the spline in the sketcher also works... I want the reversed. not editing the control points(without a clue where the lines are...) but editing some points on the lines without care how and what the control points are.
In case of Bezier curve the control points are on the curve. So when you move the controlpoints the curve always follows them.
Post Reply