Dilate and erode SVG shapes using Javascript


I finally found a way to erode and dilate polygons (offsetting) so that new geometry is created using Clipper library: https://sourceforge.net/projects/jsclipper/

Live demo of Javascript Clipper: http://jsclipper.sourceforge.net/

The Clipper can only handle polygons or multi-polygons (eg. polygons with holes), so for it to work with other graphical objects of SVG format, they have to be converted to straight lines. At least paths are rather easy to convert to lines using path.getTotalLength() and path.getPointAtLength() (http://whaticode.com/2012/02/01/converting-svg-paths-to-polygons/).

The other possibility is use this like technique (that does not create new geometry): https://stackoverflow.com/a/12723835/1691517

Is there any way to erode and dilate shapes in SVG via Javascript?

I have the following SVG example: http://jsfiddle.net/timo2012/2S4Kt/1/

There are three shapes, blue is original, green is eroded (thinned) and red is dilated (bolded). They are made in Illustrator.

I have tested erode and dilate filters, but the effect is not so good: https://dvcs.w3.org/hg/FXTF/raw-file/tip/filters/examples/feMorphology.svg

After few hours searching over internet, I have found only examples about bitmap image eroding and dilating, but nothing about vector shapes.

I have succeeded in dilating and eroding SVG polygons using Shapely ( http://toblerity.github.com/shapely/manual.html ) in Python by sending path points via Ajax call to PHP script which makes system() call to Python script, but this method is slow and requires server to do the work that could be done client side.

This is my code for dilating and eroding in Python (as you see it is quite short):


from shapely.geometry import Polygon
from shapely.geometry import MultiPolygon

import sys

if len(sys.argv)>2:

bowtie = Polygon(coords)
clean = bowtie.buffer(inset)
clean = clean.simplify(1, preserve_topology=False)
if clean.length>0:
  if clean.geom_type=="MultiPolygon":
    for n in range(0, len(clean)):
      print list(clean[n].exterior.coords)
      #print "\n"
  elif clean.geom_type=="Polygon":
    print list(clean.exterior.coords)

Also find this document, which tries to define dilate and erode in mathematical terms: http://en.wikipedia.org/wiki/Mathematical_morphology

There is a sentence "The basic idea in binary morphology is to probe an image with a simple, pre-defined shape, drawing conclusions on how this shape fits or misses the shapes in the image. This simple "probe" is called structuring element, and is itself a binary image (i.e., a subset of the space or grid)."

I assume that this method could be used in morphing vector shapes, but how...

EDIT: One comment in a reply raised a possible issue of using filters instead of creating new geometry: if someone wants to add drag handles to polygon points, then drag handles may seem to be in wrong place. This can be acceptable, because then the impression is that the original path data is untouched, which is actually the case in filters, but - after further testing - it proved that the quality is a bigger problem. According to this and this SVG filter uses pixel representation of vector graphic object instead of path data itself, which leads to not so good looking results.

EDIT2: POSSIBLE WORKAROUND: One of the answers in this page led me to use variable-width strokes and mask to achieve a good looking workaround to this issue. I made a few tests and get implemented an Adobe Illustrator -like Offset Path Effect.



You can sort of get what you seem to be after by stroking with different stroke-widths in combination with clip-path or mask. Here's an example, some explanations of how it's constructed see here and here (arrow up or down to see some other slides on that example).

It doesn't give you new geometry though, just something that might look like new geometry.


Have you actually tested SVG's native filters? This looks close enough:

<svg width="612" height="792" viewBox="0 0 612 792" xmlns="http://www.w3.org/2000/svg">

    <filter id="erode">
      <feMorphology operator="erode" in="SourceGraphic" radius="12" />
    <filter id="dilate">
      <feMorphology operator="dilate" in="SourceGraphic" radius="8" />
      <path id="original_path" d="M193.193,85c23.44,0.647,45.161,0.774,62,12c1.596,1.064,12,11.505,12,13
  <use xlink:href="#original_path" fill="#f00" filter="url(#dilate)"></use>    
  <use xlink:href="#original_path" fill="blue"></use>
  <use xlink:href="#original_path" fill="#1CFF00" filter="url(#erode)"></use>    

There is some clipping going on the dilate filter that can't seem to be resolved by increasing the filter region, but other than that it's pretty close to your illustrator rendering. Sure beats rendering server-side.



Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.