Draw a custom shape using SVG path Bezier curve - svg

Is it possible to draw a path like the image below? It is basically a circle with gaps in between.
I was just able to make a circle so far using SVG path -
http://jsfiddle.net/k6pgpze6/
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
width="500px" height="500px">
<path
d="
M 100, 250
a 150,150 0 1,0 300,0
a 150,150 0 1,0 -300,0
"
/>
</svg>

Yes is the basic answer. You could draw individual arcs (ie. one <path> per arc), or you can have a path with multiple subpaths.
<path d="M 100,250 a 150,150 0 1,0 300,0
M 300,0 a 150,150 0 1,0 -300,0" />
However these lines won't end with the oblique angles that your picture has. If you want those, then your best bet is to draw a complete circle and use a <clipPath> or <mask> to make the gaps.
Update
Here's an approximation of your picture.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
width="500px" height="500px">
<path transform="translate(250,250)"
fill="none"
stroke="black"
stroke-width="6"
d="M 39,-145
A 150,150, 0,0,1, 106,106
M 0,150
A 150,150, 0,0,1, -106,106
M -123,86
A 150,150, 0,0,1, -150,0
M -149,-13
A 150,150, 0,0,1, -39,-145" />
</svg>
To make it easier for myself, I've centred the circle at (0,0) and used a transform to move it to the right place on the page. Because I used this trick, it is easier to make sure all my coordinates (the M vales and the last two values in the A) are on the circle's circumference. It's just a bit of simple trigonometry (sin(angle)*150 and cos(angle)*150).
The trickiest part of an arc command is the large arc flag and sweep flag. The following diagram from the SVG spec is useful to look at.
In your case, you will want to use the same value for all the sweep flags (fifth value in the arc command). Choose either 1 or 0 depending on whether you are going clockwise or anti-clockwise around the circle.
For the large arc flag (fourth value), use 0 if your arc covers less than 180 degrees, or 1 if it is greater than 180 degrees.

Related

How to programmatically determine which geometric shape a svg path belongs to?

I have paths in SVG file with d attributes with values like
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300" viewBox="0 0 100 100" style="border:1px solid red" >
<path stroke="black" fill="red" d="M 48.75521890547264 45.77014427860697 Q 48.756218905472636 45.77114427860697 50.24875621890547 45.77114427860697 Q 51.74129353233831 45.77114427860697 55.72139303482587 46.26865671641791 Q 59.701492537313435 46.766169154228855 64.17910447761194 46.766169154228855 Q 68.65671641791045 46.766169154228855 73.6318407960199 47.2636815920398 Q 78.60696517412936 47.76119402985075 82.08955223880596 47.76119402985075 Q 85.57213930348259 47.76119402985075 88.55721393034827z" />
</svg>
etc.
I want to classify this path into geometric shapes like circle, rectangle, ellipse etc programmatically.When i mean programmatically i mean no image processing techniques should be involved i.e strict requirement. Is there any way to do this?
As seen from your example code used Quadratic Bezier Curves. Curves using Bezier curves are usually not written manually.
It’s easier to do this in any vector editor, or use online Bezier curve generators.
Like for example this generator:
In more detail, what all the commands for creating path and Bezier curves mean can be studied here.
Update
A quote from the documentation that explains the principle of constructing a quadratic Bezier curve
The other type of Bezier curve, the quadratic curve called with Q, is
actually a simpler curve than the cubic one. It requires one control
point which determines the slope of the curve at both the start point
and the end point. It takes two parameters: the control point and the
end point of the curve. Note that the co-ordinate deltas for q are
both relative to the previous point (that is, dx and dy are not
relative to dx1 and dy1).
Q x1 y1, x y (or q dx1 dy1, dx dy)
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M 10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>

SVG path to create triangle with rounded corners

I'm trying to create a simple svg path in the shape of a triangle with rounded corners.
I've started from this triangle:
<svg width="440" height="440">
<path d="M5,100 L70,5 L135,100 z" fill="none" stroke="black" stroke-width="3" />
</svg>
But I'm struggling to add in the corners - this is as far as I've got:
<svg width="440" height="440">
<path d="M5,100 a10,10 1 0 1 -5,-10 L70,5 L135,90 a10,10 1 0 1 5,10 z" fill="none" stroke="black" stroke-width="3" />
</svg>
What is the correct path coordinates to create the triangle with three smooth corners? Do I need to do some geometry to calculate the correct strart and end points or is there a tool I can use to configure the shape which will give me the coordinates the shape is made up of?
Inkscape has an option to convert the stroke of a path to a filled object. With that you can:
Draw a simple triangle and set fill to none.
Define a stroke with a width that is double the radius of the corner rounding you want to achieve.
Set stroke-linejoin:round (Fill and Stroke dialog -> Stroke style -> Round join).
Select from menu Path -> Stroke to Path.
Set to node selection mode. Now you can remove all nodes on the inner side.
Set fill to none again and select a stroke to your liking.

SVG arc is not following the ellipse

I want to draw a part of the ellipse with the arc path. But it doesn't work, as I thought it should. This is the code:
<svg fill="none" stroke="#f00" height="24" viewBox="0 0 24 24"
width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M16 3.4a9.7 8.3 0 1 0-9.3 14.4"/>
<ellipse cx="12.2" cy="11" rx="9.7" ry="8.3" stroke="green" opacity=".5"/>
</svg>
The picture:
The red path should follow the green path.
According to the arc code a rx ry x-axis-rotation large-arc-flag sweep-flag dx dy it should correspond to the part of ellipse that can be drawn to satisfy the constrains, as the start and the end points and the two radii. As you can see, through the point A and point B passes a green ellipse with radii 9.7 and 8.3. Why the arc with the same radii and same points doesn't correspond to it?
Can you find what should be the points A and B and radii, so that the acr is exactly the same as the part of the green ellipse?
Here is the codepen: https://codepen.io/anon/pen/eRXWLq
This is the closest I could get with completely different radii: https://codepen.io/anon/pen/EXMXNN
It's because your are using a "sweep flag" set to 0 (anticlockwise), and a "large arc" flag set to 1 (draw the longer of the two ways around the ellipse). However the anticlockwise distance between the two points on the ellipse is actually the shorter of the two ways around the ellipse.
The solution is to change the "large arc" flag to 0:
<svg fill="none" stroke="#f00" width="400px" viewBox="0 0 24 24"
width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M16 3.4a9.7 8.3 0 0 0 -9.3 14.4"/>
<ellipse cx="12.2" cy="11" rx="9.7" ry="8.3" stroke="green" opacity=".5"/>
</svg>
If you are wondering why the arc is still not perfectly accurate, it'll be either:
Your start and end points are not accurate. I.e. not perfectly on the ellipse, or
Numerical inaccuracy. See this answer for more info: How to render a svg circle using start and endAngle

How to make stroke width immune to the current transformation matrix

In SVG (and Canvas, Quartz, Postscript, ...), the transformation matrix affects both the path coordinates and the line width. Is there a way to make an adjustment so the line width is not affected? That is, in the following example, the scale is different for X and Y, which makes the square into a rectangle, which is OK, but it also makes the lines wider on two sides.
<g transform="rotate(30) scale(5,1) ">
<rect x="10" y="10" width="20" height="20"
stroke="blue" fill="none" stroke-width="2"/>
</g>
I can see that would be useful in many cases, but is there a way to opt out of it? I suppose I would like to have a separate pen TM or be able to set the pen to be an ellipse that the CTM converts into a circle, but I don't see anything like that.
Lacking that, I think I have to not tell SVG about my CTM and instead transform the coordinates myself, which means converting primitives like rect to their path equivalents.
Edit:
There is an attribute you can add to your rect to get exactly this behavior:
vector-effect="non-scaling-stroke"
This was wrong:
This will work if you apply the transform directly to the shape, not the group it is in. Of course, if you wanted to group several items and scale them all together, that approach won't work.
<rect x="10" y="10" width="20" height="20"
stroke="blue" fill="none" stroke-width="2"
transform="rotate(30) scale(5,1)"/>
This may also depend on your SVG viewer; Inkscape renders your file the way you want (stroke width not affected by scale) but Chrome renders it as you have shown.
In postscript, describing the path and performing the stroke are separate events, so it's perfectly possible to have a separate "pen" TM.
%!PS
%A Funky Shape
matrix currentmatrix %save normal matrix for stroke pen
306 396 translate
72 72 scale
1 1 5 { pop
360 5 div rotate
1 0 translate
0 0 moveto
1 1 5 { pop
360 5 div rotate
1 0 translate
1 0 lineto
-1 0 translate
} for
-1 0 translate
closepath
} for
setmatrix
[ 1 -3 4 2 0 0 ] concat %put some skew in the pen
10 rotate %makes it look more "organic"
stroke
showpage

How can I cut one shape inside another?

Is there a way to cut one shape out of another in SVG? For example, I have a rect and an ellipse and I want to make a rect with a transparent hole in the middle. I would imagine it would look something like this:
<set operation="difference" fill="black">
<rect x="10" y="10" width="50" height="50/>
<ellipse cx="35" cy="35" rx=10 ry=10/>
</set>
The closest thing I can find is clipping, which will give me the intersection of two shapes. In my example that would result in just the hole being solid and the rest of the rect being transparent.
I looked through Inkscape and there is a difference option in the path menu, but this converts the shapes to paths and then creates a new path. The identity of the shapes is lost so there is no easy way to, for example go into the svg file and change the radius of the ellipse.
Is there any ideas for how I might do this?
You must use path element to cut a hole.
See the example from the SVG specification: (you can click this link or the following image to view the real svg file)
<g fill-rule="evenodd" fill="red" stroke="black" stroke-width="3">
<path d="M 250,75 L 323,301 131,161 369,161 177,301 z"/>
<path d="M 600,81 A 107,107 0 0,1 600,295 A 107,107 0 0,1 600,81 z
M 600,139 A 49,49 0 0,1 600,237 A 49,49 0 0,1 600,139 z"/>
<path d="M 950,81 A 107,107 0 0,1 950,295 A 107,107 0 0,1 950,81 z
M 950,139 A 49,49 0 0,0 950,237 A 49,49 0 0,0 950,139 z"/>
</g>
For your case:
<path d="M10 10h50v50h-50z M23 35a14 10 0 1 1 0 0.0001 z"
stroke="blue" stroke-width="2" fill="red" fill-rule="evenodd" />
M10 10h50v50h-50z will draw a rect.
M25 35a10 10 0 1 1 0 0.0001 z will draw a ellipse.
fill-rule="evenodd" will make the hole.
The key point is draw outer shape and inner shapes(holes) in different direction (clockwise vs anti-clockwise).
Draw the outer shape clockwise and draw the inner(holes) shapes anti-clockwise.
Or conversely, draw the outer shape(holes) anti-clockwise and draw the inner shapes clockwise.
Concat the path datas of outer shape and inner shapes(holes).
You can cut more holes by concat more hole path data.
This image explain how to cut a hole:
See w3c docs:
SVG Arc Commands
and
SVG fill-rule Property.
You should be able to use the fill-rule: evenodd property to achieve this effect, if you want to prevent the fill of the rectangle from painting where the circle is. See this example from the SVG specification (image below will only display if your browser supports SVG):
For some reason, I can't seem to get this to work with shapes like the rect and ellipse that you provided in the question. Here's my attempt:
You need to use a <path> if you want to use fill-rule. But it's certainly possible to change the radius of a circle that is a subpath of the <path>, you just need to master the arc path command.
Unfortunately it seems that there's no way to do this. Neither SVG 1.0 nor SVG 1.1 do not support boolean shape operations, clipping is supported for other reasons. The closest you can get is trying to get a "inverted" shape to do the clipping.

Resources