I am trying to create an array of vertices, and then run the 'rect' function over this array to display an arbitrary amount of rectangles. Right now, I have:
var vertices = new Array();
function setup() {
createCanvas(600, 600);
...
iter(width/2 - c/2, height/2 - c/2, c);
var i;
for (i = 0; i < vertices.length; i++) {
fill(200);
rect(vertices[i]);
}
}
And then:
function iter(x, y, len) {
r_1 = random(0, 1);
if (r_1 < 0.5){
vertices.push(x, y - len*0.5, len*0.5, len*0.5);
}
}
I have seen lots about using map or foreach to run functions over arrays but I don't know why this doesn't work (specifically, using a for loop to run a function over an array). I am obviously very new to all this stuff! An explanation of what I seem to misunderstand would be very much appreciated.
Thanks
When you do
vertices.push(x,y-len*0.5,len*0.5,len*0.5)
you're calling push with four arguments, so four items get pushed to the array. Because you're calling rect with verticies[i] later, it sounds like each item of verticies should be a data container of some sort - an array or an object, otherwise the points of each vertex will be separated out over multiple indicies. For example, if you were to use an array:
function iter(x, y, len) {
r_1 = random(0, 1);
if (r_1 < 0.5){
vertices.push([x, y - len*0.5, len*0.5, len*0.5]);
}
}
And then spread each array in the vertex array into the rect argument list:
function setup() {
createCanvas(600, 600);
// ...
iter(width/2 - c/2, height/2 - c/2, c);
var i;
for (i = 0; i < vertices.length; i++) {
fill(200);
rect(...vertices[i]);
}
}
This assumes that rect is a function that accepts 4 arguments. (you could also change rect so that it accepts a single array as an argument instead, and avoid the spread syntax, if you wanted)
You also might consider using an array literal rather than new Array - calling the Array constructor is rarely a good idea:
var vertices = [];
Related
I am making a game engine called Forge.js. I have a Polygon method in a Entity class and it is not drawing the shapes. It loops through points and draws a line to each of the points. The lines however aren't being drawn. help plz
polygon method:
_polygon(points){
const local_ctx = new Path2D()
this.ctx.beginPath()
var j = 3
local_ctx.moveTo(points[0], points[1])
for (var i=0; i<=points.length; i++){
local_ctx.lineTo(points[i+2], points[i+j])
j += 2
}
this.ctx.fillStyle = constants.COLORS.black
this.ctx.fill()
}
As has been said in the comments, you need to pass the Path2D object to fill() in order for the context to draw it.
But this isn't the only issue with your code.
Your for loop probably doesn't do what you expected:
// Rewrote in order to log the indexes that are beign used in the loop
var j = 3
console.log(0, 1)
for (var i=0; i<=10; i++){
console.log(i+2, i+j)
j += 2
}
You can actually simplify this loop a lot by taking advantage of the fact that an empty subpath doesn't require an initial moveTo call. lineTo(x, y) gets automatically converted to moveTo(x, y) if your path is empty.
So we can treat all our points the same, all in a single for loop that only increments our index by 2.
const constants = { COLORS: { BLACK: "#000" } };
const obj = {
ctx: document.createElement("canvas").getContext("2d"),
_polygon(points){
const local_ctx = new Path2D()
// Since we're using a Path2D object we don't need to do anything
// on the context's subpath
// this.ctx.beginPath()
for (var i=0; i<=points.length; i+=2){
local_ctx.lineTo(points[i], points[i+1])
}
this.ctx.fillStyle = constants.COLORS.black
this.ctx.fill(local_ctx) // fill the context using the Path2D object
}
};
document.body.append(obj.ctx.canvas);
obj._polygon([13, 13, 13, 50, 50, 50]);
This question already has answers here:
Using Canvas to animate a sorting algorithm in JS
(2 answers)
Closed 3 years ago.
Im taking a Highschool CompSci 30 class and I'm working on an assignment. I'm trying to make something that will sort arrays of HSL values and display it on a canvas. I'm using two different algorithms, Bubble sort and Merge sort. My Bubble Sort works just as I want it to, it sorts and shows the process as it's sorting. My Merge Sort also works but I want it to show the process just like my Bubble Sort does. How I got my Bubble Sort to work is by adding async before my function and adding await delay(ms) after each change is made so it draws a new version of the array after however many ms. The code for merge sort is a bit different since its recursive and I'm not sure where to add a draw function or a delay or if that approach would even work.
I've tried adding async and await like I did with my Bubble Sort but the Merge Sort code is more complex and I can't get it right
This is my draw function:
function draw(){
for(y=0;y<361;y++){
hue = cArray[y].slice(4,cArray[y].indexOf(",", 4));
ctx.fillStyle = `hsl(`+ hue + `,100%,50%)`;
ctx.fillRect(x,0,4,canvas.height);
x=x+3;} //draws small strips of color
x=0; //resets after every call
}
My Bubble Sort:
async function bubbleSort(array){
for(i=0;i<array.length;i++){
for(j=1;j<array.length;j++){
var hue1 = array[j-1].slice(4,array[j-1].indexOf(","));
var hue2 = array[j].slice(4,array[j].indexOf(","));
if(hueFromHsl(array[j-1]) > hueFromHsl(array[j])){
var temp = array[j-1];
array[j-1] = array[j];
array[j] = temp;
draw(array);
}
}
await delay(1);
}
return array;
}
My Merge Sort:
function mergeSort(array){
if (array.length < 2) {return array;}
var mid = Math.floor(array.length / 2);
var left = array.slice(0, mid);
var right = array.slice(mid,array.length);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left,right){
var result = [];
var l = 0, r = 0;
while (l < left.length && r < right.length) {
if (hueFromHsl(left[l]) < hueFromHsl(right[r])) {result.push(left[l++]);}
else {result.push(right[r++]);}
}
return result.concat(left.slice(l)).concat(right.slice(r));
}
Also here is a js.do of the code: https://js.do/Brunsos/color-sort
The process should look similiar to the way my Bubble Sort looks when its used but it either finishes the sort instantly or doesnt work at all. What can I do?
Great code! The issue with displaying this is that it creates copies at each iteration using slice(), so the original array remains the same until the end. Instead of using return statements, just change the actual array. To do this, pass in indexes of the subarrays, then change the actual array. Just call draw(array) within the function. Notice now neither function returns anything, instead they change the array passed in...
async function mergeSort(array, leftIndex, rightIndex) {
length = rightIndex - leftIndex
if (length < 2) {
return array;
}
var mid = leftIndex + Math.floor(length / 2);
mergeSort(array, leftIndex, mid)
mergeSort(array, mid, rightIndex)
await delay(1000*Math.sqrt(rightIndex-leftIndex));
draw(array)
merge(array, leftIndex, mid, rightIndex)
}
function merge(array, leftIndex, mid, rightIndex) {
var result = [];
var l = leftIndex,
r = mid;
while (l < mid && r < rightIndex) {
if (array[l] < array[r]) {
result.push(array[l++]);
} else {
result.push(array[r++]);
}
}
result = result.concat(array.slice(l, mid)).concat(array.slice(r, rightIndex));
for (let i = 0; i < rightIndex - leftIndex; i++) {
array[leftIndex + i] = result[i]
}
}
Button Script:
<button id="mSort" class="sort" onclick=
"(async() => {
await mergeSort(cArray,0,360);
await delay(1600);
draw(cArray);
})()"
>Merge Sort</button>
</div>
This button script is to allow for the last draw, since the draw occurs before the final merge if you don't await then draw it will be stuck before the final merge...
I have a function that generates numbers within a range.
I created a composite type like this:
var cowPosition = {
x: 0,
y: 0
};
I also created an array:
var positionsArray = [];
then, I proceed to iterate to fill the array with the composite type.
All of this is inside a function which returns the array.
Here's the function:
function generateCowPositions(numberOfCows){
var positionsArray = [];
var cowPosition = {
x: 0,
y: 0
};
var x,y;
for (var i = 0; i < numberOfCows; i++) {
x = randomPosition(0,5);
y = randomPosition(0,5);
x = x * 80;
y = y * 80;
cowPosition.x = x;
cowPosition.y = y;
positionsArray[i] = cowPosition;
}
return positionsArray;
}
When I run it, it fills the whole array with the last two generated coordinates.
There is no "composite type" in JavaScript. What you are referring to is called an object.
The problem you are having is that objects are passed by reference, not by value. This means that if you store an object into a variable called a and modify it in some function, the value stored in a will be modified too.
What you need to do is:
function generateCowPositions(numberOfCows) {
var positionsArray = [];
// note: cowPosition object is not needed anymore, so I've removed it
var x, y;
for (var i = 0; i < cantidadVacas; i++) {
x = randomPosition(0, 5);
y = randomPosition(0, 5);
x = x * 80;
y = y * 80;
// create a new object in every intration
positionsArray[i] = {
x: x,
y: y,
};
}
return positionsArray;
}
Because there is only one instance of cowPosition in your code. So every iteration of your loop simply changes that one object, and at the end of the loop you simple keep the result of the last iteration.
Each index in the array is pointing to the exact same object, being cowPosition
I'm not sure what you are trying to accomplish 100%, but you should create a new object at each iteration. There isn't a need to initialize x and y in the object.
Simply:
function generateCowPositions(numberOfCows){
var positionsArray = [];
var cowPosition = {}
var x,y;
for (var i = 0; i < cantidadVacas; i++) {
x = randomPosition(0,5);
y = randomPosition(0,5);
x = x * 80;
y = y * 80;
cowPosition.x = x;
cowPosition.y = y;
positionsArray[i]= Object.assign({}, cowPosition);
}
return positionsArray;
}
JavaScript is an object-oriented language where objects are passed by-reference, not by-value. I suspect you're thinking of C and C++ where struct/class values are by default copied in their entirety by the = operator. In JavaScript it's closer to C# and Java where object / non-primitive types have their references copied instead.
Objects in javascript are passed as references.
positionsArray[i] = cowPosition; sets positionsArray[i] to a reference to the cowPosition object.
After the loop, all array elements reference the same cowPosition object. Therefore, since each loop of the array changes the underlying cowPosition object, all the array elements appear to have the same x and y position.
A simple solution to this problem would be to shallow copy the cowPosition object inside the loop, such that each array element references a difference position object and changing the underlying cowPosition object has no effect on the shallow copies inside the positionsArray, like so:
positionsArray[i] = JSON.parse(JSON.stringify(cowPosition));
This works by converting the object to a string and back again using javascript's native JSON implementation.
I just started fiddling around with JavaScript. Coming from Java and OO PHP things are getting weirder with every step :)
This is my introduction project to javascript in which I've set out to program multiplayer working version of Settlers of Catan. Code below is an attempt to store cube coordinates of N sized hexagonal map tiles in an array.
I've read you declare object in javascript by assigning functions to variables.
var Tile = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
};
var Map = function () {
var grid = [];
function generate_map(radius) {
for (width = -radius; width <= radius; width++) {
var r1 = Math.max(-radius, -width - radius);
var r2 = Math.min(radius, -width + radius);
for (r = r1; r <= r2; r++) {
grid.push(new Tile(width, r, -width - r));
}
}
}
};
I've tried instantiating new Map object, calling its only function and outprinting the resulting values stores in grid[] array. But for each loop is not playing nice :( I get the unexpected identifier.
var main = function () {
var basic_map = new Map();
basic_map.generate_map(3);
for each (var tile in basic_map.grid) {
console.log(tile.x, tile.y, tile.z);
}
};
main();
I am fully aware this is one of those face palm errors, but help would nevertheless be appreciated, cheers!
Change this:
function generate_map(radius) {
...to this:
this.generate_map = function(radius) {
Edit: there are actually more issues than I at first realized.... :)
A few other tips:
First, I would recommend changing:
var Tile = function (x, y, z) {
...to simply be:
function Tile(x, y, z) {
(the same goes for Map). Your current solution works fine, but it's not very idiomatic, and until ES6 there was nothing in the spec that would cause var Tile = function to cause the resulting function's 'name' property to be set to "Tile", which is useful when it comes to debugging. I recently wrote another answer that delves a bit more into the differences between, e.g., function Foo() {} and var Foo = function() {}.
Second, you probably want to rename Map to something else. Map is a core part of ES6 now (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map).
Third, even though you can create your generate_map function using this.generate_map, you may want to move it to the Map's prototype. Also, since you need to expose the grid value, you want to store it as a property, rather than a local variable scoped to the NewMapName constructor. E.g.,:
function NewMapName() {
this.grid = [];
}
NewMapName.prototype.generateMap = function(radius) {
// you can access the grid here via `this.grid`
...
};
By moving it to the prototype, that means all instances of NewMapName will share the same function reference, rather than it being created over-and-over-and-over (although maybe you really only create it once? Either way, it's more idiomatic, at a minimum). Note that I took some liberties with the "camelCasing" here (see the last point).
Fourth, your generateMap implementation is leaking some global variables (width and r, since you don't declare them with var). I would change that to this:
NewMapName.prototype.generateMap = function(radius) {
for (var width = -radius; width <= radius; width++) {
var r1 = Math.max(-radius, -width - radius);
var r2 = Math.min(radius, -width + radius);
for (var r = r1; r <= r2; r++) {
grid.push(new Tile(width, r, -width - r));
}
}
};
Fifth, your loop is kind of broken. I would refactor that as follows:
var main = function () {
var basicMap = new NewMapName();
basicMap.generateMap(3);
basicMap.grid.forEach(function(tile) {
console.log(tile.x, tile.y, tile.z);
});
};
main();
Lastly, and probably most minor, is that in JavaScript-land, camelCase is far more dominant that snake_case, so generate_map might be "better" as generateMap.
http://jsfiddle.net/goldrunt/SeAGU/52/
Line 49 checks for "false" on isOnCircle function before creating the new object. Function is on line 32. When creating more object, the function is passing when it should not pass.
if (isOnCanvas(location) && !isOnCircle(location)) {
console.log(location, isOnCanvas(location), isOnCircle(location));
create(location);
In fact I can't get the collision detection to register true no matter what values are passed to it
(Math.pow((a.x - i.x), 2) + Math.pow((a.y - i.y), 2) <= Math.pow((a.radius + i.radius), 2))
here I've fixed and given more descriptive variable names so you can see what's going on.
EDIT: I've noticed you don't always feed a circle but sometimes a point as A, which does not have a .radius property resulting in NaN, which also screws up your comparison.
function circleTest(a,b) {
var DistanceX = a.x - b.x;
var DistanceY = a.y - b.y;
var DistanceCenter = Math.sqrt(DistanceX * DistanceX + DistanceY * DistanceY);
var CollisionDistance = b.radius;
if (a.radius) CollisionDistance += a.radius
return DistanceCenter <= CollisionDistance;
}
I also noticed a problem in your function called "isOnCircle" where you are using i (a number) as if it were a circle object, with the above function this can be fixed like:
function isOnCircle(a) {
for (var i = 0; i < circles.length; i++) {
if (circleTest(a, circles[i])) return true;
}
return false;
}
Two problems:
i is the numerical index you are using to iterate through the circles array but you are using it as if it was a circle object; you need to use circles[i] to get the circle at each iteration.
a is a point and does not have a radius (in the code below I've left a.radius in just in-case you pass in a circle rather than a point and have ORed it with 0 so you get a valid number).
Defining some additional variables (for clarity) then you can replace the isOnCircle function with this:
function isOnCircle(a) {
var i=0,l=circles.length,x,y,d,c;
for (; i < l; ++i) {
c = circles[i];
x = a.x-c.x;
y = a.y-c.y;
d = (a.radius||0)+c.radius;
if (x*x+y*y <= d*d) {
return true;
}
}
return false;
}