todo
there are two approachs. one is, function that takes ordered params. where the params are the most frequently used svg element's attributes.
the other approach, is only have ordered param for required attributes. with a optional arg of an object, that can fill optional attributes.
comparison of call syntax
const svgc = f_create_svg1( , , [-1 * 6, -1 * 4, 2 * 6, 2 * 4.5], );
const svgc = f_create_svg2({ viewBox: ${-1 * 6} ${-1 * 4} ${2 * 6} ${2 * 4.5}
});
i think the second approach is better. where the ordered param should only be required attributes. because if your function have some order params that are not necessarily required, you have the issue that user needs to be familiar with what they are. but if your function's ordered parameters are only the required, user do not have to lookup the doc of your function.
once you decided to do this, there is another decision to make, namely, whether the optional arg should be filtered only by valid attributes, or, any key in the object is added as a attribute.
i think the latter is better. because that way, allows user to add any arbitrary attributes. this is important, because svg tag has tons of attributes, each element has different set of. some are universal such as id class style, some are specific to the element, and there unexpected user ones but valid such as data-xyz.
here's a example of using ordered params.
/* [ return this svg element <svg width="…" height="…" viewBox="x-min y-min x-width y-height" x="…" y="…" > • if parent is not null, append as child to it. • if style is not null, add it as a style attribute, as string. ] */ const f_create_svg = (( parent = null, width = null, height = null, [vbx, vby, vbw, vbh] = [null, null, null, null], [x, y] = [null, null], style = "", ) => { const svgg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); if (width !== null && width !== undefined) { svgg.setAttribute("width", width.toString()); } if (height !== null && height !== undefined) { svgg.setAttribute("height", height.toString()); } if (vbx !== null && vbx !== undefined) { svgg.setAttribute("viewBox", `${vbx} ${vby} ${vbw} ${vbh}`); } if (x !== null && x !== undefined) { svgg.setAttribute("x", x.toString()); svgg.setAttribute("y", y.toString()); } if (style) svgg.setAttribute("style", style); if (parent) parent.appendChild(svgg); return svgg; });
auto plot range. (find max/min and/or find point cluster). Simply using the max and min points in the data points in the plane is not a solution. Because , for some plots, you have a few points near infinity, while most of your points are say clustered around say origin. So in this case, you actually want the plot range to be around the dense data points. So, it seems, need algorithm that eliminate outliners by some statistical analysis.
adoptive sampling. Else, you get kinks at sharp turns.
• asymptotes. e.g. in hyperbola with vertical axis, where the asymptote is a diagnal line, and the parametric curve goes to infinity in one direction and comes back from the other direction of infinity.
issue: axes labeling. For textbook examples, it's simple. say, plot y=x^2, from x 1 to 10 and y 1 to 10. You can have code that create labels for them automatically, and nice.
but, in general, in real world, you don't know how wide (the x range going to be), it can be 0 to 1, or -100 to billions.font size seems very hard to control. especially in axes tick label.
svg y coordinates goes top to bottom. So, needs a flip.
you can solve the problem in few days.
one is to use svg attribute named “transform”. Attach it to the whole canvas:
/* [ attach a transform to gg ] */ const f_flip_up_down_transform = ((gg, ymin, ymax ) => { gg.setAttribute("transform", `translate(0 ${ymin + ymax} ) scale(1 -1)` )});
another solution is to do it on each and every point's coordinate in the canvas. e.g. use this function:
const f_flip_up_down_y = ((y, ymin, ymax) => /* [ returns a new coordinate y. svg coordinates goes top to bottom. Convention graph coordinate goes bottom to top. So, needs a flip. This function , the y is the y coordinate and ymin, ymax are assumed to be traditional coordinate orientation. It returns a new y, that is the svg coordinate for it. For example, u have a up-pointing triangle in 1st quadrant, the coordinates are (1,1), (3,1), (2,2). you want to plot this in svg. You need translate the coord into svg coord (so that the triangle still points up). Your ymin is 1, ymax is 2. Result is: (1,2), (3,2), (2,1). the y coord is computed by f_flip_up_down_y(1, 1,2) → 2, f_flip_up_down_y(2, 1,2) → 1 2018-02-15 ] */ ( ymin + ymax -y) );
which is better?
in svg, it's xml. It's hard to control graphics primitives such as circle, rectangle, line, etc, in svg in browser. because, you have data such as JavaScript array or json going directly to svg xml elements, and you have to convert from js array to create xml element and insert to page, and when you want to say delete a circle, you have to “select”, then remove child or such. you cannot just delete the array element in your data, such as in json. (well, there are libs that do data binding.)
so, it seems, might be nice to create a intermediate language to represent all the graphics primitives, like Mathematica. e.g. circle would be like
{
type: "circle",
center:[3,5],
radius:4
}
and similar for rectangle, line, etc. these would be the graphics primitives.
then, js code create or manipulate these, from the given data or say parametric formula. It's easy to do.
then, render them by create/convert them to svg elements and insert to the page.
however, the disadvantage seems to be the 2 step process. could be slower. also, need to think about how one'd attach event handlers to the graphic primitives, e.g. for interactive manipulate the thing, such as user dragging a point and have the a circle change center or radius. but one can still do this by , the normal way, of attaching event handlers to the generated svg elements. Alternatively, invent some syntax so that event handler can be attached to the intermediate language itself. But this will probably become complex and quite involved in design.
the alternative, what d3 seems to be doing, is to forego the intemediate language, and work directly with html xml svg.
the problem with this is, the code becomes much not intuitive. because html dom is rather very hard to work with. Also, this way is not natural. because now your lib is stuck in dom speak dom world dom way. the dom way, is not universal. Outside of webpages, it's very stupid. But if you have a intermediate language, that's rather universal, as in computational geometry.
find out how d3 does them
2018-06-18 one problem is, to add anything in a web page, typically you have to have something like a element with id e.g. id="t55402"
as anchor, so new things such as svg graphics can be added them.
e.g. google adsense uses
<ins class="adsbygoogle" …></ins>
e.g. disqus uses
<div id="disqus_thread"></div>
but i find this annoying or inelegant. How does typical web app does it?
find how how d3js example pages do them.