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))); }
/// <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 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 (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); }
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> /// 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));