Sketcher: Bezier curves

Here's the place for discussion related to coding in FreeCAD, C++ or Python. Design, interfaces and structures.
chrisb
Posts: 16831
Joined: Tue Mar 17, 2015 9:14 am

Re: Sketcher: Bezier curves

Postby chrisb » Thu Apr 27, 2017 8:44 pm

Kunda1 wrote:Soft segway: Just a little historical context: The history of Bézier curves
Well motivated mathematical insights combined with interactive graphics. Very interesting, indeed.
User avatar
microelly2
Posts: 4319
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Postby microelly2 » Fri Apr 06, 2018 7:57 pm

Is there a way to execute the Sketcher BSpline Tools "Show/Hide XYZ" commands by a python method?
abdullah
Posts: 3174
Joined: Sun May 04, 2014 3:16 pm

Re: Sketcher: Bezier curves

Postby abdullah » Sun Apr 08, 2018 6:03 am

microelly2 wrote:
Fri Apr 06, 2018 7:57 pm
Is there a way to execute the Sketcher BSpline Tools "Show/Hide XYZ" commands by a python method?
Untested, no access to FreeCAD ATM (I just copy the code from Github c++ and give you a link on how to use that info):
ParameterGrp::handle hGrpsk = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Mod/Sketcher/General");

hGrpsk->GetBool("BSplineDegreeVisible", true)
hGrpsk->GetBool("BSplineControlPolygonVisible", true)
hGrpsk->GetBool("BSplineCombVisible", true)
hGrpsk->GetBool("BSplineKnotMultiplicityVisible", true)

This you have to put into Python syntax:
https://forum.freecadweb.org/viewtopic. ... 17#p225077

Hope it helps!
User avatar
microelly2
Posts: 4319
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Postby microelly2 » Sun Apr 08, 2018 9:36 am

Thanks you. That opens the door.
User avatar
microelly2
Posts: 4319
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Postby microelly2 » Fri Apr 20, 2018 1:58 pm

because Bezier curves are easier to place than BSplines here a demo how a Bezier can work.
phpBB [video]

Endpoints of the Bezier Curves can be places exact using common Sketcher constraints
tangent direction and force is done by tangent helper points.
abdullah
Posts: 3174
Joined: Sun May 04, 2014 3:16 pm

Re: Sketcher: Bezier curves

Postby abdullah » Sat Apr 21, 2018 9:59 am

microelly2 wrote:
Fri Apr 20, 2018 1:58 pm
because Bezier curves are easier to place than BSplines here a demo how a Bezier can work.
Endpoints of the Bezier Curves can be places exact using common Sketcher constraints
tangent direction and force is done by tangent helper points.
You are just amazing! Am I wrong that you posted another video to G+ which had the typical levers of Bezièrs like the ones of inkscape?

When coding the current B-Spline functionality, I did thought of creating more tools to handle these curves. Eventually I will give it another go to B-Splines.

Regarding your video, could you please explain with a little bit more of detail what I am seeing and how it is implemented?

I mean:
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"?

My take is that there are two ways of implementing Béziers in the sketcher, as "tools" operating on B-Splines, or as native types. I have procrastinated to implement them as native types because it involves quite a lot of work (nothing that can not be done). I have not-well-thought-ideas of using "tools" to operate on B-Splines, which could be external tools, new constraints (effecting a bezier-ization of a bspline) or the like. But I do not want just to offer something that is not flexible enough (if the only right way is native support, then I will have to eventually stop procrastinating). That is why I am asking :)
User avatar
microelly2
Posts: 4319
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Postby microelly2 » Sat Apr 21, 2018 11:05 am

I have implemented it all as Sketcher Python objects. It would be nice to have it all sometime as core functionality in sketcher.
I still need the weekend to do more tests and optimize the methods. Then I will describe what I have done.
here ist the use case
phpBB [video]


I use a constrainted closed polygon inside the sketcher. The result shape (yellow curve) is calculated on top of the sketch. It is a bspline, but not inside the sketcher. I wanted to hold the sketch as simple as possible, so inside the sketch only the needed constraints are stored.
I still need some methods to hide/unhide special groups of constraints on demand.
chrisb
Posts: 16831
Joined: Tue Mar 17, 2015 9:14 am

Re: Sketcher: Bezier curves

Postby chrisb » Sat Apr 21, 2018 11:40 am

abdullah wrote:
Sat Apr 21, 2018 9:59 am
Am I wrong that you posted another video to G+ which had the typical levers of Bezièrs like the ones of inkscape?

When coding the current B-Spline functionality, I did thought of creating more tools to handle these curves. Eventually I will give it another go to B-Splines.
That would be really great to have the inkscape like handles. Currently I prefer to edit Beziers in inkscape and import them to FreeCAD :roll: .
User avatar
microelly2
Posts: 4319
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Postby microelly2 » Sat Apr 21, 2018 1:02 pm

chrisb wrote:
Sat Apr 21, 2018 11:40 am
That would be really great to have the inkscape like handles. Currently I prefer to edit Beziers in inkscape and import them to FreeCAD :roll:
That will be possible
for the poles there can be 3 states of constraints
parallel and symmetric constraints for tangent indicator --> symmetric point
no constraint --> hard corner
parallel constraint tangent indicator --> tangent
bp_766.png
bp_766.png (7.85 KiB) Viewed 262 times
User avatar
microelly2
Posts: 4319
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

Re: Sketcher: Bezier curves

Postby microelly2 » 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()