Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

nudged

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nudged - npm Package Compare versions

Comparing version 0.3.1 to 0.4.0

71

example/src/index.js

@@ -0,1 +1,6 @@

/*
A demonstration app for nudged
*/
var Hammer = require('hammerjs');

@@ -13,3 +18,3 @@ var loadimages = require('loadimages');

var drawPoint = function (ctx, px, py, label, color, ghost) {
var radius = 10;
var radius = 14;
ctx.font = '14px bold serif';

@@ -45,15 +50,35 @@ ctx.fillStyle = color;

// Improve usability by tweaking the recognizers
hammerRange.get('pan').set({ threshold: 5 });
hammerDomain.get('swipe').set({ enable: false });
hammerDomain.get('pan').set({
threshold: 5,
direction: Hammer.DIRECTION_ALL
});
hammerRange.get('swipe').set({ enable: false });
hammerRange.get('pan').set({
threshold: 5,
direction: Hammer.DIRECTION_ALL
});
// Input; point creation
hammerDomain.on('tap', function (ev) {
// Transform to canvas coordinates
var cr = canvasDomain.getBoundingClientRect();
var x = ev.center.x - cr.left;
var y = ev.center.y - cr.top;
(function defineHowToCreateDomainPoints() {
// Input; point creation
hammerDomain.on('tap', function (ev) {
// Transform to canvas coordinates
var cr = canvasDomain.getBoundingClientRect();
var x = ev.center.x - cr.left;
var y = ev.center.y - cr.top;
model.addToDomain(x, y);
});
model.addToDomain(x, y);
});
(function defineHowToPanDomainPoints() {
hammerDomain.on('press', function (ev) {
// Transform to canvas coordinates
var cr = canvasDomain.getBoundingClientRect();
var x = ev.center.x - cr.left;
var y = ev.center.y - cr.top;
model.addFixedPoint(x, y);
});
}());
(function defineHowToPanDomainAndFixedPoints() {
var movingPoint = null;

@@ -69,3 +94,3 @@ var x0 = 0;

var np = model.findNearestDomainPoint(x, y);
var np = model.findNearestDomainOrFixedPoint(x, y);
if (np !== null) {

@@ -128,2 +153,3 @@ // Found

var ran = model.getRange();
var piv = model.getFixedPoint();
var tra = model.getTransform();

@@ -136,5 +162,6 @@ var invtra = tra.getInverse();

// Domain image: always still
ctxDomain.drawImage(img, dx, dy, iw, ih);
// Apply transform
// Range image: apply transform to it
ctxRange.setTransform(tra.s, tra.r, -tra.r, tra.s, tra.tx, tra.ty);

@@ -144,3 +171,8 @@ ctxRange.drawImage(img, dx, dy, iw, ih);

// Draw points to canvases
// Draw points to the canvases
if (piv !== null) {
drawPoint(ctxDomain, piv.x, piv.y, piv.label, 'black', false);
drawPoint(ctxRange, piv.x, piv.y, piv.label, 'black', true);
}
dom.forEach(function (dp) {

@@ -167,5 +199,10 @@ // Transform the domain points to the range

var html = 'var domain = ' + JSON.stringify(domparam) + ';<br>' +
'var range = ' + JSON.stringify(ranparam) + ';<br>' +
'var trans = nudged.estimate(domain, range);<br>' +
'trans.getMatrix();<br>' +
'var range = ' + JSON.stringify(ranparam) + ';<br>';
if (piv !== null) {
html += 'var pivot = ' + JSON.stringify(pointToArray(piv)) + ';<br>';
html += 'var trans = nudged.estimateFixed(domain, range, pivot);<br>';
} else {
html += 'var trans = nudged.estimate(domain, range);<br>';
}
html += 'trans.getMatrix();<br>' +
'-> [[' + m[0][0] + ', ' + m[0][1] + ', ' + m[0][2] + '],<br>' +

@@ -172,0 +209,0 @@ ' [' + m[1][0] + ', ' + m[1][1] + ', ' + m[1][2] + '],<br>' +

@@ -11,2 +11,6 @@ var Emitter = require('component-emitter');

// If fixed point is set, pivot !== null
// If so, we use fixed point transformation.
this.pivot = null;
// Init with identity transform

@@ -18,3 +22,9 @@ this.transform = nudged.estimate([], []);

var ran = this.range.map(function (p) { return [p.x, p.y]; });
this.transform = nudged.estimate(dom, ran);
var piv;
if (this.pivot === null) {
this.transform = nudged.estimate(dom, ran);
} else {
piv = [this.pivot.x, this.pivot.y];
this.transform = nudged.estimateFixed(dom, ran, piv);
}
};

@@ -45,2 +55,14 @@

this.addFixedPoint = function (x, y) {
this.pivot = new Point(x, y, 'X');
var model = this;
this.pivot.on('update', function () {
model._updateTransform();
model.emit('update');
});
this._updateTransform();
this.emit('update');
};
this._findNearestPoint = function (list, x, y) {

@@ -72,2 +94,12 @@ // Time complexity O(n) ≈ sloooow

this.findNearestDomainOrFixedPoint = function (x, y) {
var points;
if (this.pivot === null) {
points = this.domain;
} else {
points = this.domain.concat([this.pivot]);
}
return this._findNearestPoint(points, x, y);
};
this.getDomain = function () {

@@ -81,2 +113,6 @@ return this.domain;

this.getFixedPoint = function () {
return this.pivot;
};
this.getTransform = function () {

@@ -83,0 +119,0 @@ return this.transform;

@@ -25,11 +25,2 @@ var Transform = require('./Transform');

// If length is one, the denominator becomes zero and estimates can not be
// computed. However, for this special case we can choose the translation
// be the best quess.
if (N === 1) {
tx = Y[0][0] - X[0][0];
ty = Y[0][1] - X[0][1];
return new Transform(1, 0, tx, ty);
} // else
var i, a, b, c, d;

@@ -65,3 +56,3 @@ var a1 = 0;

// It is zero iff X[i] = X[j] for every i and j in [0, n).
// In other words, iff all the domain points are the same.
// In other words, iff all the domain points are the same or there is only one domain point.
var den = N * a2 + N * b2 - a1 * a1 - b1 * b1;

@@ -68,0 +59,0 @@

@@ -1,2 +0,2 @@

var estimate = require('./estimate');
var Transform = require('./Transform');

@@ -17,18 +17,55 @@ module.exports = function estimateFixed(domain, range, pivot) {

//
var X, Y, px, py, N, X_hat, Y_hat, i;
var X, Y, N, s, r, tx, ty;
// Alias
X = domain;
Y = range;
// Allow arrays of different length but
// ignore the extra points.
N = Math.min(X.length, Y.length);
X_hat = [];
Y_hat = [];
px = pivot[0];
py = pivot[1];
var v = pivot[0];
var w = pivot[1];
var i, a, b, c, d;
var a2, b2;
a2 = b2 = 0;
var ac, bd, bc, ad;
ac = bd = bc = ad = 0;
for (i = 0; i < N; i += 1) {
X_hat.push([X[i][0] - px, X[i][1] - py]);
Y_hat.push([Y[i][0] - px, Y[i][1] - py]);
a = X[i][0] - v;
b = X[i][1] - w;
c = Y[i][0] - v;
d = Y[i][1] - w;
a2 += a * a;
b2 += b * b;
ac += a * c;
bd += b * d;
bc += b * c;
ad += a * d;
}
return estimate(X_hat, Y_hat);
// Denominator = determinant.
// It becomes zero iff N = 0 or X[i] = [v, w] for every i in [0, n).
// In other words, iff all the domain points are under the fixed point or
// there is no domain points.
var den = a2 + b2;
var eps = 0.00000001;
if (Math.abs(den) < eps) {
// The domain points are under the pivot or there is no domain points.
// We assume identity transform be the simplest guess. It keeps
// the domain points under the pivot if there is some.
return new Transform(1, 0, 0, 0);
}
// Estimators
s = (ac + bd) / den;
r = (-bc + ad) / den;
tx = w * r - v * s + v;
ty = -v * r - w * s + w;
return new Transform(s, r, tx, ty);
};

@@ -1,1 +0,1 @@

module.exports = '0.3.1';
module.exports = '0.4.0';
{
"name": "nudged",
"version": "0.3.1",
"version": "0.4.0",
"description": "Similarity transformation estimator",

@@ -5,0 +5,0 @@ "keywords": [

@@ -1,11 +0,15 @@

# nudged<sup>0.3.1</sup>
# nudged<sup>0.4.0</sup>
A JavaScript lib to estimate scale, rotation, and translation between two sets of 2D points. Applicable for example in cases where one wants to move objects by multiple fingers or where a large number of points from an eye tracker device are wanted to be corrected based on a few calibration points. In general, you can apply *nudged* in any situation where you want to move a number of points based on a few sample points.
A JavaScript lib to estimate scale, rotation, and translation between two sets of 2D points. Applicable for example in cases where one wants to move objects by multiple fingers or where a large number of points from an eye tracker device are wanted to be corrected based on a few calibration points. In general, you can apply *nudged* in any situation where you want to move a number of points based on a few sample points and optionally a fixed pivot point.
<img src="https://rawgit.com/axelpale/nudged/master/doc/nudged-logo.png" alt="Example transformation" width="300"/>
Mathematically speaking, *nudged* is an optimal least squares estimator for [affine transformation matrices](https://en.wikipedia.org/wiki/Affine_transformation) with uniform scaling, rotation, and translation and without reflection or shearing. The estimation has time complexity of O(*n*) that consists of *6n+22* multiplications and *11n+19* additions, where *n* is the cardinality (size) of the point sets. In other words, *nudged* solves an affine 2D to 2D point set registration problem in linear time.
Mathematically speaking, *nudged* is an optimal least squares estimator for [affine transformation matrices](https://en.wikipedia.org/wiki/Affine_transformation) with uniform scaling, rotation, and translation and without reflection or shearing. The estimation has time complexity of O(*n*) that consists of *6n+22* multiplications and *11n+19* additions, where *n* is the cardinality (size) of the point sets. Under the constraint of a fixed pivot point, the number of operations is even smaller. In other words, *nudged* solves an affine 2D to 2D point set registration problem in linear time.
The development of *nudged* has been supported by [Infant Cognition Laboratory](http://www.uta.fi/med/icl/index.html) at [University of Tampere](http://www.uta.fi/en/) where it is used to correct eye tracking data.
Available also [in Python](https://pypi.python.org/pypi/nudged).
## Example app

@@ -27,33 +31,42 @@

// Points before and after transformation i.e. the training data
Let `domain` and `range` be point sets before and after transformation i.e. the training data:
var domain = [[0,0], [2,0], [ 1,2]];
var range = [[1,1], [1,3], [-1,2]];
// Compute an optimal tranformation based on the points
Compute an optimal transformation based on the points:
var trans = nudged.estimate(domain, range);
// Display the transformation matrix
Alternatively, set a fixed pivot point that should not be altered in the transformation. You can think it as a pin or anchor:
var pivot = [3,3];
var pivotedTrans = nudged.estimateFixed(domain, range, pivot);
Examine the transformation matrix:
trans.getMatrix()
// [[0,-1, 1],
// [1, 0, 1],
// [0, 0, 1]]
-> [[0,-1, 1],
[1, 0, 1],
[0, 0, 1]]
trans.getRotation()
-> 1.5707... = π / 2
trans.getScale()
-> 1.0
trans.getTranslation()
-> [1, 1]
// Apply the transformation to other points
Apply the transformation to other points:
trans.transform([2,2])
// [-1,3]
-> [-1,3]
// Get rotation in radians
trans.getRotation()
// 1.5707... = π / 2
Inverse the transformation:
// Get scaling multiplier
trans.getScale()
// 1.0
var inv = trans.getInverse();
inv.transform([-1,3])
-> [2,2]
// Get horizontal and vertical movement
trans.getTranslation()
// [1, 1]
## API

@@ -64,5 +77,7 @@

Compute an optimal affine transformation from the domain to range points.
**Parameters**
- *domain*, array of [x,y] points
- *range*, array of [x,y] points
- *domain*: array of [x,y] points
- *range*: array of [x,y] points

@@ -74,2 +89,16 @@ The *domain* and *range* should have equal length. Different lengths are allowed but additional points in the longer array are ignored in the estimation.

### nudged.estimateFixed(domain, range, pivot)
Often one point, e.g. a corner of a picture, needs to stay put regardless of the domain and range.
**Parameters**
- *domain*: array of [x,y] points
- *range*: array of [x,y] points
- *pivot*: [x,y] point
The *domain* and *range* should have equal length. Different lengths are allowed but additional points in the longer array are ignored in the estimation.
**Return** new *nudged.Transform(...)* instance.
### nudged.version

@@ -92,2 +121,4 @@

Apply the transform to a point or an array of points.
**Return** an array of transformed points or single point if only a point was given. For example:

@@ -109,2 +140,4 @@

Get clockwise rotation from the positive x-axis.
**Return** rotation in radians.

@@ -111,0 +144,0 @@

@@ -109,2 +109,11 @@ var should = require('should');

describe('.estimateFixed', function () {
it('should allow domain under pivot', function () {
var t = nudged.estimateFixed([[1,1], [1,1]], [[2,2], [2,2]], [1,1]);
// Identity transform
t.transform([5,6]).should.deepEqual([5,6]);
});
});
describe('.Transform', function () {

@@ -111,0 +120,0 @@ var t;

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc