Question

I am trying to use the Clipper library to modify a graphics path.

I have list of widths that represent outlines / strokes. I want to start with the largest first and work my way down to the smallest.

For this example, we will add 2 strokes with widths of 20 and 10.

I want to take take my graphics path, and expand / offset it by 20 pixels into a new graphics path. I do not want to alter the original path. Then I want to fill the new graphics path with a solid color.

Next, I want to take my original graphics path, and expand / offset it by 10 pixels into a new graphics path. I want to fill this new path with a different color.

Then I want to fill my original path with a different color.

What is the proper way to do this. I have the following method that I created to try and do this, but it is not working properly.

private void createImage(Graphics g, GraphicsPath gp, List<int> strokeWidths)
{
  ClipperOffset pathConverter = new ClipperOffset();
  Clipper c = new Clipper();
  gp.Flatten();

  foreach(int strokeSize in strokeWidths)
  {
    g.clear();
    ClipperPolygons polyList = new ClipperPolygons();
    GraphicsPath gpTest = (GraphicsPath)gp.Clone();    
    PathToPolygon(gpTest, polyList, 100);
    gpTest.Reset();

    c.Execute(ClipType.ctUnion, polyList, PolyFillType.pftPositive, PolyFillType.pftEvenOdd);
    pathConverter.AddPaths(polyList, JoinType.jtMiter, EndType.etClosedPolygon);                  
    pathConverter.Execute(ref polyList, strokeSize * 100);

    for (int i = 0; i < polyList.Count; i++)
    {
      // reverses scaling
      PointF[] pts2 = PolygonToPointFArray(polyList[i], 100);
      gpTest.AddPolygon(pts2);
    }
    g.FillPath(new SolidBrush(Color.Red), gpTest);
  }
}

        private void PathToPolygon(GraphicsPath path, ClipperPolygons polys, Single scale)
        {
            GraphicsPathIterator pathIterator = new GraphicsPathIterator(path);
            pathIterator.Rewind();
            polys.Clear();
            PointF[] points = new PointF[pathIterator.Count];
            byte[] types = new byte[pathIterator.Count];
            pathIterator.Enumerate(ref points, ref types);
            int i = 0;
            while (i < pathIterator.Count)
            {
                ClipperPolygon pg = new ClipperPolygon();
                polys.Add(pg);
                do
                {

                    IntPoint pt = new IntPoint((int)(points[i].X * scale), (int)(points[i].Y * scale));
                    pg.Add(pt);
                    i++;
                }
                while (i < pathIterator.Count && types[i] != 0);
            }
        }
        private PointF[] PolygonToPointFArray(ClipperPolygon pg, float scale)
        {
            PointF[] result = new PointF[pg.Count];
            for (int i = 0; i < pg.Count; ++i)
            {
                result[i].X = (float)pg[i].X / scale;
                result[i].Y = (float)pg[i].Y / scale;
            }
            return result;
        }
Was it helpful?

Solution

While you've made a pretty reasonable start, you seem to be getting muddled in your createImage() function. You mention wanting different colors with the different offsets and so you're missing a colors array to match your strokeWidths array. Also, it's unclear to me what you're doing with the clipping (union) stuff, but it's probably unnecessary.

So in pseudo-code I suggest something like the following ....

static bool CreateImage(Graphics g, GraphicsPath gp, 
    List<int> offsets, List<Color> colors)
{
    const scale = 100;
    if (colors.Count < offsets.Count) return false;

    //convert GraphicsPath path to Clipper paths ...
    Clipper.Paths cpaths = GPathToCPaths(gp.Flatten(), scale);

    //setup the ClipperOffset object ...
    ClipperOffset co = new ClipperOffsets();
    co.AddPaths(cpaths, JoinType.jtMiter, EndType.etClosedPolygon); 

    //now loop through each offset ...
    foreach(offset in offsets, color in colors)
    {
      Clipper.Paths csolution = new Clipper.Paths();
      co.Execute(csolution, offset);
      if (csolution.IsEmpty) break; //useful for negative offsets
      //now convert back to floating point coordinate array ...
      PointF[] solution = CPathToPointFArray(csolution, scale);
      DrawMyPaths(Graphics g, solution, color); 
    }
}

And something to watch for if you were to use increasingly larger offsets, each polygon drawn in the 'foreach' loop would hide previously drawn polygons.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top