public override void BuildProcessing(ICommandGenerator generator) { const int AngleA = 45; var tactileTechProcess = (TactileTechProcess)TechProcess; var contour = tactileTechProcess.GetContour(); var contourPoints = contour.GetPolyPoints().ToArray(); var ray = new Ray { BasePoint = ProcessingAngle == 45 ? contourPoints.Last() : contourPoints.First(), UnitDir = Vector3d.XAxis.RotateBy(Graph.ToRad(ProcessingAngle), Vector3d.ZAxis) }; var passDir = ray.UnitDir.GetPerpendicularVector(); if (ProcessingAngle >= 90) { passDir = passDir.Negate(); } ray.BasePoint += passDir * (tactileTechProcess.BandStart1.Value - tactileTechProcess.BandSpacing.Value); var step = tactileTechProcess.BandWidth.Value + tactileTechProcess.BandSpacing.Value; var tactileParams = tactileTechProcess.TactileTechProcessParams; var engineSide = ProcessingAngle < 90 ? Side.Right : Side.Left; while (true) { var points = new Point3dCollection(); ray.IntersectWith(contour, Intersect.ExtendThis, new Plane(), points, IntPtr.Zero, IntPtr.Zero); if (points.Count == 2 && !points[0].IsEqualTo(points[1])) { var vector = (points[1] - points[0]).GetNormal() * tactileParams.Departure; var startPoint = points[0] - vector - Vector3d.ZAxis * tactileParams.Depth; var endPoint = points[1] + vector - Vector3d.ZAxis * tactileParams.Depth; if (generator.IsUpperTool) { generator.Move(startPoint.X, startPoint.Y, angleC: BuilderUtils.CalcToolAngle(ProcessingAngle.ToRad(), engineSide), angleA: AngleA); } generator.Cutting(startPoint, endPoint, CuttingFeed, tactileParams.TransitionFeed); } else if (step > 0) { ray.BasePoint += passDir * tactileTechProcess.BandSpacing.Value; step = -step; generator.Uplifting(); engineSide = engineSide.Opposite(); } else { break; } ray.BasePoint += passDir * step; } }
public override void BuildProcessing(ICommandGenerator generator) { const int CornerIndentIncrease = 5; var techProcess = (SawingTechProcess)TechProcess; var curve = ProcessingArea.GetCurve(); var thickness = techProcess.Thickness.Value; var toolDiameter = techProcess.Tool.Diameter; var engineSide = Side.None; double offsetArc = 0; double angleA = 0; if (techProcess.MachineType == MachineType.ScemaLogic) { AngleA = 0; } switch (curve) { case Arc arc: CalcArc(arc); break; case Line line: CalcLine(line); break; case Polyline polyline: CalcPolyline(polyline); break; default: throw new InvalidOperationException($"Кривая типа {curve.GetType()} не может быть обработана."); } var outerSideSign = OuterSide == Side.Left ^ curve is Line ? -1 : 1; var offsetCoeff = Math.Tan(angleA) * outerSideSign; var depthCoeff = 1 / Math.Cos(angleA); var toolThickness = techProcess.Tool.Thickness.Value * depthCoeff; var compensation = (offsetArc + (engineSide == OuterSide ^ techProcess.MachineType == MachineType.Donatoni ? toolThickness : 0)) * outerSideSign; var shift = angleA > 0 ? -thickness * offsetCoeff : 0; var sumIndent = CalcIndent(thickness) * (Convert.ToInt32(IsExactlyBegin) + Convert.ToInt32(IsExactlyEnd)); if (sumIndent >= curve.Length()) { if (AngleA != 0) { throw new InvalidOperationException("Расчет намечания выполняется только при нулевом вертикальном угле."); } var point = Scheduling(); var angle = BuilderUtils.CalcToolAngle((curve.EndPoint - curve.StartPoint).ToVector2d().Angle, engineSide); generator.Move(point.X, point.Y, angleC: angle); generator.Cutting(point.X, point.Y, point.Z, techProcess.PenetrationFeed); return; } var modes = SawingModes.ConvertAll(p => new CuttingMode { Depth = p.Depth, DepthStep = p.DepthStep, Feed = p.Feed }); var passList = BuilderUtils.GetPassList(modes, thickness, !ProcessingArea.ObjectId.IsLine()).ToList(); Curve toolpathCurve = null; foreach (var item in passList) { CreateToolpath(item.Key, compensation + shift + item.Key * offsetCoeff); if (generator.IsUpperTool) { var point = engineSide == Side.Right ^ (passList.Count() % 2 == 1) ? toolpathCurve.EndPoint : toolpathCurve.StartPoint; var vector = Vector3d.ZAxis * (item.Key + generator.ZSafety); if (angleA != 0) { vector = vector.RotateBy(outerSideSign * angleA, ((Line)toolpathCurve).Delta) * depthCoeff; } var p0 = point + vector; var angleC = BuilderUtils.CalcToolAngle(toolpathCurve, point, engineSide); generator.Move(p0.X, p0.Y, angleC: angleC, angleA: Math.Abs(AngleA)); if (techProcess.MachineType == MachineType.ScemaLogic) { generator.Command("28;;XYCZ;;;;;;", "Цикл"); } } generator.Cutting(toolpathCurve, item.Value, techProcess.PenetrationFeed, engineSide); } generator.Uplifting(Vector3d.ZAxis.RotateBy(outerSideSign * angleA, toolpathCurve.EndPoint - toolpathCurve.StartPoint) * (thickness + generator.ZSafety) * depthCoeff); if (!IsExactlyBegin || !IsExactlyEnd) { var gashCurve = curve.GetOffsetCurves(shift)[0] as Curve; if (!IsExactlyBegin) { Acad.CreateGash(gashCurve, gashCurve.StartPoint, OuterSide, thickness * depthCoeff, toolDiameter, toolThickness); } if (!IsExactlyEnd) { Acad.CreateGash(gashCurve, gashCurve.EndPoint, OuterSide, thickness * depthCoeff, toolDiameter, toolThickness); } gashCurve.Dispose(); } // Local func ------------------------ void CalcArc(Arc arc) { AngleA = 0; var startSide = Math.Sign(Math.Cos(arc.StartAngle.Round(3))); var endSide = Math.Sign(Math.Cos(arc.EndAngle.Round(3))); var cornersOneSide = Math.Sign(startSide * endSide); if (arc.TotalAngle.Round(3) > Math.PI && cornersOneSide > 0) { throw new InvalidOperationException("Обработка дуги невозможна - дуга пересекает углы 90 и 270 градусов."); } if (cornersOneSide < 0) // дуга пересекает углы 90 или 270 градусов { if (techProcess.MachineType == MachineType.ScemaLogic) { throw new InvalidOperationException("Обработка дуги невозможна - дуга пересекает угол 90 или 270 градусов."); } engineSide = startSide > 0 ? Side.Left : Side.Right; } if (OuterSide == Side.Left) // внутренний рез дуги { if (techProcess.MachineType == MachineType.Donatoni && engineSide != Side.Left) // подворот диска при вн. резе дуги { engineSide = Side.Right; //var comp = arc.Radius - Math.Sqrt(arc.Radius * arc.Radius - thickness * (toolDiameter - thickness)); //AngleA = Math.Atan2(comp, thickness).ToDeg(); var R = arc.Radius; var t = thickness; var d = toolDiameter; var comp = (2 * R * t * t - Math.Sqrt(-d * d * d * d * t * t + 4 * d * d * R * R * t * t + d * d * t * t * t * t)) / (d * d - 4 * R * R); AngleA = -Math.Atan2(comp, thickness).ToDeg(); } else { offsetArc = arc.Radius - Math.Sqrt(arc.Radius * arc.Radius - thickness * (toolDiameter - thickness)); } } if (engineSide == Side.None) { engineSide = (startSide + endSide) > 0 ? Side.Right : Side.Left; } } void CalcLine(Line line) { angleA = AngleA.ToRad(); engineSide = AngleA == 0 ? BuilderUtils.CalcEngineSide(line.Angle) : AngleA > 0 ? OuterSide : OuterSide.Opposite(); } void CalcPolyline(Polyline polyline) { int sign = 0; for (int i = 0; i < polyline.NumberOfVertices; i++) { var point = polyline.GetPoint3dAt(i); var s = Math.Sign(Math.Sin(polyline.GetTangent(point).Angle.Round(6))); if (s == 0) { continue; } if (sign == 0) { sign = s; continue; } var bulge = polyline.GetBulgeAt(i - 1); if (bulge == 0) { bulge = polyline.GetBulgeAt(i); } if (s != sign) { if (techProcess.MachineType == MachineType.ScemaLogic) { throw new InvalidOperationException("Обработка полилинии невозможна - кривая пересекает углы 90 или 270 градусов."); } var side = sign > 0 ^ bulge < 0 ? Side.Left : Side.Right; if (engineSide != Side.None) { if (engineSide != side) { throw new InvalidOperationException("Обработка полилинии невозможна."); } } else { engineSide = side; } sign = s; } else if (Math.Abs(bulge) > 1) { throw new InvalidOperationException("Обработка невозможна - дуга полилинии пересекает углы 90 и 270 градусов."); } } if (engineSide == Side.None) { engineSide = BuilderUtils.CalcEngineSide(polyline.GetTangent(polyline.StartPoint).Angle); } } void CreateToolpath(double depth, double offset) { toolpathCurve = curve.GetOffsetCurves(offset)[0] as Curve; toolpathCurve.TransformBy(Matrix3d.Displacement(-Vector3d.ZAxis * depth)); if (!IsExactlyBegin && !IsExactlyEnd) { return; } var indent = CalcIndent(depth * depthCoeff); switch (toolpathCurve) { case Line l: if (IsExactlyBegin) { l.StartPoint = l.GetPointAtDist(indent); } if (IsExactlyEnd) { l.EndPoint = l.GetPointAtDist(l.Length - indent); } break; case Arc a: var indentAngle = indent / ((Arc)curve).Radius; if (IsExactlyBegin) { a.StartAngle = a.StartAngle + indentAngle; } if (IsExactlyEnd) { a.EndAngle = a.EndAngle - indentAngle; } break; case Polyline p: if (IsExactlyBegin) { p.SetPointAt(0, p.GetPointAtDist(indent).ToPoint2d()); } //p.StartPoint = p.GetPointAtDist(indent); if (IsExactlyEnd) { //p.EndPoint = p.GetPointAtDist(p.Length - indent); p.SetPointAt(p.NumberOfVertices - 1, p.GetPointAtDist(p.Length - indent).ToPoint2d()); } break; } ; } double CalcIndent(double depth) => Math.Sqrt(depth * (toolDiameter - depth)) + CornerIndentIncrease; /// <summary> /// Расчет точки намечания /// </summary> Point3d Scheduling() { var vector = curve.EndPoint - curve.StartPoint; var depth = thickness; var point = Point3d.Origin; if (IsExactlyBegin && IsExactlyEnd) { var l = vector.Length - 2 * CornerIndentIncrease; depth = (toolDiameter - Math.Sqrt(toolDiameter * toolDiameter - l * l)) / 2; point = curve.StartPoint + vector / 2; } else { var indentVector = vector.GetNormal() * (Math.Sqrt(depth * (toolDiameter - depth)) + CornerIndentIncrease); point = IsExactlyBegin ? curve.StartPoint + indentVector : curve.EndPoint - indentVector; Acad.CreateGash(curve, IsExactlyBegin ? curve.EndPoint : curve.StartPoint, OuterSide, depth, toolDiameter, toolThickness, point); } return(point + vector.GetPerpendicularVector().GetNormal() * compensation - Vector3d.ZAxis * depth); } }
public override void BuildProcessing(ICommandGenerator generator) { if (PassList?.Any() != true) { CalcPassList(); } var tactileTechProcess = (TactileTechProcess)TechProcess; var thickness = TechProcess.Tool.Thickness.Value; var contour = tactileTechProcess.GetContour(); var contourPoints = contour.GetPolyPoints().ToArray(); var basePoint = ProcessingAngle == 45 ? contourPoints[3] : contourPoints[0]; var ray = new Ray { BasePoint = basePoint, UnitDir = Vector3d.XAxis.RotateBy(ProcessingAngle.ToRad(), Vector3d.ZAxis) }; var passDir = ray.UnitDir.GetPerpendicularVector(); if (ProcessingAngle >= 90) { passDir = passDir.Negate(); } double offset = BandStart - BandSpacing - BandWidth; var size = (contourPoints[ProcessingAngle == 0 ? 1 : ProcessingAngle == 90 ? 3 : 2] - contourPoints[0]).Length; if (IsEdgeProcessing) { if (ProcessingAngle == 45 ^ TechProcess.MachineType == MachineType.Donatoni) { Cutting(0.8 * thickness, CuttingFeed, -thickness); } if (offset > 0) { var count = (int)Math.Ceiling(offset / (0.8 * thickness)); Algorithms.Range(-0.8 * thickness * count, -0.1, 0.8 * thickness).ForEach(p => Cutting(offset + PassList[0].Pos + p, CuttingFeed)); } } do { foreach (var pass in PassList) { Cutting(offset + pass.Pos, pass.CuttingType == CuttingType.Roughing ? CuttingFeed : FeedFinishing); } offset += BandWidth + BandSpacing; }while (offset < size); if (IsEdgeProcessing) { if (offset - BandSpacing < size) { Algorithms.Range(offset - BandSpacing, size, 0.8 * thickness).ForEach(p => Cutting(p, CuttingFeed)); } if (ProcessingAngle == 45 ^ TechProcess.MachineType == MachineType.ScemaLogic) { Cutting(size - 0.8 * thickness, CuttingFeed, thickness); } } ray.Dispose(); contour.Dispose(); void Cutting(double pos, int feed, double s = 0) { if (pos < 0 || pos > size) { return; } ray.BasePoint = basePoint + passDir * pos; var points = new Point3dCollection(); ray.IntersectWith(contour, Intersect.ExtendThis, new Plane(), points, IntPtr.Zero, IntPtr.Zero); if (points.Count == 2) { var vector = (points[1] - points[0]).GetNormal() * tactileTechProcess.TactileTechProcessParams.Departure; var startPoint = points[0] + passDir * s - vector - Vector3d.ZAxis * Depth; var endPoint = points[1] + passDir * s + vector - Vector3d.ZAxis * Depth; if (generator.IsUpperTool) { generator.Move(startPoint.X, startPoint.Y, angleC: BuilderUtils.CalcToolAngle(ProcessingAngle.ToRad())); } generator.Cutting(startPoint, endPoint, feed, tactileTechProcess.TactileTechProcessParams.TransitionFeed); } } }
public override void BuildProcessing(MillingCommandGenerator generator) { var railBase = TechProcess.Rail?.GetCurve() ?? new Line(Point3d.Origin, Point3d.Origin + Vector3d.XAxis * TechProcess.Length.Value); var profile = (Profile ?? TechProcess.ProcessingArea[0]).GetCurve(); var processSide = ChangeEngineSide ? -1 : 1; CreateProfile3D(profile, railBase, 1); //var processSide = ChangeProcessSide ? -1 : 1; //CreateProfile3D(profile, railBase, processSide); var rail = CreateDepartureRail(railBase, Departure); if (Delta != 0) { var profileOffset = (Curve)profile.GetOffsetCurves(Delta)[0]; profile = profileOffset.EndPoint.GetAsVector().Length < profile.EndPoint.GetAsVector().Length ? (Curve)profile.GetOffsetCurves(-Delta)[0] : profileOffset; CreateProfile3D(profile, railBase, processSide); } if (!(railBase is Line)) { processSide *= -1; } if (railBase.IsNewObject) { railBase.Dispose(); } var side = BuilderUtils.CalcEngineSide(rail.GetFirstDerivative(0).GetAngleTo(Vector3d.XAxis)); if (ChangeEngineSide) { side = SideExt.Opposite(side); } var isMinToolCoord = IsA90 ? TechProcess.MachineType.Value != MachineType.ScemaLogic : side == Side.Right ^ TechProcess.MachineType.Value == MachineType.ScemaLogic ^ ChangeProcessSide; generator.CuttingFeed = CuttingFeed; generator.SmallFeed = TechProcess.PenetrationFeed; generator.EngineSide = side; Curve outletCurve = null; if (IsA90 && IsOutlet) { outletCurve = rail.GetOffsetCurves(TechProcess.ZSafety * processSide)[0] as Curve; var angleC = BuilderUtils.CalcToolAngle(outletCurve, outletCurve.StartPoint, side); generator.Move(outletCurve.StartPoint.X, outletCurve.StartPoint.Y, angleC: angleC, angleA: 90); } var angleA = IsA90 ? 90 : 0; var index = IsA90 ? 1 : 0; var points = BuilderUtils.GetProcessPoints(profile, index, StepPass, TechProcess.Tool.Thickness.Value, isMinToolCoord, FirstPass, LasttPass, IsExactlyBegin, IsExactlyEnd, IsProfileStep); foreach (var point in points) { var end = Math.Max(point[1 - index], PenetrationEnd ?? double.MinValue); var count = 1; var penetrationStepCalc = 0D; if (PenetrationStep.GetValueOrDefault() > 0 && PenetrationBegin.GetValueOrDefault() > end) { var allPenetration = PenetrationBegin.Value - end; count = (int)Math.Ceiling(allPenetration / PenetrationStep.Value); penetrationStepCalc = allPenetration / count; } if (IsA90 && IsOutlet && generator._isEngineStarted) { generator.Transition(z: point[index]); } var coords = Enumerable.Range(1, count).Select(p => end + (count - p) * penetrationStepCalc).ToList(); if (IsA90) { coords.ForEach(p => generator.Cutting(rail, processSide * p, point[index], angleA: angleA)); } else { coords.ForEach(p => generator.Cutting(rail, processSide * point[index], p, angleA: angleA)); } if (IsOutlet) { if (IsA90) { var pt = outletCurve.GetClosestPoint(generator.ToolPosition.Point); generator.Move(pt.X, pt.Y); } else { generator.Uplifting(); } } } rail.Dispose(); }
public override void BuildProcessing(MillingCommandGenerator generator) { var railBase = TechProcess.Rail?.GetCurve() ?? new Line(Point3d.Origin, Point3d.Origin + Vector3d.XAxis * TechProcess.Length.Value); var profile = (Profile ?? TechProcess.ProcessingArea[0]).GetCurve(); var processSide = ChangeProcessSide ? 1 : -1; CreateProfile3D(profile, railBase, processSide); var rail = CreateDepartureRail(railBase, Departure); if (Delta != 0) { var profileOffset = (Curve)profile.GetOffsetCurves(Delta)[0]; profile = profileOffset.EndPoint.GetAsVector().Length < profile.EndPoint.GetAsVector().Length ? (Curve)profile.GetOffsetCurves(-Delta)[0] : profileOffset; CreateProfile3D(profile, railBase, processSide); } var offsetSign = processSide * (railBase is Line ? 1 : -1); if (railBase.IsNewObject) { railBase.Dispose(); } var side = BuilderUtils.CalcEngineSide(rail.GetFirstDerivative(0).GetAngleTo(Vector3d.XAxis)); if (ChangeEngineSide) { side = SideExt.Opposite(side); } var isMinToolCoord = IsA90 ? TechProcess.MachineType.Value != MachineType.ScemaLogic : side == Side.Right ^ TechProcess.MachineType.Value == MachineType.ScemaLogic ^ ChangeProcessSide; generator.CuttingFeed = CuttingFeed; generator.SmallFeed = TechProcess.PenetrationFeed; generator.EngineSide = side; Curve outletCurve = null; if (IsA90 && IsOutlet) { outletCurve = rail.GetOffsetCurves(TechProcess.ZSafety * offsetSign)[0] as Curve; var angleC = BuilderUtils.CalcToolAngle(outletCurve, outletCurve.StartPoint, side); generator.Move(outletCurve.StartPoint.X, outletCurve.StartPoint.Y, angleC: angleC, angleA: 90); } var angleA = IsA90 ? 90 : 0; var index = IsA90 ? 1 : 0; var profilePoints = BuilderUtils.GetProcessPoints(profile, index, ProfileStep, TechProcess.Tool.Thickness.Value, isMinToolCoord, ProfileBegin, ProfileEnd, IsExactlyBegin, IsExactlyEnd, true); var profilePline = new Polyline(); Enumerable.Range(0, profilePoints.Count).ForEach(i => profilePline.AddVertexAt(i, profilePoints[i], 0, 0, 0)); var railLength = rail.Length(); Acad.SetLimitProgressor((int)(railLength / LongStep)); for (double dist = 0; dist < railLength; dist += LongStep) { Acad.ReportProgressor(); var point = rail.GetPointAtDist(dist); var angleC = BuilderUtils.CalcToolAngle(rail, point, side); var passPline = CalcPassPline(rail, point, profilePline, processSide); generator.Cutting(passPline, CuttingFeed, TechProcess.PenetrationFeed, angleC: angleC, angleA: angleA); if (IsOutlet) { if (IsA90) { var pt = outletCurve.GetClosestPoint(generator.ToolPosition.Point); generator.Move(pt.X, pt.Y); } else { generator.Uplifting(); } } } rail.Dispose(); }
public void BuildProcessingOld(MillingCommandGenerator generator) { var sectionProfile = (SectionProfileTechProcess)TechProcess; var toolThickness = TechProcess.Tool.Thickness.Value; var rail = sectionProfile.Rail != null?sectionProfile.Rail.GetCurve() as Line : new Line(Point3d.Origin, new Point3d(sectionProfile.Length.Value, 0, 0)); var railVector = rail.Delta.GetNormal(); var passVector = rail.Delta; var crossVector = railVector.RotateBy(-Math.PI / 2, Vector3d.ZAxis); var startPass = rail.StartPoint; var shift = TechProcess.MachineType == MachineType.Donatoni ^ BuilderUtils.CalcEngineSide(rail.Angle) == Side.Left ? toolThickness : 0; if (rail.IsNewObject) { rail.Dispose(); } var profile = sectionProfile.ProcessingArea[0].GetCurve() as Polyline; if (profile == null) { throw new Exception("Профиль должен быть полилинией"); } if (Delta != 0) { profile = (Polyline)profile.GetOffsetCurves(Delta)[0]; } var endX = Math.Max(profile.StartPoint.X, profile.EndPoint.X); var profilePoints = profile.GetPolylineFitPoints(ProfileStep).Select(p => new Point2d(endX - p.X, p.Y)).ToList(); if (profilePoints[0].X > Consts.Epsilon) { profilePoints.Reverse(); } profilePoints = profilePoints .TakeWhile(p => p.X < toolThickness) .Select(p => p - Vector2d.XAxis * toolThickness) .Concat(profilePoints) .ToList(); var passPoints = profilePoints.Select((p, i) => { var y = p.Y; for (int j = i + 1; j < profilePoints.Count && profilePoints[j].X - profilePoints[i].X < toolThickness; j++) { if (profilePoints[j].Y > y) { y = profilePoints[j].Y; } } return(new Point2d(p.X, y)); }) .ToList(); if (Departure > 0) { passPoints.Insert(0, new Point2d(-Departure, passPoints.First().Y)); passPoints.Add(new Point2d(passPoints.Last().X + Departure, passPoints.Last().Y)); } var sp = startPass + crossVector * (passPoints[0].X + shift) + passPoints[0].Y * Vector3d.ZAxis; generator.Move(sp.X, sp.Y, angleC: BuilderUtils.CalcToolAngle(railVector.GetAngleTo(Vector3d.XAxis))); var passPointsDirect = new List <Point2d>[2] { passPoints, Enumerable.Reverse(passPoints).ToList() }; int direct = 0; Acad.SetLimitProgressor((int)(passVector.Length / LongStep)); for (double x = 0; x < passVector.Length; x += LongStep) { Acad.ReportProgressor(); passPointsDirect[direct].ForEach(p => generator.GCommand(CommandNames.Cutting, 1, point: startPass + crossVector * (p.X + shift) + p.Y * Vector3d.ZAxis + x * railVector, feed: CuttingFeed)); direct = 1 - direct; } }
protected override void BuildProcessing(ICommandGenerator generator) { var rail = Rail != null?Rail.GetCurve() as Line : new Line(Point3d.Origin, new Point3d(Length.Value, 0, 0)); var startRail = rail.StartPoint; var railVector = rail.Delta.GetNormal(); if (rail.Angle >= Math.PI) { startRail = rail.EndPoint; railVector = railVector.Negate(); } var startPass = startRail - railVector * Departure; var passVector = railVector * (rail.Length + 2 * Departure); var railAngle = railVector.GetAngleTo(Vector3d.XAxis); if (Rail == null) { rail.Dispose(); } var profile = ProcessingArea[0].GetCurve(); var profileLength = profile.Length(); startPass = new Point3d(startPass.X, startPass.Y - profile.StartPoint.X, profile.StartPoint.Y); generator.ZSafety += profile.StartPoint.Y; double dist = 0; var engineSide = Side.None; double angleC; do { var point = profile.GetPointAtDist(dist); var tangent = profile.GetFirstDerivative(point); var angleA = Math.Abs(tangent.GetAngleTo(Vector3d.XAxis).ToDeg()); var side = tangent.Y < 0 ? Side.Left : Side.Right; if (engineSide != side) { engineSide = side; angleC = BuilderUtils.CalcToolAngle(railAngle, side); if (!generator.IsUpperTool) { generator.Uplifting(); } var sp = GetStartPoint(point, tangent); generator.Move(sp.X, sp.Y, angleC: angleC, angleA: angleA); } var startPoint = GetStartPoint(point, tangent); generator.Cutting(startPoint, startPoint + passVector, CuttingFeed, PenetrationFeed, angleA: angleA); dist += Step; }while (dist < profileLength); generator.Uplifting(); Point3d GetStartPoint(Point3d point, Vector3d tangent) { if (engineSide == Side.Left) { point += tangent.GetNormal() * Tool.Thickness.Value; } var profileVector = point - profile.StartPoint; return(startPass + railVector.RotateBy(Math.PI / 2, -Vector3d.ZAxis).RotateBy(profileVector.GetAngleTo(Vector3d.XAxis), railVector) * profileVector.Length); } }