Related
I've been trying to port an optimization routine from R to Rcpp. The Rcpp version isn't producing what I expect and I'm stumped as to what the problem might be. For context, the problem is to compute the inverse cdf of a "gamma(shape, scale) + normal(0, sigma^2)" distribution. In particular, given a value c, find x such that P(W' <= x) = c, where W' has the distribution described. Notice that P(W' <= x) = \int_W P(W' <= x | W) * f_W(W) dW, where W ~ gamma(shape, scale) and W' | W ~ normal(W, sigma^2).
I'm using RcppNumerical (https://stackoverflow.com/a/39449199/2875572) for integration (this seems to be working fine, as the test results indicate). It's the call to optimize that is producing the mysterious results.
R test functions:
IntegrateRTest <- function(x, SIGMA, SHAPE, SCALE) {
sapply(x,
function(x) {
integrate(f = function(W) {
# P(W' <= x | W) * f_W(W)
pnorm(x, mean = W, sd = SIGMA) * dgamma(W, shape = SHAPE, scale = SCALE)
}, 0, Inf)$value
})
}
OptimizeRTest <- function(c, SIGMA, SHAPE, SCALE) {
optimize(f = function(x) {
rhs <- integrate(f = function(W) {
# P(W' <= x | W) * f_W(W)
pnorm(x, mean = W, sd = SIGMA) *
dgamma(W, shape = SHAPE, scale = SCALE)
}, 0, Inf)$value
(c - rhs)^2
},
lower = -10,
upper = 10)
}
The Rcpp script:
#include <Rcpp.h>
#include <RcppNumerical.h>
// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::depends(RcppNumerical)]]
using namespace Rcpp;
// utility function for vectorized exponentiation
NumericVector vecpow(const NumericVector base, const NumericVector exp) {
NumericVector out(base.size());
std::transform(base.begin(), base.end(),
exp.begin(), out.begin(), static_cast<double(*)(double, double)>(::pow));
return out;
}
class Mintegrand: public Numer::Func {
private:
const double x;
const double SIGMA;
const double SHAPE;
const double SCALE;
public:
Mintegrand(double x_, double sigma_, double shape_, double scale_) : x(x_), SIGMA(sigma_), SHAPE(shape_), SCALE(scale_) {}
double operator()(const double& W) const
{
// P(W' <= x | W) * f_W(W)
return R::pnorm5(x, W, SIGMA, true, false) * R::dgamma(W, SHAPE, SCALE, false);
}
};
NumericVector objective(NumericVector x,
double c,
double SIGMA,
double SHAPE,
double SCALE) {
// for loop is to "vectorize" this function (required by stats::optimize)
NumericVector rhs(x.length());
for (int i = 0; i < x.length(); ++i) {
Mintegrand f(x[i], SIGMA, SHAPE, SCALE);
double err_est;
int err_code;
// compute P(W' <= x) = \int_W P(W' <= x | W) * f_W(W) dW
rhs[i] = Numer::integrate(f, 0.0, R_PosInf, err_est, err_code);
}
return vecpow(c - rhs, 2.0);
}
// [[Rcpp::export]]
NumericVector IntegrateTest(NumericVector x,
double SIGMA,
double SHAPE,
double SCALE) {
NumericVector rhs(x.length());
for (int i = 0; i < x.length(); ++i) {
Mintegrand f(x[i], SIGMA, SHAPE, SCALE);
double err_est;
int err_code;
// compute P(W' <= x) = \int_W P(W' <= x | W) * f_W(W) dW
rhs[i] = Numer::integrate(f, 0.0, R_PosInf, err_est, err_code);
}
return rhs;
}
// [[Rcpp::export]]
List OptimizeTest(double c,
double SIGMA,
double SHAPE,
double SCALE) {
Environment stats("package:stats");
Function optimize = stats["optimize"];
return optimize(_["f"] = InternalFunction(&objective),
_["c"] = c,
_["SIGMA"] = SIGMA,
_["SHAPE"] = SHAPE,
_["SCALE"] = SCALE,
_["lower"] = -10.0,
_["upper"] = 10.0);
}
Test results:
all.equal(IntegrateTest(seq(0, 1, .01), SIGMA = .4, SHAPE = .9, SCALE = .5),
IntegrateRTest(seq(0, 1, .01), SIGMA = .4, SHAPE = .9, SCALE = .5))
# TRUE
OptimizeTest(.9, SIGMA = .4, SHAPE = 9, SCALE = .5)
OptimizeRTest(.9, SIGMA = .4, SHAPE = 9, SCALE = .5)
# gives very different results
I'm really struggling here and I can't get it right, not even knowing why.
I'm using p5.js in WEBGL mode, I want to compute the position of on point rotated on the 3 axes around the origin in order to follow the translation and the rotation given to object through p5.js, translation and rotatation on X axis, Y axis and Z axis.
The fact is that drawing a sphere in 3d space, withing p5.js, is obtained by translating and rotating, since the sphere is created at the center in the origin, and there is no internal model giving the 3d-coordinates.
After hours of wandering through some math too high for my knowledge, I understood that the rotation over 3-axis is not as simple as I thought, and I ended up using Quaternion.js. But I'm still not able to match the visual position of the sphere in the 3d world with the coordinates I have computed out of the original point on the 2d-plane (150, 0, [0]).
For example, here the sphere is rotated on 3 axis. At the beginning the coordinates are good (if I ignore the fact that Z is negated) but at certain point it gets completely out of sync. The computed position of the sphere seems to be completely unrelated:
It's really hours that I'm trying to solve this issue, with no result, what did I miss?
Here it follows my code:
//font for WEBGL
var robotoFont;
var dotId = 0;
var rotating = true;
var orbits = [];
var dotsData = [];
function preload() {
robotoFont = loadFont('./assets/Roboto-Regular.ttf');
}
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
textFont(robotoFont);
background(0);
let orbit1 = new Orbit(0, 0, 0, 0.5, 0.5, 0.5);
orbit1.obj.push(new Dot(0, 0));
orbits.push(orbit1);
// let orbit2 = new Orbit(90, 45, 0);
// orbit2.obj.push(new Dot(0, 0));
// orbits.push(orbit2);
}
function draw() {
angleMode(DEGREES);
background(0);
orbitControl();
let len = 200;
fill('white');
stroke('white');
sphere(2);
stroke('red');
line(0, 0, 0, len, 0, 0);
text('x', len, 0)
stroke('green');
line(0, 0, 0, 0, len, 0);
text('y', 0, len)
push();
rotateX(90);
stroke('yellow');
line(0, 0, 0, 0, len, 0);
text('z', 0, len)
pop();
dotsData = [];
orbits.forEach(o => o.draw());
textSize(14);
push();
for (let i = 0; i < 2; i++) {
let yPos = -(windowHeight / 2) + 15;
for (let i = 0; i < dotsData.length; i++) {
let [id, pos, pos3d] = dotsData[i];
let [x1, y1, z1] = [pos[0].toFixed(0), pos[1].toFixed(0), pos[2].toFixed(0)];
let [x2, y2, z2] = [pos3d.x.toFixed(0), pos3d.y.toFixed(0), pos3d.z.toFixed(0)];
text(`${id}: (${x1}, ${y1}, ${z1}) -> (${x2}, ${y2}, ${z2})`, -windowWidth / 2 + 5, yPos);
yPos += 18;
}
rotateX(-90);
}
pop();
}
function mouseClicked() {
// controls.mousePressed();
}
function keyPressed() {
// controls.keyPressed(keyCode);
if (keyCode === 32) {
rotating = !rotating;
}
}
class Orbit {
constructor(x, y, z, xr, yr, zr) {
this.obj = [];
this.currentRot = [
x ? x : 0,
y ? y : 0,
z ? z : 0
]
this.rot = [
xr ? xr : 0,
yr ? yr : 0,
zr ? zr : 0
]
}
draw() {
push();
if (rotating) {
this.currentRot[0] += this.rot[0];
this.currentRot[1] += this.rot[1];
this.currentRot[2] += this.rot[2];
}
rotateY(this.currentRot[1]);
rotateX(this.currentRot[0]);
rotateZ(this.currentRot[2]);
noFill();
stroke('white');
ellipse(0, 0, 300, 300);
for (let i = 0; i < this.obj.length; i++) {
let o = this.obj[i];
o.draw();
dotsData.push([o.id, o.getPosition(), this.#get3DPos(o)]);
}
pop();
}
#get3DPos(o) {
let [x, y, z] = o.getPosition();
let w = 0;
let rotX = this.currentRot[0] * PI / 180;
let rotY = this.currentRot[1] * PI / 180;
let rotZ = this.currentRot[2] * PI / 180;
let rotation = Quaternion.fromEuler(rotZ, rotX, rotY, 'ZXY').conjugate();
[x, y, z] = rotation.rotateVector([x, y, z]);
return createVector(x, y, z);
}
}
class Dot {
constructor(angle) {
this.id = ++dotId;
this.x = cos(angle) * 150;
this.y = sin(angle) * 150;
}
draw() {
push();
fill('gray');
translate(this.x, this.y);
noStroke();
sphere(15);
pop();
}
getPosition() {
return [this.x, this.y, 0];
}
}
It doesn't work in stackoverflow because I need local asset like the font.
Here the working code: https://editor.p5js.org/cigno5/sketches/_ZVq0kjJL
I've finally sorted out. I can't really understand why works this way but I didn't need quaternion at all, and my first intuition of using matrix multiplications to apply rotation on 3-axis was correct.
What I did miss in first instance (and made my life miserable) is that matrix multiplication is not commutative. This means that applying rotation on x, y and z-axis is not equivalent to apply same rotation angle on z, y and x.
The working solution has been achieved with 3 simple steps:
Replace quaternion with matrix multiplications using vectors (method #resize2)
Rotating the drawing plane with Z-Y-X order
Doing the math of rotation in X-Y-Z order
//font for WEBGL
var robotoFont;
var dotId = 0;
var rotating = true;
var orbits = [];
var dotsData = [];
function preload() {
robotoFont = loadFont('./assets/Roboto-Regular.ttf');
}
function setup() {
createCanvas(windowWidth, windowHeight, WEBGL);
textFont(robotoFont);
background(0);
let orbit1 = new Orbit(0, 0, 0, 0.5, 0.5, 0.5);
orbit1.obj.push(new Dot(0, 0.5));
orbits.push(orbit1);
// let orbit2 = new Orbit(90, 45, 0);
// orbit2.obj.push(new Dot(0, 0));
// orbits.push(orbit2);
}
function draw() {
angleMode(DEGREES);
background(0);
orbitControl();
let len = 200;
fill('white');
stroke('white');
sphere(2);
stroke('red');
line(0, 0, 0, len, 0, 0);
text('x', len, 0)
stroke('green');
line(0, 0, 0, 0, len, 0);
text('y', 0, len)
push();
rotateX(90);
stroke('yellow');
line(0, 0, 0, 0, len, 0);
text('z', 0, len)
pop();
dotsData = [];
orbits.forEach(o => o.draw());
textSize(14);
push();
for (let i = 0; i < 2; i++) {
let yPos = -(windowHeight / 2) + 15;
for (let i = 0; i < dotsData.length; i++) {
let [id, pos, pos3d] = dotsData[i];
let [x1, y1, z1] = [pos[0].toFixed(0), pos[1].toFixed(0), pos[2].toFixed(0)];
let [x2, y2, z2] = [pos3d.x.toFixed(0), pos3d.y.toFixed(0), pos3d.z.toFixed(0)];
text(`${id}: (${x1}, ${y1}, ${z1}) -> (${x2}, ${y2}, ${z2})`, -windowWidth / 2 + 5, yPos);
yPos += 18;
}
rotateX(-90);
}
pop();
}
function mouseClicked() {
// controls.mousePressed();
}
function keyPressed() {
// controls.keyPressed(keyCode);
if (keyCode === 32) {
rotating = !rotating;
}
}
class Orbit {
constructor(x, y, z, xr, yr, zr) {
this.obj = [];
this.currentRot = [
x ? x : 0,
y ? y : 0,
z ? z : 0
]
this.rot = [
xr ? xr : 0,
yr ? yr : 0,
zr ? zr : 0
]
}
draw() {
push();
if (rotating) {
this.currentRot[0] += this.rot[0];
this.currentRot[1] += this.rot[1];
this.currentRot[2] += this.rot[2];
}
rotateZ(this.currentRot[2]);
rotateY(this.currentRot[1]);
rotateX(this.currentRot[0]);
noFill();
stroke('white');
ellipse(0, 0, 300, 300);
for (let i = 0; i < this.obj.length; i++) {
let o = this.obj[i];
o.draw();
dotsData.push([o.id, o.getPosition(), this.#get3DPos(o)]);
}
pop();
}
#get3DPos(o) {
let [x, y, z] = o.getPosition();
let pos = createVector(x, y, z);
pos = this.#rotate2(pos, createVector(1, 0, 0), this.currentRot[0]);
pos = this.#rotate2(pos, createVector(0, 1, 0), this.currentRot[1]);
pos = this.#rotate2(pos, createVector(0, 0, 1), this.currentRot[2]);
return pos;
}
//https://stackoverflow.com/questions/67458592/how-would-i-rotate-a-vector-in-3d-space-p5-js
#rotate2(vect, axis, angle) {
// Make sure our axis is a unit vector
axis = p5.Vector.normalize(axis);
return p5.Vector.add(
p5.Vector.mult(vect, cos(angle)),
p5.Vector.add(
p5.Vector.mult(
p5.Vector.cross(axis, vect),
sin(angle)
),
p5.Vector.mult(
p5.Vector.mult(
axis,
p5.Vector.dot(axis, vect)
),
(1 - cos(angle))
)
)
);
}
}
class Dot {
constructor(angle, speed) {
this.id = ++dotId;
this.angle = angle;
this.speed = speed
}
draw() {
this.angle += this.speed;
this.x = cos(this.angle) * 150;
this.y = sin(this.angle) * 150;
push();
fill('gray');
translate(this.x, this.y);
noStroke();
sphere(15);
pop();
}
getPosition() {
return [this.x, this.y, 0];
}
}
And now it works like a charm:
https://editor.p5js.org/cigno5/sketches/PqB9CEnBp
I have an Angular 7 app with a home page containing a large coloured block (enough to fill the page) at the top with a header and some images. I want to put some lava effect animations into the background similar to this
code in case link is removed:
HTML:
<canvas id="lamp-anim" class="lamp-anim" width="1034" height="613"></canvas>
CSS:
body {
background: #f857a6; /* fallback for old browsers */
background: -webkit-linear-gradient(to top, #ff5858, #f857a6); /* Chrome
10-25, Safari 5.1-6 */
background: linear-gradient(to top, #ff5858, #f857a6); /* W3C, IE 10+/
Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}
JS:
window.lavaAnimation = function() {
"use strict";
var t, i = {
screen: {
elem: null,
callback: null,
ctx: null,
width: 0,
height: 0,
left: 0,
top: 0,
init: function(t, i, s) {
return this.elem = document.getElementById(t), this.callback = i || null, "CANVAS" == this.elem.tagName && (this.ctx = this.elem.getContext("2d")), window.addEventListener("resize", function() {
this.resize()
}.bind(this), !1), this.elem.onselectstart = function() {
return !1
}, this.elem.ondrag = function() {
return !1
}, s && this.resize(), this
},
resize: function() {
var t = this.elem;
for (this.width = t.offsetWidth, this.height = t.offsetHeight, this.left = 0, this.top = 0; null != t; t = t.offsetParent) this.left += t.offsetLeft, this.top += t.offsetTop;
this.ctx && (this.elem.width = this.width, this.elem.height = this.height), this.callback && this.callback()
}
}
},
s = function(t, i) {
this.x = t, this.y = i, this.magnitude = t * t + i * i, this.computed = 0, this.force = 0
};
s.prototype.add = function(t) {
return new s(this.x + t.x, this.y + t.y)
};
var h = function(t) {
var i = .1,
h = 1.5;
this.vel = new s((Math.random() > .5 ? 1 : -1) * (.2 + .25 * Math.random()), (Math.random() > .5 ? 1 : -1) * (.2 + Math.random())), this.pos = new s(.2 * t.width + Math.random() * t.width * .6, .2 * t.height + Math.random() * t.height * .6), this.size = t.wh / 15 + (Math.random() * (h - i) + i) * (t.wh / 15), this.width = t.width, this.height = t.height
};
h.prototype.move = function() {
this.pos.x >= this.width - this.size ? (this.vel.x > 0 && (this.vel.x = -this.vel.x), this.pos.x = this.width - this.size) : this.pos.x <= this.size && (this.vel.x < 0 && (this.vel.x = -this.vel.x), this.pos.x = this.size), this.pos.y >= this.height - this.size ? (this.vel.y > 0 && (this.vel.y = -this.vel.y), this.pos.y = this.height - this.size) : this.pos.y <= this.size && (this.vel.y < 0 && (this.vel.y = -this.vel.y), this.pos.y = this.size), this.pos = this.pos.add(this.vel)
};
var e = function(t, i, e, n, a) {
this.step = 5, this.width = t, this.height = i, this.wh = Math.min(t, i), this.sx = Math.floor(this.width / this.step), this.sy = Math.floor(this.height / this.step), this.paint = !1, this.metaFill = r(t, i, t, n, a), this.plx = [0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0], this.ply = [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1], this.mscases = [0, 3, 0, 3, 1, 3, 0, 3, 2, 2, 0, 2, 1, 1, 0], this.ix = [1, 0, -1, 0, 0, 1, 0, -1, -1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1], this.grid = [], this.balls = [], this.iter = 0, this.sign = 1;
for (var o = 0; o < (this.sx + 2) * (this.sy + 2); o++) this.grid[o] = new s(o % (this.sx + 2) * this.step, Math.floor(o / (this.sx + 2)) * this.step);
for (var l = 0; e > l; l++) this.balls[l] = new h(this)
};
e.prototype.computeForce = function(t, i, s) {
var h, e = s || t + i * (this.sx + 2);
if (0 === t || 0 === i || t === this.sx || i === this.sy) h = .6 * this.sign;
else {
h = 0;
for (var r, n = this.grid[e], a = 0; r = this.balls[a++];) h += r.size * r.size / (-2 * n.x * r.pos.x - 2 * n.y * r.pos.y + r.pos.magnitude + n.magnitude);
h *= this.sign
}
return this.grid[e].force = h, h
}, e.prototype.marchingSquares = function(t) {
var i = t[0],
s = t[1],
h = t[2],
e = i + s * (this.sx + 2);
if (this.grid[e].computed === this.iter) return !1;
for (var r, n = 0, a = 0; 4 > a; a++) {
var l = i + this.ix[a + 12] + (s + this.ix[a + 16]) * (this.sx + 2),
d = this.grid[l].force;
(d > 0 && this.sign < 0 || 0 > d && this.sign > 0 || !d) && (d = this.computeForce(i + this.ix[a + 12], s + this.ix[a + 16], l)), Math.abs(d) > 1 && (n += Math.pow(2, a))
}
if (15 === n) return [i, s - 1, !1];
5 === n ? r = 2 === h ? 3 : 1 : 10 === n ? r = 3 === h ? 0 : 2 : (r = this.mscases[n], this.grid[e].computed = this.iter);
var p = this.step / (Math.abs(Math.abs(this.grid[i + this.plx[4 * r + 2] + (s + this.ply[4 * r + 2]) * (this.sx + 2)].force) - 1) / Math.abs(Math.abs(this.grid[i + this.plx[4 * r + 3] + (s + this.ply[4 * r + 3]) * (this.sx + 2)].force) - 1) + 1);
return o.lineTo(this.grid[i + this.plx[4 * r] + (s + this.ply[4 * r]) * (this.sx + 2)].x + this.ix[r] * p, this.grid[i + this.plx[4 * r + 1] + (s + this.ply[4 * r + 1]) * (this.sx + 2)].y + this.ix[r + 4] * p), this.paint = !0, [i + this.ix[r + 4], s + this.ix[r + 8], r]
}, e.prototype.renderMetaballs = function() {
for (var t, i = 0; t = this.balls[i++];) t.move();
for (this.iter++, this.sign = -this.sign, this.paint = !1, o.fillStyle = this.metaFill, o.beginPath(), i = 0; t = this.balls[i++];) {
var s = [Math.round(t.pos.x / this.step), Math.round(t.pos.y / this.step), !1];
do s = this.marchingSquares(s); while (s);
this.paint && (o.fill(), o.closePath(), o.beginPath(), this.paint = !1)
}
};
var r = function(t, i, s, h, e) {
var r = o.createRadialGradient(t / 1, i / 1, 0, t / 1, i / 1, s);
return r.addColorStop(0, h), r.addColorStop(1, e), r
};
if (document.getElementById("lamp-anim")) {
var n = function() {
requestAnimationFrame(n), o.clearRect(0, 0, a.width, a.height), t.renderMetaballs()
},
a = i.screen.init("lamp-anim", null, !0),
o = a.ctx;
a.resize(), t = new e(a.width, a.height, 6, "#3494E6", "#EC6EAD")
}
return {
run: n
}
}();
if (document.getElementById('lamp-anim')) {
lavaAnimation.run();
}
setTimeout(function() {
$('.js-works-d-list').addClass('is-loaded');
}, 150);
Is it possible to convert/do this in angular animations? Are they flexible enough to do this sort of (what id call advanced) animation?
I think the question of 'can I convert this to Angular' is a bit off because Angular runs on Typescript, which is a language built from javascript. So, yes you can do all this in Angular or rather using Typescript within an Angular app.
We're always here to help once you get some code written in an Angular app! But in general, we are here to help you were you get stuck in code and help you solve the problem. It's a bit more challenging to say 'yes it will work' without seeing how you implement it in your project and can't really guide or help you until we see how your angular components are written.
Short answer: Yeah, I think it can work. But it also depends how you implement this code into your Angular app.
I would like to integrate the functions provided by the "Fit Image" plattlet (especially 2D polynomial fit to a given input image and subtract it) in DM to script to automate the whole flow of image processing.
However, I could not find any description of how to do it.
It's appreciated if anyone know it, or have certain documentaion on this.
The script functionality for fitting is not yet officially supported/documented.
However, you can use the following examples to see how the commands work:
Commands
Boolean FitGaussian(Image* data, Image* errors, double* N, double* mu, double* sigma, double* chiSqr, double conv_cond)
ImageRef PlotGaussian(Image* data, double N, double mu, double sigma)
Boolean FitLorentzian(Image* data, Image* errors, double* I, double* x0, double* gamma, double* chiSqr, double conv_cond)
ImageRef PlotLorentzian(Image* data, double I, double x0, double gamma)
Boolean FitPolynomial(Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotPolynomial(Image* data, Image* pars)
Boolean FitGaussian2D(Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotGaussian2D(Image* data, Image* pars)
Boolean FitPolynomial2D(Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotPolynomial2D(Image* data, Image* pars)
Boolean FitFormula(dm_string formulaStr, Image* data, Image* errors, Image* pars, Image* parsToFit, double* chiSqr, double conv_cond)
ImageRef PlotFormula(dm_string formulaStr, Image* data, Image* pars)
example 1, 1D formula fit
// create the input image:
Image input := NewImage("formula test", 2, 100)
input = 500.5 - icol*11.1 + icol*icol*0.11
// add some random noise:
input += (random()-0.5)*sqrt(abs(input))
// create image with error data (not required)
Image errors := input.ImageClone()
errors = tert(input > 1, sqrt(input), 1)
// setup fit:
Image pars := NewImage("pars", 2, 3)
Image parsToFit := NewImage("pars to fit", 2, 3)
pars = 10; // starting values
parsToFit = 1;
Number chiSqr = 1e6
Number conv_cond = 0.00001
Result("\n starting pars = {")
Number xSize = pars.ImageGetDimensionSize(0)
Number i = 0
for (i = 0; i < xSize; i++)
{
Result(GetPixel(pars, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
// fit:
String formulaStr = "p0 + p1*x + p2*x**2"
Number ok = FitFormula(formulaStr, input, errors, pars, parsToFit, chiSqr, conv_cond)
Result("\n results pars = {")
for (i = 0; i < xSize; i++)
{
Result(GetPixel(pars, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
Result(", chiSqr ="+ chiSqr)
// plot results of fit:
Image plot := PlotFormula(formulaStr, input, pars)
// compare the plot and original data:
Image compare := NewImage("Compare Fit", 2, 100, 3)
compare[icol, 0] = input // original data
compare[icol, 1] = plot // fit function
compare[icol, 2] = input - plot // residuals
ImageDocument linePlotDoc = CreateImageDocument("Test Fitting")
ImageDisplay linePlotDsp = linePlotDoc.ImageDocumentAddImageDisplay(compare, 3)
linePlotDoc.ImageDocumentShow()
example 2, 2D Gaussian fit
// $BACKGROUND$
// create data image
Image img := NewImage("Gaussian2D", 2, 200, 200)
Image true_pars := NewImage("Gaussian2D Pars", 2, 6)
// true parameters
true_pars[0,0] = 1000 // height of gaussian
true_pars[1,0] = 60 // center in x
true_pars[2,0] = 50 // width in x
true_pars[3,0] = 40 // center in y
true_pars[4,0] = 80 // width in y
true_pars[5,0] = 0.7 // rotation in radians
Image data := PlotGaussian2D(img, true_pars)
data += (gaussianrandom())*sqrt(abs(data)) //add noise
ShowImage(data)
Image errors := data.ImageClone()
errors = tert(abs(data) > 1, sqrt(abs(data)), 1)
// starting parameters of fit
Image pars := NewImage("Gaussian2D Pars", 2, 6)
pars = 100
pars[0,0] = max(data) // estimate normalization from peak of data
pars[5,0] = 0 // 100 radians doesn't make sense
Image parsToFit := NewImage("tmp", 2, 6)
parsToFit = 1
Number chiSqr = 1e6
Number conv_cond = 0.00001
Result("\n starting pars = {")
Number xSize = pars.ImageGetDimensionSize(0)
Number i = 0
for (i = 0; i < xSize; i++)
{
Result(GetPixel(pars, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
// fit
Number ok = FitGaussian2D(data, errors, pars, parsToFit, chiSqr, conv_cond)
if (chiSqr > 2)
ok = FitGaussian2D(data, errors, pars, parsToFit, chiSqr, conv_cond)
Image parDif = 100.0*(pars - true_pars)/true_pars
Result("\n results pars (% dif from true)= {")
for (i = 0; i < xSize; i++)
{
Result(GetPixel(parDif, i, 0))
if (i < (xSize-1)) Result(", ")
}
Result("}")
Result(", chiSqr ="+ chiSqr)
// show residuals
Image residuals := PlotGaussian2D(img, pars)
residuals = data - residuals
ShowImage(residuals)
example 3, 2D Polynomial fit
// $BACKGROUND$
// The number of parameters are defined by the order,
// nPar = (order+1)*(order+2)/2. For example, a
// 3rd order poly will have (3+1)*(3+2)/2 = 10 parameters:
//
// x^0 x^1 x^2 x^3
// --------------------------------
// y^0 | p0 p1 p2 p3
// y^1 | p4 p5 p6 -- (the -- terms are higher)
// y^2 | p7 p8 -- -- (order so are ignored )
// y^3 | p9 -- -- --
//
//
// i.e. f(x,y|p) = p0 + p1*x + p2*x^2 + p3*x^3 + p4*y + p5*x*y
// + p6*x^2*y + p7*y^2 + p8*x*y^2 + p9*y^3
Number xImgSize = 512
Number yImgSize = 512 // create data image
Image img := NewImage("Poly2D", 2, xImgSize, yImgSize)
Image pars_true := NewImage("Poly2D Pars", 2, 3, 3)
// true parameters
pars_true[0,0] = 100
pars_true[1,0] = 60
pars_true[2,0] = -0.05
pars_true[0,1] = 70
pars_true[1,1] = 0.01
pars_true[0,2] = -0.1
Image data := PlotPolynomial2D(img, pars_true)
data += (gaussianrandom())*sqrt(abs(data)) //add noise
ShowImage(data)
Image errors := data.ImageClone()
errors = tert(abs(data) > 1, sqrt(abs(data)), 1)
// starting parameters of fit
Image pars := NewImage("Poly2D Pars", 2, 3, 3)
pars = 10
Image parsToFit := NewImage("tmp", 2, 3, 3)
parsToFit = 1
Number chiSqr = 1e6
Number conv_cond = 0.00001
Result("\n starting pars = {")
Number xSize = pars.ImageGetDimensionSize(0)
Number ySize = pars.ImageGetDimensionSize(1)
Number i, j
for (j = 0; j < ySize; j++)
{
if (j > 0) Result(", ")
Result("{")
for (i = 0; i < xSize; i++)
{
if (i > 0) Result(", ")
if ((i+j) > 2)
Result("-")
else
Result(GetPixel(pars, i, j))
}
Result("}")
}
Result("}")
// fit
Number startTicks = GetHighResTickCount()
Number ok = FitPolynomial2D(data, errors, pars, parsToFit, chiSqr, conv_cond)
Number endTicks = GetHighResTickCount()
Number secs = CalcHighResSecondsBetween(startTicks, endTicks)
Image parDif = 100*(pars - pars_true)/pars_true
Result("\n results pars (% diff from true) = {")
for (j = 0; j < ySize; j++)
{
if (j > 0) Result(", ")
Result("{")
for (i = 0; i < xSize; i++)
{
if (i > 0) Result(", ")
if ((i+j) > 2)
Result("-")
else
Result(GetPixel(parDif, i, j))
}
Result("}")
}
Result("}")
Result(", chiSqr = "+ chiSqr)
Result(", Fit Time (s) = " + secs)
// show residuals
Image residuals := PlotPolynomial2D(img, pars)
residuals = data - residuals
ShowImage(residuals)
The calculations are done in the following code:
var MIN = -10.0,
MAX = 10.0,
RANGE = MAX - MIN;
getColor(max, min, val) {
var MIN_L = 40,
MAX_L = 100;
var color = new Color();
var h = 0 / 240;
var s = 80 / 240;
var l = (((MAX_L - MIN_L) / (max - min)) * val) / 240;
color.setHSL(h, s, l);
return color;
}
initGraph() {
var x = MIN,
y = MIN,
z = 0.0;
initData() {
var data = [];
for (var i = MIN; i < MAX; i++) {
var row = [];
for (var j = MIN; j < MAX; j++) {
double z = 2*( x * x + y * y);
print('$z');
row.add({
x: x,
y: y,
z: z
});
y++;
}
data.add(row);
x++;
}
return data;
}
var data = initData();
var geometry = new Geometry();
var colors = [];
var RANGE = data.length,
height = data[0].length;
data.forEach((col) {
col.forEach((val) {
geometry.vertices.add(new Vector3(x.toDouble(), y.toDouble(), z.toDouble()));
colors.add(getColor(2.5, 0, z.toDouble()));
});
});
offset(x, y) {
return x * RANGE + y;
}
for (var x = 0; x < RANGE - 1; x++) {
for (var y = 0; y < height - 1; y++) {
Vector3 vec0;
Vector3 vec1;
Vector3 n_vec;
// one of two triangle polygons in one rectangle
vec0 = (geometry.vertices[offset(x, y)] - geometry.vertices[offset(x + 1, y)]);
vec1 = (geometry.vertices[offset(x, y)] - geometry.vertices[offset(x, y + 1)]);
n_vec.crossInto(vec0, vec1).normalize();
geometry.faces.add(new Face3(offset(x, y), offset(x + 1, y), offset(x, y + 1), n_vec, [colors[offset(x, y)], colors[offset(x + 1, y)], colors[offset(x, y + 1)]]));
geometry.faces.add(new Face3(offset(x, y), offset(x, y + 1), offset(x + 1, y), n_vec.negate(), [colors[offset(x, y)], colors[offset(x, y + 1)], colors[offset(x + 1, y)]]));
// the other one
vec0 = (geometry.vertices[offset(x + 1, y)] - geometry.vertices[offset(x + 1, y + 1)]);
vec1 = (geometry.vertices[offset(x, y + 1)] - geometry.vertices[offset(x + 1, y + 1)]);
n_vec.crossInto(vec0, vec1).normalize();
geometry.faces.add(new Face3(offset(x + 1, y), offset(x + 1, y + 1), offset(x, y + 1), n_vec, [colors[offset(x + 1, y)], colors[offset(x + 1, y + 1)], colors[offset(x, y + 1)]]));
geometry.faces.add(new Face3(offset(x + 1, y), offset(x, y + 1), offset(x + 1, y + 1), n_vec.negate(), [colors[offset(x + 1, y)], colors[offset(x, y + 1)], colors[offset(x + 1, y + 1)]]));
}
}
var material = new MeshLambertMaterial(vertexColors: VertexColors);
var mesh = new Mesh(geometry, material);
scene.add(mesh);
}
The error seems to be on the occurrence of this line:
n_vec.crossInto(vec0, vec1).normalize();
What is the null object here and how do I solve this? Could the variable 'z' be causing the issue? It first showed null, and caused a similar error (that '*' cannot be applied) and I declared it as double and that got solved. I also have a suspicion in the below lines:
data.forEach((col) {
col.forEach((val) {
geometry.vertices.add(new Vector3(x.toDouble(), y.toDouble(), z.toDouble()));
n_vec is never initialized with an instance of Vector3. crossInto requires to be called on an instance, Either you create an instance first:
Vector3 n_vec = new Vector3.zero();
...
n_vec.crossInto(vec0, vec1).normalize();
Or you use the cross method, but it creates a new instance of Vector3 (you might want to avoid new instances, than I would move the variables out of the loop):
n_vec = vec0.cross(vec1).normalize();