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)); }
/// <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()); }
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); }
/// <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); }
/// <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);
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"; }
private IEnumerable <PointF> ComputeAddendumCirclePoints() => Involutes.CirclePoints (GapWidthAngleAtPitchCircle / 2 + ToothTipOffset, ToothAngle - GapWidthAngleAtPitchCircle / 2 - ToothTipOffset - BacklashAngle, Involutes.AngleStep, AddendumCircleDiameter / 2);
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); }