Tom Bombadil
Tom Bombadil

Reputation: 135

Filling a polygon with with multiple holes with pyCairo

I't trying to fill a polygon with pyCairo, but I want certain areas to ramin unfilled. For instance, I'd like to produce something like this:

enter image description here

The grey background is the background of the SVG viewer, so represents transparent portions of the image.

I tried with this code:

import cairo

cairo.FILL_RULE_EVEN_ODD
svg_file_pointer = open('CairoPoly.svg', 'wb')
shape_container = cairo.SVGSurface(svg_file_pointer, 500, 500)
shape_description = cairo.Context(shape_container)
shape_description.rectangle(0, 0, 500, 500)
shape_description.clip_preserve()
shape_description.stroke()

shape_description.set_line_width(1)
shape_description.set_source_rgb(20/51, 0, 0)
for r in ((100, 100, 400, 400), (200, 200, 350, 300), (150, 110, 190, 390)):
    shape_description.move_to(r[0], r[1])
    shape_description.line_to(r[0], r[3])
    shape_description.line_to(r[2], r[3])
    shape_description.line_to(r[2], r[1])
    shape_description.line_to(r[0], r[1])
shape_description.close_path()
shape_description.fill()
shape_container.finish()
svg_file_pointer.close()
del shape_container

But it doesn't work. Can this be done with pyCairo, and if yes, how?

Upvotes: 1

Views: 1323

Answers (2)

Paul LeBeau
Paul LeBeau

Reputation: 101820

I'm not familiar with pyCairo, but the basic idea of what you are doing is correct from an SVG perspective. You are close, but not quite right.

What you want to do is create a path with sub-paths for each of your rectangles.

<svg width="500" height="500">
    <path d="M 100,100 L 100,400 L 400,400 L 400,100 L 100,100
             M 200,200 L 200,300 L 350,300 L 350,200 L 200,200
             M 150,110 L 150,390 L 190,390 L 190,110 L 150,110 Z"
          fill-rule="evenodd" />
</svg>

It looks like this is more or less what you are doing, but you'll also need to change the fill-rule to "evenodd". The default of "nonzero" will result in your "holes" being filled in instead of being left as holes.

set_fill_rule(cairo.FILL_RULE_EVEN_ODD)

Upvotes: 2

Aaron Digulla
Aaron Digulla

Reputation: 328614

To get a polygon with holes, you need to define the outline. The outline must be a single stroke. Edges which partially overlap will cause problems. My suggestion: Too hard to get right.

I see two solutions:

  1. Cut the shape into rectangles which can be filled and simply omit the "transparent" parts.
  2. Render the outer rectangle into a new (off screen) image. Draw the holes with a transparent color. Then overlay this image with the rest.

Note: There are 2D libraries which can intersect rectangles for you. If you have one (there might even be one in Cairo), then you can let the library calculate the necessary list of rectangles by applying boolean operations to the outer rectangle (adding and removing rectangles from it to get lists of rectangles).

Upvotes: -1

Related Questions