Dilate and erode SVG shapes using Javascript - javascript

EDIT:
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/5.0.2.1/main_demo.html
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):
#!/usr/bin/python26
from shapely.geometry import Polygon
from shapely.geometry import MultiPolygon
import sys
if len(sys.argv)>2:
inset=eval(sys.argv[1])
coords=eval(sys.argv[2])
else:
sys.exit()
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">
<defs>
<filter id="erode">
<feMorphology operator="erode" in="SourceGraphic" radius="12" />
</filter>
<filter id="dilate">
<feMorphology operator="dilate" in="SourceGraphic" radius="8" />
</filter>
<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
c0,2.941,8.191,5.669,3,12c-3.088,3.767-6.01-0.758-11-1c-19.56-0.948-33.241,12.296-33,34c0.163,14.698,8.114,24.492,4,41
c-1.408,5.649-6.571,15.857-10,21c-2.484,3.726-7.898,10.784-12,13c-4.115-11.677,2.686-27.29-6-35c-6.693-5.942-20.021-4.051-26,1
c-13.573,11.466-11.885,41.492-7,58c-5.8,1.772-18.938,7.685-23,12c-6.752-10.805-15.333-17.333-24-26c-3.307-3.307-9.371-12-15-12
c-16.772,0-13.963-15.741-13-28c1.283-16.324,1.727-28.24,4-42c1.276-7.72,8-16.411,8-23c0-7.416,15.945-29,23-29
c4.507,0,17.678-8.701,24-11C164.853,90.76,178.27,88.546,193.193,85"/>
</defs>
<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>
</svg>
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.
http://jsfiddle.net/5Qv5v/

Related

Showing in a browser an interactive 2D floorplan using html5 and javascript

I need to replace the Flash viewer I've built years ago to show interactive 2D floorplans coming from AutoCAD.
Currently, the AutoCAD files are read and converted to XML files containing the X and Y coordinates of the polygons representing the objects of the floorplan: rooms, walls, assets, etc. The objects in the drawing are clickable and can be set as visible or not depending on thematic views.
Sometimes these floorplans could be relatively big, having a lot of points.
I have already tried to use a web map control like LeafletJS as it has already the PAN and ZOOM functionality, I can insert clickable markers, and manage layers so I can show or hide objects by a thematic view. I have set the map CRS as metric, and I load data as GeoJSON. Unfortunately, with real-world mid-size floorplans, it is too much slow and sometimes became unresponsive.
The example below is made of 18630 line objects and is not very reactive on panning.
So now I'd like to draw directly the floorplan in the browser using SVG or CANVAS.
I'd prefer to use CANVAS as it is a lot faster than SVG, using also WebGL if supported, but I have to rely on a library in order to have events handlers and easy object management like a DOM.
So now I'm asking if a library like threeJS can handle easy a task like this, even if I need to map 2D objects and if it is the right technology to choose. In particular, is it possible with threeJS:
To assign events listener to objects to get their IDs?
To apply CSS3 rules to style objects, for example, to highlight a room or a table?
ThreeJS can easily draw to both SVG or Canvas elements?
With ThreeJS I can easily manage pan and zooming also?
Can it be displayed also on mobile devices? (Android and iOS)
If anyone knows better library or technology to accomplish this task I'm completely free to any suggestion.
(Please note that I need only 2D drawings because 3D has been already built with other technologies from Revit)
Using webgl (via three.js for instance) you can draw millions of simple line primitives at 60fps on GPU enabled desktop browsers.
Here's a contrived example of over a million line primitives, thrown together using three.js:
https://codesandbox.io/s/0pp3x92n4p
and here:
http://vectorslave.com/wireblueprint/index.html
Following feeela suggestion, I've implemented the visualization in SVG,
drawing the same model made up of 18630 lines.
It loads definitely faster than the GeoJSON Leaflet metric map.
The SVG is provided by an API querying for building and floor, and it returns the architecture as elements (text/plain) that I append to the element in my web page.
To append string path elements to the SVG DOM element I've used parseSVG library.
I haven't found a "native" support for pan and zoom, so I've used a jQuery library: Ariutta SVG Pan and Zoom
With Ariutta SVG pan&zoom I should be able to listen also to mobile events like pinch and touch.
Here is the javascript code:
<div id="mapArea">
<svg id="map" xmlns="http://www.w3.org/2000/svg" x="0" y="0" style="stroke: #00ff00; stroke-width: 0.2px;background:#000">
</svg>
</div>
<script type="text/javascript">
var container = document.getElementById('mapArea'),
width = container.offsetWidth,
svgMap = document.getElementById('map');
svgMap.setAttribute('width', width);
svgMap.setAttribute('height', width * 0.5);
$(document).ready(function () {
$.ajax({
type: "GET",
url: "/Public/GetSVGPlan.aspx?building=1423&floor=3",
dataType: "text",
success: function (result) {
svg = parseSVG(result);
svgMap.appendChild(svg);
svgPanZoom('#map', {
zoomEnabled: true,
controlIconsEnabled: true,
fit: true,
center: true
});
},
error: function (error) {
console.log(error);
}
});
});
</script>
The path elements returned by the API are like the following:
<path d="M173.7043 66.758, L173.7054 66.7701" />
<path d="M191.947 64.2563, L191.9198 63.9453" />
<path d="M129.3072 12.2843, L129.3301 12.3702" />
<path d="M129.3301 12.3702, L131.701 11.735" />
<path d="M191.6087 63.9725, L191.636 64.2836" />
<path d="M173.7054 66.77, L172.5553 66.9803" />
<path d="M131.3573 11.735, L129.3072 12.2843" />
<path d="M195.8466 63.9148, L195.8194 63.6037" />
<path d="M172.5553 66.9803, L172.6882 66.9687" />
<path d="M129.3074 12.2841, L129.2449 11.7366" />
<path d="M195.7694 63.6081, L195.8194 63.6037" />
<path d="M172.6882 66.9687, L173.7064 66.7821" />
<path d="M129.2451 11.7368, L129.2451 9.5381" />
<path d="M195.5083 63.631, L201.43 63.1124" />
<path d="M226.9927 14.458, L228.0006 14.4593" />
<path d="M173.7064 66.7821, L173.7075 66.7942" />
<path d="M129.2451 9.5381, L131.6964 9.5381" />
<path d="M201.4572 63.4235, L195.5356 63.9421" />
and the results:
On Internet Explorer 11 is very slow, using a lot of CPU on the rendering process for about 15s. I think this is due more to the js loop for appending the path elements, than the SVG rendering.
On mobile (Chrome on Android) has a good response and fast load/rendering (~3-5s)
Even if it works as expected, any suggestion to improve performance are very welcome!!

Place text/image on svg path centroid using D3

Ive looked and read all i could suck in, but im still having trouble with this Centroid. This is somewhat similar to another question of mine, but this is much more precise.
I need to put text / image on the centroid of these regions:
The svg data is already in my html file in the format like this:
So i need to select a path, and use the centroid function on the d attribute, and then place a dot/text/image etc.
Ive attached a JSFiddle to see all regions, dont mind the class´s/id´s.
<svg id="gamemap">
<g>
<path id='1613' class='Faction2' d='M541.02,336.29L541.71,336.09L543.77,338.27L543.53,338.58L545.92,338.99L546.76,340.12L548.44,340.06L548.55,341.76L549.62,342.38L549.24,343.35L550.58,344.54L551.35,344.98L553.38,344.66L554.17,345.63L555.37,345.08L556.2,346.28L556.8,345.6L557.82,347.73L558.51,347.76L558.17,350.15L560.8,350.12L561.12,349.61L563.89,349.94L562.98,351.84L562.99,353.94L562.06,353.97L560.77,357.72L561.33,357.73L562.06,359L563.49,358.5L563.75,357.85L564.17,358.09L564.64,361.19L565.52,361.68L564.51,362.21L564.67,363.38L565.17,363.21L565.35,364.41L566.19,364.53L566.23,365.29L569.63,364.91L570.56,365.3L571.08,364.96L570.95,363.35L571.8,363.24L572.34,361.72L574.42,361.71L574.42,362.52L575.54,363.71L576.52,363.63L577.12,366.98L576.92,367.48L576.48,367.11L576.07,368.69L575.27,369.28L574.63,368.34L573.95,368.48L572.81,367.73L572.98,367.22L568.79,367.27L569.11,368.9L570.26,368.96L570.47,370.97L569.84,371.57L569.93,372.77L567.97,373.13L567.87,374.18L566.53,374.8L566.23,376.97L567.97,378.31L567.71,378.99L568.26,379.8L569.51,379.48L570.12,379.99L569.89,381.28L570.75,381.42L571.35,380.69L571.68,381.56L572.49,381.6L572.58,384.48L570.45,386.31L571.53,387.37L575.34,387.32L576.48,386.31L577.75,387.06L578.55,386.9L578.72,386.35L579.14,388.12L582.26,388.74L582.26,388.74L581.47,393.15L580.4,393.97L579.82,395.87L579.08,395.35L578.2,396.29L577.28,396.38L577.29,397.52L578.35,397.61L579.28,399.98L577.41,400.34L577.11,401.08L574.8,400.87L573.67,400.08L572.75,400.72L573.27,402.26L572.33,403.02L572.9,403.12L572.9,406.09L571.14,407.62L570.94,409.22L568.54,410.96L567.72,412.46L566.65,412.91L565.57,412.67L565.46,413.7L564.67,413.56L564.03,414.13L564.03,414.13L563.23,413.7L563.3,412.5L561.74,411.47L557.87,411.88L557.23,410.94L555.76,411.18L555.63,410.36L554.66,409.87L554.24,407.54L553.46,408.06L553.18,407.52L552.19,407.79L552.13,406.67L551.61,406.4L549.85,407.78L550.64,408.52L550.35,409.52L548.88,409.93L546.92,411.56L545.91,411.15L545.41,411.64L544.54,410.6L544.91,410.04L544.56,409.77L546.56,408.07L547.67,406.07L544.73,404.27L543.76,404.56L543.09,406.83L542.24,406.82L539.6,408.5L538.88,407.79L538,407.92L538,407.92L537.23,405.88L537.12,402.37L536.06,401.04L535.09,400.83L534.83,399.78L535.59,399.16L534.97,398.75L535.21,397.8L533.26,395.38L533.52,394.35L534.62,394.05L534.62,392.72L537.23,391.28L536.87,390.53L535.54,390.11L534.7,388.6L533.39,389.1L531.94,387.66L530.97,388.6L530.32,387.03L529.52,386.78L528.33,384.83L528.33,384.83L528.41,383.41L529.8,381.78L529.94,380.84L532.2,379.27L533.94,379.57L535.17,379.04L535.49,373.58L534.78,368.75L534.3,368.46L535.34,367.54L533.5,369.09L533.31,368.51L533.16,368.93L532.38,368.65L532,369.91L530.61,370.02L528.8,367.47L527.48,367.8L527.47,367.3L526.14,367.25L526.14,363.27L522.15,361.35L522.76,360.8L522.44,359.18L522.85,358.72L524.05,359.8L525.49,359.83L527.33,359.09L528.25,356.19L527.62,355.4L528.01,353.55L528.68,353.53L528.87,352.71L531.71,352.3L530.81,349.43L531.44,349.31L531.64,347.98L530.57,347.83L531.23,345.53L531.87,345.02L533.86,344.93L534.43,345.96L534.87,345.52L535.88,345.99L536.09,347.44L537.04,348.1L537.23,349.32L538.33,350.45L541.8,348.99L541.87,348.25L543.42,347.42L544.39,347.69L544.48,346.42L542.96,344.27L543.61,341.8L540.93,341.91L540.88,341.21L538.99,340.21L538.31,338.99L538.71,337.22L538.71,337.22L539.3,337.63z'/><path id='1614' class='Faction2' d='M546.46,257.82L545.19,257.69L545.32,258.18L543.54,257.65L541.38,259.38L542.02,260.56L540.61,260.4L538.66,258.32L536.13,258.13L535.82,258.52L535.34,258.01L533.87,259.42L533.2,259.42L532.34,257.98L530.81,258.02L529.74,257.1L529.84,255.69L529,255.13L530.84,254.47L530.2,254L530.15,252.87L528.99,252.2L529.29,251L531.38,249.47L533.13,249.19L533.53,248.49L534.28,249.13L534.78,247.81L535.79,247.12L535.17,245.59L533.4,244.1L533.46,243.33L532.94,243.15L533.23,242.5L535.15,241.24L536.1,241.87L537.59,241.28L539.29,238.42L540,238.94L541.47,238.45L542.04,238.74L540.53,236.05L542.23,236.64L541.97,235.28L543.78,235.25L544.4,233.9L545.04,234.33L544.79,235.26L545.4,236.52L547.05,237.66L548.92,239.98L550.77,239.84L552.86,240.63L555.26,240.44L555.25,240.92L554.36,240.98L554.53,241.89L552.78,241.9L551.46,243.23L551.74,245.25L553.79,247.69L553.79,247.69L553.79,248.62L551.45,250.07L551.45,250.07L548.96,250.95L546.19,250.97L546.21,253.02L547.91,253.88L548.31,254.99L547.66,256.41L547.66,256.41z'/></g>
</svg>
JSFiddle
Hope for a solution to this. Thanks

SVG filters and interpolated values

I am creating a visualization with d3.js, and the effect I would like to achieve relies on a filter using feFlood and feBlend. For each path in the visualization, I need to interpolate a different flood-opacity value for the feFlood filter primitive. The filter is pretty simple:
<filter id="multiplyOverlay" x="0" y="0" width="100%" height="100%">
<feFlood flood-color="steelblue" flood-opacity=".1" />
<feBlend mode="multiply" in2="SourceGraphic"/>
</filter>
I don't believe there is any way to pass a value in to the filter. Will I need to create a new filter for each path (about 200) in the visualization with the appropriate flood-opacity value? Or is there some other way to achieve this?
If you're going to use different flood-opacities then you'll need to do multiple filters (although using 200 different opacity settings seems like overkill). (If it's any help to you, flood-opacity is a property and can be styled via CSS.)
Addition: Also remember that you can animate things with the SMIL <animate> element - if that's why you're doing the interpolation

D3 Multiple Visualizations Per Page Namespace IRI Conflicts [duplicate]

I've enbedded d3's force directed graph layout into extjs tabs so that each time a new tab gets added a new graph svg gets generated.
No Problemo so far.
Now I intended to turn the graph into a directed one (by adding a marker and tell the lines to use it)
Each generated svg elements is following this pattern:
<svg width="100%" height="100%">
<defs><marker id="end-arrow" viewBox="0 -5 10 10" refX="6" markerWidth="3" markerHeight="3" orient="auto"><path d="M0,-5L10,0L0,5" fill="#ccc"></path></marker>
</defs>
<g transform="translate(4,0) scale(1)"><line class="link" sig="30.84" style="stroke-width: 3;" x1="538" y1="347" x2="409" y2="467" marker-end="url(#end-arrow)"></line>
...
</g>
</svg>
With Crome everything works just fine.
So I arrived at the concusion that the structur and
the way I generate the svgs should be more or less correct.
But with Firefox the Markers will only show for the first svg. (the first tab)
All other svgs won't show any Arrowheads.
"Inspect Elements" tells me the Markers are there and that the lines are refering to them.
And this is where I'm running out of Ideas where or what to look for. :(
You have multiple non-distinct IDs within the same html or svg document. This is invalid, different UAs respond differently but as you're not allowed to do this, it doesn't really matter that they are inconsistent.

Trying to create a re-usable text-box (text with a square background-colour) in SVG 1.1?

I'm trying to create (what I thought would be!) a simple re-usable bit of SVG to show three lines of text, with a background colour - to simulate a 'post-it' note.
I have found some useful code here to get the Bounds of the Text http://my.opera.com/MacDev_ed/blog/2009/01/21/getting-boundingbox-of-svg-elements which I am using.
So: I'm creating an group of text elements like this in the 'defs' section of my SVG:
<svg id="canvas" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="post_it">
<text x="0" y="30" id="heading" class="heading">My Heading</text>
<text x="0" y="45" id="description" class="description">This will contain the description</text>
<text x="0" y="60" id="company" class="company">Very Big Company Ltd.</text>
</g>
And I'm displaying the text with a 'use' element like this:
<use id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
I'm using the onclick to trigger a call to the following javascript function (defined in 'defs' section):
function showId(elem) {
post_it_rect=getBBoxAsRectElement(elem);
document.getElementById('canvas').appendChild(post_it_rect);
}
(The 'getBBoxAsRectElement(elem)' is from the link I posted).
As this stands; this works just fine - however if I change my 'use' element to position the text in a different place like this:
<use x="100" y="100" id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
Now, the text displays in the correct place, but the resultant 'background-color' (actually a 'rect' element with opacity of 0.5) still shows on the top-left of the svg canvass - and the function used to calculate the rect is returning '-2' rather than '100' ('-98'?) as I need (I think).
What do I need to do to line up the 'rect' elements and the text elements ?
The author of the (very helpful article btw) script provides a more advanced script to draw a box round any 'bb' in an SVG, but I couldn't get this to work (missing 'transform' functions?).
I'm using Firefox 7.x to render the SVG ; and I'm loading a .svg file (ie, not embedded in html etc) straight from disk to test this).
Yes, you may need to compensate yourself for the x and y attributes on the <use> element for the time being, I'll try to find some time to update the blogpost and script.
Here's a draft SVG 1.1 test that among other things checks that the effect of the x and y attributes are included in the bbox. The line starting [myUse] is the one that tests this case, if it's red then that subtest failed. Chromium and Opera Next both pass that subtest, while Firefox nightly and IE9 doesn't. Note that the test itself has not gone through full review yet, and that it may still change.

Categories