/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Performs the plot Isolation Object actions required based on the current context /// </summary> /// <param name="isoPlotBuilder">A GCode Builder object</param> /// <param name="stateMachine">the gerber 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> /// <returns>an enum value indicating what next action to take</returns> public override GerberLine.PlotActionEnum PerformPlotIsoStep1Action(IsoPlotBuilder isoPlotBuilder, GerberFileStateMachine stateMachine, ref int errorValue, ref string errorString) { // one would think that turning off the contouring would be all that was necessary. However // contours are complex object and sometime we have to finish off the object usually this // means fill the existing contour object . if ((stateMachine.ContourDrawingModeEnabled == true) && (stateMachine.ComplexObjectList_DCode.Count != 0)) { // process the complex object by drawing in the background // Get the bounding box Rectangle boundingRect = stateMachine.GetBoundingRectangleFromComplexObjectList_DCode(); // get a list of builderIDs in the complex object list List <int> builderIDList = stateMachine.GetListOfIsoPlotBuilderIDsFromComplexObjectList_DCode(); if (builderIDList.Count > 0) { if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_BACKGROUND) { // Normal Dark polarity. Just fill it in with a backgroundpixel isoPlotBuilder.BackgroundFillGSRegionComplex(builderIDList, builderIDList[0], boundingRect.X, boundingRect.Y, boundingRect.X + boundingRect.Width, boundingRect.Y + boundingRect.Height, -1); } else if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_ERASE) { // we have reverse polarity. The object we just drew must be cleaned out and everything underneath it removed isoPlotBuilder.BackgroundFillGSByBoundaryComplexVert(builderIDList, IsoPlotObject.DEFAULT_ISOPLOTOBJECT_ID, boundingRect.X, boundingRect.Y, boundingRect.X + boundingRect.Width, boundingRect.Y + boundingRect.Height, -1); } } } // reset, we have processed the complex list stateMachine.ComplexObjectList_DCode = new List <GerberLine_DCode>(); // disable contour drawing mode stateMachine.ContourDrawingModeEnabled = false; return(GerberLine.PlotActionEnum.PlotAction_Continue); }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Perform a circular aperture flash. This is a static call so other things can call it /// if they need to do so. /// </summary> /// <param name="isoPlotBuilder">the builder opbject</param> /// <param name="stateMachine">the statemachine</param> /// <param name="xyComp">the xy compensation factor</param> /// <param name="x1">the first x value</param> /// <param name="y1">the first y value</param> /// <returns>z success, nz fail</returns> public static void PerformCircularApertureFlashWithHole(IsoPlotBuilder isoPlotBuilder, GerberFileStateMachine stateMachine, int x1, int y1, int xyComp, int workingOuterDiameter, float holeDia) { int hDia; int holeRadius = 0; int builderID = 0; int holeBuilderObjID = 0; if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_BACKGROUND) { // draw the circle builderID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, x1, y1, ((workingOuterDiameter + xyComp) / 2), GSFillModeEnum.FillMode_BACKGROUND, stateMachine.WantClockWise); // do we need to do a hole? if (holeDia > 0) { // yes we do hDia = (int)Math.Round((holeDia * stateMachine.IsoPlotPointsPerAppUnit)); holeRadius = (hDia - xyComp) / 2; if (holeRadius > 0) { // draw the circle for the hole holeBuilderObjID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, x1, y1, holeRadius, GSFillModeEnum.FillMode_BACKGROUND, stateMachine.WantClockWise); // There is more going on here than is immediately obvious // we want the inside of the hole to not be filled with isocells from the holeBuilderObjID // we could just have not filled it and only placed edge isocells // but this causes complications on the erase. It is better to fill it then remove the outer // object then remove the background only pixels of the holeBuilderObjID. This makes sure we // get all of the outer objects isocells rather than relying on hitting the edge to // figure out where to start. // remove everything belonging to the outer circle from the circle we just drew, this // leaves anything else which might be in there in there isoPlotBuilder.EraseABuilderIDFromRegionUsingABuilderID(holeBuilderObjID, builderID, x1 - holeRadius - 1, y1 - holeRadius - 1, x1 + holeRadius + 1, y1 + holeRadius + 1); // remove all background only pixels belonging to the hole circle leaving just the edge pixels isoPlotBuilder.EraseBackgroundOnlyIsoCellsByBuilderID(holeBuilderObjID, x1 - holeRadius - 1, y1 - holeRadius - 1, x1 + holeRadius + 1, y1 + holeRadius + 1); } } } else if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_ERASE) { // we have to decide whether we are drawing a hole or not. In Clear Polarity holes do not come in as solid DARK spot like you // think they would. Instead they just "do nothing" on the area the contents below them remain there and there is no fill or erase if (holeDia > 0) { // we are dealing with a erase flash with a hole // draw the circle, we use invert edges here so we draw the circle only if it is on some other background, use no fill. We will erase between it and the hole later int outerRadius = (workingOuterDiameter + xyComp) / 2; builderID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1, y1, outerRadius, GSFillModeEnum.FillMode_NONE, stateMachine.WantClockWise); // now do the hole we use invert edges so anything we draw over a background will get drawn, there is no fill hDia = (int)Math.Round((holeDia * stateMachine.IsoPlotPointsPerAppUnit)); holeRadius = (hDia - xyComp) / 2; if (holeRadius > 0) { // draw the circle for the hole using no fill. Whatever is in there is in there holeBuilderObjID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1, y1, holeRadius, GSFillModeEnum.FillMode_NONE, stateMachine.WantClockWise); } // now erase between the outer and inner circles // we are dealing with a simple erase flash, no hole List <int> builderIDList = new List <int>(); builderIDList.Add(builderID); builderIDList.Add(holeBuilderObjID); isoPlotBuilder.BackgroundFillGSByBoundaryComplexVert(builderIDList, IsoPlotObject.DEFAULT_ISOPLOTOBJECT_ID, x1 - outerRadius - 1, y1 - outerRadius - 1, x1 + outerRadius + 1, y1 + outerRadius + 1, -1); } else { // draw the circle, we use invert edges here so we draw only if it is on some other background, we still erase everything under it though int outerRadius = (workingOuterDiameter + xyComp) / 2; builderID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1, y1, outerRadius, GSFillModeEnum.FillMode_ERASE, stateMachine.WantClockWise); } } return; }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Flash an aperture for a OBRound on a GCode Plot /// /// </summary> /// <param name="isoPlotBuilder">the builder opbject</param> /// <param name="stateMachine">the statemachine</param> /// <param name="xyComp">the xy compensation factor</param> /// <param name="x1">the first x value</param> /// <param name="y1">the first y value</param> /// <returns>z success, nz fail</returns> public override void FlashApertureForGCodePlot(IsoPlotBuilder isoPlotBuilder, GerberFileStateMachine stateMachine, int x1, int y1, int xyComp) { int holeBuilderObjID = 0; int hDia; bool ellipseIsVertical = false; int workingOuterDiameter = int.MinValue; float majorEllipseAxis = float.NegativeInfinity; float minorEllipseAxis = float.NegativeInfinity; float majorEllipseAxisAdjustedForCircle = float.NegativeInfinity; int x1EllipseCompensatedCenterpoint; int y1EllipseCompensatedCenterpoint; int x2EllipseCompensatedCenterpoint; int y2EllipseCompensatedCenterpoint; float circle1StartDegrees = 90; float circle2StartDegrees = 270; float circle1SweepDegrees = 180; float circle2SweepDegrees = 180; int majorEllipseAxisEndpointCompensation = 0; int outerBoundingBoxLL_X = 0; int outerBoundingBoxLL_Y = 0; int outerBoundingBoxUR_X = 0; int outerBoundingBoxUR_Y = 0; if (isoPlotBuilder == null) { return; } if (stateMachine == null) { return; } // NOTE: it is not possible to encode an Oval or Ellipse in GCode, you have to // represent this with a line segment which has two circles at the end // the total length should be equal the longest dimension. The shortest // dimension will be used as the diameter of the circles. // NOTE: we need not deal with ellipses in here which do not have major and minor // axis parallel to the X and Y axis. There is no way to represent this in // Gerber code and they are usually encoded as an AM macro which is a // combination of two circles and a line - just as we are doing (see above) // and for exactly the same reasons. // find the shortest and longest dimensions. NOTE according to the spec OBRounds // cannot have a rotation. They are either Horizontal or Vertical if (xAxisDimension > yAxisDimension) { majorEllipseAxis = xAxisDimension; minorEllipseAxis = yAxisDimension; ellipseIsVertical = false; } else { majorEllipseAxis = yAxisDimension; minorEllipseAxis = xAxisDimension; ellipseIsVertical = true; } // now figure out the length of the line between the circles majorEllipseAxisAdjustedForCircle = majorEllipseAxis - minorEllipseAxis; // sanity checks if (majorEllipseAxisAdjustedForCircle < 0) { // what the hell? we got problems throw new Exception("Cannot draw ellipse adjusted axis less than zero."); } else if (majorEllipseAxisAdjustedForCircle == 0) { // special case, we must be dealing with a circle, just draw one workingOuterDiameter = (int)Math.Round((majorEllipseAxis * stateMachine.IsoPlotPointsPerAppUnit)); // now do the flash GerberAperture_CCode.PerformCircularApertureFlashWithHole(isoPlotBuilder, stateMachine, x1, y1, xyComp, workingOuterDiameter, HoleDiameter); return; } // we are not dealing with a circle. We are dealing with an OBRound // now figure out the diameter of the endpoint circles workingOuterDiameter = (int)Math.Round((minorEllipseAxis * stateMachine.IsoPlotPointsPerAppUnit)); // calc the amount we have to add/subtract on the center point to get to the // endpoints majorEllipseAxisEndpointCompensation = (int)Math.Round(((majorEllipseAxisAdjustedForCircle * stateMachine.IsoPlotPointsPerAppUnit) / 2)); majorEllipseAxisEndpointCompensation += (xyComp / 2); // compensate the endpoints, the way we do this is dependent on whether // the ellipse is horizontal or vertical. NOTE according to the spec OBRounds // cannot have a rotation. They are either Horizontal or Vertical if (ellipseIsVertical == false) { x1EllipseCompensatedCenterpoint = x1 - majorEllipseAxisEndpointCompensation; x2EllipseCompensatedCenterpoint = x1 + majorEllipseAxisEndpointCompensation; y1EllipseCompensatedCenterpoint = y1; y2EllipseCompensatedCenterpoint = y1; circle1StartDegrees = 90; circle1SweepDegrees = 180; circle2StartDegrees = 270; circle2SweepDegrees = 180; } else { x1EllipseCompensatedCenterpoint = x1; x2EllipseCompensatedCenterpoint = x1; y1EllipseCompensatedCenterpoint = y1 - majorEllipseAxisEndpointCompensation; y2EllipseCompensatedCenterpoint = y1 + majorEllipseAxisEndpointCompensation; circle1StartDegrees = 180; circle1SweepDegrees = 180; circle2StartDegrees = 0; circle2SweepDegrees = 180; } // calculate the endpoints of the wide line rectangle. MiscGraphicsUtils.GetWideLineEndPoints(x1EllipseCompensatedCenterpoint, y1EllipseCompensatedCenterpoint, x2EllipseCompensatedCenterpoint, y2EllipseCompensatedCenterpoint, (workingOuterDiameter + xyComp), out Point ptLL, out Point ptUL, out Point ptUR, out Point ptLR); // now set the dimensions of the bounding box if (ellipseIsVertical == false) { outerBoundingBoxLL_X = ptLL.X - x1EllipseCompensatedCenterpoint; outerBoundingBoxLL_Y = ptLL.Y; outerBoundingBoxUR_X = ptUR.X + x2EllipseCompensatedCenterpoint; outerBoundingBoxUR_Y = ptUR.Y; } else { outerBoundingBoxLL_X = ptLL.X; outerBoundingBoxLL_Y = ptLL.Y - y1EllipseCompensatedCenterpoint; outerBoundingBoxUR_X = ptUR.X; outerBoundingBoxUR_Y = ptUR.Y + y2EllipseCompensatedCenterpoint; } // now we draw according to the polarity if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_BACKGROUND) { // draw the first arc int circle1BuilderID = isoPlotBuilder.DrawGSContourArc(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, x1EllipseCompensatedCenterpoint, y1EllipseCompensatedCenterpoint, circle1StartDegrees, circle1SweepDegrees, ((workingOuterDiameter + xyComp) / 2), false, true, false); // draw the second arc int circle2BuilderID = isoPlotBuilder.DrawGSContourArc(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, x2EllipseCompensatedCenterpoint, y2EllipseCompensatedCenterpoint, circle2StartDegrees, circle2SweepDegrees, ((workingOuterDiameter + xyComp) / 2), false, true, false); // draw in the top line int line1BuilderID = isoPlotBuilder.DrawGSLineContourLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, ptUL.X, ptUL.Y, ptUR.X, ptUR.Y); // draw in the bottom line int line2BuilderID = isoPlotBuilder.DrawGSLineContourLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, ptLL.X, ptLL.Y, ptLR.X, ptLR.Y); // create a list of the builder IDs used List <int> builderIDList = new List <int>(); builderIDList.Add(circle1BuilderID); builderIDList.Add(circle2BuilderID); builderIDList.Add(line1BuilderID); builderIDList.Add(line2BuilderID); // now background fill the complex object, note how we only fill with the circle1BuilderID, This will be important when filling in the hole isoPlotBuilder.BackgroundFillGSByBoundaryComplexVert(builderIDList, circle1BuilderID, outerBoundingBoxLL_X, outerBoundingBoxLL_Y, outerBoundingBoxUR_X, outerBoundingBoxUR_Y, -1); // do we need to do a hole? if (HoleDiameter > 0) { // yes we do hDia = (int)Math.Round((HoleDiameter * stateMachine.IsoPlotPointsPerAppUnit)); int holeRadius = (hDia - xyComp) / 2; if (holeRadius > 0) { // draw the circle for the hole holeBuilderObjID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, x1, y1, holeRadius, GSFillModeEnum.FillMode_BACKGROUND, stateMachine.WantClockWise); // There is more going on here than is immediately obvious // we want the inside of the hole to not be filled with isocells from the holeBuilderObjID // we could just have not filled it and only placed edge isocells // but this causes complications on the erase. It is better to fill it then remove the outer // object then remove the background only pixels of the holeBuilderObjID. This makes sure we // get all of the outer objects isocells rather than relying on hitting the edge to // figure out where to start. // remove everything belonging to the obRoundObj we just drew, note we only filled with circle1BuilderID above so that is all we need to remove isoPlotBuilder.EraseABuilderIDFromRegionUsingABuilderID(holeBuilderObjID, circle1BuilderID, x1 - holeRadius - 1, y1 - holeRadius - 1, x1 + holeRadius + 1, y1 + holeRadius + 1); // remove all background only pixels belonging to the hole circle leaving just the edge pixels isoPlotBuilder.EraseBackgroundOnlyIsoCellsByBuilderID(holeBuilderObjID, x1 - holeRadius - 1, y1 - holeRadius - 1, x1 + holeRadius + 1, y1 + holeRadius + 1); } } } // bottom of if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_BACKGROUND) else if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_ERASE) { // we have to decide whether we are drawing a hole or not. In Clear Polarity holes do not come in as solid DARK spot like you // think they would. Instead they just "do nothing" on the area the contents below them remain there and there is no fill or erase if (HoleDiameter > 0) { // we are dealing with a erase flash with a transparent hole // draw the first arc int circle1BuilderID = isoPlotBuilder.DrawGSContourArc(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1EllipseCompensatedCenterpoint, y1EllipseCompensatedCenterpoint, circle1StartDegrees, circle1SweepDegrees, ((workingOuterDiameter + xyComp) / 2), false, true, false); // draw the second arc int circle2BuilderID = isoPlotBuilder.DrawGSContourArc(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x2EllipseCompensatedCenterpoint, y2EllipseCompensatedCenterpoint, circle2StartDegrees, circle2SweepDegrees, ((workingOuterDiameter + xyComp) / 2), false, true, false); // draw in the top line int line1BuilderID = isoPlotBuilder.DrawGSLineContourLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, ptUL.X, ptUL.Y, ptUR.X, ptUR.Y); // draw in the bottom line int line2BuilderID = isoPlotBuilder.DrawGSLineContourLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, ptLL.X, ptLL.Y, ptLR.X, ptLR.Y); // create a list of the builder IDs used List <int> builderIDList = new List <int>(); builderIDList.Add(circle1BuilderID); builderIDList.Add(circle2BuilderID); builderIDList.Add(line1BuilderID); builderIDList.Add(line2BuilderID); // note that at this point we effectively have an invert edge OBRound with FillMode_NONE // now do the hole we use invert edges so anything we draw over a background will get drawn, there is no fill hDia = (int)Math.Round((HoleDiameter * stateMachine.IsoPlotPointsPerAppUnit)); int holeRadius = (hDia - xyComp) / 2; if (holeRadius > 0) { // draw the circle for the hole using no fill. Whatever is in there is in there holeBuilderObjID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1, y1, holeRadius, GSFillModeEnum.FillMode_NONE, stateMachine.WantClockWise); } builderIDList.Add(holeBuilderObjID); // now erase between the outer and inner circles isoPlotBuilder.BackgroundFillGSByBoundaryComplexVert(builderIDList, IsoPlotObject.DEFAULT_ISOPLOTOBJECT_ID, outerBoundingBoxLL_X, outerBoundingBoxLL_Y, outerBoundingBoxUR_X, outerBoundingBoxUR_Y, -1); } else { // draw the obRound, we use invert edges here so we draw only if it is on some other background, we still erase everything under it though // draw the first arc int circle1BuilderID = isoPlotBuilder.DrawGSContourArc(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1EllipseCompensatedCenterpoint, y1EllipseCompensatedCenterpoint, circle1StartDegrees, circle1SweepDegrees, ((workingOuterDiameter + xyComp) / 2), false, true, false); // draw the second arc int circle2BuilderID = isoPlotBuilder.DrawGSContourArc(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x2EllipseCompensatedCenterpoint, y2EllipseCompensatedCenterpoint, circle2StartDegrees, circle2SweepDegrees, ((workingOuterDiameter + xyComp) / 2), false, true, false); // draw in the top line int line1BuilderID = isoPlotBuilder.DrawGSLineContourLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, ptUL.X, ptUL.Y, ptUR.X, ptUR.Y); // draw in the bottom line int line2BuilderID = isoPlotBuilder.DrawGSLineContourLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, ptLL.X, ptLL.Y, ptLR.X, ptLR.Y); // create a list of the builder IDs used List <int> builderIDList = new List <int>(); builderIDList.Add(circle1BuilderID); builderIDList.Add(circle2BuilderID); builderIDList.Add(line1BuilderID); builderIDList.Add(line2BuilderID); // now erase the background isoPlotBuilder.BackgroundFillGSByBoundaryComplexVert(builderIDList, IsoPlotObject.DEFAULT_ISOPLOTOBJECT_ID, outerBoundingBoxLL_X, outerBoundingBoxLL_Y, outerBoundingBoxUR_X, outerBoundingBoxUR_Y, -1); } } /* * DebugMessage(""); * DebugMessage("majorEllipseAxis=" + (majorEllipseAxis * stateMachine.IsoPlotPointsPerAppUnit)); * DebugMessage("minorEllipseAxis =" + (minorEllipseAxis * stateMachine.IsoPlotPointsPerAppUnit).ToString()); * DebugMessage("y1EllipseCompensatedCenterpoint =" + (y1EllipseCompensatedCenterpoint).ToString()); * DebugMessage("y2EllipseCompensatedCenterpoint =" + (y2EllipseCompensatedCenterpoint).ToString()); * DebugMessage("y2EllipseCompensatedCenterpoint-y1EllipseCompensatedCenterpoint =" + (y2EllipseCompensatedCenterpoint - y1EllipseCompensatedCenterpoint).ToString()); * DebugMessage("xyComp =" + xyComp.ToString()); * DebugMessage("workingOuterDiameter =" + workingOuterDiameter.ToString()); * DebugMessage(""); */ return; }
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Flashes the rectangular aperture at a point /// </summary> /// <param name="isoPlotBuilder">the builder opbject</param> /// <param name="stateMachine">the statemachine</param> /// <param name="xyComp">the xy compensation factor</param> /// <param name="x1">the first x value</param> /// <param name="y1">the first y value</param> /// <returns>z success, nz fail</returns> public override void FlashApertureForGCodePlot(IsoPlotBuilder isoPlotBuilder, GerberFileStateMachine stateMachine, int x1, int y1, int xyComp) { int holeBuilderObjID = 0; int hDia; if (isoPlotBuilder == null) { return; } if (stateMachine == null) { return; } int xComp = (int)Math.Round(((XAxisDimension * stateMachine.IsoPlotPointsPerAppUnit))); int yComp = (int)Math.Round(((YAxisDimension * stateMachine.IsoPlotPointsPerAppUnit) / 2)); if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_BACKGROUND) { // corrected, courtesy of MaikF int builderID = isoPlotBuilder.DrawGSLineOutLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, x1, y1 - yComp - xyComp / 2, x1, y1 + yComp + xyComp / 2, xComp + (1 * xyComp), stateMachine.BackgroundFillModeAccordingToPolarity); // do we need to do a hole? if (HoleDiameter > 0) { // yes we do hDia = (int)Math.Round((HoleDiameter * stateMachine.IsoPlotPointsPerAppUnit)); int holeRadius = (hDia - xyComp) / 2; if (holeRadius > 0) { // draw the circle for the hole holeBuilderObjID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_NORMALEDGE, x1, y1, holeRadius, GSFillModeEnum.FillMode_BACKGROUND, stateMachine.WantClockWise); // There is more going on here than is immediately obvious // we want the inside of the hole to not be filled with isocells from the holeBuilderObjID // we could just have not filled it and only placed edge isocells // but this causes complications on the erase. It is better to fill it then remove the outer // object then remove the background only pixels of the holeBuilderObjID. This makes sure we // get all of the outer objects isocells rather than relying on hitting the edge to // figure out where to start. // remove everything belonging to the outer circle from the circle we just drew, this // leaves anything else which might be in there in there isoPlotBuilder.EraseABuilderIDFromRegionUsingABuilderID(holeBuilderObjID, builderID, x1 - holeRadius - 1, y1 - holeRadius - 1, x1 + holeRadius + 1, y1 + holeRadius + 1); // remove all background only pixels belonging to the hole circle leaving just the edge pixels isoPlotBuilder.EraseBackgroundOnlyIsoCellsByBuilderID(holeBuilderObjID, x1 - holeRadius - 1, y1 - holeRadius - 1, x1 + holeRadius + 1, y1 + holeRadius + 1); } } } // bottom of if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_BACKGROUND) else if (stateMachine.BackgroundFillModeAccordingToPolarity == GSFillModeEnum.FillMode_ERASE) { // we have to decide whether we are drawing a hole or not. In Clear Polarity holes do not come in as solid DARK spot like you // think they would. Instead they just "do nothing" on the area the contents below them remain there and there is no fill or erase if (HoleDiameter > 0) { // we are dealing with a erase flash with a transparent hole // draw the rectangle, we use invert edges here so we draw the circle only if it is on some other background, use no fill. We will erase between it and the hole later int builderID = isoPlotBuilder.DrawGSLineOutLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1, y1 - yComp - xyComp / 2, x1, y1 + yComp + xyComp / 2, xComp + (1 * xyComp), GSFillModeEnum.FillMode_NONE); // now do the hole we use invert edges so anything we draw over a background will get drawn, there is no fill hDia = (int)Math.Round((HoleDiameter * stateMachine.IsoPlotPointsPerAppUnit)); int holeRadius = (hDia - xyComp) / 2; if (holeRadius > 0) { // draw the circle for the hole using no fill. Whatever is in there is in there holeBuilderObjID = isoPlotBuilder.DrawGSCircle(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1, y1, holeRadius, GSFillModeEnum.FillMode_NONE, stateMachine.WantClockWise); } // now erase between the outer and inner circles // we are dealing with a simple erase flash, no hole List <int> builderIDList = new List <int>(); builderIDList.Add(builderID); builderIDList.Add(holeBuilderObjID); MiscGraphicsUtils.GetWideLineEndPoints(x1, y1 - yComp - xyComp / 2, x1, y1 + yComp + xyComp / 2, xComp + (1 * xyComp), out Point ptLL, out Point ptUL, out Point ptUR, out Point ptLR); // now erase the background isoPlotBuilder.BackgroundFillGSByBoundaryComplexVert(builderIDList, IsoPlotObject.DEFAULT_ISOPLOTOBJECT_ID, ptLL.X, ptLL.Y, ptUR.X, ptUR.Y, -1); } else { // draw the rectangle, we use invert edges here so we draw only if it is on some other background, we still erase everything under it though int builderID = isoPlotBuilder.DrawGSLineOutLine(IsoPlotUsageTagFlagEnum.IsoPlotUsageTagFlag_INVERTEDGE, x1, y1 - yComp - xyComp / 2, x1, y1 + yComp + xyComp / 2, xComp + (1 * xyComp), GSFillModeEnum.FillMode_ERASE); } } }