Path starting and ending at same element (looping back) - svg

I'm new to the svg library (HTML5 svg tag and related) and trying to learn. I'm working on a little diagram editor in VueJS using svg. I know how to create a rectangle and even a path from one rectangle/circle/etc to another, but I'm not unclear how to approach creating a path that starts and ends at the same rectangle (i.e., a loop) - something like this
I don't care that much where the start and end points intersect the rectangle, though I'd prefer they were relatively close to one another or at least on the same edge of the rectangle.
Thanks for any nudges in the right direction.

Just create a <path> that consists of a bezier curve whose control points form a rectangle.
<svg viewBox="0 0 100 100">
<rect x="50" y="10" width="50" height="40" fill="#4472c4"/>
<path d="M 50,20 C 10,20 10,40, 50,40" fill="none" stroke="red"/>
<!-- Show grey lines to indicate the bezier control points -->
<line x1="50" y1="20" x2="10" y2="20" fill="none" stroke="lightgrey" stroke-width="0.5"/>
<line x1="50" y1="40" x2="10" y2="40" fill="none" stroke="lightgrey" stroke-width="0.5"/>
</svg>

Related

Issue with z-order of elements in SVG being rendered incorrectly

I created an SVG image as defined by the code below. The issue I am having is that the SVG image is being rendered incorrectly. The z-order of the elements in my SVG are incorrectly being rendered.
Issue: There are 3x rectangles (color = white), which are above another rectangle (color=red). These 3x rectangles are positioned to have the same y-axis as the red rectangle behind it and have the same height. Despite this positioning/height, there is a "red" border that seems to be visible on the edge of the white rectangle due to the red rectangle in the back. It seems that z-order or stacking order of the elements in the rendered image is not being respected.
Below is a screenshot of the SVG image open in the Inkscape showing the issue. Even after converting the SVG image to PNG, the z-order issue still existing.
After troubleshooting with the position, I noticed the issue randomly disappears depending on the value of the y-axis position, or SVG image size (pixels/width/height). For example, if you change edit the SVG from y="2.5807" to y="2.5", the issue goes however. However, this is a NOT a feasible solution for my issue, but the exact positing and SVG image size properties is are fixed can shouldn't be changed.
Updates
I tried #Robert Longson suggestion of using shape-rendering="crispEdges" on the root SVG. It did NOT solve the issue for my use case. Using his suggestion, it does solve the issue on how the image is rendered in a browser, however the issue is still visible in Inkscape or when SVG is converted to PNG. For my use case, I care about how the SVG is rendered after being converted to PNG.
I just found out shape-rendering="crispEdges does work when SVG is converted to PNG, but its dependent on how the conversion process works. If I use rsvg-convert command in Bash, which is provided by librsvg2-bin, to convert the SVG to PNG, the issue is fixed. However, if I use convert command in Bash, which is provided by ImageMagick, the issue still exist. If open the SVG file in Inkscape and then Export as PNG, then the issue still exist.
<svg baseProfile="full" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:xlink="http://www.w3.org/1999/xlink" width="912px" height="1140px" preserveAspectRatio="none" viewBox="0,0,8.0,5.0">
<rect x="0" y="0" width="8.0" height="5.0" fill="white" />
<rect x="0" y="2.5807" width="8.0" height="1.0" fill="red" />
<g transform="translate(2.9275,0)">
<rect x="0" y="2.5807" width="0.5" height="1.0" fill="white" />
<rect x="1.0" y="2.5807" width="1.0" height="1.0" fill="white" />
<rect x="2.5" y="2.5807" width="1.5" height="1.0" fill="white" />
</g>
</svg>
that's antialiasing you can turn it off with shape-rendering="crispEdges"
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="912px" height="1140px" preserveAspectRatio="none" viewBox="0,0,8.0,5.0" shape-rendering="crispEdges">
<rect x="0" y="0" width="8.0" height="5.0" fill="white" />
<rect x="0" y="2.5807" width="8.0" height="1.0" fill="red" />
<g transform="translate(2.9275,0)">
<rect x="0" y="2.5807" width="0.5" height="1.0" fill="white" />
<rect x="1.0" y="2.5807" width="1.0" height="1.0" fill="white" />
<rect x="2.5" y="2.5807" width="1.5" height="1.0" fill="white" />
</g>
</svg>

svg: why does y="0" start outside the viewport instead of in the top edge for text?

This is the only thing preventing me from understanding how the coordination system works...
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="400" x="0" y="0">
<text x="0" y="0">
<tspan>✉</tspan>
</text>
</svg>
If the y-axis points down why does y="0" start from the outside of the viewport? Shouldn't it start right at the top edge? It blows my logic away...
The x-axis points to the right and it starts right in the left edge of the viewport, now this is logic and normal behavior.
Why does the y-axis behave like this? Or why make one start from the outside and the other not? What is the logic behind this? Unless I'm misunderstanding how it works...
It's all in the SVG specification
the initial coordinate system has the origin at the top/left with the x-axis pointing to the right and the y-axis pointing down
The origin for text is basically the bottom left corner of the glyph for left-to-right text.
For most uses of Latin text (i.e., writing-mode:lr, text-anchor:start and alignment-baseline:baseline) the alignment-point in the glyph will be the intersection of left edge of the glyph cell (or some other glyph-specific x-axis coordinate indicating a left-side origin point) with the Latin baseline of the glyph.
default baseline is in text downside, can use attr dominant-baseline change baseline, here is mdn example
<svg viewBox="0 0 200 120" xmlns="http://www.w3.org/2000/svg">
<path d="M20,20 L180,20 M20,50 L180,50 M20,80 L180,80" stroke="grey" />
<text dominant-baseline="auto" x="30" y="20">Auto</text>
<text dominant-baseline="middle" x="30" y="50">Middle</text>
<text dominant-baseline="hanging" x="30" y="80">Hanging</text>
</svg>

SVG viewBox scales some elements differently than others

The client asked to scale the drawing down so I added width/height and viewBox to the SVG element. The problem is that some elements are scaled differently than others. The structure of the svg is like this:
<svg width="100%" height="100%" viewBox="0 50 700 200">
<defs>...</defs>
<g id="group-ab">
Here are the boxes (drawn with polyline) that are scaled correctly, both in their own group (group-a, group-b)
</g>
<g id="group-a-id">
<text>A</text>
<circle id="group-a-id-bg-circle"></circle>
</g>
<g id="group-B-id">
<text>B</text>
<circle id="group-b-id-bg-circle"></circle>
</g>
<path id="group-a-pattern" d="..."></path>
<path id="group-b-pattern" d="..."></path>
</svg>
The path is the pattern for the pieces. It seems that everything that isn't under the group "group-ab" isn't scaled correctly, see the image below. Normally (when not using viewBox) the pattern fills up the boxes and the A/B are centered with the boxes (same translates are done to them).
Why is this happening? One would think that it doesn't matter if elements are within groups or not if svg is scaled.

Avoiding the aliasing / thin "bleed between touching elements

I have an svg which has multiple stroked paths, and a rectangle shape behind them that has the shape of the paths cut out of them. Effectively, the stroked paths should be "plugging the holes" in the rectangle shape.
The reason for this is that I would like to animate the paths so that they are erased, revealing what is underneath through the holes in the rectangle shape.
That's all well and good, and the animation works fine. The problem is that there is a hairline-thin space between the holes and the outside of the path strokes, so you can see what is underneath even while the paths are still there. You can see a screen capture of that here:
How do I avoid this happening? The space is not in the svg, because making it bigger on the page still has the space hairline thin:
I figure it has something to do with the aliasing, but have no idea how to combat it. I can't just apply a thicker stroke to the paths, because then the stroke starts to bleed into the other shapes, as seen here:
What else is there to do?
You could use a mask instead of a clip-path, since masks allow using the stroke to define the masked area.
<mask id="strokemask" maskContentUnits="objectBoundingBox"
x="0" y="0" width="100%" height="100%">
<circle cx="0.5" cy="0.5" r="0.1" stroke="white" fill="white"
stroke-width="0.02"/>
<circle cx="0.5" cy="0.5" r="0.15" stroke="white" fill="none"
stroke-width="0.03"/>
<circle cx="0.5" cy="0.5" r="0.22" stroke="white" fill="none"
stroke-width="0.05"/>
<circle cx="0.5" cy="0.5" r="0.3" stroke="white" fill="none"
stroke-width="0.06"/>
</mask>
Here's a live example of an animated mask that uses some stroked circles.

Manipulating individual tiles in an SVG pattern

I'm trying to create an interactive grid for a web game (HTML, JS, etc.), in which every cell should change it's fill on hover/click. I need both a regular square grid, and a triangular grid. I want it to be vector based so that it will scale nicely to fit different screen sizes. I thought the easiest way would be to create a pattern and fill it on a rectangle. This is the code I have so far:
<pattern id="baseTile" width="10" height="10" patternUnits="userSpaceOnUse">
<path id="tile" d="M 0,0 L 0,10 10,10 10,0 Z" fill="none" stroke="gray" stroke-width="1"/>
</pattern>
For the square, and this for the triangular grid:
<pattern id="baseTile" width="10" height="10" patternUnits="userSpaceOnUse">
<path d="M 5,0 L 10,2.5 10,7.5 5,10 0,7.5 0,2.5 Z" fill="none" stroke="gray" stroke-width=".1" />
<path d="M 5,0 L 5,10" fill="none" stroke="gray" stroke-width=".1" />
<path d="M 0,2.5 L 10,7.5" fill="none" stroke="gray" stroke-width=".1" />
<path d="M 0,7.5 L 10,2.5" fill="none" stroke="gray" stroke-width=".1" />
<path d="M 0,0 L 0,2.5 M 0,7.5 L 0,10" fill="none" stroke="gray" stroke-width=".1" />
<path d="M 10,0 L 10,2.5 M 10,7.5 L 10,10" fill="none" stroke="gray" stroke-width=".1" />
</pattern>
They produce the grids I need, but I don't know how to target each cell individually. I'm guessing since I've found no information on this, it's just not possible, and some other solution other than should be used. Any ideas?
Edit:
I want to be able to cycle through different fills on mouse click. For the square grid, I'm using the code I found here: http://bl.ocks.org/bunkat/2605010 but for the triangular lattice, I have absolutely no idea where to begin. That's why I thought of .
PS: I should probably add I have no programming experience, I'm trying to make a nonogram game to teach myself some Javascript.
Patterns are purely decorative. Targetting a single tile within a pattern would be like targetting a single colour within a gradient. Better not to think about them as distinct "tiles", and instead think of it as a sheet of repeating wallpaper.
So what to do? Well, you are going to need a distinct element for each piece that you want to be able to manipulate. But since they are mostly the same, you'll want to use <use> elements to repeat the graphics. You'll need to do a bit of math to figure out how to position the triangles just right, but no worse than what you had to do to figure out that pattern. It will of course be easiest to create the elements with a loop in your JS script, although you could hard code the original elements in a <defs> section.
Moreover, you don't specify what you want to do with the individual cells. If you are going to be changing their appearance, it might help to remember that you can set styles on the <use> element and these will be inherited by the re-used graphics. So if you don't set fill/stroke directly, you can change them by styling the <use>, instead of having a separate, differently coloured template to swap in.

Resources