I am trying to solve a school assignment in p5 JavaScript. I want something to move over the canvas after one mouseclick. But it only moves a little bit and I have to click several times to get it all the way over. What have I done wrong? Shouldn't the loop make it move all the way? Can post the whole code if needed.
function CanvasPressed()
{
if ( mouseX > 0 && mouseX < 638 && mouseY > 0 && mouseY < 100 )
{
Bird.stop();
Bird.play();
for ( let b = 640; b > 0; b--)
{
x = x - 0.05;
}
}
Alright, so you've got a couple misunderstood things, here:
// purely aesthetic but in javascript functions are usually written as (i think) camelCase
// so: canvasPressed() rather than CanvasPressed(), Class-es start with upper case
function CanvasPressed()
{
// you can check for width & height if you want if ( mouseX > 0 && mouseX < width)
if ( mouseX > 0 && mouseX < 638 && mouseY > 0 && mouseY < height )
{
for ( let b = 640; b > 0; b--) // this, in this case, does the same as for(let i = 0; i < width; i ++)
{
x += 0.05
// 0.05 is very little, only a very small part of a pixel
}
// here it moves 0.05 * 640 (0.05 + 0.05 + 0.05 ... )
}
}
javascript naming conventions thingy if you want
and this is how i would make it move through the canvas:
let mouseWasPressed = false;
let x = 20
function draw() {
background(20);
ellipse(x, height / 2, 40)
if(mouseWasPressed) // don't need {} for 1 line after the if()
x ++; // x = x + 1 shortening in javascript
// }
}
function mousePressed(){
mouseWasPressed = true
}
if you don't want the "animation" you could use your previous method, but change the 0.05 to 1:
for(let i = 0; i <= width; i ++) // you don't have to add parentheses for 1 line
x ++; // x = x + 1 just a shortening in javascript
OR just
x = width // or x += width (x = x + width)
I want to write a program that draws a surface (X * Y) evenly.I already have an approach for this at the moment, but it doesn't quite work yet and is also very slow. Since this approach is far too slow, I do not want to pursue it much further.
At the beginning there is always the first point and the last one - so with an area of 10 x 10 the pixel at position 0 and the pixel at position 99.
Then the next best pixel must be found, i.e. the one with the largest distance. This is relatively easy with only two points - (99 - 0 / 2) so 49 or 48.
Now you have to look for the next best one again. So (49 - 0) / 2 or if 48 was taken before (99 - 48) / 2 so 24/25 or 74/75.
This process must be repeated until the correct sequence is found.
0,99,49,74,24,36,61,86,12,42,67,92,6,18,30,55,80,45,70,95,3,9,15,21,27,33,39,52,58,64,77,83,89,47,72,97,1,4,7,10,13,16,19,22,25,28,31,34,37,40,43,50,53,56,59,62,65,68,75,78,81,84,87,90,93,2,5,8,11,14,17,20,23,26,29,32,35,38,41,44,46,48,51,54,57,60,63,66,69,71,73,76,79,82,85,88,91,94,96,98
I also added a small example here, which shows how it should work. The function getElementOrder should be replaced by a mathematical expression to get the fastest possible solution.
// define variables
const width = 20; // this will be > 2100
const height = 20; // this will be > 1600
const size = 20;
let elements = {};
// create all cells
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
let id = x + y * height;
let div = document.createElement("div");
div.style.border = "solid 1px black";
div.style.width = size + "px";
div.style.height = size + "px";
div.style.position = "absolute";
div.style.left = x * size + "px";
div.style.top = y * size + "px";
div.style.backgroundColor = "#F0F0F0";
let textDiv = document.createElement("div");
textDiv.innerHTML = id;
textDiv.style.position = "absolute";
textDiv.style.fontSize = "6pt";
textDiv.style.top = "1px";
textDiv.style.right = "1px";
div.appendChild(textDiv);
document.body.appendChild(div);
elements[id] = div;
}
}
function getElementOrder(width, height) {
/* BAD SLOW CODE START - This sould be better: */
const length = width * height;
const order = [0, length -1];
const result = [0, length -1];
while (order.length !== length) {
let index = 0;
let diff = 0;
for (let i = 0, m = order.length - 1; i < m; i++) {
let localDiff = order[i+1] - order[i];
if (localDiff > diff) {
index = i;
diff = localDiff;
}
}
let offset = Math.floor(diff/2);
let value = order[index] + offset;
order.splice(index + 1, 0, value);
result.push(value);
}
return result;
/* BAD SLOW CODE END */
}
// get the draw order
let order = getElementOrder(width, height);
// change color of each pixel in draw order
let interval = setInterval(() => {
if (order.length === 0) {
clearInterval(interval);
return;
}
const value = order.shift();
elements[value].style.backgroundColor = "#00abab";
}, 10);
Are there any mathematical approaches to solve this problem?
You are welcome to post better solutions, approaches or links to mathematical formulas for this problem here.
I think I get what you're trying to accomplish, and what the underlying routine is. The way I see it, you're probably overcomplicating the question of "finding the biggest distance", since from what I can see, what you're basically doing is halving increasingly fine intervals.
So, here's my version:
function getElementOrder(width, height) {
const length = width * height;
const order = [ 0 ];
for (let denominator = 2; order.length < length; denominator *= 2) {
for (let enumerator = 1; enumerator < denominator; enumerator += 2) {
order.push(Math.round(length * enumerator / denominator));
}
}
return order;
}
I'm using very long and clunky variable names to make the principle behind it clearer: if you project the entire interval of [0, width*height] to the interval of [0, 1] then what you're doing is adding 1/2, then 1/4 and 3/4, then 1/8 and 3/8 and 5/8 and 7/8, and so on; each time you multiply the denominator by 2, and take all the odd-numbered multiples.
(Addendum: you can probably squeeze even better performance out of it by using a fixed-length TypedArray for the results, and adding elements by index instead of using .push(). I just didn't want to obscure the gist of the solution with the additional loop variable and such.)
I've got a 2x3 table that I'm adding to EaselJS...currently I'm building it like this:
for (var i = 0; i < 6; i++) {
if(i == 1 || i == 3 || i == 5) {
var xPos = playersBoxW;
} else {
var xPos = 0;
}
if(i == 2 || i == 3) {
var yPos = playersBoxH;
} else if (i == 4|| i == 5) {
var yPos = playersBoxH*2;
} else {
var yPos = 0;
}
playerBox[i] = new createjs.Container().set({x: xPos, y: yPos});
}
It just seems a very inefficient way of doing it and not useful if the table grows. Anyone else have an idea to simplify this?
If you are just trying to do row/column math, there is an easier way.
Here is your original example (with some code to make it work) http://jsfiddle.net/u3ds24y5/
You can just derive the column and row with a simple equation. This lets you change the number of columns and total count easily.
var column = i % num_columns;
var row = Math.floor(i / num_columns);
var x = column * column_width;
var y = row * row_height;
Here is an updated fiddle: http://jsfiddle.net/u3ds24y5/1/
Simplified code:
var cols = 2, total = 6; // Change these
for (var i = 0; i < total; i++) {
var xPos = i % cols * playersBoxW,
yPos = Math.floor(i/cols) * playersBoxH;
// Create container, etc
}
Looking at your code I think this algorithm is essentially what it boils down to:
xPos seems to be equal to the integer division of i (by table width) times playersBoxW. e.g if i = 3 and width is 2, then xPos is equal to playersBoxW times int division of 3/2 which is 1.
yPos seems to be equal to the integer division of i (by table height) times playersBoxH. e.g if i = 4 and height = 3, then yPos is equal to playersBoxH times int division of 4/3 which is 1.
function integerDivision(a, b) {
return Math.floor(a / b);
}
function makeTable(width, height, player, arr) {
var xPos, yPos, size = width*height;
for (var i = 0; i < size; i++) {
xPos = player.boxW * integerDivision(i, width);
yPos = player.boxH * integerDivision(i, height);
arr[i] = new createjs.Container().set({x: xPos, y: yPos});
}
return arr;
}
Integer division is like regular division but you throw the remainder away. So in this case we round the number down:
3/2 = 1.5 => floor the result (round down) => 1
Side node: EaslJS containers can be expensive sometimes so be careful with them.
Containers have some overhead, so you generally shouldn't create a Container to hold a single child. [easljs doc]
I am trying to write a script to place 100 circles of varying sizes onto a stage. I've outlined the concise requirements below.
Given the following:
var stage; // contains a "width" and "height" property.
var circle; // the circle class. contains x, y, radius & a unique id property.
var circleArray; // contains 100 circle instances
requirements:
write a function to place 100 circles of varying radius onto the stage.
placements must be random but evenly distributed (no clumping).
placement must be performant - this will be executing on a mobile web browser.
circles must not intersect/overlap other circles.
circle.x >= 0 must be true.
circle.y >= 0 && circle.y <= stage.height must be true.
circles may have any of the following radius sizes (assigned at creation):
150
120
90
80
65
My current attempt is a brute-force method, which does not operate efficiently. If I attempt to insert any more than ~10 circles, the browser hangs. Below is my current implementation, which I am completely OK with throwing away in favor of a more performant / better one.
Here is a live demo (NOTE: there is no actual drawing code, just the logic, but it will still lock up the browser so be warned!!) http://jsbin.com/muhiziduxu/2/edit?js,console
function adjustForOverlap (circleArray) {
// a reference to the circle that is invoking this function.
var _this = this;
// remove this circle from the array we are iterating over.
var arr = circleArray.filter(function (circle){
return circle.id !== _this.id;
});
// while repeat == true, the circle may be overlapping something.
var repeat = true;
while(repeat) {
var hasOverlap = false;
for (var i=0; i<arr.length; i++) {
var other = arr[i];
var dx = _self.x - other.x;
var dy = _self.y - other.y;
var rr = _self.radius + other.radius;
if (dx * dx + dy * dy < rr * rr) {
// if here, then an overlap was detected.
hit = true;
break;
}
}
// if hit is false, the circle didn't overlap anything, so break.
if (hit === false) {
repeat = false;
break;
} else {
// an overlap was detected, so randomize position.
_self.x = Math.random() * (stage.width*2);
_self.y = Math.random() * stage.height;
}
}
}
There are lots of efficient collision detection algorithms. Many of them work by dividing up the space into cells and maintaining a separate data structure with efficient lookup of other objects in the cell. The basic steps are:
Identify a random spot for your new circle
Determine which cells it's in
Look in each of those cells for a collision
If there's a collision, goto 1.
Else, add the new circle to each of the cells it overlaps.
You can use a simple square grid (i.e. a 2-d array) for the cell data structure, or something else like a quadtree. You can also in some cases get a bit of extra speed by trying a cheap-but-coarse collision check first (do the bounding boxes overlap), and if that returns true try the slightly more expensive and exact check.
Update
For quadtrees, check out d3-quadtree, which ought to give you a pretty good implementation, with examples.
For a (very quick, untested) 2-d array implementation:
function Grid(radius, width, height) {
// I'm not sure offhand how to find the optimum grid size.
// Let's use a radius as a starting point
this.gridX = Math.ceil(width / radius);
this.gridY = Math.ceil(height / radius);
// Determine cell size
this.cellWidth = width / this.gridX;
this.cellHeight = height / this.gridY;
// Create the grid structure
this.grid = [];
for (var i = 0; i < gridY; i++) {
// grid row
this.grid[i] = [];
for (var j = 0; j < gridX; j++) {
// Grid cell, holds refs to all circles
this.grid[i][j] = [];
}
}
}
Grid.prototype = {
// Return all cells the circle intersects. Each cell is an array
getCells: function(circle) {
var cells = [];
var grid = this.grid;
// For simplicity, just intersect the bounding boxes
var gridX1Index = Math.floor(
(circle.x - circle.radius) / this.cellWidth
);
var gridX2Index = Math.ceil(
(circle.x + circle.radius) / this.cellWidth
);
var gridY1Index = Math.floor(
(circle.y - circle.radius) / this.cellHeight
);
var gridY2Index = Math.ceil(
(circle.y + circle.radius) / this.cellHeight
);
for (var i = gridY1Index; i < gridY2Index; i++) {
for (var j = gridX1Index; j < gridX2Index; j++) {
// Add cell to list
cells.push(grid[i][j]);
}
}
return cells;
},
add: function(circle) {
this.getCells(circle).forEach(function(cell) {
cell.push(circle);
});
},
hasCollisions: function(circle) {
return this.getCells(circle).some(function(cell) {
return cell.some(function(other) {
return this.collides(circle, other);
}, this);
}, this);
},
collides: function (circle, other) {
if (circle === other) {
return false;
}
var dx = circle.x - other.x;
var dy = circle.y - other.y;
var rr = circle.radius + other.radius;
return (dx * dx + dy * dy < rr * rr);
}
};
var g = new Grid(150, 1000, 800);
g.add({x: 100, y: 100, radius: 50});
g.hasCollisions({x: 100, y:80, radius: 100});
Here's a fully-functional example: http://jsbin.com/cojoxoxufu/1/edit?js,output
Note that this only shows 30 circles. It looks like the problem is often unsolvable with your current radii, width, and height. This is set up to look for up to 500 locations for each circle before giving up and accepting a collision.
I am currently writing a tool that allows a user to change a value by pinching their screen. I am using Hammer.js to catch the pinch event and I am using the scale property of the event to determine the factor of which to change the value.
The following code illustrates how I am increasing and decreasing the value based on the pinch:
var value = 10;
var scale = false;
var previous_scale = false;
pinch.on("pinch", function(e){
// Only proceed is it is not the final touch
// The final touch has a scale of (0)
if(e.isFinal===false){
// Only proceed if the previous scale has been defined
if(previous_scale!==false){
// Calculate the difference in scales
scale = e.scale-previous_scale;
previous_scale = e.scale;
if(scale>0){
value = Math.round(value+(1+(scale*100)));
value = value>=4000 ? 4000 : value;
}else{
value = Math.round(value-(1+(Math.abs(scale)*100)));
value = value<=10 ? 10 : value;
}
console.log(value);
}else{
previous_scale = e.scale;
}
}
});
My problem is, I would like the value to do the following:
When value is less than 30, increment by 1 for each scale
When value is less than 60, increment by 5 for each scale
When value is less than 200, increment by 10 for each scale
When value is less than 1000, increment by 50 for each scale
When value is less than 2500, increment by 100 for each scale
When value is less than 5000, increment by 500 for each scale
I have tried to achieve the above by inserting the following:
if(radius>2000){
radius = 500*Math.round(radius/500);
}else if(radius>1000){
radius = 100*Math.round(radius/100);
}else if(radius>300){
radius = 50*Math.round(radius/50);
}else if(radius>50){
radius = 10*Math.round(radius/10);
}
This hasn't worked as it is only rounding the numbers, I actually need to increase the factor as the numbers get larger.
Can anyone advise on the best way to do this?
As the scalevar is already a delta, that seems pretty straightforward:
if( value >= 2500 ) {
value += 500 * scale;
}
else if( value >= 1000 ) {
value += 100 * scale;
}
else if( value >= 200 ) {
value += 50 * scale;
}
else if( value >= 60 ) {
value += 10 * scale;
}
else if( value >= 30 ) {
value += 5 * scale;
}
else {
value += scale;
}
if( value < 0 ) {
value = 0;
}
if( value >= 5000 ) {
value = 5000;
}
(assuming a cap at value = 5000).
It seems that you need the rounded number anyway (correct me if I'm wrong), so as there are a limited number of possible values you can use a linear scale modified by the pinch event to address as an index to the desired values:
var value = 10;
var scale = 10;
var previous_scale = false;
pinch.on("pinch", function(e){
// Only proceed is it is not the final touch
// The final touch has a scale of (0)
if(e.isFinal===false) {
scale = Math.round(scale * e.scale);
if ( scale < 0 ) {
// lower bound
value = 0;
} else if ( scale < 31 ) {
// 0-30 increment of 1 --> 31 values
value = scale;
} else if ( scale < 37 ) {
// 35-60 increment of 5 --> 6 values
value = 30 + (scale - 30) * 5;
} else if ( scale < 51 ) {
// 70-200 increment of 10 --> 14 values
value = 60 + (scale - 36) * 10;
} else if ( scale < 67 ) {
// 250-1000 increment of 50 --> 16 values
value = 200 + (scale - 50) * 50;
} else if ( scale < 82 ) {
// 1100-2500 increment of 100 --> 15 values
value = 1000 + (scale - 66) * 100;
} else if ( scale < 87 ) {
// 3000-5000 increment of 500 --> 5 values
value = 2500 + (scale - 81) * 500;
} else {
// higher limit
value = 5000;
}
console.log(value);
}
});
Please, tell me if this is somewhere near to what you were thinking.