What's new

Cycloidal Disk Script Question

Halen5150

Member
Hello everyone, I recently purchased Alibre after enjoying the 30 day trial. However I was curious if anyone could help me with the equation script. I am trying to draw up a cycloidal disk. I am used to the solid works equation driven curve ability. Is there anything like this in Alibre? If not, can I modify some existing python code to allow Alibre to plot the curves? Here are some example equations. I have also attached a picture of the profile of the disk. Thank you!

X = (50.0*np.cos(t))-(10.0*np.cos(t+np.arctan(np.sin(-9*t)/((5/3)-np.cos(-9*t)))))-(3.0*np.cos(10*t))
Y = (-50.0*np.sin(t))+(10.0*np.sin(t+np.arctan(np.sin(-9*t)/((5/3)-np.cos(-9*t)))))+(3.0*np.sin(10*t))

Cycloidal Disk.jpeg
 

bolsover

Alibre Super User
The bad news - Alibre does not have a feature like the solid works equation driven curve.
The good news - This looks like an ideal candidate for a Python script.
I do most of my work as Add-ons written in C# but have previously written a script to generate cycloidal clock gears - you may find helpful when writing your own script.

See here - CycloidalGears
 

Halen5150

Member
Thank you, my python skills are very lacking, but this seems like a great time to get some practice. Luckily there are a lot of python programs that do this exact thing. Maybe I can modify the existing code.
 

Halen5150

Member
So I have gotten the code this far, but I cant seem to figure out how to make it plot the points. I have tried to copy the equation sketcher but failed. I have also tried AI but either I wasn't asking the right questions, or it couldn't figure it out. It runs great in python with the matplotlib and setting the values for R, Rr, E, and N. I only have a month or so of python experience this is my first actual project :D. I really appreciate the help and knowledge. I have attached a picture of it working in python and I'll add all the code as well.



Win = Windows()

ScriptName = 'Cycloidal Disk Gen'

# Create input window
Options = []
Options.append([None, WindowsInputTypes.Image, 'Cycloid_Gen170.png', 170])
Options.append(['Rotor Radius (mm)', WindowsInputTypes.Integer, 25])
Options.append(['Roller Radius (mm)', WindowsInputTypes.Real, 5])
Options.append(['Eccentricity (mm)', WindowsInputTypes.Real, 3])
Options.append(['Number of Rollers', WindowsInputTypes.Integer, 10])
Options.append(['Plane', WindowsInputTypes.Plane, None])

Values = Win.OptionsDialog(ScriptName, Options, 170)
if Values == None:
sys.exit()

print 'Generating wavy excuse of a gear'

# Get user inputs
R = Values[1] # Rotor Radius
Rr = Values[2] # Roller Radius
E = Values[3] # Eccentricity
N = Values[4] # Number of Rollers
Pl = Values[5] # Plane Selected

# Get current part and XY plane
Prt = Pl.GetPart()

#Create The Sketch
Sk = Prt.AddSketch("Cycloid Disk", Pl)

print "Loading for a bit..."
#Import
import math
import sys

print"Sweet now I can calculate, sorry for the wait"

# Generate t values from 0 to 2π with 1000 points

Points = []
t_values = [i * 2 * math.pi / 999 for i in range(1000)]

# Calculate psi, x, and y for each t value
psi = [math.atan2(math.sin((1-N)*t), (R/(E*N) - math.cos((1-N)*t))) for t in t_values]
x = [(R*math.cos(t)) - (Rr*math.cos(t+psi)) - (E*math.cos(N*t)) for i, t in enumerate(t_values)]
y = [(-R*math.sin(t)) + (Rr*math.sin(t+psi)) + (E*math.sin(N*t)) for i, t in enumerate(t_values)]


# Add point to list
Points = [x,y]






print 'Cycloidal disk sketch created successfully!')
 

Attachments

  • Code Working.png
    Code Working.png
    91.3 KB · Views: 11

bolsover

Alibre Super User
I have this generating a sketch - but there is something wrong with the math?

Python:
Win = Windows()

ScriptName = 'Cycloidal Disk Gen'

# Create input window
Options = []
#Options.append([None, WindowsInputTypes.Image, 'Cycloid_Gen170.png', 170])
Options.append(['not used (mm)', WindowsInputTypes.Integer, 25])
Options.append(['Rotor Radius (mm)', WindowsInputTypes.Integer, 25])
Options.append(['Roller Radius (mm)', WindowsInputTypes.Real, 5])
Options.append(['Eccentricity (mm)', WindowsInputTypes.Real, 3])
Options.append(['Number of Rollers', WindowsInputTypes.Integer, 10])
Options.append(['Plane', WindowsInputTypes.Plane, None])

Values = Win.OptionsDialog(ScriptName, Options, 170)
if Values == None:
  sys.exit()

print 'Generating wavy excuse of a gear'

# Get user inputs
R = Values[1] # Rotor Radius
Rr = Values[2] # Roller Radius
E = Values[3] # Eccentricity
N = Values[4] # Number of Rollers
Pl = Values[5] # Plane Selected

# Get current part and XY plane
Prt = Pl.GetPart()

#Create The Sketch
Sk = Prt.AddSketch("Cycloid Disk", Pl)

print "Loading for a bit..."
#Import
import math
import sys

print"Sweet now I can calculate, sorry for the wait"

# Generate t values from 0 to 2π with 1000 points

Poly = Polyline()
 
t_values = [i * 2 * math.pi / 990 for i in range(1000)]

#print t_values

for t in t_values:
 
  psi = math.atan2(math.sin((1-N)*t),((R/E*N)-math.cos((1-N)*t)))

  x = (R*math.cos(t))-(Rr*math.cos(t+psi))-(E*math.cos(N*t))

  y =  (-R*math.sin(t))+(Rr*math.sin(t+psi))+(E*math.sin(N*t))

  Poly.AddPoint( PolylinePoint(x,y))
 
Sk.AddPolyline(Poly, False)
 

Cator

Senior Member
Hi @Bolsover,@Halen5150 ,

I improved the code and now it works correctly on my machine.
With python 2.7 it is also useful to import the division if you do not want to have unexpected results.
(To make the script work you need to create a screen of a cycloidal disk to be inserted in the same folder from which you start the script with the name Cycloid_Gen170.png)
Regards,
Francesco






Python:
from __future__ import division
import math
import sys
print math.pi

Win = Windows()

ScriptName = 'Cycloidal Disk Gen'

# Create input window
Options = []
Options.append([None, WindowsInputTypes.Image, 'Cycloid_Gen170.png', 170])
Options.append(['Rotor Radius (mm)', WindowsInputTypes.Real, 45])
Options.append(['Roller Radius (mm)', WindowsInputTypes.Real, 6.5])
Options.append(['Eccentricity (mm)', WindowsInputTypes.Real, 1.5])
Options.append(['Number of Rollers', WindowsInputTypes.Integer, 16])
Options.append(['Plane', WindowsInputTypes.Plane, None])

Values = Win.OptionsDialog(ScriptName, Options, 170)
if Values == None:
  sys.exit()

print 'Generating cycloidal disk profile'

# Get user inputs
R = Values[1]  # Rotor Radius
Rr = Values[2]  # Roller Radius
E = Values[3]  # Eccentricity
N = Values[4]  # Number of Rollers
Pl = Values[5]  # Plane Selected

print "Rotor Radius:", R, "Roller Radius:", Rr, "Eccentricity:", E, "Number of Rollers:", N

# Get current part and XY plane
Prt = CurrentPart()

# Create the sketch
Sk = Prt.AddSketch("Cycloid Disk", Prt.GetPlane('XY-Plane'))

# Generate t values from 0 to 2π with 10000 points
Poly = Polyline()
num_points = 10000  # Increase resolution
t_values = [i * 2 * math.pi / num_points for i in range(num_points)]
points = []

for t in t_values:
    try:
        # Calculate psi
        psi = math.atan2(math.sin((1-N)*t), ((R/(E*N)) - math.cos((1-N)*t)))
        # Calculate x and y coordinates
        x = (R * math.cos(t)) - (Rr * math.cos(t + psi)) - (E * math.cos(N * t))
        y = (-R * math.sin(t)) + (Rr * math.sin(t + psi)) + (E * math.sin(N * t))
        points.append((x, y))
    except Exception as e:
        pass  # Ignore errors silently

# Add the first point again to close the polyline
if len(points) > 0:
    points.append(points[0])

# Add points to the polyline
for point in points:
    Poly.AddPoint(PolylinePoint(point[0], point[1]))

# Add the polyline to the sketch
Sk.AddPolyline(Poly, False)
 

NateLiquidGravity

Alibre Super User
10k points is probably overkill unless you have giant gears or high precision is even possible or needed.

In my testing the Python math code is actually super fast compared to the things it asks Alibre Design to do. It likely is slowest when adding to the poly not the math.
 

Halen5150

Member
Hello @Cator, @bolsover

I really appreciate it. I will study what you all gave me and see where I went wrong. I also didn't know you could add a nice box on the forum for the code. Whoops. Haha 10k points that baby is HD... if you have any more wisdom I would love to learn.
 

NateLiquidGravity

Alibre Super User
In my testing the Python math code is actually super fast compared to the things it asks Alibre Design to do. It likely is slowest when adding to the poly not the math.
It was actually in AddPolyline.

As proof of this I give you Exhibit A with 1000 points:
3.14159265359
Generating cycloidal disk profile
Rotor Radius: 45.0 Roller Radius: 6.5 Eccentricity: 1.5 Number of Rollers: 16
Number of Points: 1000
Time To Calc Points: 0 Hours, 0 Minutes, 0 Seconds, and 26 Milliseconds.
Time To Build Poly: 0 Hours, 0 Minutes, 0 Seconds, and 3 Milliseconds.
Time To AddPolyline: 0 Hours, 0 Minutes, 47 Seconds, and 958 Milliseconds.

And Exhibit B with 2000 points:
3.14159265359
Generating cycloidal disk profile
Rotor Radius: 45.0 Roller Radius: 6.5 Eccentricity: 1.5 Number of Rollers: 16
Number of Points: 2000
Time To Calc Points: 0 Hours, 0 Minutes, 0 Seconds, and 44 Milliseconds.
Time To Build Poly: 0 Hours, 0 Minutes, 0 Seconds, and 8 Milliseconds.
Time To AddPolyline: 0 Hours, 3 Minutes, 10 Seconds, and 26 Milliseconds.

I initially tried 10k points but halted it after about 5 minutes.

Python:
from __future__ import division
import math
import sys
print math.pi
from System.Diagnostics import Stopwatch
from System import TimeSpan

Win = Windows()

ScriptName = 'Cycloidal Disk Gen'
### source https://www.alibre.com/forum/index.php?threads/cycloidal-disk-script-question.25831/

# Create input window
Options = []
Options.append([None, WindowsInputTypes.Image, 'Cycloid_Gen170.jpeg', 170])
Options.append(['Rotor Radius (mm)', WindowsInputTypes.Real, 45])
Options.append(['Roller Radius (mm)', WindowsInputTypes.Real, 6.5])
Options.append(['Eccentricity (mm)', WindowsInputTypes.Real, 1.5])
Options.append(['Number of Rollers', WindowsInputTypes.Integer, 16])
Options.append(['Plane', WindowsInputTypes.Plane, None])

Values = Win.OptionsDialog(ScriptName, Options, 170)
if Values == None:
  sys.exit()

print 'Generating cycloidal disk profile'

# Get user inputs
R = Values[1]  # Rotor Radius
Rr = Values[2]  # Roller Radius
E = Values[3]  # Eccentricity
N = Values[4]  # Number of Rollers
Pl = Values[5]  # Plane Selected

print "Rotor Radius:", R, "Roller Radius:", Rr, "Eccentricity:", E, "Number of Rollers:", N

# Get current part and XY plane
Prt = CurrentPart()

# Create the sketch
Sk = Prt.AddSketch("Cycloid Disk", Prt.GetPlane('XY-Plane'))

# Generate t values from 0 to 2π with 10000 points
Poly = Polyline()
num_points = 1000  # Increase resolution
print('Number of Points: ' + str(num_points))
t_values = [i * 2 * math.pi / num_points for i in range(num_points)]
points = []

watch = Stopwatch.StartNew()
for t in t_values:
    try:
        # Calculate psi
        psi = math.atan2(math.sin((1-N)*t), ((R/(E*N)) - math.cos((1-N)*t)))
        # Calculate x and y coordinates
        x = (R * math.cos(t)) - (Rr * math.cos(t + psi)) - (E * math.cos(N * t))
        y = (-R * math.sin(t)) + (Rr * math.sin(t + psi)) + (E * math.sin(N * t))
        points.append((x, y))
    except Exception as e:
        pass  # Ignore errors silently

# Add the first point again to close the polyline
if len(points) > 0:
    points.append(points[0])

watch.Stop()
ts_calcpoints = watch.Elapsed
#print(ts_calcpoints)
print 'Time To Calc Points: ' + str(ts_calcpoints.Hours) + ' Hours, ' + str(ts_calcpoints.Minutes) + ' Minutes, ' + str(ts_calcpoints.Seconds) + ' Seconds, and ' + str(ts_calcpoints.Milliseconds) + ' Milliseconds.'
watch.Restart()

# Add points to the polyline
for point in points:
    Poly.AddPoint(PolylinePoint(point[0], point[1]))
    
watch.Stop()
ts_buildpoly = watch.Elapsed
#print(ts_buildpoly)
print 'Time To Build Poly: ' + str(ts_buildpoly.Hours) + ' Hours, ' + str(ts_buildpoly.Minutes) + ' Minutes, ' + str(ts_buildpoly.Seconds) + ' Seconds, and ' + str(ts_buildpoly.Milliseconds) + ' Milliseconds.'
watch.Restart()

# Add the polyline to the sketch
Sk.AddPolyline(Poly, False)

watch.Stop()
ts_addpolyline = watch.Elapsed
#print(ts_addpolyline)
print 'Time To AddPolyline: ' + str(ts_addpolyline.Hours) + ' Hours, ' + str(ts_addpolyline.Minutes) + ' Minutes, ' + str(ts_addpolyline.Seconds) + ' Seconds, and ' + str(ts_addpolyline.Milliseconds) + ' Milliseconds.'
 

stepalibre

Alibre Super User
@Cator
Hi Francesco - 10k points? Yikes!
This actually reminds me why I dislike Python - it's so slow..
David
It was actually in AddPolyline.
Yes It’s Alibre Script that is slow. It’s IronPython 2.7 under the hood not Python so a little different than regular Python. The latest Python is likely much faster than IronPython 2.7. PythonNet is faster than IronPython 3. High quantities and precise geometry will get progressively slower In Alibre Script. AlibreX should be faster.

The program can be improved by calculating the points in a separate function/class and using .net types.

The other issue is that the API creates real objects in Alibre‘s GUI/file when called, there is no way to batch or have a transaction system to delay them from being realized. So when you create something it is created at that point in time in the loop. You could create a point and polyline class that generates the data then simply pass the data to the add point and add polyline functions directly. In the end we are still working within COM and IronPython so speed will always be an issue.
 

stepalibre

Alibre Super User
I generated versions, slightly faster but could be machine dependent too. But still Alibre does not handle high point counts well regardless of how they are made so you'll still experience slowness. The bottleneck is Alibre Design not with Python or IronPython, the creation of objects have fixed costs.

>>>
3.14159265359
Generating cycloidal disk profile
Rotor Radius: 45.0 Roller Radius: 6.5 Eccentricity: 1.5 Number of Rollers: 16
Number of Points: 1000
Time To Calc Points: 0 Hours, 0 Minutes, 0 Seconds, and 19 Milliseconds.
Time To Build Poly: 0 Hours, 0 Minutes, 0 Seconds, and 3 Milliseconds.
Time To AddPolyline: 0 Hours, 0 Minutes, 36 Seconds, and 223 Milliseconds.
>>>
3.14159265359
Generating cycloidal disk profile
Rotor Radius: 45.0 Roller Radius: 6.5 Eccentricity: 1.5 Number of Rollers: 16
Number of Points: 2000
Time To Calc Points: 0 Hours, 0 Minutes, 0 Seconds, and 31 Milliseconds.
Time To Build Poly: 0 Hours, 0 Minutes, 0 Seconds, and 3 Milliseconds.
Time To AddPolyline: 0 Hours, 1 Minutes, 53 Seconds, and 525 Milliseconds.
>>>
3.14159265359
Generating cycloidal disk profile
Rotor Radius: 45.0 Roller Radius: 6.5 Eccentricity: 1.5 Number of Rollers: 16
Number of Points: 1000
Time To Calc Points: 0 Hours, 0 Minutes, 0 Seconds, and 39 Milliseconds.
Time To Build Poly: 0 Hours, 0 Minutes, 0 Seconds, and 1 Milliseconds.
Time To AddPolyline: 0 Hours, 0 Minutes, 29 Seconds, and 393 Milliseconds.
>>>
3.14159265359
Generating cycloidal disk profile
Rotor Radius: 45.0 Roller Radius: 6.5 Eccentricity: 1.5 Number of Rollers: 16
Number of Points: 2000
Time To Calc Points: 0 Hours, 0 Minutes, 0 Seconds, and 23 Milliseconds.
Time To Build Poly: 0 Hours, 0 Minutes, 0 Seconds, and 1 Milliseconds.
Time To AddPolyline: 0 Hours, 1 Minutes, 51 Seconds, and 255 Milliseconds.
>>>

 

NateLiquidGravity

Alibre Super User
Part.PauseUpdating()/Part.ResumeUpdating() had negligible effect on my times.

And building the sketch in a new part without GUI using Part(string Name, bool CreateNew, bool HideEditor) and then copying that sketch back to the original part nearly doubled the time - as building in the hidden part didn't save time and copying the sketch took again about the same speed as if it was building line by line again.

3.14159265359
Generating cycloidal disk profile
Rotor Radius: 45.0 Roller Radius: 6.5 Eccentricity: 1.5 Number of Rollers: 16
Number of Points: 1000
Time To Calc Points: 0 Hours, 0 Minutes, 0 Seconds, and 35 Milliseconds.
Time To Build Poly: 0 Hours, 0 Minutes, 0 Seconds, and 4 Milliseconds.
Time To AddPolyline To Hidden Part: 0 Hours, 0 Minutes, 49 Seconds, and 354 Milliseconds.
Time To CopySketch: 0 Hours, 0 Minutes, 49 Seconds, and 226 Milliseconds.

And building my own AlibreX function using the IADSketchFigures.AddLine Method had negligible effect on my times.

So it is 100% built into Alibre Design not any Python or AlibreScript. I imagine that each line added to the sketch causes the 2d constraint engine to be involved, so each time it has more items to check against.

EDIT:
It took me way to long to check that the script isn't actually using the plane the user selects - it is mentioned in the AI script that @stepalibre posted.
 
Last edited:

stepalibre

Alibre Super User
Yes. I've ran countless tests with my addon and Alibre script/Alibre X in IronPython. The issue is Alibre Design's lack of any optimizations in object creation. The only way to improve performance/speed is .NET and headless mode programs. Too much overhead in the Alibre Design GUI + poor object creation + Alibre Script = slow. It's just not meant for true geometry programming that scales. The nature of the API and COM still affects headless mode, but at least you avoid the overhead caused by the GUI.

I'm speaking more generally, obviously this all depends on the program.


And building my own AlibreX function using the IADSketchFigures.AddLine Method had negligible effect on my times.
The APIs are the same. Where you might see improvement is possibly from .Net types instead of IronPython/Python types.
 
Last edited:

NateLiquidGravity

Alibre Super User
The APIs are the same. Where you might see improvement is possibly from .Net types instead of IronPython/Python types.
I only did that to compare to AlibreScripts AddPolyline time. If AlibreScripts time was much worse then that could be optimized. If AlibreScripts time was much better then I wanted to find out how - so I could use that in my own code.
 

Halen5150

Member
I modified this version so you can add the points in the GUI instead of hardcoding them in the script. I figured users might prefer quick outlines of the cycloid disk without waiting a full minute for the whole thing. @stepalibre, sorry, I couldn’t wrap my head around your code—haha, it’s too advanced for me, crazy awesome though!! Still, I learned a ton from this project. @NateLiquidGravity, these cycloidal disks are typically used in precision, near-zero-backlash gearboxes, so I guess they need to be pretty accurate? I'm not for sure if the bad quality would transfer to CAM though That said, you could probably get away with generating lower-quality STLs for 3D printing. I do have a dumb question, though: why couldn’t we use Sk.AddBspline(Points, False)? I tried this when I first started, getting inspiration from the Alibre script Equation Sketcher. Even the AI kept trying to use it but couldn’t figure it out.




Cycloid_Gen170.png

Python:
#Cycloid Actuator Gear Gen
#Created by Alibre Community
#Created for Alibre Community Thank you for all the knowledge you all selflessly give

from __future__ import division
import math
import sys
print math.pi


Win = Windows()

ScriptName = 'Cycloidal Disk Gen'

# Create input window
Options = []
Options.append([None, WindowsInputTypes.Image, 'Cycloid_Gen170.png', 170])
Options.append(['Rotor Radius (mm)', WindowsInputTypes.Real, 50])
Options.append(['Roller Radius (mm)', WindowsInputTypes.Real, 5])
Options.append(['Eccentricity (mm)', WindowsInputTypes.Real, 1.5])
Options.append(['Number of Rollers', WindowsInputTypes.Integer, 20])
Options.append(['Plane', WindowsInputTypes.Plane, None])
Options.append(['Num of Points/Resolution', WindowsInputTypes.Integer, 2000])
#For Num of Points the higher the detail the londer it takes, you 2000 should be plenty but will take a min

Values = Win.OptionsDialog(ScriptName, Options, 170)
if Values == None:
    sys.exit()

print 'Generating Wavy Excuse of a gear...'

# Get user inputs
R = Values[1]          # Rotor Radius
Rr = Values[2]         # Roller Radius
E = Values[3]          # Eccentricity
N = Values[4]          # Number of Rollers
Pl = Values[5]         # Plane Selected
Points = Values[6] # Resolution of Disk

print "Rotor Radius:", R, "Roller Radius:", Rr, "Eccentricity:", E, "Number of Rollers:", N, "Resolution:", Points

# Get current part and XY plane
Prt = CurrentPart()

# Create the sketch
Sk = Prt.AddSketch("Cycloid Disk", Pl)

print "One Sec this is a lot of points"

# Generate t values from 0 to 2π with selected num of points
Poly = Polyline()
t_values = [i * 2 * math.pi / Points for i in range(Points)]
points = []

for t in t_values:
    try:
        # Calculate psi
        psi = math.atan2(math.sin((1-N)*t), ((R/(E*N)) - math.cos((1-N)*t)))
        # Calculate x and y coordinates
        x = (R * math.cos(t)) - (Rr * math.cos(t + psi)) - (E * math.cos(N * t))
        y = (-R * math.sin(t)) + (Rr * math.sin(t + psi)) + (E * math.sin(N * t))
        points.append((x, y))
    except Exception as e:
        pass  # Ignore errors silently

# Add the first point again to close the polyline
if len(points) > 0:
    points.append(points[0])

# Add points to the polyline
for point in points:
    Poly.AddPoint(PolylinePoint(point[0], point[1]))

# Add the polyline to the sketch
Sk.AddPolyline(Poly, False)



print 'Cycloidal disk sketch created successfully!'
 

Attachments

  • Cycloid_Gen170.png
    Cycloid_Gen170.png
    3.6 KB · Views: 3
Last edited:

Cator

Senior Member
Lately I've been working on a script that would allow me to create cabinets with AlibreScript, starting from an old script of mine published here on the forum. Once enriched, the script creates a complete project in both models and data, from stock to weight and even price based on weight and material. So I'm not scared of waiting even five minutes for something like this, in fact I'm satisfied. Honestly, I find it frustrating to have to close and reopen an assembly when I change a value in the GPL file and randomly a complete regeneration doesn't happen, when I have to import a dxf (minutes with the screen locked hoping it doesn't crash). Those are the downtimes that I don't understand in 2025 compared to an MCAD with many years of experience, not if it takes 50 seconds or five minutes for a script that creates a complex part but still a single part within a project.
 
Top