What do the 'M', 'c' and 'z' mean in SVG paths? - svg

http://paperjs.org/examples/
I'm trying to create a custom path with Chain, and I see that Tadpoles has a predefined heart-shaped path, so I'm trying to copy it but I don't understand a few parts of it.
var heartPath = new Path('M514.69629,624.70313c-7.10205,-27.02441 -17.2373,-52.39453 -30.40576,-76.10059c-13.17383,-23.70703 -38.65137,-60.52246 -76.44434,-110.45801c-27.71631,-36.64355 -44.78174,-59.89355 -51.19189,-69.74414c-10.5376,-16.02979 -18.15527,-30.74951 -22.84717,-44.14893c-4.69727,-13.39893 -7.04297,-26.97021 -7.04297,-40.71289c0,-25.42432 8.47119,-46.72559 25.42383,-63.90381c16.94775,-17.17871 37.90527,-25.76758 62.87354,-25.76758c25.19287,0 47.06885,8.93262 65.62158,26.79834c13.96826,13.28662 25.30615,33.10059 34.01318,59.4375c7.55859,-25.88037 18.20898,-45.57666 31.95215,-59.09424c19.00879,-18.32178 40.99707,-27.48535 65.96484,-27.48535c24.7373,0 45.69531,8.53564 62.87305,25.5957c17.17871,17.06592 25.76855,37.39551 25.76855,60.98389c0,20.61377 -5.04102,42.08691 -15.11719,64.41895c-10.08203,22.33203 -29.54687,51.59521 -58.40723,87.78271c-37.56738,47.41211 -64.93457,86.35352 -82.11328,116.8125c-13.51758,24.0498 -23.82422,49.24902 -30.9209,75.58594z');
I don't understand what the M at the start of the path means, or the c in some of the values or z at the end of the path. I tried to find info about it in their docs or Google it but I can't find what I want because single letters make searching tough.
I tried to remove the M at the start and the Tadpoles stopped moving, so I assume M potentially means 'moving'? Removing the c alters the shape of the heart, but removing the z doesn't seem to change anything.

M: Move to
The command "Move To" or M, which was described above. It takes two parameters, a coordinate ' x ' and coordinate ' y ' to move to. If your cursor already was somewhere on the page, no line is drawn to connect the two places. The "Move To" command appears at the beginning of paths to specify where the drawing should start
z: Close Path
This command draws a straight line from the current position back to the first point of the path. It is often placed at the end of a path node, although not always. There is no difference between the uppercase and lowercase command.
c: Bezier Curves
The cubic curve, C, is the slightly more complex curve. Cubic Beziers take in two control points for each point. Therefore, to create a cubic Bezier, you need to specify three sets of coordinates.
source: https://developer.mozilla.org/en/docs/Web/SVG/Tutorial/Paths
-EDIT-
You can visit https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/d for a full reference to all the possible commands and their usage.

The constructor you are invoking is this:
Path(, pathData)
Where pathData is described as:
the SVG path-data that describes the geometry of this path
The documentation you should read is the one of SVG.
As #GerardoFurtado mentioned in the comments, here is a read that could be of interest for you.

Related

Calculate CV2 Homography by points and line

I have a list of points in the field (like upper_goal_point/ left_upper_outer_corner, etc.
I know their corresponding coordinates in destination image - so I can calculate the homography:
h, status = cv2.findHomography(pts_src, pts_dst)
I also have blue points in the upper corner line (look at image above), which I only know that their destination's y coordinates are 0 (because they are in the upper line), but I don't know where exactly they lay in that line.
Can I use those blue points in order to improve the homography?
P.S.
You can see that the upper corner line in the homography is not horizontal line, it's diagonal, which of course is not correct:
Actually it possible to use line correspondence in find homography.
https://www.researchgate.net/publication/220845575_Combining_Line_and_Point_Correspondences_for_Homography_Estimation
Several years ago we implement this approach in one project. During simplification all math we come up with simple trick. We transform every line a*x + b*y + c = 0 to point (a/c, b/c)
// *** Don't copy paste this code, read below! ***//
Point2f convertPointsToHomogeneousLine(Point2f p1, Point2f p2) {
Point3f p1h(p1.x, p1.y, 1);
Point3f p2h(p2.x, p2.y, 1);
Point3f lineHomo(p1h.y*p2h.z - p1h.z*p2h.y,
p1h.z*p2h.x - p1h.x*p2h.z,
p1h.x*p2h.y - p1h.y*p2h.x);
Point2f lineHomoNorm(lineHomo.x / lineHomo.z,
lineHomo.y / lineHomo.z);
return lineHomoNorm;
}
And pass this points inside. As I remember I also dig inside OpenCV implementation of findHomography and insert this lines somewhere inside to solve step. Inside OpenCV there some normalization applied to points before pass to solve step. So you need to skip this step for this kind of points.
We do not use it in production. User need to calibrate camera manually by providing lines and points on the image and in meter system. It has too complicated interface and bad stability. But in your case I think it can work better. If you will automatically find lines and correspondence.
P.S. Please note that in paper they use some normalization technique.
It will improve stability. We faced with stability problem, do not
solved it in our journey.

.translated function not working in Godot / best way to change position of bone

I'm working on a program in Godot using Gdscript, and I ran into a problem when trying to use the Transform.translated(Vector3) function. My code is supposed to move a bone to (0,0,0) by translating it by its current coordinates but with negative sign. Example: (1,2,3) would be translated by (-1,-2,-3) so it would end up at (0,0,0). For some reason when I do this, the end position of the bone is not (0,0,0), but some other coordinate. In the Godot documents, it says the .translated function is "relative to the transform's basis vectors", so maybe that's why? Also if there is a better way to change a bones position than using the Transform.translated(Vector3) function that would be helpful too. Thanks!
My Code:
bonePose = skel.get_bone_global_pose(bone)
var globalBonePose = skel.to_global(bonePose.origin)
translateVector = -globalBonePose
var newPose = bonePose.translated(translateVector)
skel.set_bone_pose(bone, newPose)
Code Output / Results:
bonePose (the original position of the bone) is around (-0.82,0.49,0.50)
translateVector (the amount the bone will be translated) is around (0.82,-0.49,-0.50)
newPose (the final position of the bone -- should be [0,0,0]) is around (0.82,-0.66,-0.46). Even when I call skel.to_global(newPose.origin) to see the global coordinates, it's (-0.76,0.44,0.42), which is not (0,0,0)
In Godot a Transform is composed of a basis (a Basis) and an origin (a Vector3). Where the origin handles the translation part of the transform, and the Basis the rest.
A Basis is the set of vectors that define the coordinate system. There is a vector that defines the x axis, another for the y axis, and another for the z axis. And this is the way Godot will encode rotation and scaling transformations.
When the documentation says "relative to the transform's basis vectors" it means the Basis will be applied to the vector you pass in. Thus, in your case, you are getting a translation on the local space of the bone. Which implies that if the bone is rotated or scaled (or something like that), that will affect the translation.
If you don't want to deal with rotation, scaling, et.al. I suggest you work with the origin of the Transform instead.
If you have a Transform and you want another that is otherwise equal but located at (0, 0, 0), you do this:
var new_transform = Transform(transform.basis, Vector.ZERO)
Or replace Vector.ZERO with whatever origin you want to give the new transform.
I also need to remind you that get_bone_global_pose and set_bone_pose do not operate on the same thing. On one hand set_bone_pose is relative to the parent bone, on the other get_bone_global_pose is relative to the Skeleton. Thus, I suggest you use set_bone_global_pose_override instead.
The final piece you need is the opposite of Spatial.to_global. Because setting the pose like as follows…
bonePose = skel.get_bone_global_pose(bone)
var newPose = Transform(bonePose.basis, Vector.ZERO)
skel.set_bone_global_pose_override(bone, newPose, 1.0)
… Would place it at the origin of the Skeleton.
Well, the opposite of Spatial.to_global is Spatial.to_local, and you would use it like this:
bonePose = skel.get_bone_global_pose(bone)
var newPose = Transform(bonePose.basis, skel.to_local(Vector.ZERO))
skel.set_bone_global_pose_override(bone, newPose, 1.0)
Here skel.to_local(Vector.ZERO) should give the origin of the world relative to the Skeleton. And given that set_bone_global_pose_override wants a Transform relative to the Skeleton, the result should be that the bone is placed at the origin of the world. With its rotation and scaling preserved.

How to get global world coordinates of bone in Godot GDscript

I'm writing a program in Godot with GDscript that aims to change the position of multiple bones in an armature. The bone gets translated by a value calculated by the point I want the bone to move to minus the current position of the bone. translateValue = endPoint - currentPoint However, all values must be in world coordinates or global position for the code to work. Godot has various methods to retrieve the bone Transform in order to access the position such as : skeleton.get_bone_pose() , skeleton.get_bone_global_pose , skeleton.get_bone_rest . I tried using skeleton.get_bone_global_pose but it didn't give the global position and seemed to still be relative to something. There's also the to_global() method, but i'm not entirely sure what it does. Could someone explain the differences between these methods and how to get global position? Thanks!
I'll start with these methods:
get_bone_rest
get_bone_custom_pose
get_bone_pose
First of all, get_bone_rest gives you the default transform of the bone, relative to its parent bone. Then the other transform are stacked, in the above order.
Then we have:
get_bone_global_pose
This method gives you the final transform of the bone. And it is relative to the Skeleton. That is, this transform already includes the previously mentioned transforms, combined from parent to child bone.
Thus, converting its result to world space is a matter of composing the transform of the Skeleton:
$Skeleton.global_transform * $Skeleton.get_bone_global_pose(bone_index)
And we have:
get_bone_global_pose_no_override
As the name suggest get_bone_global_pose_no_override ignores any global pose override. That's right, you you can override the global pose. To do that, use set_bone_global_pose_override. See also clear_bones_global_pose_override. These are all, of course, relative to the Skeleton.
The method Spatial.to_global(vector3) is unrelated to the Skeleton. It transforms a vector from the local space of the node on which you call it, to world space. Which might also be useful:
$Skeleton.to_global($Skeleton.get_bone_global_pose(bone_index).origin)

Interpreting compressed SVG path command syntax

I'm working with compressed SVG path data which has been output from SVGO, and I can't understand one part of a relative line-to command. The path itself looks like this (it's a triangle, and it displays correctly):
<path d="M2107.49 3283.96l70.68 81.44 28.54-81.69-99.22.25z"/>
Pulling apart the d attribute into commands, we get:
M: (Start coords) 2107.49 3283.96
l: (Relative line to coords) 70.68 81.44 28.54-81.69-99.22.25
z (close)
The piece I don't understand is the last 'pair' of the line-to commands: From what I understand, we should have 3 pairs of coordinates, which are either separated by a space 70.68 81.44, or where negative, no space: 28.54-81.69 = 28.54, -81.69. But what's going on in that last 'pair'? Does -99.22.25 represent -99.2 and 2.25? How would I know how to split that?
SVGO is trying to squeeze every last byte out of its compression, so it's possibly taking advantage of some implied parsing rule I can't find a reference to. Does anyone know how to deal with that last pair?
Per the BNF in the SVG specification
...
Similarly, for the string "M 0.6.5", the first coordinate of the "moveto" consumes the characters "0.6" and stops upon encountering the second decimal point because the production of a "coordinate" only allows one decimal point. The result is that the first coordinate will be "0.6" and the second coordinate will be ".5".
So -99.22.25 is -99.22 followed by .25

How to transform a path in svg so that it is parallel to its original location?

I have a path defined in SVG. I would like to make two copies of the path and translate them so that one sits parallel to the original on one side, and the other sits parallel on the other side. The idea is to end up with 3 paths, all parallel to each other and not overlapping.
I have tried simple translations such as transform="translate(10,10)" and transform="translate(-10,-10)" for the two paths, but in some paths they end up crossing each other which is not what I want.
Thanks.
Your answer should somewhat work as you've provided it. You might provide more concrete examples of your problem to evoke better solutions.
Your provided commands are going to move the item in two dimensions, not just one.
Also, keep in mind that SVG uses the upper left point as 0,0, and to the right/down are positive. Also check to make sure you're not getting tripped up by units.
If your original path has a bounding box of X,Y then the simplest way to make sure that the copied don't overlap is to translate by +X and -X, so:
translate(-X, 0)
and
translate(X, 0)
where you have computed the value of X and set it in the translate argument.
I'll give you some completely untested code written without looking at the SVG DOM spec. You can then test it and tweak it to get it to work.
First, get the bounding box of an element:
var box = mypath.getBBox();
Then clone the path twice (or make elements):
var rightCopy = mypath.cloneNode(true);
var bottomCopy = mypath.cloneNode(true);
Then transform each copy:
rightCopy.setAttribute("transform", "translate(" + box.width + ",0) " + rightCopy.getAttribute("transform"));
bottomCopy.setAttribute("transform", "translate(0," + box.height + ") " + bottomCopy.getAttribute("transform"));
The reason it looks messy is because the original path might have a transform already on it.
Then add those nodes back into the DOM:
mypath.parentNode.insertBefore(rightCopy, mypath);
mypath.parentNode.insertBefore(bottomCopy, mypath);

Resources