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));
        }
Example #2
0
        /// <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());
        }
Example #3
0
        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);
        }
Example #4
0
        /// <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);
        }
Example #5
0
        /// <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);
Example #6
0
        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";
        }
Example #7
0
 private IEnumerable <PointF> ComputeAddendumCirclePoints()
 => Involutes.CirclePoints
     (GapWidthAngleAtPitchCircle / 2 + ToothTipOffset,
     ToothAngle - GapWidthAngleAtPitchCircle / 2 - ToothTipOffset - BacklashAngle,
     Involutes.AngleStep, AddendumCircleDiameter / 2);
Example #8
0
 private IEnumerable <PointF> ComputeDedendumCirclePoints()
 => Involutes.CirclePoints
     (-(BacklashAngle + DedendumArcAngle / 2), DedendumArcAngle / 2,
     Involutes.AngleStep, DedendumCircleDiameter / 2);
Example #9
0
        /// <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);
        }