SVG "Smooth curve" clarification - svg

In the W3C standard for SVGs, I read for 'S' and 's':
(x2 y2 x y)+
Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the reflection of the second control point on the previous command relative to the current point. (If there is no previous command or if the previous command was not an C, c, S or s, assume the first control point is coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command the new current point becomes the final (x,y) coordinate pair used in the polybézier.
In the case of a polybezier, does the "current point" or "previous command" change during this subpath?
In other words, does implicitly calculated first control point ever change during a S or s subpath if multiple sets of coordinates are specified?
The standard says "at the end of the [S or s] command", so I suspect not.

I'm not sure I understand what you are asking, or what you mean by "change".
The following example:
M 0,0 S 100,100, 120,120, 200,200, 220,220
is considered to be a shorthand for:
M 0,0 S 100,100, 120,120 S 200,200, 220,220
So when you get to the second set of S coordinates (the 200s), the "previous command" is considered to be the "S 100,100 120,120", not the "M 0,0".
Is that what you are asking?

Related

Drawing quadrant shape with cv2 in Python

I'm extracting door shapes with Cubicasa5k dataset but I don't know how svg draws quarter rounds.
Extracted some nodes of svg files with xml encoding, I found that it had a dictionary including 'd' key and value under of "Panel" id tree node like :
{'d': 'M825.47,986.05 q0.00,-72.92 72.92,-72.92 l0.00,72.92Z'}
It draws like this quarter round on svg image.
I want to draw that shape on raster image with cv2 in Python with that dictionary value.
Read article about 'd' commands but I'm still confused.
How can I draw it?
Okay, I hope I understood that d command of svg clearly now
That arc shape is Bézier curve, and it is drawn concavely based on the outer point, not on the centripetal point.
According to the example above ('M825.47,986.05 q0.00,-72.92 72.92,-72.92 l0.00,72.92Z'), it means drawing process following this:
The starting point of arc shape(called Bézier curve) is (825.47, 986.05).
The drawn reference point exists at a position shifted only to the y-axis. So the reference point is (825.47+0.00, 986.05-72.92) = (825.47, 913.13)
And the end point of arc shape is set following calculation about starting point like 2 : (825.47+72.92, 986.05-72.92) = (898.39, 913.13)
Drawing door shape finishes with drawing straight line that starts from the end point of the curve and ends with l code, it points (898.39+0.00, 913.13+72.92) = (898.39, 986.05)
Simply speaking, the door shape is drawn with arc and straight line, arc starts from left bottom and ends right top, then straight line starts from that right top and ends right bottom.
So if using cv2, should call cv2.ellipse, and set center point from process 4 above, axes(same value will show circle-like shape, so it's radius) from q command above, startangle and endangle(it differs from command above)

How to get edges of a polygon using pixijs?

I'm using pixi.js to create some editable polygons. So, what I want to achieve is this:
I have one polygon
Then, when I hit the edge a small circle should appear
And next I can drag and drop that part of the edge to creating a new point for the polygon
For now, what I know is the polygon vertices and I'm thinking to use the line function (y=mx+b) to check if the point where the mouse is belongs to the edge. My problem here, is that I have no idea how to obtain that edges. Any Suggestion? Of course, if you have any other idea to do this feel free to share =).
For now, what I know is the polygon vertices
You probably draw your polygon using https://pixijs.download/dev/docs/PIXI.Graphics.html#drawPolygon method by passing to it a list of points - similar as last shape in this example: https://pixijs.io/examples/#/graphics/simple.js
// draw polygon
const path = [600, 370, 700, 460, 780, 420, 730, 570, 590, 520];
graphics.lineStyle(0);
graphics.beginFill(0x3500FA, 1);
graphics.drawPolygon(path);
graphics.endFill();
^ In that example we have 5 points: P (600, 370), Q (700, 460), R (780, 420), S (730, 570), T (590, 520).
It also means that we have 5 edges: PQ, QR, RS, ST, TP
Now, we should have some way to tell if mouse pointer "is hovered over some some edge". By "is hovered" i mean: it lies in some distance from edge - lets say said distance is 10 pixels. So we want to know if mouse pointer is 10 pixels away from some edge.
To know that we can use formula explained in Line defined by two points part in: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_two_points
P1=(x1,y1) and P2=(x2,y2) - are the beginning and end vertices of some edge (for example PQ)
(x0,y0) is our "mouse point"
You can iterate over all edges and perform above calculation - if the distance is less that 10 pixel for some edge then you have the answer. If there is more than one edge which meets this requirement then you should pick one with smallest distance (it can happen if for example mouse is placed near some vertice).
Now you have the selected edge. Now lets do following point from your question:
2. Then, when I hit the edge a small circle should appear
To calculate position of this circle we can use equation from same Wikipedia page: https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line#Line_defined_by_an_equation - the part The point on this line which is closest to (x0,y0) has coordinates:.
Here you need to convert coordinates of vertices from your selected edge to line function.
Then we can proceed to last point from your question:
3. And next I can drag and drop that part of the edge to creating a new point for the polygon
You can do it by adding new vertice to your polygon.
Lets assume that selected edge is PQ - then this new vertice should be added between vertices P and Q in the vertices list which you pass to drawPolygon method. Lets name this new vertice X. Coordinates of vertice X should be equal to current mouse coordinates.
Then you will have following edges: PX, XQ, QR, RS, ST, TP.
You probably want to activate this "mode" after mouse is clicked and when mouse button is down etc - but that is separate issue related to interactivity / GUI etc - not graphics :) .
Note: is good to separate your presentation part of application (graphics / pixi.js related things) from mechanics and interactivity / GUI etc. So for example: do your calculations in separate place (other class, method etc) from where you do your actual drawing (calling pixi.js methods, update canvas etc). Store results of calculations in some place (from above example it would be: list of vertices, position of circle, colors etc), and then when time comes to draw you take those results and just draw polygons using them. Dont mix everything in one place ;)

SVG path spec: moveTo and implicit lineTo

I am trying to write a little SVG path parser / normalizer and got one last issue with the spec:
As far as I understood the most commands support additional implicit commands and when they do so and are in relative mode, the "current point" will be updated after the last implicit command, not in between them.
But the "moveTo" command is something special and allows implicit "lineTo" commands. While the "lineTo" command it self will only update the "current point" after the last implicit command:
Draw a line from the current point to the given (x,y) coordinate which
becomes the new current point. L (uppercase) indicates that absolute
coordinates will follow; l (lowercase) indicates that relative
coordinates will follow. A number of coordinates pairs may be
specified to draw a polyline. At the end of the command, the new
current point is set to the final set of coordinates provided.
I am not sure what the "moveTo" with additional "lineTo" does.
Extract of the SVG Path Spec:
Start a new sub-path at the given (x,y) coordinate. M (uppercase)
indicates that absolute coordinates will follow; m (lowercase)
indicates that relative coordinates will follow. If a moveto is
followed by multiple pairs of coordinates, the subsequent pairs are
treated as implicit lineto commands. Hence, implicit lineto commands
will be relative if the moveto is relative, and absolute if the moveto
is absolute. If a relative moveto (m) appears as the first element of
the path, then it is treated as a pair of absolute coordinates. In
this case, subsequent pairs of coordinates are treated as relative
even though the initial moveto is interpreted as an absolute moveto.
Especially the last sentence is confusing.
And even worse, in SVGTiny Path Spec they wrote another description, while almost everything else is the same:
A new sub-path at the given (x,y) coordinate shall be started. This
shall also establish a new current point at the given coordinate. If a
relative 'moveto' (m) appears as the first element of the 'path', then
it shall treated as a pair of absolute coordinates. If a 'moveto' is
followed by multiple pairs of coordinates, the subsequent pairs shall
be treated as implicit 'lineto' commands.
Does it mean that the "current point" is updated in between (which would be inconsistent to everything else) or was it just a ambiguous description which they corrected in newer versions?
Well it all seems perfectly clear to me.
Here's an illustration of the two modes being used to draw two square (100px × 100px) boxes. The first uses absolute coordinates, and the second uses relative coordinates. As the spec states, when the first coordinate is specified with a lower-case 'm', then it is treated as an absolute coordinate, but all the coordinates that follow it are treated as relative.
<svg widtn="250" height="140" viewBox="0 0 250 140">
<g fill="none" stroke-width="5">
<!-- 1. Move with implicit LineTo (absolute) -->
<path d="M10,10 110,10 110,110 10,110z" stroke="blue" />
<!-- 2. Move with implicit LineTo (relative) -->
<path d="m120,10 100,0 0,100 -100,0z" stroke="red" />
</g>
</svg>

d3.js (svg) path directionality vs. marker-end, hive plots

Scenario:
I am currently working with d3.js Hive Plots, and for an experimental scenario, I need control of the visual directionality of the links.
Problem Source:
I am using d3.hive.v0.js "add-on", and due to the fact that Hive Plots charts do not inherently carry any visual directionality information on the links, the add-on does not adhere to source and target nodes with regards to directionality of the drawn path, and always draws the paths clockwise, and hence always pointing in the direction of the node to the left when snapping on a marker-end.
Problem Effect
Pointers are unreliable because the path "end" that the marker adheres to, is not nescessarily at the target, but may be at the source, depending on the source and targets relative position to each other.
What it boils down to
I know that I could draw my own links with my own Arcs and gain more control, instead of using the d3 Hive Plot library links, and I have tried it, but it's not easy to get nice, and the Hive Plot algorithm is pretty and I would like to use it.
Let's say I intercept d, when it comes back from the Hive Plot link path algo.
I find d = "M 8.327598234202001e-15,-136 C 101.06769626492562,-91.00176246480473 135.0052608777338,14.189624701397365 79.7912059004442,109.82317321396422";
That gives me a nice path, starting in the top, and drawing down (CW) as demostrated in
http://jsfiddle.net/hwehqhtm/1/
svg.selectAll(".test")
.data(d3.range(1))
.enter().append("path")
.attr("class", "link")
.style("stroke", "green")
.attr("stroke-width", 3)
.attr("marker-end", "url(#arrow)")
.attr("d", d)
My question is, now that I have d, is it possible to "reverse the draw direction" of the line by doing something on d ? , - making "path end" be the top end of the line, and make the marker-end go here, effectivly making the link point the other direction.
The line should be visually identical, only logically "beginning" in the other end.
The parent fiddle http://jsfiddle.net/hwehqhtm uses two different markers for start and end, but then I would have to include new logic in some marker-end and marker-start callbacks to switch them on off, and I would really prefer to just mathemagically make it draw "the other way" by "reversing d" if possible. I'm not good enough at trig to know if this even makes sense. Any comments and solutions on path directionality and markers are very welcome !
Thx
Seems that it can be done by switching around the parameters. The d above, created by the hive plot algorithm turns out to be a moveto and a curveto (bezier) in the format M x y C x1 y1 x2 y2 x y.
To reverse the curve, switching endpoint x,y in C with startpoint x,y in M and then switching the bezier control points x1,y1 with x2,y2 seems to do it.

Close SVG path (Z) with control points?

I draw SVG path like this:
Start:
M x, y
Add Curve
Q x1, y1, x, y
etc etc.... and when I want to close path I just add Z
But that last line segment now doesn't have any control points.
How can I close path and also have control points on that last segment?
Something like : Z Q x1, y1 where Z closes path (current point to first point) but it uses x1 and y1 as control points, not just straight line.
I understand your desire. Every time I draw a freehand blob in Illustrator I always set the final point on the original point and then drag to create a tangent for the control points on either side.
SVG has no such feature like this. There is no path command that does this. The closest are the shorthand path commands S and T, but these derive the first control point from the last control point on the previous command, whereas what you want is something that derives the second control point from the first control point on the next command (wrap-around style).
You could do this with JavaScript, e.g. markup like
<path d="… Z" class="smooth-close" />
…and a small script that finds all the smooth-close paths, determines the appropriate control point from the first commands and generates a S or T command and appends it to the path data. But since you did not tag your question with javascript I assume such a solution would not interest you.

Resources