The code below demonstrates how to generate a sprocket.
The generated sprocket looks like this:
The tooth profile is constructed as follows (see DIN 8196):
#include <iostream> #include <cmath> #include <gp.hxx> #include <gp_Ax1.hxx> #include <gp_Circ.hxx> #include <gp_Pln.hxx> #include <gp_Pnt2d.hxx> #include <gp_Vec2d.hxx> #include <gp_Dir2d.hxx> #include <gp_Ax2d.hxx> #include <gp_Lin2d.hxx> #include <gp_Circ2d.hxx> #include <gp_Vec.hxx> #include <Geom2d_Line.hxx> #include <GCE2d_MakeLine.hxx> #include <GC_MakeCircle.hxx> #include <Geom_Circle.hxx> #include <Handle_Geom_Circle.hxx> #include <GCE2d_MakeCircle.hxx> #include <Geom2d_Circle.hxx> #include <Handle_Geom2d_Circle.hxx> #include <GCE2d_MakeArcOfCircle.hxx> #include <Geom2d_TrimmedCurve.hxx> #include <Handle_Geom2d_TrimmedCurve.hxx> #include <GCE2d_MakeSegment.hxx> #include <GeomAPI.hxx> #include <GC_MakePlane.hxx> #include <Geom_Plane.hxx> #include <Handle_Geom_Plane.hxx> #include <BRepBuilderAPI_MakeEdge.hxx> #include <BRepBuilderAPI_MakeWire.hxx> #include <BRepBuilderAPI_MakeFace.hxx> #include <BRepPrimAPI_MakePrism.hxx> #include <BRepPrimAPI_MakeCylinder.hxx> #include <BRepPrimAPI_MakeCone.hxx> #include <BRepBuilderAPI_Transform.hxx> #include <BRepPrimAPI_MakeRevol.hxx> #include <BRepFeat_MakeCylindricalHole.hxx> #include <BRepAlgoAPI_Fuse.hxx> #include <BRepAlgoAPI_Cut.hxx> #include <BRepTools.hxx> #include <BRepLib.hxx> #include <Geom2dAPI_InterCurveCurve.hxx> #include <TopoDS_Vertex.hxx> #include <TopoDS_Edge.hxx> #include <gp_Trsf.hxx> #include <GccAna_Circ2d2TanRad.hxx> #include <BRepFilletAPI_MakeFillet2d.hxx> #include <TopExp_Explorer.hxx> #include <BRepTools_WireExplorer.hxx> using namespace std; // The basic inputs needed to build a sprocket Standard_Real roller_diameter = 10.2; Standard_Real pitch = 15.875; Standard_Integer num_teeth = 40; Standard_Real chain_width = 6.35; // Dimensions derived from the provided inputs Standard_Real roller_radius = roller_diameter / 2.0; Standard_Real tooth_angle = (2 * M_PI) / num_teeth; Standard_Real pitch_circle_diameter = pitch / sin(tooth_angle / 2.0); Standard_Real pitch_circle_radius = pitch_circle_diameter / 2.0; Standard_Real roller_contact_angle_min = (M_PI * 120 / 180) - ((M_PI / 2) / num_teeth); Standard_Real roller_contact_angle_max = (M_PI * 140 / 180) - ((M_PI / 2) / num_teeth); Standard_Real roller_contact_angle = (roller_contact_angle_min + roller_contact_angle_max) / 2.0; Standard_Real tooth_radius_min = 0.505 * roller_diameter; Standard_Real tooth_radius_max = tooth_radius_min + (0.069 * pow(roller_diameter, 1.0/3.0)); Standard_Real tooth_radius = (tooth_radius_min + tooth_radius_max) / 2.0; Standard_Real profile_radius = 0.12 * roller_diameter * (num_teeth + 2); Standard_Real top_diameter = pitch_circle_diameter + ((1-(1.6/num_teeth)) * pitch) - roller_diameter; Standard_Real top_radius = top_diameter / 2.0; Standard_Real thickness = chain_width * 0.95; // Center hole data Standard_Real center_radius = 125.0 / 2.0; // Mounting hole data Standard_Integer mounting_hole_count = 6; Standard_Real mounting_radius = 153.0 / 2.0; Standard_Real hole_radius = 8.5 / 2.0; /* * Build a single tooth */ TopoDS_Shape BuildTooth() { // Create a 2D arc to form the base of the tooth gp_Pnt2d base_center(pitch_circle_radius + (tooth_radius - roller_radius), 0); gp_Circ2d base_circle = gp_Circ2d(gp_Ax2d(base_center, gp_Dir2d()), tooth_radius); Handle(Geom2d_TrimmedCurve) trimmed_base = GCE2d_MakeArcOfCircle(base_circle, M_PI - (roller_contact_angle / 2.0), M_PI); trimmed_base->Reverse(); gp_Pnt2d p0 = trimmed_base->StartPoint(); gp_Pnt2d p1 = trimmed_base->EndPoint(); // Determine the center of the profile circle Standard_Real x_distance = cos(roller_contact_angle / 2) * (profile_radius + tooth_radius); Standard_Real y_distance = sin(roller_contact_angle / 2) * (profile_radius + tooth_radius); gp_Pnt2d profile_center(pitch_circle_radius - x_distance, y_distance); // Construct the profile circle gp_Circ2d profile_circle = gp_Circ2d(gp_Ax2d(profile_center, gp_Dir2d()), profile_center.Distance(p1)); Handle(Geom2d_Circle) geom_profile_circle = GCE2d_MakeCircle(profile_circle); // Construct the outer circle gp_Circ2d outer_circle = gp_Circ2d(gp_Ax2d(gp_Pnt2d(0, 0), gp_Dir2d()), top_radius); Handle(Geom2d_Circle) geom_outer_circle = GCE2d_MakeCircle(outer_circle); // Calculate the intersection point(s) of the profile circle // and the outer circle. If there are two points, pick the one closest // to the center of the profile circle Geom2dAPI_InterCurveCurve inter(geom_profile_circle, geom_outer_circle); Standard_Integer num_points = inter.NbPoints(); gp_Pnt2d p2; if (num_points == 2) { if (p1.Distance(inter.Point(1)) < p1.Distance(inter.Point(2))) p2 = inter.Point(1); else p2 = inter.Point(2); } else if (num_points == 1) { p2 = inter.Point(1); } else throw; // Trim the profile circle and mirror Handle(Geom2d_TrimmedCurve) trimmed_profile = GCE2d_MakeArcOfCircle(profile_circle, p1, p2); // Calculate the outermost point gp_Pnt2d p3 = gp_Pnt2d(cos(tooth_angle / 2) * top_radius, sin(tooth_angle / 2) * top_radius); // and use it to create the third arc Handle(Geom2d_TrimmedCurve) trimmed_outer = GCE2d_MakeArcOfCircle(outer_circle, p2, p3); // Mirror and reverse the three arcs gp_Ax2d mirror_axis(gp::Origin2d(), gp::DX2d().Rotated(tooth_angle / 2.0)); Handle(Geom2d_TrimmedCurve) mirror_base = Handle(Geom2d_TrimmedCurve)::DownCast(trimmed_base->Copy()); Handle(Geom2d_TrimmedCurve) mirror_profile = Handle(Geom2d_TrimmedCurve)::DownCast(trimmed_profile->Copy()); Handle(Geom2d_TrimmedCurve) mirror_outer = Handle(Geom2d_TrimmedCurve)::DownCast(trimmed_outer->Copy()); mirror_base->Mirror(mirror_axis); mirror_profile->Mirror(mirror_axis); mirror_outer->Mirror(mirror_axis); mirror_base->Reverse(); mirror_profile->Reverse(); mirror_outer->Reverse(); // Replace the two outer arcs with a single one gp_Pnt2d outer_start = trimmed_outer->StartPoint(); gp_Pnt2d outer_mid = trimmed_outer->EndPoint(); gp_Pnt2d outer_end = mirror_outer->EndPoint(); Handle(Geom2d_TrimmedCurve) outer_arc = GCE2d_MakeArcOfCircle(outer_start, outer_mid, outer_end); // Create an arc for the inside of the wedge gp_Circ2d inner_circle(gp_Ax2d(gp_Pnt2d(0, 0), gp_Dir2d()), top_radius - roller_diameter); gp_Pnt2d inner_start(top_radius - roller_diameter, 0); Handle(Geom2d_TrimmedCurve) inner_arc = GCE2d_MakeArcOfCircle(inner_circle, inner_start , tooth_angle); inner_arc->Reverse(); //Convert the 2D arcs and two extra lines to 3D edges gp_Pln plane = gp_Pln(gp::Origin(), gp::DZ()); BRepBuilderAPI_MakeEdge arc1(GeomAPI::To3d(trimmed_base, plane)); BRepBuilderAPI_MakeEdge arc2(GeomAPI::To3d(trimmed_profile, plane)); BRepBuilderAPI_MakeEdge arc3(GeomAPI::To3d(outer_arc, plane)); BRepBuilderAPI_MakeEdge arc4(GeomAPI::To3d(mirror_profile, plane)); BRepBuilderAPI_MakeEdge arc5(GeomAPI::To3d(mirror_base, plane)); gp_Pnt2d p4 = mirror_base->EndPoint(); gp_Pnt2d p5 = inner_arc->StartPoint(); BRepBuilderAPI_MakeEdge lin1(gp_Pnt(p4.X(), p4.Y(), 0), gp_Pnt(p5.X(), p5.Y(), 0)); BRepBuilderAPI_MakeEdge arc6(GeomAPI::To3d(inner_arc, plane)); gp_Pnt2d p6 = inner_arc->EndPoint(); BRepBuilderAPI_MakeEdge lin2(gp_Pnt(p6.X(), p6.Y(), 0), gp_Pnt(p0.X(), p0.Y(), 0)); // Combine the edges in a wire BRepBuilderAPI_MakeWire wire(arc1); wire.Add(arc2); wire.Add(arc3); wire.Add(arc4); wire.Add(arc5); wire.Add(lin1); wire.Add(arc6); wire.Add(lin2); // Convert the wire into a face BRepBuilderAPI_MakeFace face(wire.Wire()); // Finally, extrude the face BRepPrimAPI_MakePrism wedge(face.Shape(), gp_Vec(0.0, 0.0, thickness)); return wedge.Shape(); } /* * Round off the edge of the single tooth */ TopoDS_Shape RoundTooth(TopoDS_Shape wedge) { Standard_Real round_x = 2.6; Standard_Real round_z = 0.06 * pitch; Standard_Real round_radius = pitch; // Determine where the circle used for rounding has to start and stop gp_Pnt2d p2d_1(top_radius - round_x, 0); gp_Pnt2d p2d_2(top_radius, round_z); // Construct the rounding circle GccAna_Circ2d2TanRad round_circle(p2d_1, p2d_2, round_radius, 0.01); if (round_circle.NbSolutions() != 2) throw; gp_Circ2d round_circle_2d_1 = round_circle.ThisSolution(1); gp_Circ2d round_circle_2d_2 = round_circle.ThisSolution(2); gp_Circ2d round_circle_2d; if (round_circle_2d_1.Position().Location().Coord().Y() >= 0) round_circle_2d = round_circle_2d_1; else round_circle_2d = round_circle_2d_2; // Remove the arc used for rounding Handle(Geom2d_TrimmedCurve) trimmed_circle = GCE2d_MakeArcOfCircle(round_circle_2d, p2d_1, p2d_2); // Calculate extra points used to construct lines gp_Pnt p1(p2d_1.X(), 0, p2d_1.Y()); gp_Pnt p2(p2d_2.X(), 0, p2d_2.Y()); gp_Pnt p3(p2d_2.X() + 1, 0, p2d_2.Y()); gp_Pnt p4(p2d_2.X() + 1, 0, p2d_1.Y() - 1); gp_Pnt p5(p2d_1.X(), 0, p2d_1.Y() - 1); //Convert the arc and four extra lines into 3D edges gp_Pln plane = gp_Pln(gp_Ax3(gp::Origin(), gp::DY().Reversed(), gp::DX())); BRepBuilderAPI_MakeEdge arc1(GeomAPI::To3d(trimmed_circle, plane)); BRepBuilderAPI_MakeEdge lin1(p2, p3); BRepBuilderAPI_MakeEdge lin2(p3, p4); BRepBuilderAPI_MakeEdge lin3(p4, p5); BRepBuilderAPI_MakeEdge lin4(p5, p1); // Make a wire composed of the edges BRepBuilderAPI_MakeWire round_wire(arc1); round_wire.Add(lin1); round_wire.Add(lin2); round_wire.Add(lin3); round_wire.Add(lin4); // Turn the wire into a face BRepBuilderAPI_MakeFace round_face(round_wire); // Revolve the face around the Z axis over the tooth angle TopoDS_Shape rounding_cut_1 = BRepPrimAPI_MakeRevol(round_face, gp::OZ(), tooth_angle).Shape(); // Construct a mirrored copy of the first cutting shape gp_Trsf mirror; mirror.SetMirror(gp::XOY()); TopoDS_Shape mirrored_cut_1 = BRepBuilderAPI_Transform(rounding_cut_1, mirror, true).Shape(); // and translate it so that it ends up on the other side of the wedge gp_Trsf translate; translate.SetTranslation(gp_Vec(0, 0, thickness)); TopoDS_Shape rounding_cut_2 = BRepBuilderAPI_Transform(mirrored_cut_1, translate, false).Shape(); // Cut the wedge using the first and second cutting shape BRepAlgoAPI_Cut cut_1(wedge, rounding_cut_1); BRepAlgoAPI_Cut cut_2(cut_1, rounding_cut_2); // Return the result return cut_2.Shape(); } /* * Copy the single tooth to form a complete sprocket * This is done in two stages to speed up the fusing * Fill the center of the sprocket with a disc */ TopoDS_Shape CloneTooth(TopoDS_Shape base_shape) { gp_Trsf clone; //TopoDS_Shape aggregated_shape = base_shape; TopoDS_Shape grouped_shape = base_shape; // Find a divisor, between 1 and 8, for the number_of teeth int multiplier = 1; int max_multiplier = 1; for (int current_multiplier = 1; current_multiplier <= 8; current_multiplier++) { if ((num_teeth % multiplier) == 0) max_multiplier = current_multiplier; } multiplier = max_multiplier; for (int i = 1; i < multiplier; i++) { clone.SetRotation(gp::OZ(), - i * tooth_angle); TopoDS_Shape rotated_shape = BRepBuilderAPI_Transform(base_shape, clone, true).Shape(); grouped_shape = BRepAlgoAPI_Fuse(grouped_shape, rotated_shape).Shape(); cout << i << endl; } TopoDS_Shape aggregated_shape = grouped_shape; // Rotate the basic tooth and fuse together for (int i = 1; i < num_teeth / multiplier; i++) { clone.SetRotation(gp::OZ(), - i * multiplier * tooth_angle); TopoDS_Shape rotated_shape = BRepBuilderAPI_Transform(grouped_shape, clone, true).Shape(); aggregated_shape = BRepAlgoAPI_Fuse(aggregated_shape, rotated_shape).Shape(); cout << i << endl; } // Fuse a disc to fill in the center BRepPrimAPI_MakeCylinder cylinder(gp::XOY(), top_radius - roller_diameter, thickness); aggregated_shape = BRepAlgoAPI_Fuse(aggregated_shape, cylinder.Shape()).Shape(); return aggregated_shape; } /* * Create a hole in the center of the sprocket */ TopoDS_Shape CenterHole(TopoDS_Shape base) { BRepPrimAPI_MakeCylinder cylinder(center_radius, thickness); return BRepAlgoAPI_Cut(base, cylinder.Shape()).Shape(); } /* * Provide chamfered mounting holes */ TopoDS_Shape MountingHoles(TopoDS_Shape base) { TopoDS_Shape result = base; for (int i = 0; i < mounting_hole_count; i++) { gp_Pnt center (cos(i * M_PI/3) * mounting_radius, sin(i * M_PI/3) * mounting_radius, 0.0); gp_Ax2 center_axis (center, gp::DZ()); BRepPrimAPI_MakeCylinder cylinder(center_axis, hole_radius, thickness); result = BRepAlgoAPI_Cut(result, cylinder.Shape()).Shape(); BRepPrimAPI_MakeCone cone(center_axis, hole_radius + thickness / 2.0, hole_radius, thickness / 2.0); result = BRepAlgoAPI_Cut(result, cone.Shape()).Shape(); } return result; } /* * Create cutouts to reduce the weight of the sprocket */ TopoDS_Shape Cutout(TopoDS_Shape base) { gp_Circ2d outer(gp::OX2d(), top_radius - 1.75 * roller_diameter); gp_Circ2d inner(gp::OX2d(), center_radius + 0.75 * roller_diameter); Handle(Geom2d_Circle) geom_outer = GCE2d_MakeCircle(outer); Handle(Geom2d_Circle) geom_inner = GCE2d_MakeCircle(inner); geom_inner->Reverse(); Standard_Real base_angle = (2.0 * M_PI) / mounting_hole_count; Standard_Real hole_angle = atan(hole_radius / mounting_radius); Standard_Real correction_angle = 3 * hole_angle; gp_Lin2d left(gp::Origin2d(), gp::DX2d()); gp_Lin2d right(gp::Origin2d(), gp::DX2d()); left.Rotate(gp::Origin2d(), correction_angle); right.Rotate(gp::Origin2d(), base_angle - correction_angle); Handle(Geom2d_Line) geom_left = GCE2d_MakeLine(left); Handle(Geom2d_Line) geom_right = GCE2d_MakeLine(right); Geom2dAPI_InterCurveCurve inter_1(geom_outer, geom_left); Geom2dAPI_InterCurveCurve inter_2(geom_outer, geom_right); Geom2dAPI_InterCurveCurve inter_3(geom_inner, geom_right); Geom2dAPI_InterCurveCurve inter_4(geom_inner, geom_left); gp_Pnt2d p1, p2, p3, p4; (inter_1.Point(1).X() > 0) ? p1 = inter_1.Point(1) : p1 = inter_1.Point(2); (inter_2.Point(1).X() > 0) ? p2 = inter_2.Point(1) : p2 = inter_2.Point(2); (inter_3.Point(1).X() > 0) ? p3 = inter_3.Point(1) : p3 = inter_3.Point(2); (inter_4.Point(1).X() > 0) ? p4 = inter_4.Point(1) : p4 = inter_4.Point(2); // Trim the profile circle and mirror Handle(Geom2d_TrimmedCurve) trimmed_outer = GCE2d_MakeArcOfCircle(outer, p1, p2); Handle(Geom2d_TrimmedCurve) trimmed_inner = GCE2d_MakeArcOfCircle(inner, p4, p3); gp_Pln plane = gp_Pln(gp::Origin(), gp::DZ()); BRepBuilderAPI_MakeEdge arc1(GeomAPI::To3d(trimmed_outer, plane)); BRepBuilderAPI_MakeEdge lin1(gp_Pnt(p2.X(), p2.Y(),0), gp_Pnt(p3.X(), p3.Y(), 0)); BRepBuilderAPI_MakeEdge arc2(GeomAPI::To3d(trimmed_inner, plane)); BRepBuilderAPI_MakeEdge lin2(gp_Pnt(p4.X(), p4.Y(),0), gp_Pnt(p1.X(), p1.Y(), 0)); BRepBuilderAPI_MakeWire cutout_wire(arc1); cutout_wire.Add(lin1); cutout_wire.Add(arc2); cutout_wire.Add(lin2); // Turn the wire into a face BRepBuilderAPI_MakeFace cutout_face(cutout_wire); BRepFilletAPI_MakeFillet2d filleted_face(cutout_face.Face()); BRepTools_WireExplorer explorer(cutout_wire); while (explorer.More()) { TopoDS_Vertex vertex = TopoDS::Vertex(explorer.CurrentVertex()); filleted_face.AddFillet(vertex, roller_radius); explorer.Next(); } BRepPrimAPI_MakePrism cutout(filleted_face.Shape(), gp_Vec(0.0, 0.0, thickness)); TopoDS_Shape result = base; gp_Trsf rotate; for (int cut = 0; cut < mounting_hole_count; cut++) { rotate.SetRotation(gp::OZ(), cut * 2.0 * M_PI / mounting_hole_count); TopoDS_Shape rotated_cutout = BRepBuilderAPI_Transform(cutout.Shape(), rotate, true).Shape(); result = BRepAlgoAPI_Cut(result, rotated_cutout).Shape(); } return result; } int main() { // Build a wedge containing one tooth TopoDS_Shape wedge = BuildTooth(); // Round off the tooth TopoDS_Shape rounded_wedge = RoundTooth(wedge); // Copy and fuse the teeth to form a basic sprocket TopoDS_Shape basic_disc = CloneTooth(rounded_wedge); // Cut out a hole in the center TopoDS_Shape cut_disc = CenterHole(basic_disc); // Create mounting holes TopoDS_Shape mountable_disc = MountingHoles(cut_disc); // Remove material to reduce weight TopoDS_Shape finished_sprocket = Cutout(mountable_disc); // Write the result to a brep file BRepTools::Write(finished_sprocket, "sprocket.brep"); cout << "Done" << endl; return 0; }
Tools used:
• g++ version 5.4.0
• OpenCASCADE Community Edition 0.17.1-1
• Code::Blocks 13.12
• FreeCAD 0.15
• Blender 2.80
Tools used to generate the 2D drawings:
• LibreCAD 2.0.9-2
• Inkscape 0.91