5

From my previous thread where @Alexey Popkov proposed polygon2FilledCurve to remove the outline when exporting to PDF. Now I'm trying to apply that to this but it doesn't work. How can I fix this?

polygon2FilledCurve = # /. {Polygon[pts_ /; Depth[pts] == 3] :> 
      FilledCurve[Thread[Line[pts]]], 
     Polygon[pts_ /; Depth[pts] == 2] :> FilledCurve[Line[pts]]} &;
polygon = {{0, 1}, {1, 3}, {5, 5}, {5, 0}, {-5, 0}, {-5, 5}, {-4, 
    3}, {0, 1}};
image = ListLinePlot[polygon, Filling -> {1 -> Axis}] /. _Line -> 
   Sequence[]

Export["image.pdf", polygon2FilledCurve[image]

This is the image:

enter image description here However, the exported PDF image is not correct.

enter image description here

I'm looking for a solution which work for many types of polygon.

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
hana
  • 2,388
  • 5
  • 19

2 Answers2

6

You need to convert the GraphicsComplex style polygon to a normal, coordinate-point one:

gcpolyToNormal = # /. 
    GraphicsComplex[p_, g_, o___] :> 
     GraphicsComplex[p, 
      g /. Polygon[v_] :> Polygon[v /. i_Integer :> p[[i]]]] &;

polygon2FilledCurve = # /. {Polygon[pts_ /; Depth[pts] == 4] :> FilledCurve /@ Thread[Line[pts]], Polygon[pts_ /; Depth[pts] == 3] :> FilledCurve[Line[pts]]} &; polygon = {{0, 1}, {1, 3}, {5, 5}, {5, 0}, {-5, 0}, {-5, 5}, {-4, 3}, {0, 1}}; image = ListLinePlot[polygon, Filling -> {1 -> Axis}] /. _Line -> Sequence[] Export["image.pdf", polygon2FilledCurve[gcpolyToNormal@image]]

(I always add "/tmp/" to my test filenames, but it doesn't work on Windows, I think.)

Alternatively, one can fix polygon2FilledCurve for GraphicsComplex:

polygon2FilledCurve = # /. {Polygon[pts_ /; Depth[pts] == 2] :> 
      FilledCurve[Thread[Line[pts]]], 
     Polygon[pts_ /; Depth[pts] == 1] :> FilledCurve[Line[pts]]} &;

But then you have to decide which way you want it. Or one could write a more complicated function that parsed the polygons to see if they were "normal" or GraphicsComplex type.

Here's a way to convert polygons in GraphicsComplex to filled curves without converting them to normal form:

gcpolyToFilledCurved = # /. 
    GraphicsComplex[p_, g_, o___] :> GraphicsComplex[p, g /. {
        Polygon[v : {__Integer}] :> FilledCurve[Line[v]],
        Polygon[v : {{__Integer} ..}] :> 
         FilledCurve[Thread@Line[v]]}] &;

I see @AlexeyPopkov had already posted something like this, plus a fix to the polygon2FilledCurve from the OP. I was called to dinner before I finished my edit.

Michael E2
  • 235,386
  • 17
  • 334
  • 747
  • 2
    If you want more rigorous checking, this checks whether the polygon is in the proper form for a GraphicsComplex: gcpolyToNormal = # /. GraphicsComplex[p_, g_, o___] :> GraphicsComplex[p, g /. Polygon[v_?(VectorQ[#, IntegerQ] || AllTrue[#, VectorQ[#, IntegerQ] &] &)] :> Polygon[v /. i_Integer :> p[[i]]]] &; – Michael E2 Jul 06 '22 at 01:20
  • Actually your solution won't do the trick because after applying gcpolyToNormal we have Depth[pts] == 4, and polygon2FilledCurve won't be applied. The same mistake is with your "fix" for polygon2FilledCurve. See also my answer. – Alexey Popkov Jul 06 '22 at 03:38
  • Please check the output of updated polygon2FilledCurve[gcpolyToNormal@image] - it is as shown in the question due to the reasons explained in my answer. All cases of FilledCurve[Thread@Line[v]] should be replaced with FilledCurve /@ Thread[Line[v]]. – Alexey Popkov Jul 06 '22 at 04:20
  • Here is what I get: screenshot. – Alexey Popkov Jul 06 '22 at 04:27
  • @AlexeyPopkov Sorry, I've got bad eyes and misread which thing to fix. – Michael E2 Jul 06 '22 at 04:29
  • @AlexeyPopkov Thanks! – Michael E2 Jul 06 '22 at 04:33
4

The problem arises in the first place because the original version of polygon2FilledCurve used the single-component multi-segment syntax form of FilledCurve instead of its multi-component single-segment syntax form. Also, FilledCurve's filling behavior for overlapping components differs from the one for Polygon, hence for the conversion purposes we must use only the single-component single-segment syntax form. Here is a universal version of polygon2FilledCurve which should work correctly independently of the presence of GraphicsComplex inside of Graphics:

polygon2FilledCurve = # /. 
     g_GraphicsComplex :> (g /. {
         Polygon[pts : {{__Integer} ..}] :> FilledCurve /@ Thread[Line[pts]],
         Polygon[pts : {__Integer}] :> FilledCurve[Line[pts]]}) /.
    {Polygon[pts : {_?(MatrixQ[#, NumericQ] &) ..}] :> FilledCurve /@ Thread[Line[pts]],
     Polygon[pts_ /; MatrixQ[pts, NumericQ]] :> FilledCurve[Line[pts]]} &;

I've updated my answer in the linked thread with this implementation.


More info about the syntax and the use cases of FilledCurve can be found in this answer.

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368