import math
import sys
from math import pi as M_PI, sin, cos, pow, atan
import volmdlr
from volmdlr import curves, edges, wires, surfaces, faces, shapes
from volmdlr.utils.ocp_helpers import get_tangent_circles2d
roller_diameter = 10.2
pitch = 15.875
num_teeth = 40
chain_width = 6.35
# Dimensions derived from the provided inputs
roller_radius = roller_diameter / 2.
tooth_angle = (2 * M_PI) / num_teeth
pitch_circle_diameter = pitch / sin(tooth_angle / 2.)
pitch_circle_radius = pitch_circle_diameter / 2.
roller_contact_angle_min = (M_PI * 120 / 180) - ((M_PI / 2.) / num_teeth)
roller_contact_angle_max = (M_PI * 140 / 180) - ((M_PI / 2.) / num_teeth)
roller_contact_angle = (roller_contact_angle_min + roller_contact_angle_max) / 2.
tooth_radius_min = 0.505 * roller_diameter
tooth_radius_max = tooth_radius_min + (0.069 * pow(roller_diameter, 1.0 / 3.0))
tooth_radius = (tooth_radius_min + tooth_radius_max) / 2.
profile_radius = 0.12 * roller_diameter * (num_teeth + 2)
top_diameter = pitch_circle_diameter + ((1 - (1.6 / num_teeth)) * pitch) - roller_diameter
top_radius = top_diameter / 2.
thickness = chain_width * 0.95
# Center hole data
center_radius = 125.0 / 2.
# Mounting hole data
mounting_hole_count = 6
mounting_radius = 153.0 / 2.
hole_radius = 8.5 / 2.
def build_tooth():
vm_base_center = volmdlr.Point2D(pitch_circle_radius + (tooth_radius - roller_radius), 0)
vm_base_circle = curves.Circle2D(volmdlr.Frame2D(vm_base_center, volmdlr.X2D, volmdlr.Y2D), tooth_radius)
vm_trimmed_base = vm_base_circle.trim_with_parameters(param1=M_PI - (roller_contact_angle / 2.),
param2=M_PI, same_sense=True)
vm_trimmed_base = vm_trimmed_base.reverse()
vm_p0 = vm_trimmed_base.start
vm_p1 = vm_trimmed_base.end
# Determine the center of the profile circle
x_distance = cos(roller_contact_angle / 2.) * (profile_radius + tooth_radius)
y_distance = sin(roller_contact_angle / 2.) * (profile_radius + tooth_radius)
vm_profile_center = volmdlr.Point2D(pitch_circle_radius - x_distance, y_distance)
# Construct the profile circle
vm_profile_circle = curves.Circle2D(volmdlr.Frame2D(vm_profile_center, volmdlr.X2D, volmdlr.Y2D),
vm_profile_center.point_distance(vm_p1))
vm_outer_circle = curves.Circle2D(volmdlr.OXY, top_radius)
inter = vm_profile_circle.intersections(vm_outer_circle)
num_points = len(inter)
if num_points == 2:
if vm_p1.point_distance(inter[0]) < vm_p1.point_distance(inter[0]):
vm_p2 = inter[0]
else:
vm_p2 = inter[1]
elif num_points == 1:
vm_p2 = inter[0]
else:
sys.exit(-1)
# Trim the profile circle and mirror
vm_trimmed_profile = vm_profile_circle.trim(vm_p1, vm_p2)
# Calculate the outermost point
p3 = volmdlr.Point2D(cos(tooth_angle / 2.) * top_radius,
sin(tooth_angle / 2.) * top_radius)
# and use it to create the third arc
trimmed_outer = vm_outer_circle.trim(vm_p2, p3)
vm_mirror_axis = volmdlr.OXY.rotation(center=volmdlr.O2D, angle=tooth_angle / 2., rotate_basis=True)
mirror_base = vm_trimmed_base.mirror(vm_mirror_axis.origin, vm_mirror_axis.u)
mirror_profile = vm_trimmed_profile.mirror(vm_mirror_axis.origin, vm_mirror_axis.u)
mirror_outer = trimmed_outer.mirror(vm_mirror_axis.origin, vm_mirror_axis.u)
# mirror_base = mirror_base.reverse()
# Replace the two outer arcs with a single one
outer_start = trimmed_outer.start
outer_mid = trimmed_outer.end
outer_end = mirror_outer.end
outer_arc = edges.Arc2D.from_3_points(outer_start, outer_mid, outer_end)
# Create an arc for the inside of the wedge
inner_circle = curves.Circle2D(volmdlr.OXY, top_radius - roller_diameter)
inner_start = volmdlr.Point2D(top_radius - roller_diameter, 0)
inner_start_parameter = inner_circle.get_angle_at_point(inner_start)
inner_arc = inner_circle.trim_with_parameters(inner_start_parameter, inner_start_parameter+tooth_angle)
inner_arc = inner_arc.reverse()
plane = surfaces.Plane3D(volmdlr.OXYZ)
p4 = mirror_base.end
p5 = inner_arc.start
lineseg1 = edges.LineSegment2D(p4, p5)
p6 = inner_arc.end
lineseg2 = edges.LineSegment2D(p6, vm_p0)
wire = wires.Contour2D(wires.merge_primitives_points([vm_trimmed_base, vm_trimmed_profile, outer_arc,
mirror_profile, mirror_base, lineseg1, inner_arc, lineseg2]))
face = faces.PlaneFace3D(surface3d=plane, surface2d=surfaces.Surface2D(outer_contour=wire, inner_contours=[]))
return shapes.Solid.make_extrusion(face, thickness)
def round_tooth(wedge):
round_x = 2.6
round_z = 0.06 * pitch
round_radius = pitch
# Determine where the circle used for rounding has to start and stop
p2d_1 = volmdlr.Point2D(top_radius - round_x, 0)
p2d_2 = volmdlr.Point2D(top_radius, round_z)
# Construct the rounding circle
round_circle = get_tangent_circles2d(point1=p2d_1, point2=p2d_2, radius=round_radius, tol=0.01)
if len(round_circle) != 2:
sys.exit(-2)
round_circle_2d_1, round_circle_2d_2 = round_circle
if round_circle_2d_1.frame.origin.y >= 0:
round_circle_2d = round_circle_2d_1
else:
round_circle_2d = round_circle_2d_2
# Remove the arc used for rounding
trimmed_circle = round_circle_2d.trim(p2d_1, p2d_2)
# Calculate extra points used to construct lines
p1 = volmdlr.Point2D(p2d_1.x, p2d_1.y)
p2 = volmdlr.Point2D(p2d_2.x, p2d_2.y)
p3 = volmdlr.Point2D(p2d_2.x + 1, p2d_2.y)
p4 = volmdlr.Point2D(p2d_2.x + 1, p2d_1.y - 1)
p5 = volmdlr.Point2D(p2d_1.x, p2d_1.y - 1)
lineseg1 = edges.LineSegment2D(p2, p3)
lineseg2 = edges.LineSegment2D(p3, p4)
lineseg3 = edges.LineSegment2D(p4, p5)
lineseg4 = edges.LineSegment2D(p5, p1)
ordered_edges = wires.WireMixin.order_primitives(
primitives=[trimmed_circle, lineseg1, lineseg2, lineseg3, lineseg4], tol=1e-6)
wire = wires.Contour2D(wires.merge_primitives_points(ordered_edges))
frame = volmdlr.Frame3D(origin=volmdlr.Point3D(0.0, 0.0, 0.0), u=volmdlr.Vector3D(1.0, -0.0, 0.0),
v=volmdlr.Vector3D(-0.0, 0.0, 1.0), w=volmdlr.Vector3D(-0.0, -1.0, 0.0))
face = faces.PlaneFace3D(surface3d=surfaces.Plane3D(frame), surface2d=surfaces.Surface2D(wire, []))
rounding_cut_1 = shapes.Solid.make_revolve(shape=face, axis_point=volmdlr.O3D, axis=volmdlr.Z3D, angle=tooth_angle)
mirrored_cut_1 = rounding_cut_1.mirror(volmdlr.OXYZ)
rounding_cut_2 = mirrored_cut_1.translation(volmdlr.Vector3D(0, 0, thickness))
cut_1 = wedge.subtraction(rounding_cut_1)[0]
cut_2 = cut_1.subtraction(rounding_cut_2)[0]
return cut_2
def clone_tooth(base_shape):
grouped_shape = base_shape
# Find a divisor, between 1 and 8, for the number_of teeth
multiplier = 1
max_multiplier = 1
for i in range(0, 8):
if num_teeth % multiplier == 0:
max_multiplier = i + 1
multiplier = max_multiplier
axis = volmdlr.Z3D
center = volmdlr.O3D
for i in range(1, multiplier):
angle = -i * tooth_angle
rotated_shape = base_shape.rotation(center, axis, angle)
grouped_shape = grouped_shape.union(rotated_shape)[0]
# Rotate the basic tooth and fuse together
aggregated_shape = grouped_shape
for i in range(1, int(num_teeth / multiplier)):
angle = - i * multiplier * tooth_angle
rotated_shape = grouped_shape.rotation(center, axis, angle)
aggregated_shape = aggregated_shape.union(rotated_shape)[0]
cylinder = shapes.Solid.make_cylinder(top_radius - roller_diameter, thickness)
aggregated_shape = aggregated_shape.union(cylinder)[0]
return aggregated_shape
def center_hole(base):
cylinder = shapes.Solid.make_cylinder(center_radius, thickness)
return base.subtraction(cylinder)[0]
def mounting_holes(base):
result = base
for i in range(0, mounting_hole_count):
center = volmdlr.Point3D(cos(i * M_PI / 3) * mounting_radius,
sin(i * M_PI / 3) * mounting_radius, 0.0)
cylinder = shapes.Solid.make_cylinder(radius=hole_radius, height=thickness,
point=center, direction=volmdlr.Z3D)
result = result.subtraction(cylinder)[0]
cone = shapes.Solid.make_cone(radius1=hole_radius + thickness / 2., radius2=hole_radius,
height=thickness / 2., point=center, direction=volmdlr.Z3D)
result = result.subtraction(cone)[0]
return result
wedge = build_tooth()
rounded_wedge = round_tooth(wedge)
basic_disk = clone_tooth(rounded_wedge)
cut_disc = center_hole(basic_disk)
mountable_disc = mounting_holes(cut_disc)
sprocket = cut_out(mountable_disc)
sprocket.color = (1, 0.3, 0.3)
sprocket.display_3d()