Unable to register new color javascript tracking.js - tracking.js

I'm trying to add an identification to a green color.
All default colors work fine but I have not been able to add a new color for color with camera.
for more info : ColorTracker
window.onload = function() {
var video = document.getElementById('video');
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var tracker = new tracking.ColorTracker();
tracking.ColorTracker.registerColor('green', function (r, g, b) {
if (r < 50 && g > 200 && b < 50) {
return true;
}
return false;
});
tracking.track('#video', tracker, { camera: true });
tracker.on('track', function(event) {
context.clearRect(0, 0, canvas.width, canvas.height);
event.data.forEach(function(rect) {
if (rect.color === 'custom') {
rect.color = tracker.customColor;
}
context.strokeStyle = rect.color;
context.strokeRect(rect.x, rect.y, rect.width, rect.height);
context.font = '11px Helvetica';
context.fillStyle = "#fff";
context.fillText('x: ' + rect.x + 'px', rect.x + rect.width + 5, rect.y + 11);
context.fillText('y: ' + rect.y + 'px', rect.x + rect.width + 5, rect.y + 22);
});
});
initGUIControllers(tracker);
};

You are doing it right, just the color tracking is very picky. It will choose whatever is closest to the G in the RGB color. It does pick up some black and other colors because of that. I would recommend getting the RGB color of the exact green you want to track and go off of that. I included the few I have working in my project( this does not mean these will work for your green)
tracking.ColorTracker.registerColor('darkgreen', function (r, g, b) {
if (r < 120 && r > 80 && g < 150 && b < 70) {
return true;
}
return false;
});
tracking.ColorTracker.registerColor('lightgreen', function (r, g, b) {
if (r < 30 && g < 100 && b < 30) {
return true;
}
return false;
});
tracking.ColorTracker.registerColor('green', function (r, g, b) {
if (r < 30 && g < 50 && b < 30) {
return true;
}
return false;
});

Related

Extending circleMarker "icon" formatting

I found some genius code. But I do not understand how they designed the marker pin
Is the MarkerPin made from svg in some way or did they just come up with the design out of maths?
I have this svg icon I would like to use. So how would i go about translating that to the code below? I may be stupid and they are completely unrelated to svg.
L.MarkerPin = L.CircleMarker.extend({
_updatePath: function () {
this._renderer._updateMarkerPin(this);
},
_containsPoint: function (p) {
var r = this._radius;
var insideCircle =
p.add([0, r * 2]).distanceTo(this._point) <= r + this._clickTolerance();
var a = this._point,
b = a.subtract([0.58 * r, r]),
c = a.subtract([-0.58 * r, r]);
var insideTriangle = true;
var ap_x = p.x - a.x;
var ap_y = p.y - a.y;
var p_ab = (b.x - a.x) * ap_y - (b.y - a.y) * ap_x > 0;
var p_ac = (c.x - a.x) * ap_y - (c.y - a.y) * ap_x > 0;
var p_bc = (c.x - b.x) * (p.y - b.y) - (c.y - b.y) * (p.x - b.x) > 0;
if (p_ac === p_ab) insideTriangle = false;
if (p_bc !== p_ab) insideTriangle = false;
return insideTriangle || insideCircle;
}
});
Jsfiddle with complete example:
https://jsfiddle.net/n2jf5c4q/
The code you posted is only to check if a point (f.e. a click position) is in the area of the Pin. I think this is made because the creator don't wanted that the click event is fired when it is clicked on the area of a circle.
What you mean is this code:
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.x - 0.58 * r, p.y - r);
ctx.arc(p.x, p.y - 2 * r, r, -Math.PI * 1.161, Math.PI * 0.161);
ctx.closePath();
This is normal canvas drawing and has nothing to do with svg
Update
I tried a little bit around and i found a way, but this is not supported for all browsers.
You can use Path2D render the svg as canvas path (maybe you have to resize the svg)
window.p = new Path2D("M0-0.1h20.8v20.9H0V-0.1z M0.5,0.4v19.9h19.8V0.4H0.5z M19,14h-3.2l1.6,3.4h-1.7L14,14H6.7l-1.6,3.4H3.3L4.9,14H1.8v-1.6 h3.9l0.8-1.6H4.1V9.1h12.5v1.6h-2.4l0.8,1.6h4V14z M7.4,12.4h5.8l-0.7-1.6H8.2L8.1,11l-0.3,0.6L7.4,12.4z");
L.Canvas.include({
_updateCircle: function (layer) {
if (!this._drawing || layer._empty()) { return; }
var p = layer._point,
ctx = this._ctx,
r = Math.max(Math.round(layer._radius), 1),
s = (Math.max(Math.round(layer._radiusY), 1) || r) / r;
if (s !== 1) {
ctx.save();
ctx.scale(1, s);
}
ctx.beginPath();
ctx.arc(p.x, p.y / s, r, 0, Math.PI * 2, false);
if (s !== 1) {
ctx.restore();
}
var x = p.x-10; // -10 because of ~20 is the img size
var y = p.y-10;
ctx.translate(x,y);
ctx.fill(window.p);
//this._fillStroke(ctx, layer);
},
});
var canvasRenderer = L.canvas();
new L.CircleMarker([51.505, -0.09], {
renderer: canvasRenderer
}).bindPopup('hello').addTo(mymap);

Get two offset points between two points

Hi I need help finding coordinate or points offset from two endpoints of a line. In my program, I would like to specify the two points and the offset. Then I need to calculate the two offset coordinates.
I worked something out using trigonometry but it only works in some cases and when the line is in the positive quadrant.
Here is an image describing what I need to find:
Points on line
Ok so I need to find X3,Y3 and X4,Y4 coordinates.
My method I followed:
Calculate angle:
Ang = atan((Y2 - Y1)/(X2 - X1))
To find X3:
X3 = X1 + Offset * Cos(Ang)
The same concept for Y3
The issue is that if the line is in a different quadrant the point info is not correct... Any help, please.
This question is a clear case for using 2d vector math. The idea is that we subtract p1 from p2 to give us a vector that describes the length and direction of the line. We then normalize this vector, such that it has a length of 1. If you then multiply this normalized vector with the number of units you'd like to move away from the end and add the result to the end-point, you'll have a new point.
Consider an example walking along the x axis:
p1 = 0,0
p2 = 10,0
dif = p2 - p1 = (10,0)
length is 10, so it's 10 times too long - we divide it by 10 to get a vector 1 unit long.
If we then move 5 times (1,0), we end up at 5,0 - 5 units away, bewdy!
Here's a function that achieves the same thing:
function calcOffsetPoint(x1,y1, x2,y2, distTowardsP2fromP1)
{
var p1 = new vec2d(x1,y1);
var p2 = new vec2d(x2,y2);
var delta = p2.sub(p1);
var dirVec = delta.clone();
dirVec.normalize();
dirVec.timesEquals(distTowardsP2fromP1);
var resultPoint = p1.add(dirVec);
return resultPoint;
}
As you can see, this makes use of something I've called vec2d. There's a copy of it in the following snippet:
"use strict";
function byId(id){return document.getElemetById(id)}
function newEl(tag){return document.createElement(tag)}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
var end1 = new vec2d(0,0);
var end2 = new vec2d(10,0);
var midPoint = calcOffsetPoint(end1.x,end1.y, end2.x,end2.y, 5);
console.log( midPoint.toStringN(2) );
}
class vec2d
{
constructor(x=0, y=0)
{
this.mX = x;
this.mY = y;
}
get x(){return this.mX;}
set x(newX){this.mX = newX;}
get y(){return this.mY;}
set y(newY){this.mY = newY;}
add(other)
{
return new vec2d(this.x+other.x, this.y+other.y);
}
sub(other)
{
return new vec2d(this.x-other.x, this.y-other.y);
}
timesEquals(scalar)
{
this.x *= scalar;
this.y *= scalar;
return this;
}
divByEquals(scalar)
{
this.x /= scalar;
this.y /= scalar;
return this;
}
dotProd(other)
{
return this.x*other.x + this.y*other.y;
}
length()
{
return Math.hypot(this.x, this.y);
}
normalize()
{
this.divByEquals( this.length() );
return this;
}
perpendicular()
{
var tmp = this.x;
this.x = -this.y;
this.y = tmp;
return this;
}
clone()
{
return vec2d.clone(this);
}
static clone(other)
{
return new vec2d(other.x, other.y);
}
toString(){return `vec2d {x: ${this.x}, y: ${this.y}}`}
toStringN(n){return `vec2d {x: ${this.x.toFixed(n)}, y: ${this.y.toFixed(n)}}`}
}
function calcOffsetPoint(x1,y1, x2,y2, distTowardsP2fromP1)
{
var p1 = new vec2d(x1,y1);
var p2 = new vec2d(x2,y2);
var delta = p2.sub(p1);
var dirVec = delta.clone();
dirVec.normalize();
dirVec.timesEquals(distTowardsP2fromP1);
var resultPoint = p1.add(dirVec);
return resultPoint;
}
I had some spare time over the weekend, so put together a working demo of the image you posted. Have a play around. Make sure you run it in full-screen, so you can see the sliders that set the offsets for p3 and p4. Disregard the coordinate-system transformation stuff, that's just there to allow me to make an image the same dimensions as your image yet conveniently display it in a window with about 5% the area. The questions come from the exercise section of some old text-book I was reading over the weekend.
"use strict";
class vec2d
{
constructor(x=0,y=0)
{
this.x = x;
this.y = y;
}
abs()
{
this.x = Math.abs(this.x);
this.y = Math.abs(this.y);
return this;
}
add(vec1)
{
return new vec2d(this.x+vec1.x, this.y+vec1.y);
}
sub(vec1)
{
return new vec2d(this.x-vec1.x, this.y-vec1.y);
}
mul(scalar)
{
return new vec2d(this.x*scalar, this.y*scalar);
}
plusEquals(vec1)
{
this.x += vec1.x;
this.y += vec1.y;
return this;
}
minusEquals(vec1)
{
this.x -= vec1.x;
this.y -= vec1.y;
return this;
}
timesEquals(scalar)
{
this.x *= scalar;
this.y *= scalar;
return this;
}
divByEquals(scalar)
{
this.x /= scalar;
this.y /= scalar;
return this;
}
normalize()
{
var len = this.length;
this.x /= len;
this.y /= len;
return this;
}
get length()
{
//return Math.sqrt( (this.x*this.x)+(this.y*this.y) );
return Math.hypot( this.x, this.y );
}
set length(newLen)
{
var invLen = newLen / this.length;
this.timesEquals(invLen);
}
dotProd(vec1)
{
return this.x*vec1.x + this.y*vec1.y;
}
perp()
{
var tmp = this.x;
this.x = -this.y;
this.y = tmp;
return this;
}
wedge(other)
{ // computes an area for parallelograms
return this.x*other.y - this.y*other.x;
}
static clone(other)
{
var result = new vec2d(other.x, other.y);
return result;
}
clone() // clone self
{
return vec2d.clone(this);
}
setTo(other)
{
this.x = other.x;
this.y = other.y;
}
get(){ return {x:this.x, y:this.y}; }
toString(){ return `vec2d {x: ${this.x}, y: ${this.y}}` }
toStringN(n){ return `vec2d {x: ${this.x.toFixed(n)}, y: ${this.y.toFixed(n)}}` }
print(){console.log(this.toString())}
};
class mat3
{
static clone(other)
{
var result = new mat3();
other.elems.forEach(
function(el, index, collection)
{
result.elems[index] = el;
}
);
return result;
}
clone()
{
return mat3.clone(this);
}
constructor(a,b,c,d,e,f)
{
if (arguments.length < 6)
this.setIdentity();
else
this.elems = [a,b,0,c,d,0,e,f,1];
}
setIdentity()
{
this.elems = [1,0,0, 0,1,0, 0,0,1];
}
multiply(other, shouldPrepend)
{
var a, b, c = new mat3();
if (shouldPrepend === true)
{
a = other;
b = this;
}
else
{
a = this;
b = other;
}
c.elems[0] = a.elems[0]*b.elems[0] + a.elems[1]*b.elems[3] + a.elems[2]*b.elems[6];
c.elems[1] = a.elems[0]*b.elems[1] + a.elems[1]*b.elems[4] + a.elems[2]*b.elems[7];
c.elems[2] = a.elems[0]*b.elems[2] + a.elems[1]*b.elems[5] + a.elems[2]*b.elems[8];
// row 1
c.elems[3] = a.elems[3]*b.elems[0] + a.elems[4]*b.elems[3] + a.elems[5]*b.elems[6];
c.elems[4] = a.elems[3]*b.elems[1] + a.elems[4]*b.elems[4] + a.elems[5]*b.elems[7];
c.elems[5] = a.elems[3]*b.elems[2] + a.elems[4]*b.elems[5] + a.elems[5]*b.elems[8];
// row 2
c.elems[6] = a.elems[6]*b.elems[0] + a.elems[7]*b.elems[3] + a.elems[8]*b.elems[6];
c.elems[7] = a.elems[6]*b.elems[1] + a.elems[7]*b.elems[4] + a.elems[8]*b.elems[7];
c.elems[8] = a.elems[6]*b.elems[2] + a.elems[7]*b.elems[5] + a.elems[8]*b.elems[8];
for (var i=0; i<9; i++)
this.elems[i] = c.elems[i];
}
transformVec2s(pointList)
{
var i, n = pointList.length;
for (i=0; i<n; i++)
{
var x = pointList[i].x*this.elems[0] + pointList[i].y*this.elems[3] + this.elems[6];
var y = pointList[i].x*this.elems[1] + pointList[i].y*this.elems[4] + this.elems[7];
pointList[i].x = x;
pointList[i].y = y;
}
}
makeTransformedPoints(pointList)
{
var result = [];
for (var i=0,n=pointList.length;i<n;i++)
{
var x = pointList[i].x*this.elems[0] + pointList[i].y*this.elems[3] + this.elems[6];
var y = pointList[i].x*this.elems[1] + pointList[i].y*this.elems[4] + this.elems[7];
result.push( new vec2d(x,y) );
}
return result;
}
rotate(degrees, shouldPrepend)
{
var tmp = new mat3();
tmp.elems[0] = Math.cos( degrees/180.0 * Math.PI );
tmp.elems[1] = -Math.sin( degrees/180.0 * Math.PI );
tmp.elems[3] = -tmp.elems[1];
tmp.elems[4] = tmp.elems[0];
this.multiply(tmp, shouldPrepend);
}
scaleEach(scaleX, scaleY, shouldPrepend)
{
var tmp = new mat3();
tmp.elems[0] = scaleX;
tmp.elems[4] = scaleY;
this.multiply(tmp, shouldPrepend);
}
scaleBoth(scaleAmount, shouldPrepend)
{
var tmp = new mat3();
tmp.elems[0] = scaleAmount;
tmp.elems[4] = scaleAmount;
this.multiply(tmp, shouldPrepend);
}
translate(transX, transY, shouldPrepend)
{
var tmp = new mat3();
tmp.elems[6] = transX;
tmp.elems[7] = transY;
this.multiply(tmp, shouldPrepend);
}
determinant()
{
var result, a, b;
a = ( (this.elems[0]*this.elems[4]*this.elems[8])
+ (this.elems[1]*this.elems[5]*this.elems[6])
+ (this.elems[2]*this.elems[3]*this.elems[7]) );
b = ( (this.elems[2]*this.elems[4]+this.elems[6])
+ (this.elems[1]*this.elems[3]+this.elems[8])
+ (this.elems[0]*this.elems[5]+this.elems[7]) );
result = a - b;
return result;
}
isInvertible()
{
return (this.determinant() != 0);
}
invert()
{
var det = this.determinant();
if (det == 0)
return;
var a,b,c,d,e,f,g,h,i;
a = this.elems[0]; b = this.elems[1]; c = this.elems[2];
d = this.elems[3]; e = this.elems[4]; f = this.elems[5];
g = this.elems[6]; h = this.elems[7]; i = this.elems[8];
this.elems[0] = (e*i - f*h); this.elems[1] = -((b*i) - (c*h)); this.elems[2] = (b*f)-(c*e);
this.elems[3] = -(d*i - f*g); this.elems[4] = (a*i) - (c*g); this.elems[5] = -( (a*f) - (c*d) );
this.elems[6] = (d*h - e*g); this.elems[7] = -((a*h) - (b*g)); this.elems[8] = (a*e)-(b*d);
var detInv = 1.0 / det;
for (var i=0; i<9; i++)
this.elems[i] *= detInv;
return this;
}
reset()
{
this.setIdentity();
}
print()
{
var str = '';
for (var i=0; i<9; i++)
{
if (i && i%3==0)
str += "\n";
str += " " + this.elems[i].toFixed(5);
}
console.log(str);
}
}
function byId(id){return document.getElementById(id)}
function newEl(tag){return document.createElement(tag)}
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
byId('output').addEventListener('mousemove', onMouseMove, false);
byId('slider1').addEventListener('input', onSliderInput, false);
byId('slider2').addEventListener('input', onSliderInput, false);
draw();
}
//(400-48)/400 = 0.88
var invMat, svgInvMat;
function onMouseMove(evt)
{
var mousePos = new vec2d(evt.offsetX,evt.offsetY);
var worldPos = mousePos.clone();
invMat.transformVec2s( [worldPos] );
byId('screenMouse').textContent = `screen: ${mousePos.x},${mousePos.y}`;
byId('worldMouse').textContent = `world: ${worldPos.x.toFixed(1)}, ${worldPos.y.toFixed(1)}`;
}
function onSliderInput(evt)
{
draw();
}
function updateSliderLabels()
{
byId('ofset1Output').textContent = byId('slider1').value;
byId('ofset2Output').textContent = byId('slider2').value;
}
function draw()
{
var can = byId('output');
var ctx = can.getContext('2d');
ctx.clearRect(0,0,can.width,can.height);
var orientMat = evaluateViewOrientationMatrix(0.06*can.width,can.height-24, 0,-1);
var scaleMat = computeWindowToViewPortMatrix(2052,1317, can.width,can.height);
var viewMat = scaleMat.clone();
viewMat.multiply(orientMat);
console.log('viewMat');
viewMat.print();
invMat = viewMat.clone().invert();
for (var i=0; i<9; i++)
invMat.elems[i] /= invMat.elems[8];
ctx.strokeStyle = '#fff';
var axisPts = [ new vec2d(0,1070), new vec2d(0,0), new vec2d(0.88*2052,0) ]; // xAxis line 88% of image width
var axis = viewMat.makeTransformedPoints(axisPts);
drawLine(axis[0].x,axis[0].y, axis[1].x,axis[1].y, ctx);
drawLine(axis[1].x,axis[1].y, axis[2].x,axis[2].y, ctx);
var lineEnds = [new vec2d(330,263), new vec2d(1455,809)];
var pts2 = viewMat.makeTransformedPoints(lineEnds);
drawCircle(pts2[0].x,pts2[0].y, 4, ctx);
drawCircle(pts2[1].x,pts2[1].y, 4, ctx);
drawLine(pts2[0].x,pts2[0].y, pts2[1].x,pts2[1].y, ctx);
var rawP3 = calcOffsetCoords(lineEnds[0].x,lineEnds[0].y, lineEnds[1].x,lineEnds[1].y, byId('slider1').value);
var rawP4 = calcOffsetCoords(lineEnds[1].x,lineEnds[1].y, lineEnds[0].x,lineEnds[0].y, byId('slider2').value);
var ofsPts = viewMat.makeTransformedPoints( [rawP3, rawP4] );
drawCircle(ofsPts[0].x,ofsPts[0].y, 4, ctx);
drawCircle(ofsPts[1].x,ofsPts[1].y, 4, ctx);
updateSliderLabels();
}
function calcOffsetCoords(x1,y1, x2,y2, offset)
{
var dx = x2 - x1;
var dy = y2 - y1;
var lineLen = Math.hypot(dx, dy);
var normDx=0, normDy=0;
if (lineLen != 0)
{
normDx = dx / lineLen;
normDy = dy / lineLen;
}
var resultX = x1 + (offset * normDx);
var resultY = y1 + (offset * normDy);
return {x:resultX,y:resultY};//new vec2d(resultX,resultY); //{x:resultX,y:resultY};
}
// Exercise 6-1:
// Write a procedure to implement the evaluateViewOrientationMatrix function that calculates the elements of the
// matrix for transforming world coordinates to viewing coordinates, given the viewing coordinate origin Porigin and
// the viewUp vector
function evalViewOrientMatrix(screenOriginX,screenOriginY, worldUpVectorX,worldUpVectorY)
{
var worldUp = {x: worldUpVectorX, y: worldUpVectorY};
var len = Math.hypot(worldUp.x, worldUp.y);
if (len != 0)
len = 1.0 / len;
worldUp.x *= len;
worldUp.y *= len;
var worldRight = {x: worldUp.y, y: -worldUp.x};
var rotMat = svg.createSVGMatrix();
rotMat.a = worldRight.x;
rotMat.b = worldRight.y;
rotMat.c = worldUp.x;
rotMat.d = worldUp.y;
var transMat = svg.createSVGMatrix();
transMat = transMat.translate(screenOriginX, screenOriginY);
var result = rotMat.multiply(transMat);
return result;
}
function evaluateViewOrientationMatrix(screenOriginX,screenOriginY, worldUpVectorX,worldUpVectorY)
{
var worldUp = new vec2d(worldUpVectorX, worldUpVectorY);
worldUp.normalize();
var worldRight = worldUp.clone().perp();
var rotMat = new mat3();
rotMat.elems[0] = worldRight.x; rotMat.elems[1] = worldRight.y;
rotMat.elems[3] = worldUp.x; rotMat.elems[4] = worldUp.y;
var transMat = new mat3();
transMat.translate(screenOriginX,screenOriginY);
var result = rotMat.clone();
result.multiply(transMat);
return result;
}
/*
0 1 2
3 4 5
6 7 8
translation
-----------
1 0 0
0 1 0
tX tY 1
scaling
---------
sX 0 0
0 sY 0
0 0 1
rotation
--------
cosX -sinX 0
sinX cosX 0
0 0 1
*/
// Exercise 6-2:
// Derive the window to viewport transformation equations 6-3 by first scaling the window to
// the size of the viewport and then translating the scaled window to the viewport position
function computeWindowToViewPortMatrix(windowWidth,windowHeight,viewPortWidth,viewPortHeight)
{
var result = new mat3();
result.scaleEach(viewPortWidth/windowWidth,viewPortHeight/windowHeight);
return result;
}
// returns an SVGMatrix
function compWnd2ViewMat(windowWidth,windowHeight,viewPortWidth,viewPortHeight)
{
var result = svg.createSVGMatrix();
return result.scaleNonUniform(viewPortWidth/windowWidth,viewPortHeight/windowHeight);
}
function drawLine(x1,y1,x2,y2,ctx)
{
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke();
}
function drawCircle(x,y,radius,ctx)
{
ctx.beginPath();
ctx.arc(x, y, radius, 0, (Math.PI/180)*360, false);
ctx.stroke();
ctx.closePath();
}
canvas
{
background-color: black;
}
.container
{
display: inline-block;
background-color: #888;
border: solid 4px #555;
}
#screenMouse, #worldMouse, .control
{
display: inline-block;
width: calc(513px/2 - 2*8px);
margin-left: 8px;
}
<body>
<div class='container'>
<canvas id='output' width='513' height='329'></canvas><br>
<div id='screenMouse'></div><div id='worldMouse'></div>
<div>
<div class='control'>P2 ofs: <input id='slider1' type='range' min='0' max='500' value='301'><span id='ofset1Output'></span></div>
<div class='control'>P3 ofs: <input id='slider2' type='range' min='0' max='500' value='285'><span id='ofset2Output'></span></div>
</div>
</div>
</body>

Dygraphs - How do I restrict drawing to canvas

I have a graph where I am using underlays to draw vertical lines between the points. I have a line of code that restricts these vertical lines to NOT draw outside the active canvas. But when I use this underlayCallback, the 'points' are still drawn outside the canvas. If I remove my underlayCallback, the points are restricted to the canvas as one would expect. Here is what they look like and my code. (Sorry, the site is too secure to provide working sample.)
g[i] = new Dygraph(thisdiv, mylines, {
labels: graphlbls[i],
ylabel: graphunits[i].capitalizeFirstLetter(),
xlabel: '',
xLabelHeight:15,
yLabelWidth:15,
rightGap: 5,
labelsDivStyles: {
'text-align': 'right',
'background': 'none'
},
colors: ['#D48513','#1D6EB5'],
title: graphtitles[i],
titleHeight:23,
drawPoints: true,
showRoller: false,
drawXGrid: false,
drawYGrid: true,
strokeWidth: 0,
pointSize: 4,
highlightCircleSize: 6,
gridLineColor: "#ddd",
axisLabelFontSize: 12,
xAxisHeight: 20,
valueRange: [minval, maxval],
rangeSelectorHeight: 30,
showRangeSelector: true,
rangeSelectorPlotFillColor: '#ffffff',
rangeSelectorPlotStrokeColor: '#ffffff',
interactionModel: Dygraph.defaultInteractionModel,
axes: {
x: {
valueFormatter: function (ms) {
var d = new Date(ms);
var day = "0"+d.getDate();
var month = "0"+(d.getMonth()+1);
var year = d.getFullYear();
var hour = "0"+ d.getHours();
var min = "0"+d.getMinutes();
var p = "AM";
if (hour > 12) { p = "PM"; hour = hour - 12; }
if (df == 0) var dd = month.slice(-2)+"/"+day.slice(-2)+"/"+year;
if (df == 1) var dd = day.slice(-2)+"/"+month.slice(-2)+"/"+year;
if (tf == 0) var tt = hour.slice(-2)+":"+min.slice(-2)+" "+p+" ";
if (tf == 1) var tt = hour.slice(-2)+":"+min.slice(-2)+" ";
return dd + " - " + tt;
}
}
},
underlayCallback: function(ctx, area, g) {
//if (typeof(g[i]) == 'undefined') return; // won't be set on the initial draw.
var range = g.xAxisRange();
var rows = g.numRows();
// get max and min y
for (var i = 0; i < rows; i++) {
miny = 99999;
maxy = -99999;
xx = g.getValue(i,0);
if (xx < range[0] || xx > range[1]) continue; // constrain to graph canvas
for (var j=1; j<= range.length; j++) {
if (g.getValue(i,j) <= miny) miny = g.getValue(i,j);
if (g.getValue(i,j) >= maxy) maxy = g.getValue(i,j);
}
p1 = g.toDomCoords(xx, miny);
p2 = g.toDomCoords(xx, maxy);
ctx.strokeStyle = "rgba(192,192,224,1)";
ctx.lineWidth = 1.0;
ctx.beginPath();
ctx.moveTo(p1[0], p1[1]);
ctx.lineTo(p2[0], p2[1]);
ctx.closePath();
ctx.stroke();
ctx.restore();
}
}
});
You're calling ctx.restore() many times without corresponding calls to ctx.save(). This pops off dygraphs' own drawing context, including the clipping rectangle. Make one call to save at the top of your underlayCallback and one to restore at the end.
Stepping back a bit, what you're doing might be easier with a custom plotter, rather than an underlayCallback.

Visualization of recursive Fibonacci calculation

Is there any program/website which can visualize a graph of a recursive Fibonacci calculation.
I want to show how many recursion steps will be needed.
I found this to be a neat little challenge, let me share my implementation:
var canvas = document.getElementById("canvas");
var width = canvas.width;
var height = canvas.height;
var ctx = canvas.getContext("2d");
var FN = 8;
var FONT_SIZE = 11; // in points
var SHOW_BOXES = false;
var SHOW_DISCS = true;
var SHOW_FIB_N = true;
function Tree(fn) {
var pdata = {};
pdata.lhs = null;
pdata.rhs = null;
pdata.fn = fn;
this.getLeft = function() { return pdata.lhs; };
this.setLeft = function(node) { pdata.lhs = node; };
this.getRight = function() { return pdata.rhs; };
this.setRight = function(node) { pdata.rhs = node; };
this.getFn = function() { return pdata.fn; };
}
function fib(n) {
if(n == 0)
return new Tree(0);
if(n == 1)
return new Tree(1);
else {
var lhs = fib(n-1);
var rhs = fib(n-2);
var root = new Tree(lhs.getFn() + rhs.getFn());
root.setLeft(lhs);
root.setRight(rhs);
return root;
}
}
var root = fib(FN);
function Box(x0, y0, x1, y1) {
if(arguments.length < 4) {
x0 = 1;
y0 = 1;
x1 = -1;
y1 = -1;
}
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
this.width = function() { return this.x1 - this.x0; };
this.height = function() { return this.y1 - this.y0; };
this.offset = function(x, y) {
this.x0 += x;
this.y0 += y;
this.x1 += x;
this.y1 += y;
};
this.extend = function(x, y) {
if(this.x1 < this.x0 || this.y1 < this.y0) {
this.x0 = x;
this.x1 = x;
this.y0 = y;
this.y1 = y;
} else {
this.x0 = this.x0 < x ? this.x0 : x;
this.y0 = this.y0 < y ? this.y0 : y;
this.x1 = this.x1 > x ? this.x1 : x;
this.y1 = this.y1 > y ? this.y1 : y;
}
}
};
(function () {
// assume spheres of radius 0.5
function setBounds(node, offX, offY) {
var bbox = new Box(offX, offY, offX + 1, offY + 1);
if(node.getLeft() != null || node.getRight() != null) {
var lhs = node.getLeft(), rhs = node.getRight();
if(lhs != null) {
setBounds(lhs, offX + 0, offY + 1.1);
bbox.extend(lhs.bbox.x0, lhs.bbox.y0);
bbox.extend(lhs.bbox.x1, lhs.bbox.y1);
}
if(rhs != null) {
setBounds(rhs, offX + (lhs != null ? lhs.bbox.width() : 0), offY + 1.1);
bbox.extend(rhs.bbox.x0, rhs.bbox.y0);
bbox.extend(rhs.bbox.x1, rhs.bbox.y1);
}
}
node.bbox = bbox;
}
setBounds(root, 0, 0);
})();
var transf = (function() {
var b = 2;
var sx = (width - 2 * b) / root.bbox.width();
var sy = (height - 2 * b) / root.bbox.height();
return {
ox: b / sx - root.bbox.x0,
oy: b / sx - root.bbox.y0,
sx: sx,
sy: sy,
};
})();
transf.smin = Math.min(transf.sx, transf.sy);
ctx.clearRect(0, 0, width, height);
(function(g) {
g.font = FONT_SIZE + "pt Arial";
g.textAlign = "center";
g.strokeStyle = "#000000";
function draw(node, pX, pY) {
if(node == null) return;
var cX = (node.bbox.x0 + node.bbox.x1) / 2;
var cY = (node.bbox.y0 + 0.5);
var radius = 0.475;
cX = transf.sx * (cX + transf.ox);
cY = transf.sy * (cY + transf.oy);
radius *= transf.smin;
draw(node.getLeft(), cX, cY);
draw(node.getRight(), cX, cY);
if(SHOW_BOXES) {
g.fillStyle = "#ff0000";
g.beginPath();
g.moveTo(transf.sx * (node.bbox.x0 + transf.ox), transf.sy * (node.bbox.y0 + transf.oy));
g.lineTo(transf.sx * (node.bbox.x1 + transf.ox), transf.sy * (node.bbox.y0 + transf.oy));
g.lineTo(transf.sx * (node.bbox.x1 + transf.ox), transf.sy * (node.bbox.y1 + transf.oy));
g.lineTo(transf.sx * (node.bbox.x0 + transf.ox), transf.sy * (node.bbox.y1 + transf.oy));
g.closePath();
g.stroke();
}
if(SHOW_DISCS) {
if(arguments.length >= 3) {
g.beginPath();
g.moveTo(pX, pY);
g.lineTo(cX, cY);
g.stroke();
}
g.fillStyle = "#ff0000";
g.beginPath();
g.arc(cX, cY, radius, 0, 2 * Math.PI);
g.fill();
g.stroke();
}
if(SHOW_FIB_N) {
g.fillStyle = "#0000ff";
g.fillText(node.getFn(), cX, cY + FONT_SIZE / 2);
}
}
draw(root);
})(ctx);
<canvas id="canvas" width="800" height="480" />

kineticjs - createImageRegion for pixels with opacity > 0 not only 1

So the createImageRegion method ignores all pixels with a tiny bit of alpha/opacity.
How can you make this function so that also pixels with an opacity of .5 or something will be count for hitdetection?
I looked into KineticJS. A colorKey is added to a hitregion, but it transforms the key to a hex key with no alpha. I can't figure out a way how i could make this work.
Help is much appriciated!
this is where the magic happens. But i don't understand how i can include pixels whith any kind of transpanacy but 0
createImageHitRegion: function (callback) {
var canvas = new Kinetic.Canvas(this.attrs.width, this.attrs.height);
var context = canvas.getContext();
context.drawImage(this.attrs.image, 0, 0);
try {
var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight());
var data = imageData.data;
var rgbColorKey = Kinetic.Type._hexToRgb(this.colorKey);
// replace non transparent pixels with color key
for (var i = 0, n = data.length; i < n; i += 4) {
data[i] = rgbColorKey.r;
data[i + 1] = rgbColorKey.g;
data[i + 2] = rgbColorKey.b;
// i+3 is alpha (the fourth element)
}
var that = this;
Kinetic.Type._getImage(imageData, function (imageObj) {
that.imageHitRegion = imageObj;
if (callback) {
callback();
}
});
}
catch (e) {
Kinetic.Global.warn('Unable to create image hit region. ' + e.message);
}
}
When i include: data[i + 3] = 255 (rgbColorKey.a) does not excist, none of the imageevents are working anymore
I found the answer:
for (var i = 0, n = data.length; i < n; i += 4) {
data[i] = rgbColorKey.r;
data[i + 1] = rgbColorKey.g;
data[i + 2] = rgbColorKey.b;
if (!ignoreAlpha && data[i + 3] > 0) {
data[i + 3] = 255;
}
}
This way, every pixel which has a bit of transparancy left, it will get a full color. So if you prototype this to kinetixJS it should work:
Kinetic.Image.prototype.createImageHitRegion = function (callback, ignoreTransparantPixels) {
var canvas = new Kinetic.Canvas(this.attrs.width, this.attrs.height);
var context = canvas.getContext();
var _ignoreTransparantPixels;
if(typeof ignoreTransparantPixels == 'undefined'){
_ignoreTransparantPixels = false;
}
context.drawImage(this.attrs.image, 0, 0);
try {
var imageData = context.getImageData(0, 0, canvas.getWidth(), canvas.getHeight());
var data = imageData.data;
var rgbColorKey = Kinetic.Type._hexToRgb(this.colorKey);
// replace non transparent pixels with color key
for (var i = 0, n = data.length; i < n; i += 4) {
data[i] = rgbColorKey.r;
data[i + 1] = rgbColorKey.g;
data[i + 2] = rgbColorKey.b;
if (!_ignoreTransparantPixels && data[i + 3] > 0) {
data[i + 3] = 255;
}
//trace(data[i + 3]);
// i+3 is alpha (the fourth element)
}
var that = this;
Kinetic.Type._getImage(imageData, function (imageObj) {
that.imageHitRegion = imageObj;
if (callback) {
callback();
}
});
}
catch (e) {
Kinetic.Global.warn('Unable to create image hit region. ' + e.message);
}
}