Test Sprocket#

_images/test_sprocket.png
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()