Convert stroke data to SCG Ink format - javascript

I'd like to use Seshat—a handwritten math expression parser—for a project I'm working on, but I'm having some trouble understanding how to provide the program its proper input, an InkML or SCG Ink file.
I've taken a long look at an online example that exists here, and I see that they get a Javascript array of stroke information from an HTML Canvas field with this JS library applied, but I don't know what happens that array after it gets POSTed to their server.
I've read the SCG Ink spec, and I think it might be relatively easy to parse the array into the format, but I'm hoping there's something obvious I'm missing that would make this trivial. Any help would be greatly appreciated.

I emailed the Seshat author and he suggested I convert the input to SCG Ink, which turned out to be pretty easy if you take the JavaScript libraries used at http://cat.prhlt.upv.es/mer/. Specifically, you want jquery.sketchable.memento.min.js, jquery.sketchable.min.js, and jsketch.min.js in addition to regular old jQuery. Here's what I did in case anyone else is interested in Seshat.
Notice from the same page that in main.js they apply the Sketchable library to the HTML canvas area with this block of code:
var $canvas = $('#drawing-canvas').sketchable({
graphics: {
strokeStyle: "red",
firstPointSize: 2
}
});
Now we can take a look at their submitStrokes() function to see how to take the strokes from Sketchable and convert to SCG Ink. The line var strokes = $canvas.sketchable('strokes'); gets the strokes, and then the line strokes = transform(strokes); applies a quick transformation to extract only the data they need. Here's the transform() function for reference:
function transform(strokes) {
for (var i = 0; i < strokes.length; ++i)
for (var j = 0, stroke = strokes[i]; j < stroke.length; ++j)
strokes[i][j] = [ strokes[i][j][0], strokes[i][j][1] ];
return strokes;
};
The value returned fro transform() is a three-dimensional array of strokes and points. (Each element of the first dimension is a stroke, each element of the second dimension is a point, and the third dimension is x-, y-coordinates.) On that site they go ahead and POST that array to the server which must handle the final conversion to SCG Ink. I wrote a JavaScript function to handle it:
function strokesToScg(strokes) {
var scg = 'SCG_INK\n' + strokes.length + '\n'
strokes.forEach(function (stroke) {
scg += stroke.length + '\n'
stroke.forEach(function (p) {
scg += p[0] + ' ' + p[1] + '\n'
})
})
return scg
}
And that's it. The value returned from strokesToScg() is a string describing a series of strokes in the SCG Ink format.

Related

In knitr HTML output, include line numbers of R Markdown source code?

Question: Is there an automatic way to add the line numbers of the original R Markdown source code to the formatted code portions of the HTML output produced by knitr?
Purpose: My ultimate goal is to be able to quickly move to parts of my source R Markdown code that I identify need editing while reviewing the HTML output. Using line numbers is the fastest way I know to do this, but I welcome hearing others' strategies.
Solutions I've tried:
Although the chunk option attr.source = '.numberLines' will attractively add line numbers to the code parts of the HTML output, that option doesn't provide the source-code line numbers automatically (you must force that manually using .startFrom) -- instead, the lines are renumbered at the beginning of each chunk and after each piece of output. In the following illustration, I've included .startFrom to force the line numbering to start at 10, to match the line number for test_data <- rnorm(10) which is the line number I want to see. A practical solution, however, needs the starting number to be automatic. Also, in the HTML output (shown beneath the code) the hist(test_data) line is renumbered starting with the same starting number, 10. I would want that to be 12, as in the source code.
This question (How can I add line numbers that go across chunks in Rmarkdown?) is related, but the OP just needed any unique identifier for each line, not necessarily the line numbers of the source code, with the solution being sequential numbers unrelated to the source-code line numbers.
Considered option: I've considered preprocessing my code by running an initial script that will add line numbers as comments at the end of lines, but I'd prefer a solution that is contained within the main knitr file.
Reverted to this update based on your request
I'm glad you figured out the issue. I hadn't considered or tested the code for a chunk that only had a single line of code. However, based on your feedback, I've accounted for it now.
If you'd like it accounted for and would like to keep the color in the code, let me know. I'll add it back with the fix for single lines of code. (I'll stick to the ES6.)
This version uses the line numbers you'll see in the source pane of RStudio. You must use RStudio for this to work. The following changes to the RMD are necessary:
The library(jsonlite) and library(dplyr)
An R chunk, which you could mark include and echo as false
A set of script tags outside of and after that chunk
A JS chunk (modified from my original answer)
The R chunk and R script need to be placed at the end of your RMD.
The JS chunk can be placed anywhere.
The R chunk and script tags **in order!
Put me at the end of the RMD.
```{r ignoreMe,include=F,echo=F}
# get all lines of RMD into object for numbering; R => JS object
cx <- rstudioapi::getSourceEditorContext()$contents
cxt <- data.frame(rws = cx, id = 1:length(cx)) %>% toJSON()
```
<script id='dat'>`r cxt`</script>
The JS chunk
This collects the R object that you made in the R chunk, but its placement does not matter. All of the R code will execute before this regardless of where you place it in your RMD.
```{r gimme,engine="js",results="as-is",echo=F}
setTimeout(function(){
scrCx = document.querySelector("#dat"); // call R created JSON*
cxt = JSON.parse(scrCx.innerHTML);
echoes = document.querySelectorAll('pre > code'); // capture echoes to #
j = 0;
for(i=0; i < echoes.length; i++){ // for each chunk
txt = echoes[i].innerText;
ix = finder(txt, cxt, j); // call finder, which calls looker
stxt = txt.replace(/^/gm, () => `${ix++} `); // for each line
echoes[i].innerText = stxt; // replace with numbered lines
j = ix; // all indices should be bigger than previous numbering
}
}, 300);
function looker(str) { //get the first string in chunk echo
k = 0;
if(str.includes("\n")) {
ind = str.indexOf("\n");
} else {
ind = str.length + 1;
}
sret = str.substring(0, ind);
oind = ind; // start where left off
while(sret === null || sret === "" || sret === " "){
nInd = str.indexOf("\n", oind + 1); // loop if string is blank!
sret = str.substring(oind + 1, nInd);
k++;
ind = oind;
oind = nInd;
}
return {sret, k}; // return string AND how many rows were blank/offset
}
function finder(txt, jstr, j) {
txsp = looker(txt);
xi = jstr.findIndex(function(item, j){ // search JSON match
return item.rws === txsp.sret; // search after last index
})
xx = xi - txsp.k + 1; // minus # of blank lines; add 1 (JS starts at 0)
return xx;
}
```
If you wanted to validate the line numbers, you can use the object cx, like cx[102] should match the 102 in the HTML and the 102 in the source pane.
I've added comments so that you're able to understand the purpose of the code. However, if something's not clear, let me know.
ORIGINAL
What I think you're looking for is a line number for each line of the echoes, not necessarily anything else. If that's the case, add this to your RMD. If there are any chunks that you don't want to be numbered, add the chunk option include=F. The code still runs, but it won't show the content in the output. You may want to add that chunk option to this JS chunk.
```{r gimme,engine="js",results="as-is"}
setTimeout(function(){
// number all lines that reflect echoes
echoes = document.querySelectorAll('pre > code');
j = 1;
for(i=0; i < echoes.length; i++){ // for each chunk
txt = echoes[i].innerText.replace(/^/gm, () => `${j++} `); // for each line
echoes[i].innerText = txt; // replace with numbered lines
}
}, 300)
```
It doesn't matter where you put this (at the end, at the beginning). You won't get anything from this chunk if you try to run it inline. You have to knit for it to work.
I assembled some arbitrary code to number with this chunk.

D3: Draw part of interpolated path with dashed stroke

I'm drawing a simple interpolated line using D3.js (output as path). However, I want part of the path to have a dashed stroke if a boolean in a data point is set to true:
Link to image
An easy solution would be to just draw it with <line> segments instead - but that way I lose the interpolation.
Then I found an example from mbostock, showing how to draw a gradient along a interpolated path. I modified it to just draw transparent path fills whenever the boolean was set to true and white fills when false - while my old path is all dashed.
That works (queue the above screenshot) - but by adding around thousand path elements to the DOM contra having only a single path.
It's not desirable with that many DOM elements, especially since I'm going to make more curves and the site needs to be mobile optimized. Am I missing a much simpler solution?
Wouldn't mind a modified version of mbostock's example doing the heavy calculations in advance, as long as the DOM output is simple.
Thanks!
I prepared this example for another SO question. Screenshot is here:
I think you have enough material there to devise a solution that fits your needs.
Take a look also at this page:
SVG Path Styling
You could use stroke-dasharray to add dashes in the stroke of the generated path in the right places. That would entail finding the proper dash lengths. This can be done by calling pathElm.getPathLength() on the path up to the point where you want it to start being dashed, and to where you want it to end.
Let's say path A that is the part that is before the dashes should start. Set the d attribute with that part and call getPathLength() on it. Let's call this length a.
Append the the part of the path that should be dashed to the d attribute, then call getPathLength() again. Let's call this length b.
Create a new path element with the remaining part of the path, then call getPathLength() on that. Let's call this length c.
Construct a stroke-dasharray property string something like this:
var a = getPathLengthA();
var b = getPathLengthB();
var c = getPathLengthC();
var dasharray = a + " ";
for(var usedlen = 0; usedlen < (b-a); ) {
dasharray += "5 10 "; // just whatever dash pattern you need
usedlen += 15; // add the dash pattern length from the line above
}
dasharray += c;
pathElement.style.strokeDasharray = dasharray;
Here's a static example of that.

JavaScript function is only working the first time I call it?

I was wondering whether you guys could help me troubleshoot an issue I'm having. Hopefully identifying the problem won't require you look into the documentation of the graphics package I'm using, but if it does, here you go: http://raphaeljs.com/reference.html#Element.transform.
I have the following block of code
window.setInterval(function()
{
mycirc.transform("t1,1");
}, 500);
which of course should call the function mycirc.transform("t1,1") every half-second. That function is supposed to translate the x and y coordinates of mycirc each by 1 unit (look at Element.transform([tstr]) on http://raphaeljs.com/reference.html#Element.transform).
However, when I test my page, mycirc gets translated once and then the subsequent calls have no effect. I used console.log(...) to test and make sure:
window.setInterval(function()
{
var bb = mycirc.getBBox();
console.log("coords before transformation: " + bb.x + "," + bb.y);
mycirc.transform("t1,1");
var bb = mycirc.getBBox();
console.log("coords after transformation: " + bb.x + "," + bb.y);
}, 500);
yields
coords before transformation: 120.98508107696858,106 jsfunctions.js:411
coords after transformation: 121.98508107696858,107 jsfunctions.js:414
coords before transformation: 121.98508107696858,107 jsfunctions.js:411
coords after transformation: 121.98508107696858,107 jsfunctions.js:411
etcetera.
Any idea why this might be?
(I tried to look through the source code for the graphics package, but it's unreadable because of no whitespace.)
your code
mycirc.transform("t1,1");
isn't relative to current state. It just transforms from original state to t1,1 and then from t1,1 to t1,1 etc.
You should calculate transformation every time.
EDIT: So it would need a global variable, incremented every time like:
var xyPos = 1;
window.setInterval(function()
{
mycirc.transform("t"+xyPos+","+xyPos);
xyPos++;
}, 500);
In the usage section of the docs you linked, it shows how to prepend and append transformations. This implies to me that you code will just reset the transformation to the same thing every time. I have never used this graphics library, so I can't say for sure, but try something like the following and see if it works:
mycirc.transform("t1,1");
mycirc.transform("...t1,1");
mycirc.transform("...t1,1");
mycirc.transform("...t1,1");
I believe that will apply the same transform 4 times. Of course, you will then need to convert this logic into an interval for your use.
I think this is expected behavior. Quoting the docs:
Adds transformation to the element which is separate to other
attributes, i.e. translation doesn’t change x or y of the rectange.
Try:
var amount = 1;
window.setInterval(function()
{
mycirc.transform("t" + [amount, amount].join(','));
amount++;
}, 500);

Simplifying SVG path strings by reducing number of nodes

I am generating a large SVG path string that represents a line chart.
Beneath the chart I have a slider for selecting a time range slice. Behind the slider is a mini preview of the whole line chart.
I am currently scaling down the path to generate the preview however in doing so I am ending up with tens of nodes per pixel and therefore far more detail then is necessary. Of course this gives the browser more rendering to do than it has to.
There is plenty of info available on compressing svg strings (gzipping etc), though little on algorithms that actually simplify the path by reducing the nodes.
I am using Raphaeljs and am looking for a javascript based solution. Any ideas?
Simplify.js is probably what you're looking after.
Given your line chart consists of straight line segments only (which by definition it should), you can use it like this:
var tolerance = 3
var pathSegArray = []
for (var i=0; i<path.pathSegList.numberOfItems; i++) {
pathSegArray.push(path.pathSegList.getItem(i))
}
var newPathArray = simplify(pathSegArray,tolerance)
var newD = "M";
for (i=0; i<newPathArray.length; i++) {
newD += newPathArray[i].x + " " + newPathArray[i].y + " "
}
path.setAttribute("d",newD)

Parsing XML Text with jQuery

I'm working with a database that has X and Y points per group, it's being used to draw outlines of images.
Right now in my web side this code is what I use to get the points:
var Drawing = $(XML).find('DrawingXML');
alert($(Drawing[1]).text());
Result:
<DrawingPoints>
<Point><X>1</X><Y>2</Y></Point>
<Point><X>2</X><Y>4</Y></Point>
<Point><X>3</X><Y>5</Y></Point>
<Point><X>2</X><Y>2</Y></Point>
<Point><X>0</X><Y>4</Y></Point>
</DrawingPoints>
Using the .replace() call only changes one item so it's usable for something like this:
.replace("</DrawingPoints>","");
but if I want to replace all 'Point' tags I'm out of luck.
My goal is to use the canvas feature to draw the points out so I want it to be parsed like this:
ctx.beginPath();
ctx.moveTo(1,2);
ctx.lineTo(2,4);
ctx.lineTo(3,5);
ctx.lineTo(2,2);
ctx.lineTo(0,4);
ctx.stroke();
I'm not going to use this with IE browsers just Safari/Chrome, if that helps out.
In this case you'll probably save an awful lot of brainache by using a library instead of writing your own code.
I reckon d3 does what you need:
d3.xml
d3.geo.path
Check out this question/answer. It's not Prototype specific and should help you here.
How to parse XML string with Prototype?
Get all your X and Y values at once:
var points = {};
points.X = Array();
points.Y = Array();
var ix = 0;
$(XML).find('DrawingXML DrawingPoints Point X').each(function()
{
points.X[ix++] = $(this).text();
});
$(XML).find('DrawingXML DrawingPoints Point Y').each(function()
{
points.Y[ix++] = $(this).text();
});
This might not be exact, I didn't test it and my Javascript is a bit rusty, but you get the idea.

Categories