5

I have this fiddle : https://jsfiddle.net/thatOneGuy/1spn8nne/1/

This has two paths, for now, they are just rect but are actually complicated shape. I have used this code to create two rects with 'holes' in :

createRects(dataPointsPath2, 'blue');
createHoles(dataPointsCircle2);
createRects(dataPointsPath1, 'red');
createHoles(dataPointsCircle1);

What I need are the holes to be removed from the filled path so you can see the rect behind it.

How do I go about removing a shape (circle) from a filled path ?

EDIT

I have just realised, a clip path will probably work well for this, I'll try implement but if someone has any ideas I'd appreciate the help :)

altocumulus
  • 21,179
  • 13
  • 61
  • 84
thatOneGuy
  • 9,977
  • 7
  • 48
  • 90

2 Answers2

8

Here's one way to draw a rect with a hole in. It relies on the evenodd fill-rule. The circle is inside the rect it becomes a hole in the rect and the blue background shows through.

  <svg viewBox="0 0 250 250">
  <rect width="100%" height="100%" fill="blue"/>
    <path fill="#b4edb4" fill-rule="evenodd"  d="M230,230H8V13h223
  V236z M 120 80 a 50 50 0 1 0 0.00001 0z"/>
</svg>
Robert Longson
  • 118,664
  • 26
  • 252
  • 242
  • Great stuff, this will help a lot, thanks, appreciate it – thatOneGuy Jul 29 '16 at 09:38
  • Do you have any idea how to create the combined path from the data I provided ? I have the points for the rect and points for the circles in the question – thatOneGuy Jul 29 '16 at 09:52
  • 1
    Tthe circle is formed from the elliptical arc (a command, the prior M command and the final Z). The rest form the rect. – Robert Longson Jul 29 '16 at 10:02
  • Thanks again, sorry to be a pain but do you know how to produce this via D3 using the data I have provided ? – thatOneGuy Jul 29 '16 at 10:21
  • 1
    I'm too lazy to do that. Try it yourself and ask a new question if you get stuck. – Robert Longson Jul 29 '16 at 10:23
  • Aren't we all, no worries appreciate the help :) – thatOneGuy Jul 29 '16 at 10:24
  • @RobertLongson Marvelous, what a great answer! But, if I am not mistaken, the evenodd [fill-rule](https://www.w3.org/TR/SVG/painting.html#FillRuleProperty) won't take the direction of the crossed path segments into account, right? That's something used to evaluate the insideness for the nonzero fill-rule. – altocumulus Jul 29 '16 at 12:43
  • @altocumulus Indeed, I misremembered, evenodd just needs the circle to be iside the rect, nonzero would require directionality too. – Robert Longson Jul 29 '16 at 12:51
1

@Robert Longson gave a nice answer but I was unable to duplicate it using my dataset and D3. So, thanks to @PaulLeBeau for his point about masks.

For the mask, you need a rect element, filled white for it to work. It uses this to know what to mask against(I think).

var thisMask = thisContainer.append("svg:mask")
    .attr("id", board + '_mask')

  thisMask.append("rect")
    .attr('x', 0)
    .attr('y', 0)
    .attr('width', "100%")
    .attr('height', "100%")
    .style('fill','white')

And in the same mask element, you need the rest of the shapes you want to remove from the path.

So in my case, I wanted a collection of circles :

thisMask.selectAll('.circle')
    .data(data)
    .enter()
    .append("circle")
    .attr('cx', function(d) {
      console.log('clippath', d)
      return d.x
    })
    .attr('cy', function(d) {
      return d.y
    })
    .attr('r', function(d) {
      return d.radius
    })

And that's it. I have updated the fiddle : https://jsfiddle.net/thatOneGuy/1spn8nne/4/

thatOneGuy
  • 9,977
  • 7
  • 48
  • 90