Esempio n. 1
0
        /// <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());
        }
Esempio n. 2
0
        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)));
        }
Esempio n. 3
0
        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)));
        }
Esempio n. 4
0
        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));
            }
        }
Esempio n. 5
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));
            }
        }
Esempio n. 6
0
        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));
        }
Esempio n. 8
0
        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);
        }
Esempio n. 9
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());
        }
Esempio n. 10
0
        /// <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();
        }
Esempio n. 12
0
 private static double Sqr(double x) => Involutes.Square(x);
Esempio n. 13
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);
        }
Esempio n. 14
0
 private IEnumerable <PointF> ComputeDedendumCirclePoints()
 => Involutes.CirclePoints
     (-(BacklashAngle + DedendumArcAngle / 2), DedendumArcAngle / 2,
     Involutes.AngleStep, DedendumCircleDiameter / 2);
Esempio n. 15
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);
        }
Esempio n. 16
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);
Esempio n. 17
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);
        }
Esempio n. 18
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";
        }
Esempio n. 19
0
        /// <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);
Esempio n. 20
0
 private IEnumerable <PointF> ComputeAddendumCirclePoints()
 => Involutes.CirclePoints
     (GapWidthAngleAtPitchCircle / 2 + ToothTipOffset,
     ToothAngle - GapWidthAngleAtPitchCircle / 2 - ToothTipOffset - BacklashAngle,
     Involutes.AngleStep, AddendumCircleDiameter / 2);
Esempio n. 21
0
        /// <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));
Esempio n. 22
0
        /// <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);