I'm pretty new at SVG and Raphael, but I've been using Illustrator for many years, so I have some assumptions on how it works. I want to combine two paths which should return a single element.
I need to make a speech bubble, but it could be anything really. In this case I tried to make two rect, one with round corners and another square rect which was rotated. It looked alright, but when I tried to move the speech bubble, the rotated element moved in the wrong direction, because of the 45 degree rotation.
How can I compbine paths which I can later manipulate as if it was a single element/path?
Here you go DEMO
var paper = Raphael('canvas',400,400),
r1 = paper.rect(100,100,200,100).attr({fill:'black'}),
r2 = paper.rect(130,130,140,40,5).attr({fill:'white','stroke':'white'}),
r3 = paper.path("M200 170L240 170 220 180z").attr({fill:'white', 'stroke':'white'}),
p = paper.set(r1,r2,r3);
// the rest of the code is in the demo
Note, that it is easier to create triangle via path() and not worry about rotation.
Good Luck ;)
If you mean merging paths like using the Illustrator Pathfinder panel - turning several paths into one path (not a set of paths), merging overlap - I'm pretty sure there's no direct Raphael or SVG equivalent.
The closest thing is, creating a compound path (aka composite path) - the equivalent of cmd-8 or Object > Make Compound Path in Illustrator. This merges paths together into one path, but doesn't remove areas of overlap.
Basically, for a set paper.set( paper.path('M0,0 4,0 0,4z'),paper.path('M9,9 4,9 9,4z') );, an equivalent compound path would be paper.path('M0,0 4,0 0,4z M9,9 4,9 9,4z'); - just joining the path definitions into one, each starting with its own M.
Some differences to Raphael sets:
Less overhead (it's just one complex path, not several separate individual path elements)
Fewer surprises when you move it, sort it, etc:
Things applied to a Raphael set apply to each item in turn, not to the set as a unit - so for example toFront() changes the order within the set, and transforms centre around each item, like Illustrator's transform each, unless you give the transform static co-ordinates.
Things applied to a compound path, however, apply to the whole compound path as one unit.
It's not possible for subpaths of a compound path to have different attributes
Things like gradients apply once across the whole of the compound path, whereas with sets, there will be separate gradients on each separate path
JSBIN demo - compare the gradients, and see how the compound pair is just one path on the DOM.
Here's a simple plugin that adds the ability to take a set and create a compound path from it:
Raphael.st.compoundPath = function(){
var positions = [];
this.forEach( function( element ){
positions.push( element.compoundPath() );
});
return positions.join('');
}
Raphael.el.compoundPath = function(){
var path = this.attr('path');
return path ? Raphael.parsePathString( path ).join('') : '';
}
Then use it like this:
var someSet = paper.set(paper.path('M0,0 4,0 0,4z'),paper.path('M9,9 4,9 9,4z'));
var compPath = paper.path( someSet.compoundPath() );
someSet.remove(); // if you want to replace the set with a compound path
Note that it only combines paths in a set - if you need to combine other shapes with paths, you'll need a way to convert them into paths first.
First thing is that you could push your 2 elements into a Raphael set which you would later move with Element.transform(). This would let you apply the move handler once, and not twice.
Also for your issue, it is acually documented:
... There are also alternative “absolute” translation, rotation and
scale: T, R and S. They will not take previous transformation into
account. For example, ...T100,0 will always move element 100 px
horisontally, while ...t100,0 could move it vertically if there is r90
before. Just compare results of r90t100,0 and r90T100,0. ...
Related
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.
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)
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.
Suppose I have some equation, say:
$$\underbrace{ \frac{a}{b} }_{c}$$
And, I want to get the location of the $c$ in the HTML/CSS/SVG output of MathJax.
Is there a way to do this? I.e. I'd like to do something like:
$$\underbrace{ \frac{a}{b} }_{c\invisiblemarkerXYZ}$$
then be able to do a query to get the DOM element corresponding with invisiblemarkerXYZ
Thanks!
EDIT this is what I want to do:
Equation 1 = $$\underbrace{\frac{a}{b}}{A}$$
Equation 2 = $$A = \sum_{i=1}^n i$$
Now, I want to draw a line (via SVG) of the two A's. Thus, I need some way to obtain the location of the MathJax elements.
You can use \cssId{XYZ}{c} to set the id="XYZ" on the element used for the c, and can then use document.getElementById("XYZ") to obtain that DOM element. But the output from MathJax's HTML-CSS and SVG processors is not designed to be manipulated after the fact. For example, in general you will not be able to get the dimensions of the element from the HTML-CSS output as the offsetHeight and offsetWidth may not be what you expect them to be. (The height is frequently set to 0, for example.)
Can you say something more about what you are trying to accomplish?
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);