- BezLinApp
+ Hello World!
diff --git a/js/approximation/approximator.js b/js/approximation/approximator.js
index 0fd97d1631dfc4fb9a193a6c3e7d916fd0895b2b..76de7fcd957cc20c83732f8c567c08de13546cc4 100644
--- a/js/approximation/approximator.js
+++ b/js/approximation/approximator.js
@@ -1,9 +1,16 @@
var method = Approximator.prototype;
// constructor
-function Approximator(paths, precision) {
- this._paths = paths;
+function Approximator(svgParser, precision) {
+ this._parser = svgParser;
this._precision = precision;
+ // precision can either be the precisionMap or the overall precision-value (if no heatmap was provided)
+ this._heatmapProvided = ((typeof precision) === "object") ? true : false;
+ // Defaults for scale and offset
+ this._scaleX = 1;
+ this._scaleY = 1;
+ this._offsetX = 0;
+ this._offsetY = 0;
}
/**
@@ -62,7 +69,8 @@ method.buildCurveCommands = function(curves) {
var curve = curves[i];
var d = this.getDistance(curve[0][0], curve[0][1], curve[3][0], curve[3][1]);
- var p = this._precision.getPrecision(curve[0][0], curve[0][1]);
+ // get the precision, mind the offset and the scale for the heatmap
+ var p = (this._heatmapProvided) ? this._precision.getPrecision((curve[0][0]-this._offsetX)/this._scaleX, (curve[0][1]-this._offsetY)/this._scaleY) : this._precision;
if (d <= p) {
// transform into "L"-command from p0 -> p3
commands.push(["L", curve[3][0], curve[3][1]]);
@@ -172,11 +180,26 @@ method.approximatePath = function(pathNo) {
Starts the whole approximation-process
*/
method.approximateData = function(callback) {
- for (var i = 0; i < this._paths.length; i++) {
- var retPath = this.approximatePath(i);
- this._paths[i] = retPath;
- }
- callback(null, this._paths);
+ var self = this;
+
+ this._parser.getSVGPaths(function(err, result) {
+ self._paths = result;
+ if (self._heatmapProvided) {
+ // calculate the bounding box first!
+ self._parser.calculateBoundingBox();
+ var scale = self._parser.getScale(self._precision.getWidth(), self._precision.getHeight());
+ self._scaleX = scale[0];
+ self._scaleY = scale[1];
+ var offset = self._parser.getOffset();
+ self._offsetX = offset[0];
+ self._offsetY = offset[1];
+ }
+ for (var i = 0; i < self._paths.length; i++) {
+ var retPath = self.approximatePath(i);
+ self._paths[i] = retPath;
+ }
+ callback(null, self._paths);
+ })
};
module.exports = Approximator;
diff --git a/js/approximation/precisionMap.js b/js/approximation/precisionMap.js
index 668d3318b40ae0cf16093360b3f70520972c13d2..cbf4cb7d2cb42a8ffd398ef57401bfd1fd09044e 100644
--- a/js/approximation/precisionMap.js
+++ b/js/approximation/precisionMap.js
@@ -2,32 +2,26 @@ var method = PrecisionMap.prototype;
// constructor
function PrecisionMap(data, min, max) {
- if ((typeof data) == 'object') {
- this._data = data;
-
- /*
- FYI - NDArray Format: ((x),(y),(r,g,b,a))
- Access pixel with ....get(x,y,c)
- e.g.:
- console.log("Red", pixels.get(0,0,0), pixels.get(0,0,1), pixels.get(0,0,2), pixels.get(0,0,3));
- console.log("Blue", pixels.get(1,0,0), pixels.get(1,0,1), pixels.get(1,0,2), pixels.get(1,0,3));
- console.log("Black", pixels.get(2,0,0), pixels.get(2,0,1), pixels.get(2,0,2), pixels.get(2,0,3));
- */
-
- // get the dimensions
- var shape = this._data.shape.slice();
- this._w = shape[0];
- this._h = shape[1];
- this._c = shape[2];
-
- this._min = min;
- this._max = max;
- this._diff = max-min;
- } else {
- // no heatmap provided, we only have the "max"-value
- this._data = null;
- this._max = data;
- }
+ this._data = data;
+
+ /*
+ FYI - NDArray Format: ((x),(y),(r,g,b,a))
+ Access pixel with ....get(x,y,c)
+ e.g.:
+ console.log("Red", pixels.get(0,0,0), pixels.get(0,0,1), pixels.get(0,0,2), pixels.get(0,0,3));
+ console.log("Blue", pixels.get(1,0,0), pixels.get(1,0,1), pixels.get(1,0,2), pixels.get(1,0,3));
+ console.log("Black", pixels.get(2,0,0), pixels.get(2,0,1), pixels.get(2,0,2), pixels.get(2,0,3));
+ */
+
+ // get the dimensions
+ var shape = this._data.shape.slice();
+ this._w = shape[0];
+ this._h = shape[1];
+ this._c = shape[2];
+
+ this._min = parseFloat(min);
+ this._max = parseFloat(max);
+ this._diff = min-max;
}
/**
@@ -35,13 +29,18 @@ function PrecisionMap(data, min, max) {
x and y are floored in the method.
*/
method.getPrecision = function(x, y) {
- if (this._data == null) {
- return this._max;
- }
// Note: (0,0) in SVG is "bottom left" and (0,0) in PNG is "top left"
- var value = this._data.get(Math.floor(x), Math.floor(this._h - y), 0);
- var p = this._max + this._diff * (x/255);
+ var value = this._data.get(Math.floor(x), Math.floor(y), 0);
+ var p = this._max + this._diff * (value/255);
return p;
};
+method.getHeight = function() {
+ return this._h;
+}
+
+method.getWidth = function() {
+ return this._w;
+}
+
module.exports = PrecisionMap;
diff --git a/js/parser/svg-parser.js b/js/parser/svg-parser.js
index 5a52457e715cbcc22145bb0e835f5be3ded59620..82f01955c36b1e8f169ef60a70ab282e35378dc4 100644
--- a/js/parser/svg-parser.js
+++ b/js/parser/svg-parser.js
@@ -1,12 +1,181 @@
var method = SVGParser.prototype;
// empty constructor
-function SVGParser() {
+function SVGParser(path) {
+ this._path = path;
this._fs = require('fs');
this._xml2js = require('xml2js');
this._parsedData = null;
}
+method.getHeight = function() {
+ return this._parsedData.svg.$.height;
+}
+
+method.getBezierFunctionValue = function(t, p0, p1, p2, p3) {
+ return (p0*Math.pow(1-t,3) + 3 * p1 * t * Math.pow(1-t,2) + 3 * p2 * (1-t) * Math.pow(t,2) + p3 * Math.pow(t, 3));
+}
+
+method.getMaxOfArray = function(numArray) {
+ return Math.max.apply(null, numArray);
+}
+
+method.getMinOfArray = function(numArray) {
+ return Math.min.apply(null, numArray);
+}
+
+method.getMinMaxOfCurve = function(p0, p1, p2, p3) {
+ // for x
+ var a_x = 3*(-p0[0]+3*p1[0]-3*p2[0]+p3[0]);
+ var b_x = 6*(p0[0]-2*p1[0]+p2[0]);
+ var c_x = 3*(p1[0]-p0[0]);
+
+ var t_x1 = (-b_x + Math.sqrt(Math.pow(b_x,2)-4*a_x*c_x))/(2*a_x);
+ var t_x2 = (-b_x - Math.sqrt(Math.pow(b_x,2)-4*a_x*c_x))/(2*a_x);
+
+ var comps_x = [];
+ // value for t=0
+ comps_x.push(this.getBezierFunctionValue(0, p0[0], p1[0], p2[0], p3[0]));
+ // value for t=1
+ comps_x.push(this.getBezierFunctionValue(1, p0[0], p1[0], p2[0], p3[0]));
+
+ if (t_x1 >= 0 && t_x1 <= 1) {
+ comps_x.push(this.getBezierFunctionValue(t_x1, p0[0], p1[0], p2[0], p3[0]));
+ }
+ if (t_x2 >= 0 && t_x2 <= 1) {
+ comps_x.push(this.getBezierFunctionValue(t_x2, p0[0], p1[0], p2[0], p3[0]));
+ }
+
+ var xMin = this.getMinOfArray(comps_x);
+ var xMax = this.getMaxOfArray(comps_x);
+
+ // for y
+ var y_p0 = this.getHeight()-p0[1], y_p1 = this.getHeight()-p1[1], y_p2 = this.getHeight()-p2[1], y_p3 = this.getHeight()-p3[1];
+
+ var a_y = 3*(-y_p0+3*y_p1-3*y_p2+y_p3);
+ var b_y = 6*(y_p0-2*y_p1+y_p2);
+ var c_y = 3*(y_p1-y_p0);
+
+ var t_y1 = (-b_y + Math.sqrt(Math.pow(b_y,2)-4*a_y*c_y))/(2*a_y);
+ var t_y2 = (-b_y - Math.sqrt(Math.pow(b_y,2)-4*a_y*c_y))/(2*a_y);
+
+ var comps_y = [];
+ // value for t=0
+ comps_y.push(this.getBezierFunctionValue(0, y_p0, y_p1, y_p2, y_p3));
+ // value for t=1
+ comps_y.push(this.getBezierFunctionValue(1, y_p0, y_p1, y_p2, y_p3));
+
+ if (t_y1 >= 0 && t_y1 <= 1) {
+ comps_y.push(this.getBezierFunctionValue(t_y1, y_p0, y_p1, y_p2, y_p3));
+ }
+ if (t_y2 >= 0 && t_y2 <= 1) {
+ comps_y.push(this.getBezierFunctionValue(t_y2, y_p0, y_p1, y_p2, y_p3));
+ }
+
+ var yMin = this.getMinOfArray(comps_y);
+ var yMax = this.getMaxOfArray(comps_y);
+
+ return [xMin, xMax, yMin, yMax];
+}
+
+method.calculateBoundingBox = function() {
+ var xMin = Infinity, xMax = 0, yMin = Infinity, yMax = 0;
+ var paths = this._parsedPaths;
+ for (var i = 0; i < paths.length; i++) {
+ // paths
+ var path = paths[i];
+ var sPoint = [0,0];
+ for (var j = 0; j < path.length; j++) {
+ // commands
+ var cmd = path[j];
+ switch (cmd[0]) {
+ case "m":
+ // adjust sPoint to values (relative)
+ sPoint[0] += cmd[1];
+ sPoint[1] += cmd[2];
+ xMin = Math.min(xMin, sPoint[0]);
+ xMax = Math.max(xMax, sPoint[0]);
+ yMin = Math.min(yMin, sPoint[1]);
+ yMax = Math.max(yMax, sPoint[1]);
+ break;
+ case "M":
+ // adjust sPoint to values (absolute)
+ sPoint[0] = cmd[1];
+ sPoint[1] = cmd[2];
+ xMin = Math.min(xMin, sPoint[0]);
+ xMax = Math.max(xMax, sPoint[0]);
+ yMin = Math.min(yMin, sPoint[1]);
+ yMax = Math.max(yMax, sPoint[1]);
+ break;
+ case "c":
+
+ var p1 = [sPoint[0]+cmd[1],sPoint[1]+cmd[2]];
+ var p2 = [sPoint[0]+cmd[3],sPoint[1]+cmd[4]];
+ var p3 = [sPoint[0]+cmd[5],sPoint[1]+cmd[6]];
+
+ // calculate the x and y peaks of this curve, compare with current min/max
+ var curveMinMax = this.getMinMaxOfCurve(sPoint, p1, p2, p3);
+ xMin = Math.min(xMin, curveMinMax[0]);
+ xMax = Math.max(xMax, curveMinMax[1]);
+ yMin = Math.min(yMin, curveMinMax[2]);
+ yMax = Math.max(yMax, curveMinMax[3]);
+
+ // set new startingpoint to last point of second curve
+ sPoint[0] = p3[0];
+ sPoint[1] = p3[1];
+ break;
+ case "C":
+ var p1 = [cmd[1],cmd[2]];
+ var p2 = [cmd[3],cmd[4]];
+ var p3 = [cmd[5],cmd[6]];
+
+ // calculate the x and y peaks of this curve, compare with current min/max
+ var curveMinMax = this.getMinMaxOfCurve(sPoint, p1, p2, p3);
+ xMin = Math.min(xMin, curveMinMax[0]);
+ xMax = Math.max(xMax, curveMinMax[1]);
+ yMin = Math.min(yMin, curveMinMax[2]);
+ yMax = Math.max(yMax, curveMinMax[3]);
+
+ // set new startingpoint to last point of second curve
+ sPoint[0] = p3[0];
+ sPoint[1] = p3[1];
+ break;
+ case "l":
+ // set sPoint (relative)
+ var px = sPoint[0] + cmd[1];
+ var py = sPoint[1] + cmd[2];
+ xMin = Math.min(xMin, px, sPoint[0]);
+ xMax = Math.max(xMax, px, sPoint[0]);
+ yMin = Math.min(yMin, py, sPoint[1]);
+ yMax = Math.max(yMax, py, sPoint[1]);
+ sPoint[0] = px;
+ sPoint[1] = py;
+ break;
+ case "L":
+ // set sPoint (absolute)
+ xMin = Math.min(xMin, cmd[1], sPoint[0]);
+ xMax = Math.max(xMax, cmd[1], sPoint[0]);
+ yMin = Math.min(yMin, cmd[2], sPoint[1]);
+ yMax = Math.max(yMax, cmd[2], sPoint[1]);
+ sPoint[0] = cmd[1];
+ sPoint[1] = cmd[2];
+ break;
+ default:
+ // TODO: default behaviour
+ }
+ }
+ }
+ this._boundingBox = [xMin, xMax, yMin, yMax];
+}
+
+method.getScale = function(w, h) {
+ return [(this._boundingBox[1] - this._boundingBox[0])/w, (this._boundingBox[3] - this._boundingBox[2])/h];
+}
+
+method.getOffset = function() {
+ return [this._boundingBox[0], this.getHeight() - this._boundingBox[3]];
+}
+
/**
Takes the SVG-file that was approximated, substitutes it's path's d-attributes and writes it to a new file.
*/
@@ -42,14 +211,13 @@ method.writeSVG = function(filename, nPaths, callback) {
An example could look like this:
[[["m", 96, 363], ["C", 407, 189, 587, 328, 587, 328]], [...], ...]
*/
-method.getSVGPaths = function(path, callback) {
- this._path = path;
+method.getSVGPaths = function(callback) {
var parse = require('parse-svg-path');
// for getting the correct context in anonymous function
var self = this;
var parser = new this._xml2js.Parser();
- this._fs.readFile(path, function(err, data) {
+ this._fs.readFile(self._path, function(err, data) {
if (err) {
callback(err, null);
} else {
@@ -69,6 +237,7 @@ method.getSVGPaths = function(path, callback) {
var parsedPath = parse(pathData);
data.push(parsedPath);
}
+ self._parsedPaths = data;
callback(null, data);
}
});
diff --git a/js/script.js b/js/script.js
index b01df3e5247c02d144b5603ee4dbeee4dacc87cf..b2d44bb8727aa4116d7c988750ff08191d50beb8 100644
--- a/js/script.js
+++ b/js/script.js
@@ -11,11 +11,13 @@ $(function(){
var printOut = function(text) {
var d = new Date();
output.insertAdjacentHTML('beforeend', "
" + dateformat(d, "HH:MM:ss") + " - " + text + "
");
+ output.scrollTop = output.scrollHeight;
}
var printErr = function(text) {
var d = new Date();
output.insertAdjacentHTML('beforeend', "
" + dateformat(d, "HH:MM:ss") + " - " + text + "
");
+ output.scrollTop = output.scrollHeight;
}
/**
@@ -92,67 +94,55 @@ $(function(){
alert("No SVG submitted!");
} else {
var SVGParser = require("./js/parser/svg-parser.js");
+ svgParser = new SVGParser(svg);
+
+ if (heatmap != null) {
+ var PrecisionMap = require('./js/approximation/precisionMap.js');
+ printOut("Reading PNG...");
+ // the user has provided a heatmap, so we need to consider the precision-values from it:
+ // first, parse the file:
+ var getPixels = require("get-pixels");
+ getPixels(heatmap, function(err, pixels) {
+ if (err) {
+ printErr(err);
+ return;
+ }
- printOut("Reading SVG...");
- svgParser = new SVGParser();
- var data = svgParser.getSVGPaths(svg, function(err, result) {
- if (err) {
- printErr(err);
- } else {
- printOut("Parsed SVG-Paths: " + result.length + " paths.")
- console.dir(result);
-
- var PrecisionMap = require('./js/approximation/precisionMap.js');
-
- if (heatmap != null) {
- printOut("Reading PNG...");
- // the user has provided a heatmap, so we need to consider the precision-values from it:
- // first, parse the file:
- var getPixels = require("get-pixels");
- getPixels(heatmap, function(err, pixels) {
- if (err) {
- printErr(err);
- return;
- }
-
- // now, get the specified min/max values and create the precisionMap!
- var pMax = document.getElementById('pMax').value;
- var pMin = document.getElementById('pMin').value;
-
- printOut("Creating precisionmap...");
- var map = new PrecisionMap(pixels, pMin, pMax);
-
- // start the approximator with SVG-data and the precisionMap
- printOut("Approximating paths with heatmap...");
- var approximator = new Approximator(result, map);
- approximator.approximateData(function(err, nPaths) {
- if (err) {
- printErr(err);
- return;
- }
- writePathsToFile(nPaths);
- console.dir(nPaths);
- });
- });
- } else {
- // the user did not provide a heatmap, so just start the approximator
- // get the pMax-value, this will be the precision for all positions
- var pMax = document.getElementById('pMax').value;
- var map = new PrecisionMap(pMax);
-
- printOut("Approximating paths without heatmap...");
- var approximator = new Approximator(result, map);
- approximator.approximateData(function(err, nPaths) {
- if (err) {
- printErr(err);
- return;
- }
- writePathsToFile(nPaths);
- console.dir(nPaths);
- });
+ // now, get the specified min/max values and create the precisionMap!
+ var pMax = document.getElementById('pMax').value;
+ var pMin = document.getElementById('pMin').value;
+
+ printOut("Creating precisionmap...");
+ var map = new PrecisionMap(pixels, pMin, pMax);
+
+ // start the approximator with SVG-data and the precisionMap
+ printOut("Approximating paths with heatmap...");
+ var approximator = new Approximator(svgParser, map);
+ approximator.approximateData(function(err, nPaths) {
+ if (err) {
+ printErr(err);
+ return;
+ }
+ writePathsToFile(nPaths);
+ console.dir(nPaths);
+ });
+ });
+ } else {
+ // the user did not provide a heatmap, so just start the approximator
+ // get the pMax-value, this will be the precision for all positions
+ var pMax = document.getElementById('pMax').value;
+
+ printOut("Approximating paths without heatmap...");
+ var approximator = new Approximator(svgParser, pMax);
+ approximator.approximateData(function(err, nPaths) {
+ if (err) {
+ printErr(err);
+ return;
}
- }
- });
+ writePathsToFile(nPaths);
+ console.dir(nPaths);
+ });
+ }
}
}
diff --git a/main.js b/main.js
index f9d492b4e333cc231ebc2f8c383d8155662f280c..ca2d1a0f260208d9ca27def7810f939a0290a81d 100644
--- a/main.js
+++ b/main.js
@@ -21,10 +21,8 @@ app.on('window-all-closed', function() {
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
-
// Create the browser window.
- // Note: icon won't work for OSX, only for Linux and Windows
- mainWindow = new BrowserWindow({icon:'./res/icon.png', width: 800, height: 600});
+ mainWindow = new BrowserWindow({width: 800, height: 600});
// and load the index.html of the app.
mainWindow.loadURL('file://' + __dirname + '/index.html');
diff --git a/res/icon.png b/res/icon.png
deleted file mode 100644
index 4b46f46be3ace9821e540ea1b1f847619dbfe417..0000000000000000000000000000000000000000
Binary files a/res/icon.png and /dev/null differ