1

This post may be too much on the technical side for this forum, but I don't know which other forum may be appropriate.

I would like to use the SVG clip path to act as a threshold for if you will show the object underneath or not. I would want to be able to take the decision to not show the underlying object based off of if

  • it's partially obscured by clip path,
  • >50% of object is clipped, or
  • threshold value (X %) for how much the object is obscured.

You could say that the first two are just special cases of the latter one.

Let's start from a generated picture to exemplify, and one edited to remove any object that is in any way cut however so slightly by the clip path. (As you can see the grid sample is not symmetric within the clip path, so it seems somewhat malformed, but that's expected.)

Dot grid clipped by circular clip path Dot grid clipped by circular clip path and edited to remove any dot partially obscured by clip path

I have looked at the clipPath attributes and I cannot find anything related to clipping in the SVG standard that would support this. Is this possible with the current SVG standard to use the clip path (or some other mechanism?) or do I need to resort to JS?!

(Another option I would like to see would be to make the clipped objects more transparent relative to how much they are clipped. This should be way harder but would not make a difference if you need to use JS to accomplish the above.)

A probably naïve way to do the clipping is shown here in the SVG markup and the left image is what results from this code:

<svg width="400" height="300" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg">
  <defs>
    <pattern id="small_dot_grid_element" width="7" height="7" patternUnits="userSpaceOnUse">
      <circle cx="4" cy="4" r="3" style="fill: black; stroke: none;" />
    </pattern>
    <clipPath id="circular_crop">
      <circle cx="150" cy="150" r="120" style="fill: black; stroke: none;" />      
    </clipPath>
  </defs>
  <rect width="100%" height="100%" style="fill: url(#small_dot_grid_element); clip-path: url(#circular_crop);" />
</svg>

3 Answers 3

3

An old trick which does not do the wanted thing, but something resembling. It removes all closed paths or preset closed curve objects (polygons, ellipses, circles) which are partially or fully outside a given path. The objects must not overlap and they must all have the same color - everyone will lose its individual fill and stroke. The program = Inkscape.

enter image description here

  1. Black circles. The red closed path is the border. Only 3 of the circles are fully inside the red path and will stay, others will be removed

  2. Another closed path (=blue) is drawn. It's around all except a small part of it visits inside the red path

  3. The red path is lifted to top and subtracted from the blue curve (=Path > Difference) The circles which should stay are outside the result.

  4. Select all and apply Path > Union

  5. Apply Path > Break Apart and delete that part of the union which is one contiguous chunk

1
  • Offcourse for dots you can offset the path for even partial hits.
    – joojaa
    Nov 7, 2021 at 19:00
2

(There is already a correct answer to your specific question. This is more of an extended comment with an alternative idea.)

As mentioned in comments it might be easiest to prepare the graphics in a vector drawing application like Illustrator or Inkscape (anyway that's how a traditional graphic designer thinks).

For a similar result, you could instead render the SVGs on a canvas, loop through each pixels of the canvas and add an SVG circle for each pixel which has a color above a certain threshold.

Both methods have the problem that the circles are created at a certain scale. You would have to always display all shapes at the same scale or you would get circles in different sizes. When scaling/zooming in, the circles would also be scaled.

Wouldn't it be a cooler effect if you instead regarded the circle pattern as a kind of rasterization? Like having a monitor with circular pixels. So the circles should never move or scale, instead the graphics should always be rendered using the same grid. This could be achieved in several different ways.

A simple method could be to render your graphics as black and white pixels with no anti-aliasing at a low resolution but scaled up and then add a pattern on top to create the circles.

Here is a quick and naïve JSFiddle showing the idea.

I see no advantage in rendering SVG circles instead of raster images. The user won't be able to tell the difference and, as mentioned above, the SVG circles won't necessarily be scalable as such as you might not want the circles to scale visually anyway.

If you need to be able to detect mouse interaction with the different shapes you would have to either write your on sprite class with mouse interactions or have a separate, hidden layer with the original SVGs and of course make sure they are always scaled exactly like the actual displayed image.

You could also look into making a WebGL shader that does the job or use some existing library like perhaps PixiJS.

No matter which method you choose, it's going to get a bit convoluted. The way you display the graphics is somewhat weaved into the functionality. Without knowing the details of your project, it's hard to tell which method would be best.

1

What you're trying to do is not possible with SVG alone. Any scripting language that can access the data (like JavaScript) should be able to do what you're trying to do.

The logic is actually pretty simple - get the line from the center of the big circle to the center of a little circle. Then continue on in that direction for the distance of the little circle's radius and get the point. If the distance from that point to the center of the big circle is greater than the radius of the big circle, hide/remove the little circle. Repeat for every little circle.

5
  • Thanks! I suspected this... The circle example was just that. What I was trying to accomplish is more difficult, and I have a clip path that is assymmetrical and is not easily described with a function. A complex example would have been to be able to construct maps using only SVG and onHover or DOM scripting for highlighting certain areas and introducing interactivity (e.g., clickable links).
    – Andy
    Nov 7, 2021 at 9:40
  • I'm guessing editing the SVG by hand is probably going to be your best bet for something more complex. The interactivity seems easily doable using something like JS. Nov 7, 2021 at 12:48
  • Depending on the nature of the shapes filling the larger shapes (it appears you have a grid of items?) you could potentially render this as a raster image, then go pixel by pixel to determine which shapes were cut off. It's not as trivial but could work if you need to batch a bunch of these. Nov 7, 2021 at 13:38
  • 1
    Most likely the best way to do something like this is to just use a graphics program when creating the shapes and such and do so in exactly the way that you need. Nov 7, 2021 at 13:39
  • I really wanted to avoid doing "by hand" as I already drew 4 different images earlier in Gimp but wanted to explore how I might produce them in raw SVG without any programming / scripting to be able to showcase the possibility to have the SVG renderer seemingly partially generate scalable content. I guess that SVG 2 won't have something like this either then as I believe that a release candidate is due soon... I will have to resort to generating the coordinates for all of these dots and then apply some further scripting to have valid SVG XML.
    – Andy
    Nov 7, 2021 at 14:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.