/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Performs the action the plot GCode code action required based on the current context /// </summary> /// <param name="graphicsObj">a graphics object on which to plot</param> /// <param name="stateMachine">the ggcode plot state machine</param> /// <param name="errorString">the error string we return on fail</param> /// <param name="errorValue">the error value we return on fail, z success, nz fail </param> /// <param name="wantEndPointMarkers">if true we draw the endpoints of the gcodes /// in a different color</param> /// <returns>z success, nz fail</returns> public virtual PlotActionEnum PerformPlotGCodeAction(Graphics graphicsObj, GCodeFileStateMachine stateMachine, bool wantEndPointMarkers, ref int errorValue, ref string errorString) { // ignore this errorValue = 0; errorString = ""; return(PlotActionEnum.PlotAction_Continue); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// We cannot always accept the number of tabs that have been applied. This /// tests this here /// </summary> /// <returns>list of GCodeCmd objects or null for fail</returns> public int CalcAdjustedNumberOfTabs(GCodeFileStateMachine stateMachine) { int adjustedNumTabs; // calc the length of a tab in isoplot units float TabLenInIsoPlotUnits = TabLength * stateMachine.IsoPlotPointsPerAppUnit; // figure out our adjusted number of tabs adjustedNumTabs = 0; if (NumberOfTabs <= 0) { adjustedNumTabs = 0; } else { float accumulatedTabLen = 0; float halfSegLen = this.CalcIsoSegmentLength(stateMachine) / 2; // add our Number of tabs in ensuring the total sum of the // tab lengths is not greater than half the total length for (int i = 0; i < NumberOfTabs; i++) { accumulatedTabLen += TabLenInIsoPlotUnits; if (accumulatedTabLen >= halfSegLen) { break; } // we can safely add this tab adjustedNumTabs++; } } return(adjustedNumTabs); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// The coords in the GCodeCmds are stored in isoPlot format (ints) this /// makes it simple to plot it. The output GCode file needs real world /// coordinates such as that used in the Gerber file. This proc performs /// the conversion. /// /// This one is for the X coordinate since the X offset can differ from Y /// /// </summary> /// <param name="stateMachine">the statemachine</param> /// <param name="xCoordToConvert">the coord to convert</param> public float ConvertIsoPlotCoordToGCodeOutputCoord_X(GCodeFileStateMachine stateMachine, int xCoordToConvert) { // basically we divide by the isoPlot scaling to get plot values. Note that the xCoordToConvert // will have been shifed so that all values are non negative. This puts the origin in the lower left // hand corner. If the user wanted the GCode output to be defined relative to the center of the plot // we must SUBTRACT it from the value here float interimX = (xCoordToConvert / stateMachine.IsoPlotPointsPerAppUnit) - stateMachine.GCodeOutputPlotOriginAdjust_X + (stateMachine.AbsoluteOffset_X / stateMachine.IsoPlotPointsPerAppUnit); // are we mirroring around a veritical axis? if (stateMachine.MirrorOnConversionToGCode == IsoFlipModeEnum.X_Flip) { interimX = (interimX * -1); // if we are not offsetting the X origin this means the origin must be at 0,0. After mirroring our // origin will be at 0,0 but all the coords will be negative from that. This effectively makes the gcode // start point in the lower right corner. If we at 2x the X center point adjust we can put it all back // properly if (stateMachine.GCodeOutputPlotOriginAdjust_X == 0) { // note stateMachine.GCodeOutputPlotOriginAdjust_X and stateMachine.GCodeMirrorAxisPlotCoord_X are both // the mid points of the gcode plot interimX = interimX + (2 * stateMachine.GCodeMirrorAxisPlotCoord_X); } } return(interimX); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Performs the action the plot requires based on the current context /// </summary> /// <param name="graphicsObj">a graphics object on which to plot</param> /// <param name="stateMachine">the gcode plot state machine</param> /// <param name="errorString">the error string we return on fail</param> /// <param name="errorValue">the error value we return on fail, z success, nz fail </param> /// <param name="wantEndPointMarkers">if true we draw the endpoints of the gcodes /// in a different color</param> /// <returns>an enum value indicating what next action to take</returns> public override PlotActionEnum PerformPlotGCodeAction(Graphics graphicsObj, GCodeFileStateMachine stateMachine, bool wantEndPointMarkers, ref int errorValue, ref string errorString) { Brush workingBrush = null; if (stateMachine == null) { errorValue = 999; errorString = "PerformPlotGCodeAction (Line) stateMachine == null"; return(PlotActionEnum.PlotAction_FailWithError); } // we only plot this on cuts or touchdowns if (((zMoveHeight == GCodeZMoveHeightEnum.GCodeZMoveHeight_ZCoordForCut) || (zMoveHeight == GCodeZMoveHeightEnum.GCodeZMoveHeight_ZCoordForAlt1Cut)) == false) { return(PlotActionEnum.PlotAction_Continue); } // if the drill width <=0 we do not plot if (drillDiameter <= 0) { return(PlotActionEnum.PlotAction_Continue); } // get the brush workingBrush = stateMachine.PlotBorderBrush; // now draw in a circle of appropriate diameter MiscGraphicsUtils.FillEllipseCenteredOnPoint(graphicsObj, workingBrush, x0, y0, drillDiameter, drillDiameter); return(PlotActionEnum.PlotAction_Continue); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode lines for this object as they would be written to the text file. Will /// never return a null value. Can often return more than one line if the /// current context in the stateMachine requires it /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { StringBuilder sb = new StringBuilder(); float zCoordForMove = -1; if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("M5 ERROR: stateMachine==null"); } // set the height we wish to move at if (ZMoveHeight == GCodeZMoveHeightEnum.GCodeZMoveHeight_ZCoordForClear) { zCoordForMove = stateMachine.ZCoordForClear; } else if (ZMoveHeight == GCodeZMoveHeightEnum.GCodeZMoveHeight_ZCoordForCut) { zCoordForMove = stateMachine.ZCoordForCut; } else if (ZMoveHeight == GCodeZMoveHeightEnum.GCodeZMoveHeight_ZCoordForAlt1Cut) { zCoordForMove = stateMachine.ZCoordForAlt1Cut; } else { zCoordForMove = stateMachine.ZCoordForMove; } // set z axis depth now - we always write it out if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } if (wantLinearMove == true) { sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_ZAXIS + zCoordForMove.ToString()); stateMachine.LastGCodeZCoord = zCoordForMove; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentZFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentZFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentZFeedrate; } } else { sb.Append(GCODEWORD_MOVERAPID + " " + GCODEWORD_ZAXIS + zCoordForMove.ToString()); stateMachine.LastGCodeZCoord = zCoordForMove; // no feedrates on MOVERAPID, these are set by the machine controller } sb.Append(stateMachine.LineTerminator); return(sb.ToString()); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// The coords in the GCodeCmds are stored in isoPlot format (ints) this /// makes it simple to plot it. The output GCode file needs real world /// coordinates such as that used in the Gerber file. This proc performs /// the conversion. /// /// This one is for the Y coordinate since the X offset can differ from Y /// /// </summary> /// <param name="stateMachine">the statemachine</param> /// <param name="yCoordToConvert">the coord to convert</param> public float ConvertIsoPlotCoordToGCodeOutputCoord_Y(GCodeFileStateMachine stateMachine, int yCoordToConvert) { // basically we divide by the isoPlot scaling to get plot values. Note that the xCoordToConvert // will have been shifed so that all values are non negative. This puts the origin in the lower left // hand corner. If the user wanted the GCode output to be defined relative to the center of the plot // we must SUBTRACT it from the value here float interimY = (yCoordToConvert / stateMachine.IsoPlotPointsPerAppUnit) - stateMachine.GCodeOutputPlotOriginAdjust_Y + (stateMachine.AbsoluteOffset_Y / stateMachine.IsoPlotPointsPerAppUnit); return(interimY); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode lines for this object as they would be written to the text file. Will /// never return a null value. Can often return more than one line if the /// current context in the stateMachine requires it /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { StringBuilder sb = new StringBuilder(); float zCoordForMove = -1; if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("M5 ERROR: stateMachine==null"); } // set the height we wish to move at if (RapidMoveHeight == GCodeRapidMoveHeightEnum.GCodeRapidMoveHeight_ZCoordForClear) { zCoordForMove = stateMachine.ZCoordForClear; } else { zCoordForMove = stateMachine.ZCoordForMove; } // Is our Z Axis correctly set? if (Math.Round(stateMachine.LastGCodeZCoord, 3) != Math.Round(zCoordForMove, 3)) { // no it is not, send it to the move depth if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVERAPID + " " + GCODEWORD_ZAXIS + zCoordForMove.ToString()); stateMachine.LastGCodeZCoord = zCoordForMove; sb.Append(stateMachine.LineTerminator); } float gX0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_X(stateMachine, x0), 3); float gY0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_Y(stateMachine, y0), 3); float gX0OffsetCompensated_Rounded = (float)Math.Round(gX0OffsetCompensated_Raw, 3); float gY0OffsetCompensated_Rounded = (float)Math.Round(gY0OffsetCompensated_Raw, 3); // Now move quickly to the start, we know our Z is now ok and above the pcb if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVERAPID + " " + GCODEWORD_XAXIS + gX0OffsetCompensated_Rounded.ToString() + " " + GCODEWORD_YAXIS + gY0OffsetCompensated_Rounded.ToString()); stateMachine.LastGCodeXCoord = gX0OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY0OffsetCompensated_Rounded; sb.Append(stateMachine.LineTerminator); return(sb.ToString()); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode line as it would be written to the text file. Will /// never return a null value /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("M5 ERROR: stateMachine==null"); } if (CommentIsPresent == true) { return(GCODEWORD_COMMENTOPEN + CommentText + GCODEWORD_COMMENTCLOSE + stateMachine.LineTerminator); } else { return(stateMachine.LineTerminator); } }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode line as it would be written to the text file. Will /// never return a null value /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("T ERROR: stateMachine==null"); } StringBuilder sb = new StringBuilder(); // for a tool change we must stop the spindle or EMC emits a really // odd "cannot change tools with cutter radius compensation" error if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_SPINDLESTOP); sb.Append(stateMachine.LineTerminator); if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_TOOLSELECT + toolNumber.ToString()); if (CommentIsPresent == true) { sb.Append(" " + GCODEWORD_COMMENTOPEN + CommentText + GCODEWORD_COMMENTCLOSE); } sb.Append(stateMachine.LineTerminator); // now force the tool change if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_TOOLCHANGE); sb.Append(stateMachine.LineTerminator); if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_SPINDLESTART_CW); sb.Append(stateMachine.LineTerminator); return(sb.ToString()); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the length of the isoplot segment (in application units) /// </summary> public float CalcIsoSegmentLength(GCodeFileStateMachine stateMachine) { float fX0; float fY0; float fX1; float fY1; if (stateMachine == null) { return(0); } fX0 = ((float)X0); fY0 = ((float)Y0); fX1 = ((float)X1); fY1 = ((float)Y1); isoSegmentLength = (float)Math.Sqrt(((fX1 - fX0) * (fX1 - fX0)) + ((fY1 - fY0) * (fY1 - fY0))); return(isoSegmentLength); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Performs the action the plot requires based on the current context /// </summary> /// <param name="graphicsObj">a graphics object on which to plot</param> /// <param name="stateMachine">the gcode plot state machine</param> /// <param name="errorString">the error string we return on fail</param> /// <param name="errorValue">the error value we return on fail, z success, nz fail </param> /// <param name="wantEndPointMarkers">if true we draw the endpoints of the gcodes /// in a different color</param> /// <returns>an enum value indicating what next action to take</returns> public override PlotActionEnum PerformPlotGCodeAction(Graphics graphicsObj, GCodeFileStateMachine stateMachine, bool wantEndPointMarkers, ref int errorValue, ref string errorString) { Pen workingPen = null; Brush workingBrush = null; if (stateMachine == null) { errorValue = 999; errorString = "PerformPlotGCodeAction (Line) stateMachine == null"; return(PlotActionEnum.PlotAction_FailWithError); } if (DoNotEmitToGCode == true) { return(PlotActionEnum.PlotAction_Continue); } // GetPen, it will have been set up to have the proper width and color workingPen = stateMachine.PlotBorderPen; workingBrush = stateMachine.PlotBorderBrush; //DebugTODO("remove this"); //if (DebugID == 3) workingPen = Pens.Red; //if (DebugID == 1) workingPen = Pens.Green; //if (DebugID == 6) workingPen = Pens.Blue; //if (DebugID == 4) workingPen = Pens.DarkOrange; //if (DebugID != 3) //{ // return PlotActionEnum.PlotAction_Continue; //} // Draw the line graphicsObj.DrawLine(workingPen, x0, y0, x1, y1); // now draw in the circular end points of the line MiscGraphicsUtils.FillEllipseCenteredOnPoint(graphicsObj, workingBrush, x0, y0, workingPen.Width, workingPen.Width); MiscGraphicsUtils.FillEllipseCenteredOnPoint(graphicsObj, workingBrush, x1, y1, workingPen.Width, workingPen.Width); return(PlotActionEnum.PlotAction_Continue); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode line as it would be written to the text file. Will /// never return a null value /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("M5 ERROR: stateMachine==null"); } StringBuilder sb = new StringBuilder(); if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(CommandWord); if (CommentIsPresent == true) { sb.Append(" " + GCODEWORD_COMMENTOPEN + CommentText + GCODEWORD_COMMENTCLOSE); } sb.Append(stateMachine.LineTerminator); return(sb.ToString()); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode lines for this object as they would be written to the text file. Will /// never return a null value. Can often return more than one line if the /// current context in the stateMachine requires it /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { StringBuilder sb = new StringBuilder(); if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("M5 ERROR: stateMachine==null"); } // calc some values float gX0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_X(stateMachine, X0), 3); float gX0OffsetCompensated_Rounded = (float)Math.Round(gX0OffsetCompensated_Raw, 3); float gY0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_Y(stateMachine, Y0), 3); float gY0OffsetCompensated_Rounded = (float)Math.Round(gY0OffsetCompensated_Raw, 3); // do we want line numbers if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } // Now set the start position sb.Append(GCODEWORD_SETPOSITION + " "); sb.Append(GCODEWORD_XAXIS + gX0OffsetCompensated_Rounded.ToString() + " "); sb.Append(GCODEWORD_YAXIS + gY0OffsetCompensated_Rounded.ToString()); if (CommentIsPresent == true) { sb.Append(" " + GCODEWORD_COMMENTOPEN + CommentText + GCODEWORD_COMMENTCLOSE); } sb.Append(stateMachine.LineTerminator); stateMachine.LastGCodeXCoord = gX0OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY0OffsetCompensated_Rounded; return(sb.ToString()); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Converts the contents of the isoPlotSegment to an appropriate sequence of GCodeCmd objects /// </summary> /// <returns>list of GCodeCmd objects or null for fail</returns> public override List <GCodeCmd> GetGCodeCmds(GCodeFileStateMachine stateMachine) { GCodeCmd_Arc arcObj = null; List <GCodeCmd> retList = new List <GCodeCmd>(); // build the arc now if (ReverseOnConversionToGCode == true) { // NOTE we use the chainedStart_X as point x1,y1 here. arcObj = new GCodeCmd_Arc(x0, y0, ChainedStart_X, ChainedStart_Y, xCenter, yCenter, radius, wantClockWise, isMultiQuadrantArc, this.PointsAddedCount, this.DebugID); arcObj.ReverseOnConversionToGCode = true; } else { // NOTE we use the chainedStart_X as point x0,y0 here. arcObj = new GCodeCmd_Arc(ChainedStart_X, ChainedStart_Y, x1, y1, xCenter, yCenter, radius, wantClockWise, isMultiQuadrantArc, this.PointsAddedCount, this.DebugID); arcObj.ReverseOnConversionToGCode = false; } arcObj.DoNotEmitToGCode = DoNotEmitToGCode; // return it retList.Add(arcObj); return(retList); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Completely resets this class /// </summary> public void Reset() { stateMachine = new GCodeFileStateMachine(); SourceLines = new List <GCodeCmd>(); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode lines for this object as they would be written to the text file. Will /// never return a null value. Can often return more than one line if the /// current context in the stateMachine requires it /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { StringBuilder sb = new StringBuilder(); if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("M5 ERROR: stateMachine==null"); } if (DoNotEmitToGCode == true) { return(sb.ToString()); } float gX0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_X(stateMachine, x0), 3); float gY0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_Y(stateMachine, y0), 3); float gX1OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_X(stateMachine, x1), 3); float gY1OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_Y(stateMachine, y1), 3); float gX0OffsetCompensated_Rounded = (float)Math.Round(gX0OffsetCompensated_Raw, 3); float gY0OffsetCompensated_Rounded = (float)Math.Round(gY0OffsetCompensated_Raw, 3); float gX1OffsetCompensated_Rounded = (float)Math.Round(gX1OffsetCompensated_Raw, 3); float gY1OffsetCompensated_Rounded = (float)Math.Round(gY1OffsetCompensated_Raw, 3); // are we drawing this value in reverse? if (this.ReverseOnConversionToGCode == true) { float tmpX = gX0OffsetCompensated_Rounded; float tmpY = gY0OffsetCompensated_Rounded; gX0OffsetCompensated_Rounded = gX1OffsetCompensated_Rounded; gY0OffsetCompensated_Rounded = gY1OffsetCompensated_Rounded; gX1OffsetCompensated_Rounded = tmpX; gY1OffsetCompensated_Rounded = tmpY; } float gXLastGCodeCoord_Rounded = (float)Math.Round(stateMachine.LastGCodeXCoord, 3); float gYLastGCodeCoord_Rounded = (float)Math.Round(stateMachine.LastGCodeYCoord, 3); // are we currently on our start coord? if ((gX0OffsetCompensated_Rounded == gXLastGCodeCoord_Rounded) && (gY0OffsetCompensated_Rounded == gYLastGCodeCoord_Rounded)) { // yes we are, we do not need to move the tool head there. Is our Z Axis correctly set? if (Math.Round(stateMachine.LastGCodeZCoord, 3) != Math.Round(stateMachine.ZCoordForCut, 3)) { // no it is not, send it down to to the CUT depth if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_ZAXIS + stateMachine.ZCoordForCut.ToString()); stateMachine.LastGCodeZCoord = stateMachine.ZCoordForCut; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentZFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentZFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentZFeedrate; } sb.Append(stateMachine.LineTerminator); } // now move the tool head, cutting as we go if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_XAXIS + gX1OffsetCompensated_Rounded.ToString() + " " + GCODEWORD_YAXIS + gY1OffsetCompensated_Rounded.ToString()); stateMachine.LastGCodeXCoord = gX1OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY1OffsetCompensated_Rounded; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentXYFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentXYFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentXYFeedrate; } sb.Append(stateMachine.LineTerminator); } else { // no, we are not on the start coord. Is our Z Axis correctly set? if ((((Math.Round(stateMachine.LastGCodeZCoord, 3) == Math.Round(stateMachine.ZCoordForMove, 3)) || (Math.Round(stateMachine.LastGCodeZCoord, 3) == Math.Round(stateMachine.ZCoordForClear, 3)))) == false) { // no it is not, pull it up to the MOVE distance if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_ZAXIS + stateMachine.ZCoordForMove.ToString()); stateMachine.LastGCodeZCoord = stateMachine.ZCoordForMove; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentZFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentZFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentZFeedrate; } sb.Append(stateMachine.LineTerminator); } // Now move quickly to the start, we know our Z is now be ok and above the pcb if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVERAPID + " " + GCODEWORD_XAXIS + gX0OffsetCompensated_Rounded.ToString() + " " + GCODEWORD_YAXIS + gY0OffsetCompensated_Rounded.ToString()); stateMachine.LastGCodeXCoord = gX0OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY0OffsetCompensated_Rounded; sb.Append(stateMachine.LineTerminator); // now put our Z down to the cut depth if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_ZAXIS + stateMachine.ZCoordForCut.ToString()); stateMachine.LastGCodeZCoord = stateMachine.ZCoordForCut; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentZFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentZFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentZFeedrate; } sb.Append(stateMachine.LineTerminator); // now move to the end of the line, cutting as we go if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_XAXIS + gX1OffsetCompensated_Rounded.ToString() + " " + GCODEWORD_YAXIS + gY1OffsetCompensated_Rounded.ToString()); stateMachine.LastGCodeXCoord = gX1OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY1OffsetCompensated_Rounded; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentXYFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentXYFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentXYFeedrate; } sb.Append(stateMachine.LineTerminator); } //DebugTODO("for diagnostics only"); //sb.Append(stateMachine.LineTerminator); return(sb.ToString()); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Converts the contents of the isoPlotSegment to an appropriate GCodeCmd object /// </summary> /// <returns>list of GCodeCmd objects or null for fail</returns> public override List <GCodeCmd> GetGCodeCmds(GCodeFileStateMachine stateMachine) { int adjustedNumTabs; GCodeCmd_Line lineObj = null; GCodeCmd_Line lineObjSegment = null; List <GCodeCmd> retList = new List <GCodeCmd>(); float fX0; float fY0; float fX1; float fY1; List <PointF> ptListMidCenters = new List <PointF>(); // build the line now if (ReverseOnConversionToGCode == true) { // NOTE we use the chainedStart_X as point x1,y1 here. lineObj = new GCodeCmd_Line(x0, y0, ChainedStart_X, ChainedStart_Y, this.PointsAddedCount, this.DebugID); fX0 = (float)x0; fY0 = (float)y0; fX1 = (float)ChainedStart_X; fY1 = (float)ChainedStart_Y; lineObj.ReverseOnConversionToGCode = true; } else { // NOTE we use the chainedStart_X as point x0,y0 here. lineObj = new GCodeCmd_Line(ChainedStart_X, ChainedStart_Y, x1, y1, this.PointsAddedCount, this.DebugID); fX0 = (float)ChainedStart_X; fY0 = (float)ChainedStart_Y; fX1 = (float)x1; fY1 = (float)y1; lineObj.ReverseOnConversionToGCode = false; } lineObj.DoNotEmitToGCode = DoNotEmitToGCode; //DebugMessage("IsoSegmentLength=" + IsoSegmentLength(stateMachine).ToString()); // figure out our adjusted number of tabs adjustedNumTabs = 0; if (NumberOfTabs <= 0) { adjustedNumTabs = 0; } else { adjustedNumTabs = CalcAdjustedNumberOfTabs(stateMachine); } //DebugMessage("NumberOfTabs=" + NumberOfTabs.ToString()); //DebugMessage("adjustedNumTabs=" + adjustedNumTabs.ToString()); //DebugMessage("TabLength=" + TabLength.ToString()); // do we need to break the line to add tabs? if ((NumberOfTabs <= 0) || (adjustedNumTabs <= 0) || (TabLength <= 0)) { // no we do not, add it and return it as a single line in a list retList.Add(lineObj); return(retList); } // calc the length of a tab in isoplot units float TabLenInIsoPlotUnits = TabLength * stateMachine.IsoPlotPointsPerAppUnit; // we use half of half of the Isolation width to compensate for the width of the bit float millRadiusCompensatedTabLenInIsoPlotUnits = TabLenInIsoPlotUnits + ((stateMachine.IsolationWidth * stateMachine.IsoPlotPointsPerAppUnit) / 2); // this is the amount of a segment repressented by a tab float pctOfSegmentInASingleTab = millRadiusCompensatedTabLenInIsoPlotUnits / IsoSegmentLength; // we deduct half a tab width from the midpoint to calc the new start and end points float pctOfSegmentInASingleHalfTab = pctOfSegmentInASingleTab / 2; // set this now int numSegments = adjustedNumTabs + 1; // figure out the set of points starting and ending each line segment // add the original line start point ptListMidCenters.Add(new PointF(fX0, fY0)); // this is based on the line partitioning algorythm courtesy of Tom Sirgedas // https://stackoverflow.com/questions/3542402/partition-line-into-equal-parts for (float i = 1; i < numSegments; i++) { float weightedAverage = (i / numSegments); float workingNearSidePct = pctOfSegmentInASingleHalfTab; float workingFarSidePct = pctOfSegmentInASingleHalfTab; // calc the start of the segment float startX = fX0 * (1 - (weightedAverage - workingNearSidePct)) + fX1 * (weightedAverage - workingNearSidePct); float startY = fY0 * (1 - (weightedAverage - workingNearSidePct)) + fY1 * (weightedAverage - workingNearSidePct); // calc the end of the segment float endX = fX0 * (1 - (weightedAverage + workingFarSidePct)) + fX1 * (weightedAverage + workingFarSidePct); float endY = fY0 * (1 - (weightedAverage + workingFarSidePct)) + fY1 * (weightedAverage + workingFarSidePct); // add the point about this center ptListMidCenters.Add(new PointF(startX, startY)); ptListMidCenters.Add(new PointF(endX, endY)); } // add the original line end point ptListMidCenters.Add(new PointF(fX1, fY1)); // by this point we have a list of points, each pair represents a line we should draw // now we go through the points list and build our lines from them for (int i = 0; i < ptListMidCenters.Count; i += 2) { float startX = ptListMidCenters[i].X; float startY = ptListMidCenters[i].Y; float endX = ptListMidCenters[i + 1].X; float endY = ptListMidCenters[i + 1].Y; // convert to ints int startXInt = (int)Math.Round(startX, 0); int startYInt = (int)Math.Round(startY, 0); int endXInt = (int)Math.Round(endX, 0); int endYInt = (int)Math.Round(endY, 0); // create a new line, give it a debugID based off the original full length one, and copy other info in as well lineObjSegment = new GCodeCmd_Line(startXInt, startYInt, endXInt, endYInt, this.PointsAddedCount, (int)((this.DebugID * DEFAULT_NEW_DEBUG_ID_MULTIPLIER) + i)); lineObjSegment.ReverseOnConversionToGCode = this.ReverseOnConversionToGCode; // add it retList.Add(lineObjSegment); } // was the original line object reversed? If so we have to reverse the order of the segments if (this.ReverseOnConversionToGCode == true) { retList.Reverse(); } // return this return(retList); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Converts the contents of the isoPlotSegment to an appropriate GCodeCmd object /// </summary> /// <returns>list of GCodeCmd objects or null for fail</returns> public abstract List <GCodeCmd> GetGCodeCmds(GCodeFileStateMachine stateMachine);
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode line as it would be written to the text file. Will /// never return a null value /// </summary> /// <param name="stateMachine">the statemachine string</param> public abstract string GetGCodeCmd(GCodeFileStateMachine stateMachine);
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Gets the GCode lines for this object as they would be written to the text file. Will /// never return a null value. Can often return more than one line if the /// current context in the stateMachine requires it /// </summary> /// <param name="stateMachine">the stateMachine</param> public override string GetGCodeCmd(GCodeFileStateMachine stateMachine) { StringBuilder sb = new StringBuilder(); string gcodeWordArcDirection = GCodeCmd.GCODEWORD_MOVEINCIRCLECCW; float xCenterOffset = -1; float yCenterOffset = -1; double radius; double theta; float xTrueEndPoint; float yTrueEndPoint; float xEndDistance; float yEndDistance; if (stateMachine == null) { LogMessage("GetGCodeCmd: stateMachine == null"); return("M5 ERROR: stateMachine==null"); } if (DoNotEmitToGCode == true) { return(sb.ToString()); } // set our arc direction code word if (arcDirection == GCodeCmd_ArcDirectionEnum.GCodeArcDirection_CCW) { gcodeWordArcDirection = GCodeCmd.GCODEWORD_MOVEINCIRCLECCW; } else { gcodeWordArcDirection = GCodeCmd.GCODEWORD_MOVEINCIRCLECW; } // we need to reverse the direction if we are flipping if (stateMachine.MirrorOnConversionToGCode == IsoFlipModeEnum.X_Flip) { gcodeWordArcDirection = GetReversedArcDirection(gcodeWordArcDirection); } float gX0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_X(stateMachine, x0), 3); float gY0OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_Y(stateMachine, y0), 3); float gX1OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_X(stateMachine, x1), 3); float gY1OffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_Y(stateMachine, y1), 3); float gXCenterOffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_X(stateMachine, xCenter), 3); float gYCenterOffsetCompensated_Raw = (float)Math.Round(ConvertIsoPlotCoordToGCodeOutputCoord_Y(stateMachine, yCenter), 3); float gX0OffsetCompensated_Rounded = (float)Math.Round(gX0OffsetCompensated_Raw, 3); float gY0OffsetCompensated_Rounded = (float)Math.Round(gY0OffsetCompensated_Raw, 3); float gX1OffsetCompensated_Rounded = (float)Math.Round(gX1OffsetCompensated_Raw, 3); float gY1OffsetCompensated_Rounded = (float)Math.Round(gY1OffsetCompensated_Raw, 3); // are we drawing this value in reverse? if (this.ReverseOnConversionToGCode == true) { float tmpX = gX0OffsetCompensated_Rounded; float tmpY = gY0OffsetCompensated_Rounded; gX0OffsetCompensated_Rounded = gX1OffsetCompensated_Rounded; gY0OffsetCompensated_Rounded = gY1OffsetCompensated_Rounded; gX1OffsetCompensated_Rounded = tmpX; gY1OffsetCompensated_Rounded = tmpY; // do this again gcodeWordArcDirection = GetReversedArcDirection(gcodeWordArcDirection); } float gXCenterOffsetCompensated_Rounded = (float)Math.Round(gXCenterOffsetCompensated_Raw, 4); float gYCenterOffsetCompensated_Rounded = (float)Math.Round(gYCenterOffsetCompensated_Raw, 4); float gXLastGCodeCoord_Rounded = (float)Math.Round(stateMachine.LastGCodeXCoord, 3); float gYLastGCodeCoord_Rounded = (float)Math.Round(stateMachine.LastGCodeYCoord, 3); // sb.Append("(ZZZZZ---->GO2Arc)"); sb.Append(stateMachine.LineTerminator); // are we currently on our start coord? if ((gX0OffsetCompensated_Rounded == gXLastGCodeCoord_Rounded) && (gY0OffsetCompensated_Rounded == gYLastGCodeCoord_Rounded)) { // yes we are, we do not need to move the tool head there. Is our Z Axis correctly set? if (Math.Round(stateMachine.LastGCodeZCoord, 3) != Math.Round(stateMachine.ZCoordForCut, 3)) { // no it is not, send it down to to the CUT depth if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_ZAXIS + stateMachine.ZCoordForCut.ToString()); stateMachine.LastGCodeZCoord = stateMachine.ZCoordForCut; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentZFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentZFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentZFeedrate; } sb.Append(stateMachine.LineTerminator); } // now move the tool head, cutting as we go if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } // we must calc our offsets to the center, the g?CenterOffsetCompensated_Rounded are actually the center coordinates xCenterOffset = gXCenterOffsetCompensated_Rounded - gX0OffsetCompensated_Rounded; yCenterOffset = gYCenterOffsetCompensated_Rounded - gY0OffsetCompensated_Rounded; // The way the G02,G03 commands work is they have a start point, an offset to the center // and the endpoint of the arc. It must be true that the endpoint of the arc is actually on // the arc itself. If this is not the case then most machine controllers will throw an error. // Given the new chain calculation method this problem does not appear to exist. If needed // the commented out code below tells what to do // assume the true endpoints are the ones we got xTrueEndPoint = gX1OffsetCompensated_Rounded; yTrueEndPoint = gY1OffsetCompensated_Rounded; /* not operational any more, and did not work well * // The endpoint error can happen due to the way we interpolate things that our endpoint is a bit off the arc * // so we calculate the best endpoint actually on the arc and use that in the G02/G03 command * // then we add a little compensating linear move (at the same Z position) to get us to the * // point we need to end at. Usually this little "step" in the arc etc is so small it is invisible * * // we need the radius, calculated off the start point * radius = MiscGraphicsUtils.GetDistanceBetweenTwoPoints(gX0OffsetCompensated_Raw, gY0OffsetCompensated_Raw, gXCenterOffsetCompensated_Rounded, gYCenterOffsetCompensated_Rounded); */ // now emit the GCode with the true endpoint sb.Append(gcodeWordArcDirection + " " + GCODEWORD_XAXIS + xTrueEndPoint.ToString() + " " + GCODEWORD_YAXIS + yTrueEndPoint.ToString() + " " + GCODEWORD_XARCCENTER + xCenterOffset.ToString() + " " + GCODEWORD_YARCCENTER + yCenterOffset.ToString()); // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentXYFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentXYFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentXYFeedrate; } sb.Append(stateMachine.LineTerminator); stateMachine.LastGCodeXCoord = gX1OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY1OffsetCompensated_Rounded; } else { // no, we are not on the start coord. Is our Z Axis correctly set? if ((((Math.Round(stateMachine.LastGCodeZCoord, 3) == Math.Round(stateMachine.ZCoordForMove, 3)) || (Math.Round(stateMachine.LastGCodeZCoord, 3) == Math.Round(stateMachine.ZCoordForClear, 3)))) == false) { // no it is not, pull it up to the MOVE distance if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_ZAXIS + stateMachine.ZCoordForMove.ToString()); stateMachine.LastGCodeZCoord = stateMachine.ZCoordForMove; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentZFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentZFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentZFeedrate; } sb.Append(stateMachine.LineTerminator); } // Now move quickly to the start, we know our Z is now be ok and above the pcb if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVERAPID + " " + GCODEWORD_XAXIS + gX0OffsetCompensated_Rounded.ToString() + " " + GCODEWORD_YAXIS + gY0OffsetCompensated_Rounded.ToString()); stateMachine.LastGCodeXCoord = gX0OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY0OffsetCompensated_Rounded; sb.Append(stateMachine.LineTerminator); // now put our Z down to the cut depth if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_ZAXIS + stateMachine.ZCoordForCut.ToString()); stateMachine.LastGCodeZCoord = stateMachine.ZCoordForCut; // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentZFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentZFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentZFeedrate; } sb.Append(stateMachine.LineTerminator); // now move to the end of the arc, cutting as we go if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } // we must calc our offsets to the center xCenterOffset = (float)Math.Round(gXCenterOffsetCompensated_Rounded - stateMachine.LastGCodeXCoord, 4); yCenterOffset = (float)Math.Round(gYCenterOffsetCompensated_Rounded - stateMachine.LastGCodeYCoord, 4); // The way the G02,G03 commands work is they have a start point, an offset to the center // and the endpoint of the arc. It must be true that the endpoint of the arc is actually on // the arc itself. If this is not the case then most machine controllers will throw an error // it can happen, due to the way we interpolate things that our endpoint is a bit off the arc // so we calculate the best endpoint actually on the arc and use that in the G02/G03 command // then we add a little compensating linear move (at the same Z position) to get us to the // point we need to end at. Usually this little "step" in the arc etc is so small it is invisible // we need the radius radius = Math.Sqrt((xCenterOffset * xCenterOffset) + (yCenterOffset * yCenterOffset)); // we need the xdistance and ydistances of the end point to the center xEndDistance = (float)Math.Round(gX1OffsetCompensated_Rounded - gXCenterOffsetCompensated_Rounded, 4); yEndDistance = (float)Math.Round(gY1OffsetCompensated_Rounded - gYCenterOffsetCompensated_Rounded, 4); if (xEndDistance != 0) { // we need the angle theta = Math.Atan2(yEndDistance, xEndDistance); // calc the true end points xTrueEndPoint = (float)Math.Round(gXCenterOffsetCompensated_Rounded + (radius * Math.Cos(theta)), 4); yTrueEndPoint = (float)Math.Round(gYCenterOffsetCompensated_Rounded + (radius * Math.Sin(theta)), 4); } else { // assume the true endpoints are the ones we got xTrueEndPoint = gX1OffsetCompensated_Rounded; yTrueEndPoint = gY1OffsetCompensated_Rounded; } // now emit the GCode with the true endpoint sb.Append(gcodeWordArcDirection + " " + GCODEWORD_XAXIS + xTrueEndPoint.ToString() + " " + GCODEWORD_YAXIS + yTrueEndPoint.ToString() + " " + GCODEWORD_XARCCENTER + xCenterOffset.ToString() + " " + GCODEWORD_YARCCENTER + yCenterOffset.ToString()); // do we need to adjust the feedrate? if (stateMachine.LastFeedRate != stateMachine.CurrentXYFeedrate) { // yes we do sb.Append(" " + GCODEWORD_FEEDRATE + stateMachine.CurrentXYFeedrate.ToString()); // remember this now stateMachine.LastFeedRate = stateMachine.CurrentXYFeedrate; } sb.Append(stateMachine.LineTerminator); // now do we need to add a little linear run so we actually end up on the // true XY end point if ((xTrueEndPoint != gX1OffsetCompensated_Rounded) || (yTrueEndPoint != gY1OffsetCompensated_Rounded)) { if (stateMachine.GCodeFileManager.ShowGCodeCmdNumbers == true) { sb.Append(stateMachine.BuildNextLineNumberString() + " "); } sb.Append(GCODEWORD_MOVEINLINE + " " + GCODEWORD_XAXIS + gX1OffsetCompensated_Rounded.ToString() + " " + GCODEWORD_YAXIS + gY1OffsetCompensated_Rounded.ToString()); sb.Append(stateMachine.LineTerminator); // DebugTODO("remove this line"); // sb.Append("(YYYYYY---->xCenter=" + X1CenterOffsetCompensated.ToString() + " Ycenter=" + Y1CenterOffsetCompensated.ToString()+")"); // sb.Append(stateMachine.LineTerminator); // sb.Append("(YYYYYY---->AutoGenerated Compensation)"); // sb.Append(stateMachine.LineTerminator); } stateMachine.LastGCodeXCoord = gX1OffsetCompensated_Rounded; stateMachine.LastGCodeYCoord = gY1OffsetCompensated_Rounded; } //DebugTODO("for diagnostics only"); //sb.Append(stateMachine.LineTerminator); return(sb.ToString()); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Performs the action the plot requires based on the current context. Note /// that the coordinates stored in GCodeCmds in here are not in isoPlot coords. /// They coords are are in /// </summary> /// <param name="graphicsObj">a graphics object on which to plot</param> /// <param name="stateMachine">the gcode plot state machine</param> /// <param name="errorString">the error string we return on fail</param> /// <param name="errorValue">the error value we return on fail, z success, nz fail </param> /// <param name="wantEndPointMarkers">if true we draw the endpoints of the gcodes /// in a different color</param> /// <returns>an enum value indicating what next action to take</returns> public override PlotActionEnum PerformPlotGCodeAction(Graphics graphicsObj, GCodeFileStateMachine stateMachine, bool wantEndPointMarkers, ref int errorValue, ref string errorString) { Pen workingPen = null; Brush workingBrush = null; if (DoNotEmitToGCode == true) { return(PlotActionEnum.PlotAction_Continue); } // get the current X,Y coordinates, these will already be scaled, origin compensated and flipped float startXCoord = x0; float startYCoord = y0; // get the end X,Y coordinate float endXCoord = x1; float endYCoord = y1; // get the arc center point, by adding on the specified offsets. Note we do not involve // the end coords here. These are calculated off the start coords float arcCenterXCoord = xCenter; float arcCenterYCoord = yCenter; // get our points PointF startPoint = new PointF(startXCoord, startYCoord); PointF endPoint = new PointF(endXCoord, endYCoord); PointF centerPoint = new PointF(arcCenterXCoord, arcCenterYCoord); //DebugMessage("PG startPoint=" + startPoint.ToString() + ", endPoint=" + endPoint.ToString() + ", centerPoint=" + centerPoint.ToString() + ", radius=" + radius); // A NOTE on the direction of angles here. Gerber can specifiy clockwise or counter clockwise. .NET only wants its angles specified counter // clockwise when it draws. GCode can go either way. However, the arcs drawn here are segments of a larger arc (or cicle) and these are // built out of isoplot segments. The isoplot segment collector always goes around arcs in a counter clockwise direction. This essentially // converts any clockwise arcs into counter clockwise ones. // this means that all start points, end points and sweep angles are ALWAYS listed as CounterClockwise here even if the original gerber was // clockwise. This means we do not have to do any conversion for the display even if the original angle was clockwise. // get our start and sweep angles MiscGraphicsUtils.GetCounterClockwiseStartAndSweepAnglesFrom2PointsAndCenter(startPoint, endPoint, centerPoint, out float startAngleInDegrees, out float sweepAngleInDegrees); //DebugMessage(" PGa startAngleInDegrees=" + startAngleInDegrees.ToString() + ", sweepAngleInDegrees=" + sweepAngleInDegrees.ToString()); // are we dealing with a sweep angle of 0 degrees? if (sweepAngleInDegrees == 0) { // yes, this actually may be a full circle or it may just be a tiny little arc segment with an angle so small // that it cannot be drawn. We can get an idea by looking at the number of isoPlotCells the arc uses. A small // number indicates that we are actually a circle if (IsoPlotCellsInObject >= ARBITRARY_MIN_NUMBER_OF_CELLS_FOR_0_DEGREE_ARC) { sweepAngleInDegrees = 360; } } // GetPen, it will have been set up to have the proper width and color workingPen = stateMachine.PlotBorderPen; workingBrush = stateMachine.PlotBorderBrush; // create an enclosing rectangle RectangleF boundingRect = new RectangleF(arcCenterXCoord - radius, arcCenterYCoord - radius, 2 * radius, 2 * radius); //DebugTODO("remove this"); //if (DebugID == 23) workingPen = Pens.Red; //if (DebugID == 25) workingPen = Pens.Green; //if (DebugID == 20) workingPen = Pens.Blue; //if (DebugID != 9) //{ // return PlotActionEnum.PlotAction_Continue; //} // Draw arc graphicsObj.DrawArc(workingPen, boundingRect, startAngleInDegrees, sweepAngleInDegrees); // if we did not do nothing or a full circle draw these in if (((sweepAngleInDegrees == 0) || (sweepAngleInDegrees == 360)) == false) { // now draw in the circular end points of the line MiscGraphicsUtils.FillEllipseCenteredOnPoint(graphicsObj, workingBrush, startXCoord, startYCoord, workingPen.Width, workingPen.Width); MiscGraphicsUtils.FillEllipseCenteredOnPoint(graphicsObj, workingBrush, endXCoord, endYCoord, workingPen.Width, workingPen.Width); } return(PlotActionEnum.PlotAction_Continue); }