[Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Have some feature requests, feedback, cool stuff to share, or want to know where FreeCAD is going? This is the place.
Forum rules
Be nice to others! Read the FreeCAD code of conduct!
LibrEars
Posts: 9
Joined: Wed Apr 14, 2021 3:43 pm

[Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby LibrEars » Wed Apr 14, 2021 4:12 pm

[Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Hi all,

I am looking for an easy way to create parametric periodic sketcher objects (like a sine function, sawtooth shape, square etc). I tried the rectangular array function but it gets complicated quickly and the points do not get coincidentally constrained in my case.

So I got the idea of a new parametric tool icon in the sketcher workbench, analogue to the new helix feature in part design. I would constrain it graphically as in the picture attached:
parametric_sine_v2.png
parametric_sine_v2.png (26.78 KiB) Viewed 764 times

Also there could be the possibility to change the parameters in a dialog similar to the helix-tool. Moreover it would be awesome to have second order functionality (draw the function on top of a function, arc, spline, ...).

For second order it would make sense to introduce the possibility to change between floating periods and quantized values. Like this, a closed wire (e.g. closed B-Spline with known length) could be superimposed with a closed wave-function:
Image



A real world application would be e.g. printed conductive lines on stretchable materials: https://www.mdpi.com/1996-1944/11/3/375

I had a look into python scripting and found some hints here:
I have not managed to script a python skript myself yet. Is there something planned for FreeCAD (I think the code from ArcOfParabola would be a good starting point but I would need to learn C++ first :) ). Also can someone help me with a python workaround?


PS: attached there is the inkscape/svg file of the drawing
Attachments
parametric_sine_v3.svg
(21.42 KiB) Downloaded 11 times
parametric_sine_v2.svg
(20.28 KiB) Downloaded 10 times
LibrEars
Posts: 9
Joined: Wed Apr 14, 2021 3:43 pm

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby LibrEars » Thu Apr 15, 2021 2:32 pm

Hi again,

here is a 'workaround' for a sine function with a python macro. You need to copy the following code into a new macro on your machine:

Code: Select all

# Script to draw a periodic sine function in FreeCAD
import FreeCAD
import Draft
import math

# 2 * pi * rad = 360°   ->  math.sin(rad)

def sinus(x, a,p,ph):
	'''
	Returns the sinus with:    
	a = amplitude in mm
	p = period in mm
	ph = phase in mm
	'''

	return  a*math.sin(1/p * 2*math.pi* x + ph* 2*math.pi)



# Draw the periodic sine function approximated as a B-spline in x-direction (can be turned and converted to sketch later)

a =1  # in mm
p =2 # in mm
ph = 0  # in mm

x0 = 0  # start position in mm
l = 10 # in mm

#pts = 1001  # how many points do we want to draw the B-spline with?  How many points do we need and were should they be located to have a 'smooth' sine function?
pts = int (l * p) +1 # calculate number of points for maxima and turning points  -> without a phase-shift it is a good aproximation with as few as possible points -> only a small deviation at the start and the end

v = []  # list to store the vectors

# Introduce quantization option
N = 0 # how many oszillations for the line ?- skip period definition if N=0

if N == 0:
	for vec in range(pts):
		x = x0 + vec * l /(pts-1)
		v.append(FreeCAD.Base.Vector(x, sinus(x, a,p,ph), 0))


else:
	for vec in range(pts):
		x = x0 + vec * l /(pts-1)
		p = 2*l / N  # first order oszillation is only [0,pi] - 'half a sinus'
		v.append(FreeCAD.Base.Vector(x, sinus(x, a,p,ph), 0))

Draft.makeBSpline(v)
App.activeDocument().recompute(None,True,True)
Open a new document in FreeCAD and run the macro (you can edit the values towards your needs). This will draw a B-spline draw object. You can rotate it and move it according to your needs as well as convert it into a sketch from the Draw workbench. Here are some screenshots:

B-spline_sine_with_python.png
B-spline_sine_with_python.png (36.45 KiB) Viewed 725 times
B-spline_sine_with_python_oszillation_N_states.png
B-spline_sine_with_python_oszillation_N_states.png (63.68 KiB) Viewed 725 times
(Attached you can find the FCStd file)
Attachments
FreeCAD_custom_sine_macro.FCStd
(255.78 KiB) Downloaded 10 times
chrisb
Posts: 35373
Joined: Tue Mar 17, 2015 9:14 am

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby chrisb » Thu Apr 15, 2021 3:40 pm

The addon macro "3D Parametric Curve" can do this with a constant z value.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
LibrEars
Posts: 9
Joined: Wed Apr 14, 2021 3:43 pm

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby LibrEars » Fri Apr 16, 2021 9:38 am

What a great Macro! I am new to the whole macro-world, thank you for posting it =)

I would still see it as a 'workaround' regarding the new feature I would love to see in FreeCad. B-splines and polylines are very hard to constrain once they grow large. With a new object type for parametric curves the full potential of the sketcher workbench could be released. Imagine you could constrain a long periodic function by 6 degrees of freedom or so (amplitude, period, phase, length [could also be path-length], x, y + maybe tilt & rotation?)? Also we would not need to live with approximations of the functions any more.
abdullah
Posts: 4080
Joined: Sun May 04, 2014 3:16 pm

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby abdullah » Fri Apr 23, 2021 7:13 pm

LibrEars wrote: Wed Apr 14, 2021 4:12 pm [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Hi all,

I am looking for an easy way to create parametric periodic sketcher objects (like a sine function, sawtooth shape, square etc).
It looks to me you are rather thinking of a parametric transformation, as you are not defining by it the object subject to repetition, but rather the period. If it is so, the sketcher does not have support for parametric transformations. Tools such as the sketcher arrays are just automation functions. They cannot be edited (upps I wanted to say 5 columns and 4 rows...so undo-redo or delete-redo).

If you were talking about shapes on its own right, currently the geometries supported by the sketch, most of them allowing to be constrained on all its parameters (notable exception the B-Spline), require a full solver implementation. There may be room for a new type of functionality - parametric object -, but I have serious problems to understand how this object would be made to interact with the rest of the sketcher. For example, such objects should have very well defined start and end points, so that other geometries can connect to them, but then this would require at least basic functionality at solver level for this endpoints (some "equation" that fixes them in place).

When you refer to geometries of known length (B-Splines), a constraint of length type has been asked several times, and even implemented for some geometries in DeepSOICs alternative constraint solver (experimental project, not in FreeCAD master) as a constraint. However, this has not been done for the current solver (planeGCS). It is not straightforward.

If you ask me, the best way is probably some kind of Python feature that generates the wire you are looking for parametrically...
LibrEars
Posts: 9
Joined: Wed Apr 14, 2021 3:43 pm

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby LibrEars » Tue Apr 27, 2021 10:51 am

Thank you for the reply abdullah! This helps to clarify my toughs. I am thinking of shapes on its own right and constrained on all its parameters indeed :) The beauty of mathematical functions (whereas periodic functions are a subgroup) is in my opinion that each point is in a relation to each other and you can find constrains like 'endpoints' or 'turning-points' in a mathematical manner (equation).

I will try to define the points for the solver (which can interact with the rest of the sketcher) from my first sketch and the sine function (it lives in its own coordinate system which can be rotated or eventually mapped on a second wire):


y = f(x) = a * sin(b * x + c)
e.g.

a = 1
b = 1
c = 0 (not like in the image above - for simplification)

Points of the function:

Starting point P_start:
x_start (e.g = -0.7) -> f(x_start) = a * sin(b * x_start + c) -> P_start = P(x_start, y) = P(-0.7, a * sin(b *(-0.7) + c)) = P(-0.7, 0.95)

Endpoint point similar with x_end (e.g. = 4.6) -> P_end = (4.6, 0.59)

Point to change the phase P_c = P(c *2*pi, 0) = P(0, 0) (if you shift this point (in x-direction, it is constrained on the x-axis), thy y-value of P_left, P_right would change but not the x-value)

Point to change the period P_p = (b*pi + c, 0) = P(pi, 0) (if you shift this point (in x-direction, it is constrained on the x-axis), thy y-value of P_left, P_right would change but not the x-value)

Points of the cross:

Zero
P0 = P(0, 0)

Left point P_left = P(x_start, 0) = P(-0.7,0)
Right point P_right = P(x_end, 0) = P(4.6, 0)

Top point P_top = P(0, a) = P(0, 1)
Bottom point P_bottom = P(0, -a) = P(0, -1) (P_top and P_bottom are symmetrically constrained towards P0 -> if you move them (in y-direction), the parameter a would automatically change)


What do you think? Like this also other mathematical functions could be possible as well (in fact, i thought the Hyperbola support works like this).
LibrEars
Posts: 9
Joined: Wed Apr 14, 2021 3:43 pm

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby LibrEars » Tue May 04, 2021 1:20 pm

How do I know that the community agrees that this is a valid Feature :?: - i feel it is worth a ticket but I am unsure if the concept of the feature was clarified enough.


If you ask me, the best way is probably some kind of Python feature that generates the wire you are looking for parametrically...
Maybe it would be a good starting point. What is the difference of the code I wrote or the proposed Macro to a python feature? Would a python feature be accessible via GUI in the sketcher and could it interactively interact with other objects & constrains?
chrisb
Posts: 35373
Joined: Tue Mar 17, 2015 9:14 am

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby chrisb » Tue May 04, 2021 5:02 pm

LibrEars wrote: Tue Apr 27, 2021 10:51 am What do you think?
This looks to me as if these periodics should be rather self contained. In that case, why have them in sketcher? They could well live outside and be created by a macro. To have them inside Sketcher would be needed if arbitrary points should be placed on them including tangents and normals.
A macro exists already: 3D Parametric Curve.
A Sketcher Lecture with in-depth information is available in English, auf Deutsch, en français, en español.
User avatar
GlouGlou
Posts: 3105
Joined: Sun Apr 05, 2015 9:02 am
Location: La Rochelle, France

Re: [Feature Request]: sketcher WB - new parametric tool - periodic wires - waveform-generator - sine function

Postby GlouGlou » Tue May 04, 2021 7:09 pm

I'm agree with that. No need to make the sketcher too complicated. You can draw a 2D or 3D function outside and then re use it inside a body or sketch. I've wrote a macro from the original 3D parametric Curve.
Hope u can enjoy with the right function here:
https://mathcurve.com/courbes2d/courbes2d.shtml

Code: Select all

from __future__ import division
import FreeCAD, Part, Draft
from FreeCAD import Base
from math import *

# Crée un object courbe 3D paramétrique à 5 paramètres:
# X(a,b,c,d,e)
# Y(a,b,c,d,e)
# Z(a,b,c,d,e)
# La courbe est décrite par un nombre de points
# par défaut une spline est crée, un polyline est possible en option
# CreateFace semble buggé... mais est ce vraiment utile? :-D


class MathCurve(object):
   def __init__(self, obj):
       
      obj.addProperty("App::PropertyString","a","Parameters","Set a parameter a(t)").a = "1"
      obj.addProperty("App::PropertyString","b","Parameters","Set b parameter b(a,t)").b = "0"
      obj.addProperty("App::PropertyString","c","Parameters","Set c parameter c(a,b,t)").c = "0"
      obj.addProperty("App::PropertyString","d","Parameters","Set d parameter d(a,b,c,t)").d = "0"
      obj.addProperty("App::PropertyString","e","Parameters","Set e parameter e(a,b,c,d,t)").e = "0"
      obj.addProperty("App::PropertyString","X","Equation","X expression X(a,b,c,d,e,t)").X = "t"
      obj.addProperty("App::PropertyString","Y","Equation","Y expression Y(a,b,c,d,e,t)").Y = "a*sin(t)"
      obj.addProperty("App::PropertyString","Z","Equation","Z expression Z(a,b,c,d,e,t)").Z = "0"
      obj.addProperty("App::PropertyFloat","CurveStart","Curve","Start t").CurveStart = 0
      obj.addProperty("App::PropertyFloat","CurveEnd","Curve","End t").CurveEnd = 6.283185
      obj.addProperty("App::PropertyInteger","NbPts","Curve","Number of points").NbPts = 20
      obj.addProperty("App::PropertyBool","CreateFace","Curve","Wheter to create a face or not").CreateFace = False
      obj.addProperty("App::PropertyBool","Close","Curve","close the curve or not").Close = False
      obj.addProperty("App::PropertyBool","Wire","Curve","Make a wire instead a Spline").Wire = False
      obj.Proxy = self
       
   def onChanged(self, obj, prop):
      if prop == "a" or prop == "b" or prop == "c" or prop == "d" or prop == "e" or prop == "X" or prop == "Y"or prop == "Z" or prop == "start" or prop == "end" or prop == "interval" or prop == "createFace" or prop == "close":
        self.execute(obj)

   def execute(self, obj):
      
      dt = (obj.CurveEnd-obj.CurveStart)/(obj.NbPts-1)
      pts = []
      t = obj.CurveStart
      a = b = c = d = x = y = z = 0
        
      for i in range(obj.NbPts):
        try:
            value = "a"
            a = eval(obj.a)
            value = "b"
            b = eval(obj.b)
            value = "c"
            c = eval(obj.c)
            value = "d"
            d = eval(obj.d)
            value = "e"
            e = eval(obj.e)
            value = "x"
            x = eval(obj.X)
            value = "y"
            y = eval(obj.Y)
            value = "z"
            z = eval(obj.Z)
            
        except ZeroDivisionError:
            print("Warning: Error division by zero in calculus of "+ value +" for t=" +str(t)+" !")
        except:
            print("Error in the formula of " + value +" !")
                      
        pts.append(FreeCAD.Vector(x,y,z))
        t += dt
      
      if obj.Close:                # pour fermer la courbe on ajoute un point au début
            t = obj.CurveStart
            a = eval(obj.a)
            b = eval(obj.b)
            c = eval(obj.c)
            d = eval(obj.d)
            e = eval(obj.e)
            x = eval(obj.X)
            y = eval(obj.Y)
            z = eval(obj.Z)
            pts.append(FreeCAD.Vector(x,y,z))
            
      if obj.Wire == False:
        curve = Part.BSplineCurve(pts)
        if obj.Close:
            curve.setPeriodic()
        if obj.CreateFace:
            sh = Part.Face(Part.Wire(curve.toShape()))
        else:
            sh = curve.toShape()
      else:
        
        wire = Part.makePolygon(pts)
        if obj.CreateFace == False:
            sh = Part.Wire(wire)
        else:
            sh = Part.Face(wire)
  
      sh.Placement = obj.Placement
      obj.Shape = sh

class ViewProviderMath3DCurve(object):
    def __init__(self, obj, icon):
        obj.Proxy = self
        self.icone = icon
        self.ViewObject = obj
                
    def getIcon(self):
        return self.icone

    def attach(self, vobj):
        self.ViewObject = vobj
        self.Object = vobj.Object
          
    
    def __getstate__(self):
        return None

    def __setstate__(self,state):
        return None
     
def MakeMathCurve():
    doc = FreeCAD.activeDocument()
    if doc == None:
        doc = FreeCAD.newDocument()
    obj=doc.addObject("Part::FeaturePython","Math3DCurve")    
    obj.addExtension("Part::AttachExtensionPython","obj")
    
    ViewProviderMath3DCurve (obj.ViewObject, setIconInMacro(""))
    MathCurve(obj) 
     
def setIconInMacro(self):
    return """
    /* XPM */
    static char * xpm[] = {
    /* width height num_colors chars_per_pixel */
    "22 22 8 1",
    /* colors */
    "` c #000000",
    ". c #242424",
    "# c #484848",
    "a c #6d6d6d",
    "b c #919191",
    "c c #b6b6b6",
    "d c #dadada",
    "e c #ffffff",
    /* pixels */
    "aeeeb.beeeeeeeeeeeeeee",
    "#eea```#eeeeeeeeeeeeee",
    "#ed``a``beeeeeeeeeeeee",
    "#ea`beb`.eeeeeeeeeeeee",
    "#d`.eee#`beeeeeeeeeeee",
    "#b`aeeec`.eeeeeeeeeeee",
    "##`ceeee.`ceeeeeeeeeee",
    "...deeeea`beeeeeeeeeee",
    ".`#eeeeec`#eeeeeeeeeee",
    "``bfeeeee.`deeeeeeeeee",
    "``#aaaaabb`bbaaaaaaaaa",
    ".aaaaaaabc`#caaaaaa#`#",
    "#eeeeeeeee..eeeeeeea`c",
    "#eeeeeeeee#`beeeeee.`d",
    "#eeeeeeeeea`#eeeeed`.e",
    "#eeeeeeeeed``deeeeb`ae",
    "#eeeeeeeeeea`beeee.`ce",
    "#eeeeeeeeeec`.eeec`.ee",
    "#eeeeeeeeeee#`bee#`bee",
    "#eeeeeeeeeeec``ba`.eee",
    "#eeeeeeeeeeeeb````ceee",
    "aeeeeeeeeeeeeeb..ceeee"};
    """

if __name__ == '__main__':
    MakeMathCurve()
Attachments
sin.FCStd
(12.26 KiB) Downloaded 7 times