Create a polygon in SVG from a group of elements in JS - javascript

I have an SVG with groups of elements (which are all a uniform shape but the group shape can vary. Obviously putting an outline on the group will give me a rectangle shape. What I am trying to achieve is a polygon that outlines the group giving a rough trace of the items:
http://codepen.io/wroughtec/pen/OXvRrq
(below is an example with circles although have also included a path example as that is what I am actually being given)
<h2>No Outline</h2>
<svg viewbox="0 0 1000 200">
<g>
<circle cx="20" cy="20" r="10" fill="grey" />
<circle cx="50" cy="20" r="10" fill="grey" />
<circle cx="50" cy="50" r="10" fill="grey" />
<circle cx="80" cy="50" r="10" fill="grey" />
<circle cx="110" cy="50" r="10" fill="grey" />
<circle cx="140" cy="50" r="10" fill="grey" />
<circle cx="50" cy="80" r="10" fill="grey" />
<circle cx="80" cy="80" r="10" fill="grey" />
<circle cx="110" cy="80" r="10" fill="grey" />
<circle cx="140" cy="80" r="10" fill="grey" />
<circle cx="170" cy="80" r="10" fill="grey" />
<circle cx="200" cy="80" r="10" fill="grey" />
<circle cx="170" cy="110" r="10" fill="grey" />
<circle cx="200" cy="110" r="10" fill="grey" />
</g>
</svg>
I need to be able to do this automatically in JS but getting the x and y of the circles gives me the center point so my manual one I have had to cheat to expand to the outer shape of the circles and remove unnecessary points (i.e. points on the same line or in the middle).

It sounds like what you're looking for is the convex hull of the points.
D3.js implements this, and a good example can be found here. (The code is rather lengthy, so I won't reproduce it here; the main function of interest is d3.geom.hull)
Note that D3.js v4 was just recently released, and almost all code online (including what I linked) is written for v3. There are significant changes between the two, but v4 still has a convex hull function.

Related

SVG connect two points with a line, and automatically update the line if a point is moved

I'd like to connect two points (circles) with a line:
window.onclick = () => {
document.getElementById('c2').setAttribute("cx", 150);
}
<svg>
<circle cx="10" cy="10" r="2" id="c1" />
<circle cx="90" cy="50" r="2" id="c2" />
<line x1="10" y1="10" x2="90" y2="50" stroke="black" />
</svg><br>
Click here to move a circle.
such that if I modify the center of any <circle> with setAttribute("cx", 150) then the line automatically follows the new circle position.
Is there a way to do this with <use>? Something like (pseudo-code):
<svg>
<circle cx="10" cy="10" r="2" id="c1" />
<circle cx="90" cy="50" r="2" id="c2" />
<use x1=xlink:c1:cx y1=xlink:c1:cy x2=xlink:c2:cx y2=xlink:c2:cy stroke="black" type="line" />
</svg>
Goal: I don't want to have to set the coordinates two times, in both the circle and line. Instead I would like to set the coordinates once, and that the line uses a reference to the circle elements.
Note: I have read SVG connect two points with a line but it did not help.
You can use a <marker> that can be placed on start, middle and end of an element.
window.onclick = () => {
document.getElementById('l1').setAttribute("x2", 150);
}
<svg viewBox="0 0 200 100" width="200">
<defs>
<marker id="circle" viewBox="0 0 4 4" refX="2"
refY="2" markerWidth="4" markerHeight="4">
<circle cx="2" cy="2" r="2" />
</marker>
</defs>
<line id="l1" x1="10" y1="10" x2="90" y2="50" stroke="black"
marker-start="url(#circle)" marker-end="url(#circle)"/>
</svg><br>
Click here to move a circle.

Click and drag to select and move points on a SVG

With a SVG with lines and points like this:
svg line { stroke: black; stroke-width: 2px; }
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" id="svg">
<circle id="1a" cx="20" cy="20" r="3"></circle><circle id="1b" cx="20" cy="120" r="3"></circle><line id="1c" x1="20" y1="20" x2="20" y2="120"></line>
<circle id="2a" cx="20" cy="120" r="3"></circle><circle id="2b" cx="60" cy="80" r="3"></circle><line id="2c" x1="20" y1="120" x2="60" y2="80"></line>
<circle id="3a" cx="60" cy="80" r="3"></circle><circle id="3b" cx="100" cy="120" r="3"></circle><line id="3c" x1="60" y1="80" x2="100" y2="120"></line>
<circle id="4a" cx="100" cy="120" r="3"></circle><circle id="4b" cx="140" cy="100" r="3"></circle><line id="4c" x1="100" y1="120" x2="140" y2="100"></line>
<circle id="5a" cx="140" cy="100" r="3"></circle><circle id="5b" cx="100" cy="20" r="3"></circle><line id="5c" x1="140" y1="100" x2="100" y2="20"></line>
</svg>
is there a way with a HTML <svg> to be able to click and drag to select points? So that we can then mouse drag to move these points?
Before re-inventing the wheel, are there ways to do this directly with a <svg>?
For example, how to use the Drag and Drop API (see https://coursesweb.net/javascript/drag-drop-html5-attributes_t) to achieve this:
<tag draggable='true' ondragstart='handler(event)' id='draggable_elm'>Content</tag>
for SVG elements?

Onclick JavaScript Variables

Java script -
I am trying to do this task to get into coding but it's getting a bit confusing
My HTML:
<svg height="400" width="400">
<circle cx="205" cy="203" r="150" fill="black" />
<circle cx="200" cy="200" r="150" fill="#FFCE54"/>
<circle cx="150" cy="160" r="43" fill="black" />
<circle cx="150" cy="160" r="40" fill="white" />
<circle cx="250" cy="160" r="43" fill="black" />
<circle cx="250" cy="160" r="40" fill="white" />
<circle cx="150" cy="160" r="20" fill="black"/>
<circle cx="250" cy="160" r="20" fill="black"/>
<circle cx="145" cy="154" r="5" fill="white"/>
<circle cx="245" cy="154" r="5" fill="white"/>
<path d="M 135.5,260 q 65,45 130,0"
stroke="red" stroke-width="12" fill-opacity="0" />
id="mouth"/>
</svg>
My Javascript code:
faceShape.onclick = function(){
faceShape.setAttribute("fill", "#A0D468");
};
The problem is that when I run the code, I get this error -> Uncaught TypeError: Cannot set property 'onclick' of null"
Please help. Am I doing something wrong?
faceShape does not appear anywhere in your markup. I am guessing based on what you provided that you want to add an id="faceShape" to your <circle> elements or something similar. The error message is saying that there is nothing assigned to faceShape that is why it can't read any property of null.
<svg height="400" width="400">
<circle cx="205" cy="203" r="150" fill="black" />
<circle id="faceShape" cx="200" cy="200" r="150" fill="#FFCE54"/> //added id
<circle cx="150" cy="160" r="43" fill="black" />
<circle cx="150" cy="160" r="40" fill="white" />
<circle cx="250" cy="160" r="43" fill="black" />
<circle cx="250" cy="160" r="40" fill="white" />
<circle cx="150" cy="160" r="20" fill="black"/>
<circle cx="250" cy="160" r="20" fill="black"/>
<circle cx="145" cy="154" r="5" fill="white"/>
<circle cx="245" cy="154" r="5" fill="white"/>
<path d="M 135.5,260 q 65,45 130,0"
stroke="red" stroke-width="12" fill-opacity="0" />
id="mouth"/>
</svg>
I fully changed your javascript to make the click work:
document.getElementById("faceShape").addEventListener("click", change);
function change() {
document.getElementById("faceShape").setAttribute("fill", "#A0D468")
};
Here is a working jsfiddle of what I think you're trying to accomplish: https://jsfiddle.net/g0k5joxq/1/
If not then you can use the code to extrapolate to your circumstances.

Inkscape: SVG to PDF removes groups

I've made a SVG generator using SVG.js, which creates over 100 Path elements, all of them placed in 6 different groups. The resulting SVG file opened with Adobe Illustrator shows the groups ok.
However, I'm using Inkscape in command-line (required for automation process) to convert the SVG file to PDF.
inkscape --file=input.svg --export-area-drawing --without-gui --export-pdf=output.pdf
The PDF file is generated with all its Path elements on the same level, without any kind of groups/layers, which is mandatory for my project.
Is there a way Inkscape does not ungroup the elements?
Attached an image of the final result (left side, SVG file - right side, the converted PDF)
Image: SVG and PDF layers
Example SVG:
<svg width="100%" height="100%" viewBox="0 0 95 50" xmlns="http://www.w3.org/2000/svg">
<g stroke="green" fill="white" stroke-width="5">
<circle cx="25" cy="25" r="15" />
<circle cx="40" cy="25" r="15" />
<circle cx="55" cy="25" r="15" />
<circle cx="70" cy="25" r="15" />
</g>
<g stroke="red" fill="white" stroke-width="5">
<circle cx="25" cy="25" r="15" />
<circle cx="40" cy="25" r="15" />
<circle cx="55" cy="25" r="15" />
<circle cx="70" cy="25" r="15" />
</g>

How to get all elements at mouse position?

I have many elements on the same position and I want to listen for hover event on every element behind even if they are behind other elements, is there a way I can do this?
(They are not hierarchically related and sometimes they are circles, polygons, etc, so checking for bounding rect is not ok)
http://jsfiddle.net/4NdNS/4/
$circles.on("mouseover",function(){console.log(this);});
this is the solution:
FIDDLE
html:
<div id=response></div>
<svg id="mycircle Area">
<circle id="C1" fill="none" r="20" stroke="black" stroke-width="4" cx="100" cy="100"></circle>
<circle fill="none" r="20" stroke="black" stroke-width="4" cx="100" cy="100"></circle>
<circle fill="none" r="20" stroke="black" stroke-width="4" cx="100" cy="100"></circle>
</svg>
jq:
$('circle').on("mousedown",function(e){
$("#response").append($(e).attr('id')+' ');
e.preventDefault();
});
css:
circle{
pointer-events: all;
}
this is your edited fiddle

Categories

Resources