FeatureScript 961; import(path : "onshape/std/geometry.fs", version : "961.0"); annotation { "Feature Type Name" : "Bevel Gear" } export const myFeature = defineFeature(function(context is Context, id is Id, definition is map) precondition { // Define the parameters of the feature type annotation { "Name" : "Module" } isInteger(definition.module, POSITIVE_COUNT_BOUNDS); annotation { "Name" : "Pressure Angle Input type" } definition.PressAngInputType is PressureAngleType; if(definition.PressAngInputType == PressureAngleType.standardPressAngle) { annotation { "Name" : "Pressure Angle (degrees)" } definition.stdPressAngle is stdPressAngles; } else { annotation { "Name" : "Custom Pressure Angle (degrees)" } isAngle(definition.pressureAngle, ANGLE_360_BOUNDS); } annotation { "Name" : "Number of Teeths in Gear" } isInteger(definition.gearTeeths, POSITIVE_COUNT_BOUNDS); annotation { "Name" : "Face Width" } isLength(definition.faceWidth, LENGTH_BOUNDS); annotation { "Name" : "Root Fillet Radius" } isLength(definition.RootFilletRadius, LENGTH_BOUNDS); annotation { "Name" : "Gear Ratio" } isInteger(definition.gearRatio, POSITIVE_COUNT_BOUNDS); annotation { "Name" : "Hole Diameter" } isLength(definition.HoleDia, LENGTH_BOUNDS); //End of Parameter def } { // Define the function's action //Calculations for gear nomenclatures var diameteralPitch = (25.4 / definition.module); var gearPitchDia = (definition.gearTeeths / diameteralPitch) * meter; definition.pinionTeeths = round(definition.gearTeeths / definition.gearRatio); var pinionPitchDia = (definition.pinionTeeths / diameteralPitch) * meter; var theta = atan(gearPitchDia / pinionPitchDia); theta = ((theta * 180 / PI) / radian); var zOffset = (definition.faceWidth * cos((theta / 2) * degree)); if(zOffset > ((pinionPitchDia / 2) * 0.9)) { throw regenError("Component failed to create - Face Width greater than cone distance"); } if(zOffset < ((pinionPitchDia / 2) * 0.2)) { throw regenError("Component failed to create - Face Width less than minimum required value."); } var pitchDiameter = gearPitchDia + definition.faceWidth * sin(theta * 0.5 * degree); var planeOffset = 0 * meter; // Recalculate diametral Pitch diameteralPitch = definition.gearTeeths / pitchDiameter * meter; var dedendum = 0; dedendum = calculateDedendum(context, diameteralPitch) * meter; var rootDiameter = pitchDiameter - (2 * dedendum); var baseCircleDiameter = pitchDiameter * cos(definition.pressureAngle); var outsideDiameter = (definition.gearTeeths + 2) / diameteralPitch * meter; // Add base circle sketch; var baseCircle1 = newSketch(context, id + "baseCircle1", { "sketchPlane" : qCreatedBy(makeId("Front"), EntityType.FACE) }); skCircle(baseCircle1, "baseCircle1", { "center" : vector(0, 0) * meter, "radius" : 1 * (baseCircleDiameter / 2) }); skSolve(baseCircle1); // End of base circle sketch // Calculate and define nomenclatures for offset Plane for Loft operation var pitchDiameter2 = gearPitchDia - (definition.faceWidth * sin(theta * degree) / 2); var diameteralPitch2 = definition.gearTeeths / pitchDiameter2 * meter; var dedendum2 = 0; dedendum2 = calculateDedendum(context, diameteralPitch2) * meter; if (dedendum2 == 0) throw regenError("failed to calculate value of dedndum"); planeOffset = zOffset; //Creating Offset Plane opPlane(context, id + "plane1", { "plane" : plane(vector(0, (-1) * zOffset / meter, 0) * meter, vector(0, -1, 0 )), "width" : 6 * inch, "height" : 6 * inch }); var rootDiameter2 = pitchDiameter2 - (2 * dedendum2); var baseCircleDiameter2 = pitchDiameter2 * cos(definition.pressureAngle); var outsideDiameter2 = (definition.gearTeeths + 2) / diameteralPitch2 * meter; // Add base circle 2 sketch var baseCircle2 = newSketch(context, id + "baseCircle2", { "sketchPlane" : qCreatedBy((id + "plane1"), EntityType.FACE) }); skCircle(baseCircle2, "baseCircle2", { "center" : vector(0, 0) * meter, "radius" : 1 * (baseCircleDiameter2 / 2) }); skSolve(baseCircle2); // End of base circle sketch // Create sketch entity for shaft. var shaftCircle = newSketch(context, id + "shaftCircle", { "sketchPlane" : qCreatedBy(makeId("Front"), EntityType.FACE) }); skCircle(shaftCircle, "shaftCircle", { "center" : vector(0, 0) * meter, "radius" : 1 * (definition.HoleDia / 2) }); skSolve(shaftCircle); // ### Executing Loft Operation ############################################################ opLoft(context, id + "loft1", { "profileSubqueries" : [ qCreatedBy(id + "baseCircle1", EntityType.FACE), qCreatedBy(id + "baseCircle2", EntityType.FACE) ], }); // Begining Calculations for Involute points for gear teeth sketch. var involutePoints1 = []; var involutePoints2 = []; var tempInvoluteSet = []; var toothThicknessAngle = ((2 * PI) / (2 * definition.gearTeeths)) * 180 / PI; var involutePointCount = 15; var involuteIntersectionRadius = baseCircleDiameter / 2; var involuteSize = (outsideDiameter / 2) - (baseCircleDiameter / 2); planeOffset = 0 * meter; for (var t = 0; t < involutePointCount; t += 1) { involuteIntersectionRadius = (baseCircleDiameter / 2.0) + ((involuteSize / (involutePointCount - 1)) * t); var newPt = involutePoint(context, baseCircleDiameter / 2, involuteIntersectionRadius, planeOffset); tempInvoluteSet = append(tempInvoluteSet, newPt); } // Determine the angle between the X axis and a line between the origin of the curve and the intersection point between the involute and the pitch diameter circle. var pitchInvolutePt = involutePoint(context, baseCircleDiameter / 2, pitchDiameter / 2, planeOffset); var pitchPointAngle = atan(pitchInvolutePt[1] / pitchInvolutePt[0]) * 180 /(PI * radian); // Determine the angle defined by the tooth thickness as measured at the pitch diameter circle. //Angle to rotate the curve var rotateAngle = (-1) * (toothThicknessAngle / 2 + pitchPointAngle); // Rotate the involute so the intersection point lies on the x axis. var cosAngle = cos(rotateAngle * degree); var sinAngle = sin(rotateAngle * degree); for (var pt in tempInvoluteSet) { pt[0] = pt[0] * cosAngle - pt[1] * sinAngle; pt[1] = pt[0] * sinAngle + pt[1] * cosAngle; involutePoints1 = append(involutePoints1, vector( pt[0], pt[1] ) * meter); // Create a new set of points with a negated y. This effectively mirrors the original points about the X axis. var negatedPt = vector(pt[0], (-1) * pt[1] ); involutePoints2 = append(involutePoints2, negatedPt * meter); } // Create sketch for Teeth Profile var Involute1 = newSketch(context, id + "Involute1", { "sketchPlane" : qCreatedBy(makeId("Front"), EntityType.FACE) }); skFitSpline(Involute1, "spline1", { "points" : involutePoints1 }); skFitSpline(Involute1, "spline2", { "points" : involutePoints2 }); skArc(Involute1, "gearTop", { "start" : 1 * involutePoints1[involutePointCount - 1] , "mid" : 1 * vector(outsideDiameter / 2, 0 * meter ), "end" : 1 * involutePoints2[involutePointCount - 1] }); skArc(Involute1, "gearBase", { "start" : 1 * involutePoints1[0] , "mid" : 1 * vector(baseCircleDiameter / 2, 0 * meter ), "end" : 1 * involutePoints2[0] }); skSolve(Involute1); //##### Begining Calculations for Involute points for gear teeth sketch on offset Plane for loft operation. var tempInvoluteSet2 = []; var involutePts1 = []; var involutePts2 = []; involuteIntersectionRadius = baseCircleDiameter2 / 2; involuteSize = (outsideDiameter2 / 2) - (baseCircleDiameter2 / 2); planeOffset = zOffset; for (var t = 0; t < involutePointCount; t += 1) { involuteIntersectionRadius = (baseCircleDiameter2 / 2.0) + ((involuteSize / (involutePointCount - 1)) * t); var newPt = involutePoint(context, baseCircleDiameter2 / 2, involuteIntersectionRadius, planeOffset); tempInvoluteSet2 = append(tempInvoluteSet2, newPt); } // Determine the angle between the X axis and a line between the origin of the curve and the intersection point between the involute and the pitch diameter circle. pitchInvolutePt = involutePoint(context, baseCircleDiameter2 / 2, pitchDiameter2 / 2, planeOffset); debug(context, pitchInvolutePt); pitchPointAngle = atan(pitchInvolutePt[1] / pitchInvolutePt[0]) * 180 /(PI * radian); // Determine the angle defined by the tooth thickness as measured at the pitch diameter circle. //Angle to rotate the curve rotateAngle = (-1) * (toothThicknessAngle / 2 + pitchPointAngle);//-backlashAngle // Rotate the involute so the intersection point lies on the x axis. cosAngle = cos(rotateAngle * degree); sinAngle = sin(rotateAngle * degree); for (var pt in tempInvoluteSet2) { pt[0] = pt[0] * cosAngle - pt[1] * sinAngle; pt[1] = pt[0] * sinAngle + pt[1] * cosAngle; involutePts1 = append(involutePts1, vector( pt[0], pt[1] ) * meter); // Create a new set of points with a negated y. This effectively mirrors the original points about the X axis. var negatedPt = vector(pt[0], (-1) * pt[1] ); involutePts2 = append(involutePts2, negatedPt * meter); } // Create sketch for Teeth Profile for offset plane var Involute2 = newSketch(context, id + "Involute2", { "sketchPlane" : qCreatedBy((id + "plane1"), EntityType.FACE) }); skFitSpline(Involute2, "spline1", { "points" : involutePts1 }); skFitSpline(Involute2, "spline2", { "points" : involutePts2 }); skArc(Involute2, "gearTop", { "start" : 1 * involutePts1[involutePointCount - 1] , "mid" : 1 * vector(outsideDiameter2 / 2, 0 * meter ), "end" : 1 * involutePts2[involutePointCount - 1] }); skArc(Involute2, "gearBase", { "start" : 1 * involutePts1[0] , "mid" : 1 * vector(baseCircleDiameter2 / 2, 0 * meter ), "end" : 1 * involutePts2[0] }); skSolve(Involute2); // Loft Operation for Teeth Profile opLoft(context, id + "loft2", { "profileSubqueries" : [ qCreatedBy(id + "Involute1", EntityType.FACE), qCreatedBy(id + "Involute2", EntityType.FACE) ], }); opFitSpline(context, id + "Axis", { "points" : [vector( 0, 0, 0) * inch, vector(0, -1, 0) * inch] }); const axis = qCreatedBy(id + "Axis", EntityType.EDGE); // Circular patteren operation for creating all the terth profile bodies around base. circularPattern(context, id + "gearTeeths", { "patternType" : PatternType.PART, "entities" : qCreatedBy(id + "loft2", EntityType.BODY), "axis" : axis, "angle" : 360 * degree, "instanceCount" : definition.gearTeeths, "equalSpace" : true }); }); //Function to calculate dedendum value. export function calculateDedendum(context, diametralPitch)returns number { if(diametralPitch < ((20 * (PI / 180)) - 0.000001)) { return (1.157 / diametralPitch); } else { var circularPitch = PI / diametralPitch; if(circularPitch >= 20) { return (1.25 / diametralPitch); } else { return (1.2 / diametralPitch); } } } const PRESSURE_ANGLE_BOUNDS = { (degree) : [14.5, 20, 25] } as AngleBoundSpec; export enum stdPressAngles { annotation { "NAME" : "14.5 degree" } PressAngle_14_5 , annotation { "NAME" : "20 degree" } PressAngle_20, annotation { "NAME" : "25 degree" } PressAngle_25 } export enum PressureAngleType { annotation { "Name" : "Standard Pressure Angle(Recomended)" } standardPressAngle, annotation { "Name" : "Custom Pressure Angle" } customPressAngle } // Function to calculate Involute Point coordinates / vectors export function involutePoint(context, baseCircleRadius, involuteIntersectionRadius, planeOffset)returns Vector { var l = sqrt(involuteIntersectionRadius ^ 2 - baseCircleRadius ^ 2 ); var alpha = (l / baseCircleRadius); alpha = alpha * 180 / PI; var thta = alpha - (180 / PI) * (acos(baseCircleRadius / involuteIntersectionRadius)) / radian; var x = involuteIntersectionRadius * cos(thta * degree) / meter; var y = involuteIntersectionRadius * sin(thta * degree) / meter; return vector(x,y); }