Faster SVG Path manipulation - javascript

So I want to make a drawing tool using SVG, I'm using a rather naive approach to change the d attribute of my Path:
$("div#drawarea").bind("mousemove", function(ev) {
ev.preventDefault();
ev.stopPropagation();
var pX= (ev.pageX - this.offsetLeft);
var pY= (ev.pageY - this.offsetTop);
$path.attr("d", $path.attr("d") + " L" +pX+ "," + pY); //using jquery-svg here to change the d attribute
});
As you can see I do this on the mousemove function. The code works but it becomes unresponsive when the mouse is moving fast creating numerous straight lines when I actually want it to be smooth lines. I think this is happening because the numerous string concatenations I'm doing on the mousemove event (the d attribute on the path can become quite big when the click has been held for long, thousands of characters long in fact).
I'm wondering if there is any native way to add new values at the end of a path instead of manipulating the d attribute directly. I checked the jquery-svg sourcecode and it seems that the library also uses the naive string concatenation mode internally so using its methods would not wield any benefit.
Also I'm wondering if this is the case or if the browser just limits the amount of mousemove events (once every X milliseconds?) that can be triggered and so no performance optimizations would improve this.

Use the SVG pathseg DOM methods. You have to write more complicated code but the browser doesn't have to reparse the whole path attribute. Firefox for instance does take advantage of this and it's quite likely other broswers also.

In case someone else stumbeld upon the quesion of what is the fastes way to update an SVG-Path data attribute (for realtime applications), I run a small test on that:
http://jsperf.com/svg-path-test
Yes, setting it as string means that it needs to be parsed, which isn't the case for the DOM SVG interface but the first method is still much faster. Maybee the interface updates the DOM with each point added, slowing down the whole process.

Related

CANNON.js: check if a body is being constrained

I've been trying to make a multiplayer game using javascript (most of which is on the server, using Node.js); one of the core mechanics I want to make is that players will be able to design their own fighting style (right down to how they swing their sword etc). Problem is that I can't find any simple way of constraining players' movements.
I first tried to write a method that checks then clamps the player's style so it doesn't look like they're breaking every limb simultaneously, but that didn't really work. I then found the wonderful CANNON.ConeTwistConstraint, but after looking through the documentation I've found that CANNON.js's constraints don't seem to have any sort of built-in function for just testing whether two bodies are exceeding the constraint's limits. I've thought about having my game just create objects in a separate simulation and check whether a force is being applied to either object, but I'm not sure about how to go about this, or if there's a better way.
Is there a simple/easier solution to my problem? If not, what would be the least CPU-intensive way of implementing the above?
You can manually check if the ConeTwistConstraint is hitting its limit. If you have a look at the method CANNON.ConeEquation.prototype.computeB, you can see that it computes the constraint violation, "g", using cos() and a dot product. You can simply do the same with the following code.
var eq = coneTwistConstraint.coneEquation;
var g = Math.cos(eq.angle) - eq.axisA.dot(eq.axisB);
if(g > 0) {
// Constraint limit exceeded
} else {
// Constraint is within limits
}
Using the same strategy, you can check if the twist limit is exceeded. Since the twist equation is a CANNON.RotationalEquation, the code becomes:
var eq2 = coneTwistConstraint.twistEquation;
var g2 = Math.cos(eq2.maxAngle) - eq2.axisA.dot(eq2.axisB);

Drawing plots interactively in a web app

I am looking for a library preferably in JavaScript, that will allow a user to draw a plot (simple one consisting of vertical and horizontal steps) like this one:
The idea is that when the user is done with the plot I can generate data points from the graph and process them.
I don't know where to start, I am looking to start learning to do this within a JS based framework (meteor) but I can't find a library that allows for something like this. The closest library I found is d3.js but I couldn't find any example that allows for this.
Would anyone be able to point out to me a sample example to start from? Would you know of a better suited library to accomplish what I am asking for?
Here is a relatively simple fiddle which accomplishes some of what you asked for, excluding axis (which are relatively easy and has plenty of examples). It uses D3 for all the drawing and mouse event handling. On click it simply executes svg.append("circle").attr("r", 5), and if it's not the first click (i.e. linking points) then it also will create a path element using the previous mouse click coordinates:
svg.insert("path", "circle").attr("d", function () {
return [
"M", prevClickLoc[0], prevClickLoc[1],
"L", prevClickLoc[0], y,
"L", x, y].join(" ");
})
Where x and y are the current mouse coordinates. Also has an export button that will output a list in the form of cx,cy,cx,cy,... :: d,d,d,d,.... On import, you could easily split this array into two using indexOf("::") or whatever you choose if you want to change the formatting. Then just exectue for (x in circles) {svg.append("circle").attr("cx", function...).attr("cy", function...);} and do something similar for paths for (y in paths) {svg.append("path").attr("d", function(){return paths[y];});}. It would be even easier if on export you made the cxcy array in the format cx;cy,cx;cy since then you could simply split the array at each comma and then split each index of the resulting array at the semicolon for a nice nested array.
Small update in this version, you can only place points if the current mouse x is greater than the previous x coordinate, and it also has the line d3.event.stopPropagation(); which prevents accidental highlighting of the page.

How can I find the line-breaks created by word-wrapping in contentEditable or elsewhere?

I'm trying to find the actual number of lines currently displayed to the user by a browser's layout engine. Finding 'hard' breaks, <'br'> tags etc, is easy enough, but I can't find a way to see, in code, what I can count onscreen.
I have searched here, and there are a few questions/answers implying it can't be done, or is at the least very involved, but they are all several years out of date, and perhaps things have changed.
textContent doesn't do it, and using the div's height doesn't work in this application. Maybe there's a jQuery way I'm unaware of?
Divide the height of the whole thing by the height of one character. This method works in IE, Safari, FF and Chrome, though the situation is fairly simple, only one size of font etc, so could do with a more thorough workout before being declared The Right Way; it needs empty node checks etc.
function getLineCount(node) {
if (node) {
var range = document.createRange();
range.setStart(node, 0);
range.setEnd(node, 1);
var h1 = range.getBoundingClientRect().height;
range.setEnd(node, node.length);
return Math.round(range.getBoundingClientRect().height / h1);
}
};
Is the Tumbleweed Badge also known as crickets?

create animations from Javascript using embedded SVG in XHTML

I use embedded SVG in XHTML and want to create animations from Javascript, but it does not work as expected
I am modeling business processes with XPDL and connect the simulation to a SVG graphics which I animate using javascript. I am doing this in Firefox, and model and graphics are embedded in XHTML. Now the problem is that I want to use an animateMotion-Tag to move an object along a path. Both are already existing, so I tried writing my solution into the SVG file, and this worked fine. It looked like:
<animateMotion xlink:href="#id1" rotate="auto" dur="2s">
<mpath xlink:href="#id2">
</animateMotion>
Of course, the namespaces are set correctly, so this works as expected. I trigger it manually, so there is no begin time needed. Now, my approach for doing the same thing in an existing mixed XHTML/SVG-dom:
function moveAlongPath(elemId,pathId,rotate,duration)
{
var svgNs = "http://www.w3.org/2000/svg";
var xlinkNs = "http://www.w3.org/1999/xlink";
var motionElem = document.createElementNS(svgNs,"animateMotion");
motionElem.setAttributeNS(xlinkNs,"href","#" + elemId);
motionElem.setAttributeNS(svgNs,"rotate",rotate);
motionElem.setAttributeNS(svgNs,"dur",duration + "ms");
var pathRef = document.createElementNS(svgNs,"mpath");
pathRef.setAttributeNS(xlinkNs,"href","#" + pathId);
motionElem.appendChild(pathRef);
var animElem = svgRootNode.getElementById(elemId); // It is indeed the <svg>-Node
animElem.appendChild(motionElem);
// Setting x and y to 0 is important for the Element to be "on" the Path
animElem.setAttribute("x",0);
animElem.setAttribute("y",0);
motionElem.beginElement();
}
When I check the dom in firebug, this seems to produce the same node structure with the same attributes, although the href isnt prefixed with xlink:, but setAttributeNS should do this, right? The problem here is that i cannot start the animation with beginElement(). Nothing happens here.
I hope there is help out there, i am really desperate right now.
EDIT:
I found it at last. The problem disappears when I use
setAttributeNS(null,"attr",value)
instead of
setAttributeNS(svgNs,"attr",value)
Correct this if I am wrong, but is not my first approach the way XML was thought? That there shouldn't be namespaceless attributes? Anyway - SOLVED!
Use
setAttributeNS(null,"attr",value)
instead of
setAttributeNS(svgNs,"attr",value)
variableElementNS.href.baseVal = value;

apply all transform matrices

I am looking for a possibly fast way to apply all transform matrices of a given svg-graphic. In other words: the algorithm should remove all "transform" attributes and transform all coordinates of the graphic to absolute coordinates.
Is their any library that can do this, or is their any SVGDomInterface method that coulld do that?
EDIT::
If I call the consolidate method like this:
$.each( svg.find( 'path' ), function( i ){
this.transform.baseVal.consolidate();
});
nothing happens, if i call it like this:
$.each( svg.find( 'path' ), function( i ){
this.transform.animVal.consolidate();
});
i get this error:
So, how should i use the "consolidate" method, on which elements shall I call it?
Greetings
philipp
Here's a jsFiddle with some javascript library code (based in part on Raphael.js) to bake the transforms into all paths' data:
http://jsfiddle.net/ecmanaut/2Wez8/
(Not sure what's up with Opera here, though; it's usually best in class on SVG. I may be stumbling in some way the other browsers are more forgiving about.)
The consolidate method only reduces the list of matrices to a single matrix. And the error you get on the animVal example is because you are not allowed to modify the animated values (consolidate destructively modifies the transform list).
To answer your question, no there's no existing method in SVG DOM that applies the transforms by modifying the values of paths etc. There are options in Inkscape (and Illustrator too IIRC) for applying transforms like that.
If you're looking for a library or utility that does this you can try SVG Cleaner.
SVG Cleaner didn't seem to apply all transforms for me, but Inkscape does. Here's the command line I use when I need to apply one:
inkscape copy-of-file.svg --select=id-of-node \
--verb=EditCut --verb=EditPaste \
--verb=FileSave --verb=FileClose
Assuming you have "Transforms -> Store transformation" set to "Optimized" in inkscape's prefs (I believe it is on by default), this will apply it and produce the wanted result. Be sure you operate on a copy of your input file, as the operation replaces your original file!
If you are running this on a mac, you may want to first do this in your shell:
alias inkscape=/Applications/Inkscape.app/Contents/Resources/bin/inkscape
Better use EditPasteInPlace instead of EditPaste. EditPaste pastes at the mouse location, which is not the location of the node.
In order to retreive the path relative to some other, parent DOM node (e.g. the SVG root container), you can use the technique here.
It uses SVG.getTransformToElement which calculates the transform between a parent and some node on the SVG tree. The returned object contains methods to return the inverse of the transform, etc, to do practical things with it.
How to determine size of Raphael object after scaling & rotating it?
I dont know exactly what you are trying to achieve, but this has the power to do it.

Categories

Resources