/// <summary> /// Given the gap number around the gear, calculate the points on the /// curve for the undercut from where it is tangential to the dedendum /// circle round to where it crosses the pitch circle. This is sufficient /// for all gears containing five or more teeth. At four teeth, the /// undercut exceeds the pitch circle.This function calculates /// the under cut anticlockwise from the gap selected. /// </summary> /// <param name="gap">The numbered gap between teeth. Gap zero lies /// along the positive X axis, with gaps numbered anticlockwise /// from there.</param> /// <returns>The list of points making up the trochoid from the /// dedendum circle up to the pitch circle</returns> public IEnumerable <PointF> AnticlockwiseUndercut(int gap) { double gapCentreAngle = (gap % ToothCount) * ToothAngle; return(UndercutPoints .Select(p => Involutes.RotateAboutOrigin(gapCentreAngle, p)) .Reverse()); }
private PointF PointAtAngle(double angle) { double radius = InnerDiameter / 2 + (PitchCircleDiameter - InnerDiameter) / 2 * angle / ToothAngle; return(Involutes.CreatePt(radius * Math.Cos(angle), radius * Math.Sin(angle))); }
private IEnumerable <PointF> ReflectAndAddBacklash(IEnumerable <PointF> points, int gap) { // The angles between the middles of adjacent // teeth in radians is 2*PI / ToothCount double gapCentreAngle = (gap % ToothCount) * ToothAngle - BacklashAngle; return(ReflectY(points).Select(p => Involutes.RotateAboutOrigin(gapCentreAngle, p))); }
private IEnumerable <PointF> ComputeUndercutPoints() { int lowerLimit = AngleIndexFloor(-UndercutAngleAtPitchCircle); int upperLimit = AngleIndexFloor(DedendumArcAngle / 2); for (int i = lowerLimit; i <= upperLimit; i++) { double angle = (i % Involutes.PointsPerRotation) * Involutes.AngleStep; yield return(Involutes.InvolutePlusOffset(PitchCircleDiameter / 2, -Module * (1 - ProfileShift), Module * (Math.PI / 4 - Math.Tan(PressureAngle)), angle, 0)); } }
private IEnumerable <PointF> ComputeInvolutePoints() { double involuteBaseAngle = GapWidthAngleAtPitchCircle / 2 - ToothBaseOffset; int limit = AngleIndexFloor(AddendumInvoluteAngle); for (int i = limit; i >= 0; i--) { double angle = (i % Involutes.PointsPerRotation) * Involutes.AngleStep; yield return(Involutes.InvolutePlusOffset (BaseCircleDiameter / 2, 0, 0, angle, involuteBaseAngle)); } }
public ChainSprocket(int teeth, double wire, double err, double inner, double outer, double cutDiameter, double backlash) { ToothCount = teeth; WireThickness = wire; OuterLinkWidth = outer; MaxError = err; Backlash = backlash; InnerLinkLength = inner; CutDiameter = cutDiameter; SetInformation(); var curves = CalculatePoints(); OuterToothProfile = Involutes.LinearReduction (curves.outer, (float)MaxError); InnerToothProfile = Involutes.LinearReduction (curves.inner, (float)MaxError); }
private List <PointF> ComputeOnePitch() { List <PointF> points = new List <PointF> { ToothTip, FaceEnd }; double startAngle = -Math.PI / 2 + UndercutAngle; points.AddRange( Involutes.CirclePoints( BackAngle, 2 * Math.PI + startAngle, Involutes.AngleStep, CutDiameter / 2, UnderCutCentre) .Reverse()); points.Add(BackTip); points.AddRange( Involutes.CirclePoints( GapAngle, ToothAngle, Involutes.AngleStep, PitchCircleDiameter / 2)); return(Involutes.LinearReduction(points, (float)MaxError)); }
private void InitPointLists() { InvolutePoints = new List <PointF>(ComputeInvolutePoints()); UndercutPoints = new List <PointF>(ComputeUndercutPoints()); DedendumPoints = new List <PointF>(ComputeDedendumCirclePoints()); PointF?intersection = null; if (intersection == null || !intersection.HasValue) { intersection = Involutes.ClosestPoint(InvolutePoints, UndercutPoints); } underCutPoint = intersection.Value; int involuteIdx = Involutes.IndexOfLastPointWithGreaterXVal(InvolutePoints, underCutPoint.X); int undercutIdx = Involutes.IndexOfLastPointWithGreaterXVal(UndercutPoints, underCutPoint.X); InvolutePoints.RemoveRange(involuteIdx + 1, InvolutePoints.Count - involuteIdx - 1); UndercutPoints.RemoveRange(0, undercutIdx + 1); if (CutDiameter > 0 && AdjustPointsForCircularCutter()) { Information += "Undercut and dedendum adjusted for cutter diameter\r\n"; } if (ToothGapAtUndercut < CutDiameter) { Information += $"Cutter dia. {CutDiameter} too wide for tooth gap of {ToothGapAtUndercut}\r\n"; } // TODO: Ideally should use AdjustPointsForCircularCutter return // value to prevent reduction in tooth addendum height used for // backlash management. At present this is not done. The tooth // height is still reduced by the backlash amount, even though // the undercut has increased the dedendum depth to accommodate // the diameter of cutter. AddendumPoints = new List <PointF>(ComputeAddendumCirclePoints()); InvolutePoints = Involutes.LinearReduction(InvolutePoints, (float)MaxError); UndercutPoints = Involutes.LinearReduction(UndercutPoints, (float)MaxError); DedendumPoints = Involutes.LinearReduction(DedendumPoints, (float)MaxError); AddendumPoints = Involutes.LinearReduction(AddendumPoints, (float)MaxError); }
/// <summary> /// Create a hexagonal key shape /// around the inlay so that /// two gears can be married on /// the same centres correctly /// </summary> /// <returns>The list of points /// corresponding to the hex key</returns> private List <PointF> CalculateHexKey() { // Generate the points for one sixth of the key double ctrToFace = KeyWidth / 2; PointF cornerCtr = Involutes.CreatePt( ctrToFace - Gear.CutDiameter / 2, (ctrToFace - Gear.CutDiameter / 2) / Math.Sqrt(3.0)); List <PointF> firstSegment = new List <PointF> { Involutes.CreatePt(ctrToFace, 0), Involutes.CreatePt(ctrToFace, cornerCtr.Y) }; firstSegment.AddRange( Involutes.CirclePoints(0, Math.PI / 3, Involutes.AngleStep, Gear.CutDiameter / 2, cornerCtr)); firstSegment.Add(Involutes.CreatePt(ctrToFace / 2, Math.Sin(Math.PI / 3) * ctrToFace)); firstSegment = Involutes.LinearReduction(firstSegment, (float)Gear.MaxError); return(Enumerable .Range(0, 6) .Select(i => Involutes.RotateAboutOrigin(i * Math.PI / 3.0, firstSegment)) .SelectMany(ep => ep).ToList()); }
/// <summary> /// Given we are meshed with another gear where this and /// the other gear might have a profile shift, calculate /// the contact length for the two gears /// </summary> /// <param name="meshedGear">The gear with which we are /// meshed</param> /// <returns>The length of the path along which the two /// gears are in contact before breaking apart</returns> public double ContactDistanceWithGear(GearParameters meshedGear) { if (!CanMeshWith(meshedGear)) { Information = "Gears have differing modules or pressure angles"; return(0); } double distanceBetweenCentres = PitchCircleDiameter / 2 + meshedGear.PitchCircleDiameter / 2 + Module * (ProfileShift + meshedGear.ProfileShift); double distToPitchPoint = distanceBetweenCentres * BaseCircleDiameter / (BaseCircleDiameter + meshedGear.BaseCircleDiameter); double meshedDistToPitchPoint = distanceBetweenCentres - distToPitchPoint; double contactLength = Math.Sqrt(Involutes.DiffOfSquares(distToPitchPoint, BaseCircleDiameter / 2)) - Math.Sqrt(SquaredUndercutRadius - Involutes.Square(BaseCircleDiameter / 2)); double meshedContactLength = Math.Sqrt(Involutes.DiffOfSquares(meshedDistToPitchPoint, meshedGear.BaseCircleDiameter / 2)) - Math.Sqrt(meshedGear.SquaredUndercutRadius - Involutes.Square(meshedGear.BaseCircleDiameter / 2)); return(contactLength + meshedContactLength); }
private void CalculatePoints() { // Unrotated tooth tip is on the X axis ToothTip = Involutes.CreatePt(PitchCircleDiameter / 2, 0); // Navigate down the slope of the tooth face FaceEnd = Involutes.CreatePt( PitchCircleDiameter / 2 - ToothFaceLength * Math.Cos(UndercutAngle), -ToothFaceLength * Math.Sin(UndercutAngle)); // Now find the centre of the undercut circle UnderCutCentre = Involutes.CreatePt (FaceEnd.X - CutDiameter * Math.Sin(UndercutAngle) / 2, FaceEnd.Y + CutDiameter * Math.Cos(UndercutAngle) / 2); // Find the coordinates of the back tip corner of // the next tooth in an anticlockwise direction BackTip = Involutes.CreatePt(ToothTip.X * Math.Cos(GapAngle), ToothTip.X * Math.Sin(GapAngle)); // Find the coordinates of the tangent to the // undercut circle of a line drawn from the // back of the tooth tip double tipToEndAngle = Math.Asin(CutDiameter / (2 * Involutes.DistanceBetween(BackTip, UnderCutCentre))); double tiptoCtrAngle = Math.Atan2(BackTip.Y - UnderCutCentre.Y, BackTip.X - UnderCutCentre.X); BackAngle = tiptoCtrAngle + Math.PI / 2 - tipToEndAngle; OneToothProfile = ComputeOnePitch(); }
private static double Sqr(double x) => Involutes.Square(x);
private (List <PointF> outer, List <PointF> inner) CalculatePoints() { // Calculate the inside radius of one end of a link, // the two distances for the unequal faces of the // polygon that is the profile for the curvature of // the chiin links around the sprocket, and the angle // between two adjacent links in the chain double r = OuterLinkWidth / 2 - WireThickness; double a = InnerLinkLength + 2 * r; double b = InnerLinkLength - 2 * r; double C = Math.PI * (ToothCount - 1) / (double)ToothCount; // Use the cosine rule to find the distance between // adjacent pairs of links double lSquared = Sqr(a); lSquared += Sqr(b); lSquared -= 2 * a * b * Math.Cos(C); double l = Math.Sqrt(lSquared); // Find the other two angles, A and B at the other two // corners of the same triangle // double A = Math.Acos((lSquared + a * a - b * b) / (2 * l * a)); // double B = Math.PI - A - C; // Now compute the radius from the centre of the // sprocket to one end of the line between adjacent // pairs of links radius = l / (2 * Math.Sin(Math.PI / ToothCount)); // We shall set the origin at the centre of the sprocket. The // point in the middle of a link that is parallel to the // plane of the sprocket lies on the X axis, the link being // parallel to the Y axis. T is the centre of the semicircle // formed by one end of the link PointF t = Involutes.CreatePt( Involutes.RootDiffOfSquares(radius, b / 2), b / 2); // C is the centre of the wire for the next link, lying on a // line from T in the direction of the 3rd link double sinTooth = Math.Sin(Math.PI / ToothCount); double cosTooth = Math.Cos(Math.PI / ToothCount); PointF c = Involutes.CreatePt( t.X - (r - WireThickness / 2) * sinTooth, t.Y + (r - WireThickness / 2) * cosTooth); // U is the point on the second link parallel to the X // axis, closest to the centre of the first link. // V is the point on the second link just past its // curvature due to the wire thickness, where it // becomes parallel to the diagonal face of the sprocket // beyond the first link. PointF u = Involutes.CreatePt(c.X, c.Y - WireThickness / 2); double arcAngle; double arcRadius = CutDiameter / 2; // Assume cutter bigger than wire PointF convexCtr; // Find length to mid point of face on which // perpendicular link lies double oz = Involutes .RootDiffOfSquares(radius, a / 2) - WireThickness / 2; PointF z = Involutes.CreatePt(oz * cosTooth, oz * sinTooth); if (WireThickness >= CutDiameter) { arcRadius = WireThickness / 2; // v = Involutes.CreatePt( // c.X - arcRadius * cosTooth, // c.Y - arcRadius * sinTooth); arcAngle = Math.PI * (0.5 - 1.0 / ToothCount); convexCtr = c; } else { // Coordinate of cutter centre when cutting corner // between parallel and perpendicular links convexCtr = Involutes .CreatePt(u.X, u.Y + arcRadius); // Find the distance from the cutter centre to the sloping face double cPerp = u.X * cosTooth - oz + sinTooth * (u.Y + arcRadius); // Find the angle from the perpendicular to the intersection point arcAngle = Math.Acos(cPerp / arcRadius) + Math.PI / 2 - Math.PI / ToothCount; } // Now plot one chain link worth of profile List <PointF> points = new() { Involutes.CreatePt(t.X + OuterLinkWidth / 2, 0) }; PointF cUpper = Involutes.CreatePt(t.X, u.Y - OuterLinkWidth / 2); points.AddRange(Involutes.CirclePoints( 0, Math.PI / 2, Math.PI / 180, OuterLinkWidth / 2, cUpper)); points.AddRange(Involutes.CirclePoints( 1.5 * Math.PI - arcAngle, 1.5 * Math.PI, Math.PI / 180, arcRadius, convexCtr) .Reverse()); points.Add(z); // Now calculate the recess profile double grooveStartAngle = Math.Acos(WireThickness / OuterLinkWidth); grooveStartAngle = Math.PI * (1.0 + ToothCount) / ToothCount - grooveStartAngle; PointF bt = Involutes.CreatePt(t.X - Backlash * sinTooth, t.Y + Backlash * cosTooth); List <PointF> groovePoints = new(); InnerDiameter = 2 * t.X - OuterLinkWidth; Module = InnerDiameter / ToothCount; groovePoints.Add(Involutes.CreatePt(InnerDiameter / 2 - Backlash * sinTooth, 0)); groovePoints.AddRange(Involutes.CirclePoints( grooveStartAngle, Math.PI, Math.PI / 180, OuterLinkWidth / 2, bt) .Reverse()); groovePoints.Add(z); return(points, groovePoints); }
private IEnumerable <PointF> ComputeDedendumCirclePoints() => Involutes.CirclePoints (-(BacklashAngle + DedendumArcAngle / 2), DedendumArcAngle / 2, Involutes.AngleStep, DedendumCircleDiameter / 2);
/// <summary> /// Once the undercut points and the dedendum points have been calculated, /// but before the number of points are reduced based on an error tolerance, /// any concave parts of the tooth profile may be too sharp cornered for /// a given cutter radius. This method redraws the undercut and dedendum /// profile so that it can accommodate the diameter of cutter we are /// intending to cut the gear with. /// </summary> /// <param name="cutterRadius">Radius of the end mill bit in mm</param> /// <returns>True if the points had to be adjusted, false if the /// curves were all sufficiently shallow that adjustment did /// not take place</returns> private bool AdjustPointsForCircularCutter() { // First find the point at which and end-mill of the specified cutter // radius can no longer cut inside the concave profile of the undercut int i = 0; bool cornerFound = false; PointF cutterCentre = PointF.Empty; while (!cornerFound && i < UndercutPoints.Count - 2) { PointF[] centres = Involutes.CircleCentres(UndercutPoints[i], UndercutPoints[i + 1], CutDiameter / 2); if (centres[0].Y < centres[1].Y) { cutterCentre = centres[0]; } else { cutterCentre = centres[1]; } cornerFound = Involutes.PointInCircle(UndercutPoints[i + 2], cutterCentre, CutDiameter / 2); i++; // i indexes the last point from the undercut point list that we can cut to } // If no adjustment took place, quit here if (!cornerFound) { return(false); } // Copy across the curve that we are able to follow before // deviating from it according to cutter radius UndercutPoints = new List <PointF>(UndercutPoints.Take(i + 1)); // Calculate the angle for the last point in the undercut curve double startAngle = Math.Atan2(UndercutPoints.Last().Y - cutterCentre.Y, UndercutPoints.Last().X - cutterCentre.X); // Find the point on the cutter circle that intersects a line from // its centre to the centre of the gear profile (origin 0,0) double cutterCentreRadius = Math.Sqrt(Involutes.SumOfSquares(cutterCentre.X, cutterCentre.Y)); // Find the end angle for the cutter radius curve. This is the same as // the angle at the origin to the cutter centre, reflected by 180 degrees. double endAngle = Math.Atan2(cutterCentre.Y, cutterCentre.X) + Math.PI; // Add points around the cutter diameter from the last point // of adjustedUndercut, to the point at which it crosses yDedendum (y value). UndercutPoints.AddRange(Involutes.CirclePoints (startAngle, endAngle, Math.PI / 180, CutDiameter / 2, cutterCentre)); // Then add new dedendum circle points round to the y=0 axis, based on // the tangent to the cutter circle at yDedendum. DedendumPoints = new List <PointF>(Involutes.CirclePoints (-(BacklashAngle + endAngle - Math.PI), endAngle - Math.PI, Involutes.AngleStep, cutterCentreRadius - CutDiameter / 2)); // Record the new dedendum diameter since the cutter has reduced it cutterAdjustedDedendumDircleDiameter = 2 * cutterCentreRadius - CutDiameter; // Undercut points and dedendum points were rewritten. Return // true to flag this fact. return(true); }
/// <summary> /// Calculate the points that form the inlaid circle /// </summary> /// <returns>Sequence of points that make up the bearing /// inlay in the middle of the gear</returns> private List <PointF> CalculateInlay() => Involutes.LinearReduction(Involutes.CirclePoints (-Math.PI, Math.PI, Involutes.AngleStep, InlayDiameter / 2).ToList(), (float)Gear.MaxError);
/// <summary> /// Calculate the sequence of cutout point sequences that make /// the spokes in the gear. Used to try and reduce the inertia /// of larger gears. /// </summary> /// <param name="spokes">The number of spokes to put between /// the gear hub and the teeth-bearing perimeter</param> /// <returns>The point lists that each make up the /// outline of the cutouts</returns> private List <List <PointF> > CalculateCutouts(int spokes) { List <List <PointF> > cutouts = new List <List <PointF> >(); if (spokes < 3) { return(cutouts); } // Set some design constants double cornerRadius = Gear.Module; double spokeThickness = 2.0 * Gear.Module; double minHubDiameter = 8 * Gear.Module; // Calculate the minimum hub diameter for a given number of // spokes, a specified spoke thickness and corner radius. double hubDiameter = (spokeThickness + 2 * cornerRadius) / Math.Sin(Math.PI / spokes) - cornerRadius; if (hubDiameter < minHubDiameter) { hubDiameter = minHubDiameter; } // Calculate the corner at the outer end of one side of a spoke. // For this reference spoke we assume the spoke runs along the // positive X axis. We shall rotate it for other spokes. double rimDiameter = Gear.InnerDiameter - 2.0 * spokeThickness; if (rimDiameter < hubDiameter + 4 * cornerRadius) { return(cutouts); } double cornerCentreY = spokeThickness / 2 + cornerRadius; double rimCornerCentreX = Math.Sqrt (Involutes.DiffOfSquares(rimDiameter / 2 - cornerRadius, cornerCentreY)); PointF rimCornerCentre = Involutes.CreatePt(rimCornerCentreX, cornerCentreY); double angleAtRim = Math.Atan2(cornerCentreY, rimCornerCentreX); IEnumerable <PointF> outerCorner = Involutes.CirclePoints (-Math.PI / 2, angleAtRim, Involutes.AngleStep, cornerRadius, rimCornerCentre); // Calculate the corner at the inner end of a spoke. //double hubCornerCentreX = Math.Sqrt(Square(hubDiameter / 2 + cornerRadius) // - Square(cornerCentreY)); double hubCornerCentreX = Math.Sqrt (Involutes.DiffOfSquares(hubDiameter / 2 + cornerRadius, cornerCentreY)); PointF hubCornerCentre = Involutes.CreatePt(hubCornerCentreX, cornerCentreY); double angleAtHub = Math.Atan2(cornerCentreY, hubCornerCentreX); IEnumerable <PointF> innerCorner = Involutes.CirclePoints (Math.PI + angleAtHub, 3.0 * Math.PI / 2, Involutes.AngleStep, cornerRadius, hubCornerCentre); // Calculate the outer rim circle segment IEnumerable <PointF> outerRimSegment = Involutes.CirclePoints (angleAtRim, 2 * Math.PI / spokes - angleAtRim, Involutes.AngleStep, rimDiameter / 2); // Calculate the hub circle segment IEnumerable <PointF> hubSegment = Involutes.CirclePoints (angleAtHub, 2 * Math.PI / spokes - angleAtHub, Involutes.AngleStep, hubDiameter / 2); // Calculate the far side of the cutout. Reflect the inner spoke // across the X axis, and reverse its points. Then rotate it round the gear // by the angle between adjacent spokes. This gives us the correct // list of points. IEnumerable <PointF> nearSide = innerCorner.Concat(outerCorner); IEnumerable <PointF> farSide = GearParameters.ReflectY(nearSide) .Select(p => Involutes.RotateAboutOrigin(2 * Math.PI / spokes, p)) .Reverse(); // Now create the lists of points for each of the cut outs List <PointF> cutout = new List <PointF>(); cutout.AddRange(nearSide); cutout.AddRange(outerRimSegment); cutout.AddRange(farSide); cutout.AddRange(hubSegment.Reverse()); cutout = Involutes.LinearReduction(cutout, (float)Gear.MaxError); cutouts.Add(cutout); for (int i = 1; i < spokes; i++) { cutouts.Add(new List <PointF>(cutout.Select (p => Involutes.RotateAboutOrigin(2 * Math.PI * i / spokes, p)))); } return(cutouts); }
private void CalculatePoints() { List <PointF> points = new List <PointF>(); double innerRadius = InnerDiameter / 2; double cutterRadius = CutDiameter / 2; // Calculate the cutter centre for the tooth // of the ratchet gear at the innermost end double m = -ToothDepth / (innerRadius * ToothAngle); double xs = Math.Sqrt(cutterRadius * cutterRadius / (1 + m * m)); double ys = m * xs; PointF cutterCentre = Involutes.CreatePt(innerRadius + xs, ys); // Find the start angle and the end angle for the curve. The curve starts // at the X axis and heads towards negative Y. The end angle is where the // curve becomes tangential to a radial from the centre of the ratchet. // If the cutter centre lies outside the pitch circle, then the ratchet // will not be able to have a tooth at right angles to the direction // of rotation, and the ratchet is able to skip teeth under high torque. // Nonetheless, we calculate the tooth shape and return a warning. double radiusOfCutterCentre = Involutes.DistanceBetween(PointF.Empty, cutterCentre); double cutterCentreAngle = Math.Asin(-ys / radiusOfCutterCentre); double tangentAngle = cutterCentreAngle + Math.Asin(cutterRadius / radiusOfCutterCentre); double endAngle = Math.PI - Math.Atan2(-ys, xs); double startAngle = 3 * Math.PI / 2 - tangentAngle; points.Add(Involutes.RotateAboutOrigin (-ToothAngle, PointAtAngle(ToothAngle - tangentAngle))); // Add the points for the cutter curve points.AddRange(Involutes.CirclePoints (endAngle, startAngle, Involutes.AngleStep, cutterRadius, cutterCentre) .Reverse()); // Check that the radius of the cutter was not too great to // provide a latch at right angles to the radial from the centre // of the ratchet. double actualInnerRadius = Involutes.DistanceBetween(PointF.Empty, cutterCentre) - cutterRadius; double actualOuterRadius = Involutes.DistanceBetween(PointF.Empty, points[0]); if (Involutes.DistanceBetween(PointF.Empty, points[1]) > actualOuterRadius) { Information += "Cutter diameter too great for locking ratchet. Setting it to zero.\r\n"; CutDiameter = 0; CalculatePoints(); return; } // Now add the slope for (double angle = 0; angle < ToothAngle - tangentAngle; angle += Involutes.AngleStep) { points.Add(PointAtAngle(angle)); } OneToothProfile = Involutes.LinearReduction(points, (float)MaxError); Information += $"Actual inner diameter: {2 * actualInnerRadius:N2}, " + $"actual outer diameter: {2 * actualOuterRadius:N2}\r\n"; }
/// <summary> /// Generate the sequence of points describing the /// shape of a single tooth profile /// </summary> /// <param name="gap">Which tooth profile we want. /// For gap = 0, we generate the tooth whose /// tip lies on the positive X axis. Teeth rotate /// anticlockwise from there for increasing /// values of gap.</param> /// <returns>The set of points describing the /// profile of the selected tooth.</returns> public IEnumerable <PointF> ToothProfile(int gap) => Involutes.RotateAboutOrigin ((gap % ToothCount) * ToothAngle, OneToothProfile);
private IEnumerable <PointF> ComputeAddendumCirclePoints() => Involutes.CirclePoints (GapWidthAngleAtPitchCircle / 2 + ToothTipOffset, ToothAngle - GapWidthAngleAtPitchCircle / 2 - ToothTipOffset - BacklashAngle, Involutes.AngleStep, AddendumCircleDiameter / 2);
/// <summary> /// Reflect a sequence of points to the opposite side of the X axis /// </summary> /// <param name="points">The points to be reflected</param> /// <returns>The reflected points</returns> public static IEnumerable <PointF> ReflectY(IEnumerable <PointF> points) => points.Select(p => Involutes.CreatePt(p.X, -p.Y));
/// <summary> /// Given a list of points, rotate them about the origin /// by a whole number of tooth angles /// </summary> /// <param name="points">The points to rotate about the origin</param> /// <param name="gap">The gap number for the tooth, in the /// range 0 to ToothCount - 1</param> /// <returns>The rotated set of points</returns> private IEnumerable <PointF> RotateByTeeth(IEnumerable <PointF> points, int gap) => Involutes.RotateAboutOrigin((gap % ToothCount) * ToothAngle, points);