Random pattern while animating simulated gradient in canvas element - javascript

This is a query about something that popped up while I was experimenting with the canvas element via javascript. I wanted to have an array of points that formed a gradient which moved with time, which works perfectly apart from a bizarre pattern that comes up (only after the first wave or more), which also changes according to the number of columns and rows in the canvas (changing the size of the points just makes the patterns bigger or smaller, it's always on the same pixels.
Here's a little demo of what I mean with a bit of interface for you to mess around with, an example of the changing patterns is if the number of rows is changed to 0.75x the number of columns from the original (i.e. 40 columns, 30 rows).
http://codepen.io/zephyr/pen/GpwwWB
Javascript:
String.prototype.hexToRGBA = function(a) {
function cutHex(h) {
return (h.charAt(0) == "#") ? h.substring(1, 7) : h
}
var r = parseInt((cutHex(this)).substring(0, 2), 16);
var g = parseInt((cutHex(this)).substring(2, 4), 16);
var b = parseInt((cutHex(this)).substring(4, 6), 16);
return 'rgba(' + r.toString() + ',' + g.toString() + ',' + b.toString() + ',' + a.toString() + ')';
}
CanvasRenderingContext2D.prototype.clearDrawRect = function(shape) {
this.clearRect(shape.position.x, shape.position.y, shape.size, shape.size);
this.fillStyle = shape.color.base;
this.fillRect(shape.position.x, shape.position.y, shape.size, shape.size);
}
CanvasRenderingContext2D.prototype.render = function(render) {
(function animate() {
requestAnimationFrame(animate);
render();
})();
}
CanvasRenderingContext2D.prototype.renderAndThrottleFpsAt = function(fps, render) {
var fpsInterval, startTime, now, then, elapsed;
fpsInterval = 1000 / fps;
then = Date.now();
startTime = then;
(function animate() {
requestAnimationFrame(animate);
now = Date.now();
elapsed = now - then;
if (elapsed > fpsInterval) {
then = now - (elapsed % fpsInterval);
render();
}
})();
}
CanvasRenderingContext2D.prototype.pool = {};
CanvasRenderingContext2D.prototype.parsePoint = function(x, y, s, c) {
return {
color: c,
position: {
x: x,
y: y
},
size: s
}
}
CanvasRenderingContext2D.prototype.fillPointsPool = function(size, cols, rows, color) {
var i = cols;
var j = rows;
while(i--){
while(j--){
var x = i * size;
var y = j * size;
var a = (i * j) / (cols * rows);
var c = {
hex: color,
alpha: a,
dir: 1
};
if (typeof this.pool.points == 'undefined') {
this.pool.points = [this.parsePoint(x, y, size, c)];
} else {
this.pool.points.push(this.parsePoint(x, y, size, c));
}
}
j = rows;
}
}
CanvasRenderingContext2D.prototype.updatePointsPool = function(size, cols, rows, color) {
this.pool.points = [];
this.clearRect(0,0,this.canvas.width,this.canvas.height);
this.fillPointsPool(size, cols, rows, color);
}
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Populate Points
var size = document.getElementById('size');
var cols = document.getElementById('cols');
var rows = document.getElementById('rows');
var color = document.getElementById('color');
ctx.fillPointsPool(size.value, cols.value, rows.value, color.value);
size.oninput = function(){
ctx.updatePointsPool(this.value, cols.value, rows.value, color.value);
}
cols.oninput = function(){
ctx.updatePointsPool(size.value, this.value, rows.value, color.value);
}
rows.oninput = function(){
ctx.updatePointsPool(size.value, cols.value, this.value, color.value);
}
color.oninput = function(){
ctx.updatePointsPool(size.value, cols.value, rows.value, this.value);
}
ctx.renderAndThrottleFpsAt(60, function(){
var i = 0;
var len = ctx.pool.points.length;
while (i<len) {
var point = ctx.pool.points[i];
// Change alpha for wave
var delta = 0.01;
point.color.alpha = point.color.alpha + (delta * point.color.dir);
if (point.color.alpha > 1) {
point.color.dir = -1;
} else if (point.color.alpha <= 0) {
point.color.dir = 1;
}
// Calculate rgba value with new alpha
point.color.base = point.color.hex.hexToRGBA(point.color.alpha);
ctx.clearDrawRect(point);
i++;
}
});
Do any of you have an idea of what's causing the pattern to appear, and any suggestions on a fix for this?
Note: I will be changing the updatePointsPool function

You are forgetting to clamp your alpha values when you change the direction. The small error in the alpha value accumulates slowly producing the unwanted artifacts you see as the animation progresses.
To fix add the top and bottom limits to alpha in the code just after you add delta direction to alpha.
if (point.color.alpha > 1) {
point.color.alpha = 1; // clamp alpha max
point.color.dir = -1;
} else if (point.color.alpha <= 0) {
point.color.alpha = 0; // clamp alpha min
point.color.dir = 1;
}

Related

mxGraph straight parallel edges?

Is there any solution for this? We need parallel edges, but not that way how ParallelEdgeLayout makes that.
I tried to rewrite the layout function of parallel layout to move the edges, but edges has no usable geometry: x, y, width, height are 0. There I couldn't move them anywhere.
Tried to use setStyle, but did not do anything:
var s = model.getStyle(parallels[i]) + 'entryX='+x0+';exitX='+x0+';'
console.log(s)
model.setStyle(parallels[i], s);
Oh my God! Here is a solution. Not the dream edges, but almost... Maybe there are more beautiful solutions (post one), but this is mine for showing real parallel edges:
var spx = 1 / 22;
var spy = 1 / 8;
var x0 = 0.5;
var y0 = 0.5;
for (var i = 0; i < parallels.length; i++) {
var source = view.getVisibleTerminal(parallels[i], true);
var target = view.getVisibleTerminal(parallels[i], false);
var src = model.getGeometry(source);
var trg = model.getGeometry(target);
var srcx = src.x, srcy = src.y, trgx = trg.x, trgy = trg.y;
if (parallels[i].getParent() != source.getParent()) {
var pGeo = model.getGeometry(source.getParent());
srcx = src.x + pGeo.x;
srcy = src.y + pGeo.y;
}
if (parallels[i].getParent() != target.getParent()) {
var pGeo = model.getGeometry(target.getParent());
trgx = trg.x + pGeo.x;
trgy = trg.y + pGeo.y;
}
var scx = srcx + src.width; // source element right
var scy = srcy + src.height; // source element bottom
var tcx = trgx + trg.width; // target element right
var tcy = trgy + trg.height; // target element bottom
var dx = tcx - scx; // len x
var dy = tcy - scy; // len y
var sourcePointX, sourcePointY, targetPointX, targetPointY;
if (Math.abs(dx) > Math.abs(dy)) { // horizontal
sourcePointY = y0;
targetPointY = y0;
if (srcx < trgx) { // left to right
sourcePointX = 1;
targetPointX = 0;
} else {
sourcePointX = 0;
targetPointX = 1;
}
} else {
sourcePointX = x0;
targetPointX = x0;
if (srcy < trgy) { // top to bottom
sourcePointY = 1;
targetPointY = 0;
} else {
sourcePointY = 0;
targetPointY = 1;
}
}
this.graph.setConnectionConstraint(parallels[i], parallels[i].source, true, new mxConnectionConstraint(new mxPoint(sourcePointX, sourcePointY), true));
this.graph.setConnectionConstraint(parallels[i], parallels[i].target, false, new mxConnectionConstraint(new mxPoint(targetPointX, targetPointY), true));
if (i % 2) {
x0 += spx * (i + 1);
y0 += spy * (i + 1);
} else {
x0 -= spx * (i + 1);
y0 -= spy * (i + 1);
}
}

I wrote a simple particle filter and it won't work with more than a few hundred particles. I can't tell why

I have written a very simple 1d particle filter using javascript and a HTML5 canvas. It seems to work pretty well until the number of particles hits 283. I am not all that experienced with either particle filters or javascript, but I cannot see why 283 is the magic number! Please could someone help?
Edit:
OK, so it seems the magic number isn't fixed between browsers, but if I set nparticles = 1000 it complains TypeError: particles[count] is undefined and I don't know why that should be the case. Any tips would be gratefully received.
var c;
var ctx;
var start_x = 600;
var x_speed = 2;
var dt = 1;
var nparticles = 282;
var gauss_dist;
var particles = [];
function getRandomArbitrary(min, max) {
return Math.random() * (max - min) + min;
}
function drawBackground()
{
ctx.clearRect(0, 0, c.width, c.height);
ctx.fillStyle="#FFFFFF";
ctx.fillRect(0,0,800,400);
ctx.fillStyle="#00FF00";
ctx.fillRect(0,350,800,50);
ctx.fillStyle="#FF0000";
ctx.fillRect(50,300,5,50);
ctx.fillRect(750,300,5,50);
}
function drawRobot()
{
ctx.fillStyle="#000000";
start_x += x_speed*dt;
if (start_x >= 750 || start_x <= 50)
{
x_speed *= -1;
}
ctx.fillRect(start_x, 300, 10, 50);
}
function getDistanceToNearest(x)
{
var to_far =Math.abs(x - 750);
var to_near = Math.abs(x - 50);
var true_dist;
if (to_far > to_near)
{
true_dist = to_near;
}
else
{
true_dist = to_far;
}
true_dist = true_dist - 25 + 50.0*(Math.random()+Math.random()+Math.random()+Math.random()+Math.random()+Math.random()) / 3.0;
return true_dist;
}
function w_gauss(a, b)
{
error = a - b;
g = Math.exp(-((error *error) / (2 * 50.*50.)));
return g;
}
function loop()
{
var i = 0;
drawBackground();
drawRobot();
for (i=0; i < nparticles; i++)
{
ctx.fillStyle="#0000FF";
ctx.fillRect(particles[i].x, 345, 2,2);
}
var u = getDistanceToNearest(start_x);
var weights = [];
var wsum = 0.0;
for (i=0; i < nparticles; i++)
{
var distance = getDistanceToNearest(particles[i].x);
var w = w_gauss(u, distance);
// console.log(""+w);
weights.push(w);
wsum += w;
}
//console.log("Wsum = "+wsum);
var weights_norm = [];
var trimmed_particles = [];
for (i=0; i < nparticles; i++)
{
var w_norm = weights[i] / wsum;
if (w_norm > 0.0001)
{
weights_norm.push(w_norm);
trimmed_particles.push(particles[i]);
// console.log(""+w_norm);
}
}
particles = [];
for (i=0; i< trimmed_particles.length; i++)
{
particles.push(trimmed_particles[i]);
}
//particles = trimmed_particles;
console.log("Number of particles remaining = " + weights_norm.length + " " + particles.length);
var cummul = [weights_norm[0]];
for (i=1; i < weights_norm.length; i++)
{
cummul.push(cummul[i-1]+weights_norm[i]);
// console.log(""+cummul[i]);
}
/*
for (i=0; i<particles.length; i++)
{
console.log(particles[i]);
}
*/
// console.log("Beginning Resampling");
var resample = [];
for (i = 0; i < nparticles; i++)
{
var rand = Math.random();
var count = 0;
while(cummul[count]<=rand /*&& count < cummul.length*/)
{
count += 1;
}
//console.log("Count: "+count+ "Trimmed Particles: "+ particles.length);
//console.log("Cumulative frequency: "+cummul[count]);
//console.log(particles[count]);
//console.log("x "+particles[count].x+" y "+particles[count].y+" weight "+weights_norm[count]);
resample.push({"x":particles[count].x + (x_speed+ getRandomArbitrary(-0.5,0.5))*dt, "y":particles[count].y, "weight":weights_norm[count]});
}
for (i=0; i < resample.length; i++)
{
particles[i] = resample[i];
}
//particles = resample;
}
function init()
{
c=document.getElementById("theCanvas");
ctx = c.getContext("2d");
//gauss_dist = gaussian(50, 50);
// Take a random sample using inverse transform sampling method.
//setup initial particle sample
for (i=0; i < nparticles; i++)
{
rand_x = getRandomArbitrary(50, 750);
particle = {"x": rand_x, "y": 345.0, "weight":1.0/100.0};
particles.push(particle);
rand_y = 345;
}
ctx=c.getContext("2d");
drawBackground();
setInterval(loop, 500);
}
init();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<canvas id="theCanvas" width="800" height="400" style="border:2px solid #FFFFFF;">
</canvas>
</body>
</html>
In the function loop the variable count gets larger than the length of the array particles. If I change the line
while(cummul[count]<=rand)
to
while(cummul[count]<=rand && count < particles.length-1)
I can raise nparticles without error.
You need to check the array boundaries more often.

Julia Set Viewer

I've been trying to make a julia set viewer over at my site http://thejamespaterson.com/scripts/julia/, but I'm currently having trouble getting the program to display the correct julia set. For example, when testing with C value 0+0i, I get the following image:
The result is supposed to be a circle. I'm not sure why this is happening. I wrote my own complex numbers library and plotting functions, and they are posted below. Any help would be appreciated;
function complexNum(real, imaginary) {
this.real = real;
this.imaginary = imaginary;
return this;
}
function addComplex(c1, c2) {
this.real = c1.real + c2.real;
this.imaginary = c1.imaginary + c2.imaginary;
return this;
}
function multComplex(c1, c2) {
this.real = (c1.real * c2.real) - (c1.imaginary * c2.imaginary);
this.imaginary = (c1.real * c2.imaginary) + (c2.real * c1.imaginary);
return this;
}
function dispComplex(c) {
var sign = '';
if (c.imaginary >= 0) {
sign = '+';
}
return c.real + sign + c.imaginary + "i";
}
function getComplexModulus(c) {
return Math.sqrt((c.real * c.real) + (c.imaginary * c.imaginary));
}
//globals
var MAXITERATION = 100;
var BOUNDARY = 4;
var CANVASID = "juliaDraw";
var CONTEXT = document.getElementById("juliaDraw").getContext('2d');
var HEIGHT = 750;
var WIDTH = 750;
var juliaImageData = CONTEXT.createImageData(WIDTH, HEIGHT);
function readInput(inputID) {
return document.getElementById(inputID).value;
}
function drawPointOnCanvas(x, y, color) {
//console.log('drawing pixel at '+x+','+y);
CONTEXT.fillStyle = color;
CONTEXT.fillRect(x, y, 1, 1);
}
function createArray(length) {
var arr = new Array(length || 0),
i = length;
if (arguments.length > 1) {
var args = Array.prototype.slice.call(arguments, 1);
while (i--) arr[length - 1 - i] = createArray.apply(this, args);
}
return arr;
}
function doesPointEscape(c, complexNum) {
var iterations = 0;
var escaped = false;
while ((!escaped) && (iterations < MAXITERATION)) {
if (getComplexModulus(complexNum) > BOUNDARY) {
escaped = true;
}
complexNum = addComplex(multComplex(complexNum, complexNum), c);
iterations++;
}
if (escaped) {
return true;
} else {
return false;
}
}
function plotJuliaSet(canvasID, width, height, c, start, stepsize) {
var complexNumberArray = createArray(width + 1, height + 1);
var doesPointEscapeArray = createArray(width + 1, height + 1);
var real = start.real;
var imaginary = start.imaginary;
console.log('====Drawing Set====');
console.log('c = ' + dispComplex(c));
for (var x = 0; x <= width; x++) {
imaginary = start.imaginary;
for (var y = 0; y <= height; y++) {
complexNumberArray[x][y] = new complexNum(real, imaginary);
doesPointEscapeArray[x][y] = doesPointEscape(c, complexNumberArray[x][y]);
if (doesPointEscapeArray[x][y]) {
//drawPointOnCanvas(x, y,'blue');
} else {
drawPointOnCanvas(x, y, 'black');
//console.log('point '+dispComplex(complexNumberArray[x][y])+' does not escape');
}
imaginary = imaginary - stepsize;
}
real = real + stepsize;
}
//CONTEXT.putImageData(juliaImageData, 0, 0);
console.log('done');
}
function defaultDraw() {
CONTEXT.clearRect(0, 0, WIDTH, HEIGHT);
var start = new complexNum(-2, 2);
var c = new complexNum(0, 0);
plotJuliaSet(CANVASID, WIDTH, HEIGHT, c, start, 2 / 350);
}
function drawJulia() {
CONTEXT.clearRect(0, 0, WIDTH, HEIGHT);
var start = new complexNum(-2, 2);
var c = new complexNum(readInput('realValue') * 1, readInput('imagValue') * 1);
plotJuliaSet(CANVASID, WIDTH, HEIGHT, c, start, 2 / 350);
}
<!doctype html>
<html>
<head>
<title>Julia Set Viewer</title>
<style>
.desc {
float: right;
width: 300px;
}
#juliaDraw {
border: 1px dotted;
float: left;
}
</style>
</head>
<body>
<div class="desc">
<h1>Julia Set Viewer</h1>
<p>You can view Julia sets with this simple online tool. Don't know what a Julia set is? Learn about it here.
This script uses a complex number library that I built to handle the arithmetic required to process these images. The source code is hosted on my github.
</p>
</div>
<canvas id="juliaDraw" width=750 height=750 onClick="defaultDraw()"></canvas>
<div class="controls">
<form>
<label>Real:
<input type="text" id="realValue" value="0">
</label>
<label>Imag:
<input type="text" id="imagValue" value="0">
</label>
<input type="button" onClick="drawJulia()">
</form>
</div>
<script src="complex.js"></script>
<script src="juliaset.js"></script>
</body>
</html>
The problem stems from confusion over the use of the this pointer in Javascript.
Change your Julia calculation in doesPointEscape() to
complexNum = new addComplex(new multComplex(complexNum, complexNum), c);
and it works.
This will return a new complex number from multComplex, then add it to c and return a new complex number from addComplex that is assigned to complexNum.
Your multComplex and addComplex functions use the this pointer, but for the this pointer to be referring to one of your complex numbers, you would have to be calling the function on an existing one, or calling new to create a new one.
Alternatively, you could rewrite your multComplex() and addComplex() functions as
function multComplex(c1, c2) {
var real = (c1.real * c2.real) - (c1.imaginary * c2.imaginary);
var imaginary = (c1.real * c2.imaginary) + (c2.real * c1.imaginary);
return new ComplexNum(real, imaginary);
}
function addComplex(c1, c2) {
var real = c1.real + c2.real;
var imaginary = c1.imaginary + c2.imaginary;
return new ComplexNum(real, imaginary);
}
then your doesPointEscape() function should work as-is.

Creating a bouncing box animation on canvas

I need to create an animation of dropping box, which supposed to bounce 10 times when it reaches a certain Y point on canvas, each time twice lower that the previous. So I have the animation of the dropping box, but I can't make the bounce work. Here are the functions that I wrote:
function dropBox(y, width, height) {
var img_box = new Image();
img_box.src = 'images/gift_box_small.png';
var box_y_pos = y;
if(y==0)
box_y_pos = y-img_box.naturalHeight;
img_box.onload = function(){
ctx_overlay.save();
ctx_overlay.clearRect(0,0,width,height);
ctx_overlay.drawImage(img_box, (width/2)-(img_box.naturalWidth/2), box_y_pos);
ctx_overlay.restore();
}
box_y_pos += 3;
var box_bottom_position = box_y_pos - img_box.naturalHeight;
if(box_y_pos+img_box.naturalHeight<height-25)
var loopTimer = setTimeout(function() {dropBox(box_y_pos, width, height)},24);
else
bounceBox(img_box, box_y_pos, box_y_pos, (height/2)-(img_box.naturalHeight/2), "up");
}
function bounceBox(img, img_final_pos, y, midway_pos, direction){
var midway = midway_pos;
var direction = direction;
var img_y_pos = y;
img.onload = function(){
ctx_overlay.save();
ctx_overlay.clearRect(0,0,docWidth,docHeight);
ctx_overlay.drawImage(img, (docWidth/2)-(img.naturalWidth/2), img_y_pos);
ctx_overlay.restore();
}
for(var i = 0; i < 10; i++){
if(direction=="up"){
//going up
if(img_y_pos>midway_){
img_y_pos -= 3;
var loopTimer = setTimeout(function() {bounceBox(img, img_final_pos, img_y_pos, midway_pos, "up")},24);
} else {
img_y_pos += 3;
midway = Math.floor(midway /= 2);
if(midway%2>0)
midway += 1;
var loopTimer = setTimeout(function() {bounceBox(img, img_final_pos, img_y_pos, midway_pos, "down")},24);
}
} else {
//going down
if(img_y_pos < img_final_pos){
img_y_pos += 3;
var loopTimer = setTimeout(function() {bounceBox(img, img_final_pos, img_y_pos, midway_pos, "down")},24);
}
}
}
}
JSFiddle: http://jsfiddle.net/n2derqgw/3/
Why isn't it working and how can I make it work?
To avoid getting a headache, you've better handle the animation within a single function called with a setInterval.
And keep all animation-related data in one object.
So the code below does not exactly what you want, but should get you started :
http://jsfiddle.net/n2derqgw/4/
Setup :
var canvas_overlay, ctx_overlay, docWidth, docHeight;
var img_box = new Image();
img_box.src = 'http://corkeynet.com/test/images/gift_box_small.png';
var mustBeReadyCount = 2; // must load image and window
img_box.onload = launchWhenReady;
window.onload = launchWhenReady;
var animationStep = '';
var boxAnimationData = {
animationStep: '',
y: 0,
maxY: 0,
bounceCount: 6,
direction: -1,
bounceHeight: 0
};
function launchWhenReady() {
mustBeReadyCount--;
if (mustBeReadyCount) return;
docWidth = window.innerWidth;
docHeight = window.innerHeight;
canvas_overlay = document.getElementById('canvas_overlay');
ctx_overlay = canvas_overlay.getContext('2d');
resizeCanvas(docWidth, docHeight);
boxAnimationData.animationStep = 'falling';
boxAnimationData.bounceHeight = docHeight / 2 - img_box.height;
setInterval(animateBox, 30);
};
More interesting code is here :
function animateBox() {
if (boxAnimationData.animationStep == 'falling') dropBox();
else if (boxAnimationData.animationStep == 'bouncing') bounceBox();
}
function dropBox() {
ctx_overlay.clearRect(0, 0, docWidth, docHeight);
boxAnimationData.y += 3;
if (boxAnimationData.y + img_box.height > docHeight) {
boxAnimationData.animationStep = 'bouncing';
}
ctx_overlay.drawImage(img_box, (docWidth / 2) - (img_box.width / 2), boxAnimationData.y);
}
function bounceBox() {
ctx_overlay.clearRect(0, 0, docWidth, docHeight);
boxAnimationData.y += boxAnimationData.direction * 3;
if (boxAnimationData.y + img_box.height > docHeight) {
// reached floor ? swap direction
boxAnimationData.direction *= -1;
// and reduce jump height
boxAnimationData.bounceHeight *= 3 / 2;
boxAnimationData.bounceCount--;
if (!boxAnimationData.bounceCount) boxAnimationData.animationStep = '';
} else if (boxAnimationData.y < boxAnimationData.bounceHeight) {
boxAnimationData.direction *= -1;
}
ctx_overlay.drawImage(img_box, (docWidth / 2) - (img_box.width / 2), boxAnimationData.y);
}

PaperJS - How do I connect all items that have a distance of X from any given item? (Item interactivity)

I have a project I am trying to get an animated <canvas> working with Paper JS. What I am curious about is if there is anything built into PaperJS that allows the ability to detect interactivity between items (i.e. if a item is X distance from any other item on the layer). Here is what I have so far:
HTML
<canvas id="myCanvas" resize></canvas>
CSS
html, body{margin:0; padding: 0;}
#myCanvas{width: 100%; height: 100%;}
JS
$(function(){
var canvas = $('#myCanvas')[0];
paper.setup(canvas);
var viewSize = paper.view.size;
var itemCount = 20;
var theBall = new paper.Path.Rectangle({
point : [0,0],
size : 10,
fillColor : '#00a950',
});
var theBallSymbol = new paper.Symbol(theBall);
// Create and place symbol on view
for (var i = 1; i <= itemCount; i++) {
var center = paper.Point.random().multiply(viewSize);
var placedSymbol = theBallSymbol.place(center);
placedSymbol.scale(i / itemCount);
placedSymbol.data = {
origin : center,
direction : (Math.round(Math.random())) ? 'right' : 'left',
}
placedSymbol.onFrame = function(e){
var pathWidth = this.bounds.width * 20;
var center = this.data.origin;
var moveValue = this.bounds.width / 20;
if(this.data.direction == 'right'){
if(this.position.x < center.x + pathWidth){
this.position.x += moveValue;
} else{
this.position.x -= moveValue;
this.data.direction = 'left';
}
} else {
if(this.position.x > center.x - pathWidth){
this.position.x -= moveValue;
} else {
this.position.x += moveValue;
this.data.direction = 'right';
}
}
}
}
paper.view.onFrame = function (e){
// For entire view
for (var i = 0; i < itemCount; i++) {
var item = paper.project.activeLayer.children[i];
// I imagine I would need to do something here
// I tried a hitTest already, but I'm not sure
// that will give me the information I would need
}
}
});
JSFiddle
That part so far is working well. What I am curious about how I can do the following:
Whenever any given item (the squares) come within a distance of X between each other, create a line (path) between them
The idea is very similar to this page: http://panasonic.jp/shaver/lamdash/dna/
Any ideas would be greatly appreciated. Thanks!
Paper.js does not keep track of the inter-point distance between an item's center and all other items. The only way to gather that information is to manually loop through them.
In your case, I think it would be easiest to:
Create an array of lines
Only keep lines that might become shorter than the threshold value
Loop through the lines array on each onFrame() and adjust the opacity.
By only choosing lines that will come within a threshold value, you can avoid creating unnecessary paths that would slow the framerate. Without this, you'd be checking ~5 times as many items.
Here's a quick example:
$(function(){
var canvas = $('#myCanvas')[0];
paper.setup(canvas);
var viewSize = paper.view.size;
var itemCount = 60;
//setup arrays to change line segments
var ballArray = [];
var lineArray = [];
//threshold distance for lines
var threshold = Math.sqrt(paper.view.size.width*paper.view.size.height)/5;
var theBall = new paper.Path.Rectangle({
point : [0,0],
size : 10,
fillColor : '#00a950',
});
var theBallSymbol = new paper.Symbol(theBall);
// Create and place symbol on view
for (var i = 1; i <= itemCount; i++) {
var center = paper.Point.random().multiply(viewSize);
var placedSymbol = theBallSymbol.place(center);
placedSymbol.scale(i / itemCount);
placedSymbol.data = {
origin : center,
direction : (Math.round(Math.random())) ? 'right' : 'left',
}
// Keep each placedSymbol in an array
ballArray.push( placedSymbol );
placedSymbol.onFrame = function(e){
var pathWidth = this.bounds.width * 20;
var center = this.data.origin;
var moveValue = this.bounds.width / 20;
if(this.data.direction == 'right'){
if(this.position.x < center.x + pathWidth){
this.position.x += moveValue;
} else{
this.position.x -= moveValue;
this.data.direction = 'left';
}
} else {
if(this.position.x > center.x - pathWidth){
this.position.x -= moveValue;
} else {
this.position.x += moveValue;
this.data.direction = 'right';
}
}
}
}
// Run through every possible line
// Only keep lines whose length might become less than threshold
for (var i = 0; i < itemCount; i++) {
for (j = i + 1, point1 = ballArray[i].data.origin; j < itemCount; j++) {
if ( Math.abs(point1.y - ballArray[j].bounds.center.y) < threshold && Math.abs(point1.x - ballArray[j].data.origin.x) < 4 * threshold) {
var line = new paper.Path.Line( point1, ballArray[j].bounds.center ) ;
line.strokeColor = 'black';
line.strokeWidth = .5;
//note the index of the line's segments
line.point1 = i;
line.point2 = j;
if (line.length > 1.4 * threshold && ballArray[j].data.direction == ballArray[i].data.direction) {
line.remove();
}
else {
lineArray.push(line);
}
}
}
}
paper.view.onFrame = function (e){
// Update the segments of each line
// Change each line's opacity with respect to distance
for (var i = 0, l = lineArray.length; i < l; i++) {
var line = lineArray[i];
line.segments[0].point = ballArray[line.point1].bounds.center;
line.segments[1].point = ballArray[line.point2].bounds.center;
if(line.length < threshold) {
line.opacity = (threshold - line.length) / threshold;
}
else line.opacity = 0;
}
}
});

Categories

Resources