Changing an Object's attributes also Affects Another Object - javascript

Problem: When I change the points of a polygon poly2, it also changes the points of another polygon poly!!
Why does changing one also change the other, and how do we decouple them?
console.log(poly.getPoints()[1].x); // 100
// Make a change to `poly2`
poly2.setPoints(poly.getPoints());
poly2.getPoints()[1].x=200
console.log(poly.getPoints()[1].x); // 200 (both poly and poly2 are affected!)
jsfiddle: http://jsfiddle.net/8hFyv/

poly2.setPoints(poly.getPoints());
This is your problem. The points array is the very same object.
Since you have arrays in your array, the slice(0) trick won't work, you need deep copy.
Fortunately, you're using jQuery, which has a method to do it.
Replace the above line with:
poly2.setPoints($.extend(true, [], poly.getPoints()));

Your poly and poly2 object are referencing the same array of points when you do this:
poly2.setPoints(poly.getPoints());
Change it to this:
poly2.setPoints([0, 0, 100, 0, 100, 100, 0, 100]);

To clone the points, vs. sharing them between polygons, you'll need to create new objects for each yourself.
You can do this with map:
poly2.setPoints(poly.getPoints().map(function (p) {
return { x: p.x, y: p.y };
}));
Or, with jQuery.map:
poly2.setPoints($.map(poly.getPoints(), function (p) {
return { x: p.x, y: p.y };
}));

The other answers are correct in assessing the problem, but there's another way you can solve it: "clone" the points array when you set it. In other words:
poly2.setPoints(poly.getPoints().slice());
If for some reason getPoints() returns something other than an array, you'll need a different cloning approach (eg. the one that axel.michel suggested), but since I think it does that should work for you.

The problem is, that poly.getPoints is a set of Kinetic Pointer Objects, to get rid of it, try the following:
poly2.setPoints(JSON.parse(JSON.stringify(poly.getPoints())));

Related

Keep a flat array as a reference copy to a 2D or 3D array

I have an array of point objects with x and y parameters such as :
this.points = [p1, p2, p3,..];
Which I flatten as a 1D array like so :
this.coords = [];
for(let p of this.points){
this.coords.push(p.x);
this.coords.push(p.y);
}
The coords array is now :
this.coords = [p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, ...]
My question is the following, I would like the modifications of either one of the arrays affect the other. Because right now when I make a modification to either this.coords or this.points, the other one isn't updated.
Is there a way of doing this, or am I thinking about this wrong and there is a general guideline to coding coordinates that I do not know of yes ?
Thanks
JavaScript has two kinds of values: reference values and primitive values.
Primitive values, like numbers, are copied and editing the copy never influences the original and the other way around.
Because your x and y coordinates are probably primitive numerical values, this is not possible.
You could encapsulate them in reference values, for example using arrays with 1 element.
const points = [{x: [1], y: [2]}, {x: [3], y:[4]}];
const flat = [points[0].x,points[0].y,points[1].x,points[1].y];
flat[0][0]+=9;
console.log(points[0].x)
But the question is why you want to do that in the first place.
If that is some kind of extreme cache optimization, like making your own commercial game engine, any kind of method to synchronize the two data structures will probably more than neutralize that optimization benefit.

Name Firebase child as sequence array

I want to use Fusionchart to read my Firebase data and create a chart in my web app. but my Firebase DB has a wrong structure, so the Fusionchart can't get data (my Firebase config is right).
Following is the code that I write data to Firebase, num is a value increased in each loop. But as shown in the attached picture, the child's name is not added as a sequence number.
Another question is I don't want the unique key inside the child 1, just six various inside the child one is ok.
Any suggestions will be appreciated.
firebase.database().ref('testdata/User1').child(num).push({
x: posX,
y: posY,
MaxSpeed: maxSpeed,
steps: counter,
time: Timeperiod /1000,
speed: SpeedRecord,
});
If you don't want the push ID inside your pseudo-numeric keys, call set instead of push.
So:
firebase.database().ref('testdata/User1').child(num).set({
x: posX,
y: posY,
MaxSpeed: maxSpeed,
steps: counter,
time: Timeperiod /1000,
speed: SpeedRecord,
});
Your other problems seems (it's impossible to be certain, since you didn't include the code for the increment) to come from the fact that num is a string. If that is indeed the case, increment it with:
num = String(parseInt(num) + 1);
Using such numeric keys is an antipattern in Firebase though, so I'd usually recommend against using them. If you must, at least pad them til a certain length, so that you can sort/filter on them easily.
Something as simple as:
num = String(parseInt(num) + 1).padLeft(5, "0");
Will work on all modern browsers, and ensures that all keys-that-look-like-numbers-but-behave-like-strings will show up in the order you expect.

Interdependent derived attributes in Ampersand?

I'd like to create Ampersand State representation of a vector which simultaneously holds information about it's polar and rectangular representation.
i.e. I would like the user to be able to do:
vector.angle = 90
vector.mag = 1
console.log vector.y #=> 1
-or-
vector.x = 0
vector.y = 1
console.log vector.angle #=> 90
Can anyone think of a way to do this with ampersand?
This is the old question, but someone might need this.
Right away I can think of one way to do this. You'd need to make all of your variables independent and then listen to changes to update other values. So you'd define in props of the model variables angle, mag, x, y, and then attach event listeners in initialize of your view or somewhere else to each of these variables. For example, for angle you'd do something like this:
model.on('change:angle', function(model) {
//first, calculate new x and y values. In your model you have a new angle value
if (NEW_CALCULATED_X_VALUE != model.x || NEW_CALCULATED_Y_VALUE != model.y) {
model.set({
x: NEW_CALCULATED_X_VALUE
}, {
silent: true//I'm not triggering the 'change' event yet to avoid circular dependencies.
});
model.set({
y: NEW_CALCULATED_Y_VALUE
}, {
silent: true
});
//now, once I've set all new values, I can trigger events.
model.trigger('change:x', model); //this will trigger model.on('change:x'), but since based on this change angle and mag won't change, no circular dependency will appear.
model.trigger('change:y', model);
}
})
and repeat this for each of four variables (there is room for optimisation, but you get my idea. In order to avoid circular dependencies with this example you need to make sure that once you recalculate, for example, angle by using whatever x, you get the same angle.

Javascript array of canvas objects? Scope issue? ocanvas.js

I'm new to javascript and canvas, but not entirely new to programming (I have some HTML/PHP experience).
I've always had an interest in web games that use grids, so after taking a few javascript courses online, I found some javascript code that dynamically created hexagonal grids of canvas objects.
I've been playing around with the code, adding click events to highlight the hex you're on, trying to put random numbers on the hexes to depict numbers of "units" etc. Sort of mocking up what a game might look like, just for the learning experience.
One thing that I've been trying to do, and I can't seem to figure out, is how to define canvas objects in an array, so that I can reference them by row/column. It seems like it would be much easier, in the context of a game, to reference a grid cell by row/column so that you can do actions based on that location.
I'm using ocanvas.js to create canvas objects, so ideally I'd love to be able to define a grid cell as:
hex[row][col] = canvas.display.polygon({
x: x0,
y: y0,
sides: 6,
radius: this.radius,
rotation: 0,
fill: fillColor,
stroke: "outside 1px #000",
row: row,
col: col,
zIndex: "back",
selected: false
});
hex[row][col].add();
However, I've noticed that mutlidimensional arrays don't work in javascript like they do in PHP. In PHP, you don't have to predefine the scope of an array. You can just do:
$array = [];
$array[1][5] = "foo";
And it'll put that value in the [1][5] position. In my hex code here: http://tny.cz/14282e49 about 3/4 down I have a comment showing that I want to call my hex[col][row] object... but I'm always getting "Uncaught TypeError: Cannot read property '5' of undefined" errors, where '5' is the column its trying to access.
So, this is a 2 part question:
1) Am I not defining my hex array correctly to access the canvas objects like that?
2) Is there a better way that I'm just not seeing, to access specific canvas objects in a column/row grid format?
Part I:
Before assigning a value to hex[row][col], you need to make sure that hex[row] exists.
hex[row] = hex[row] || [];
hex[row][col] = canvas.display.polygon({
x: x0,
y: y0,
sides: 6,
radius: this.radius,
rotation: 0,
fill: fillColor,
stroke: "outside 1px #000",
row: row,
col: col,
zIndex: "back",
selected: false
});
hex[row][col].add();
The line hex[row] = hex[row] || []; says "if hex[row] exists, use it. otherwise create a new array. Assign the result to hex[row]."
The || "or" operator is used as a "default" here. See this page as a reference: http://seanmonstar.com/post/707078771/guard-and-default-operators
Part II:
http://www.redblobgames.com/grids/hexagons/#coordinates
I recommend using the "axial coordinate" system used at that resource. Read the entire thing. It's a long post and is way too much information for a SO answer here.

How to use a Matrix with RaphaelJS

I would like to move a RaphaelElement and created a simple test case:
var paper = Raphael(0, 0, 500, 500);
var rect = paper.rect(50, 50, 100, 100);
rect.attr("fill", "blue");
rect.matrix.translate(300, 300);
But the rectangle isn't moved. At first I thought that for the matrix was probably not updated correctly and tried this:
//...
alert("" + rect.matrix.x(0,0)); // prints 0
rect.matrix.translate(300, 300);
alert("" + rect.matrix.x(0,0)); //prints 300
Obviously the matrix is changed but the rectangle does not care about that. Therefore I changed my code to:
rect.matrix=rect.matrix.translate(300,300);
But that either crashes the program or has no effect at all.
It seems like I'm missing some sort of update method, to apply a matrix to a RaphaelElement. Something that looks like this:
rect.updateMatrix();
I've searched in the documentation but didn't find such a method. What is the canonical usage of matrices in RaphaelJs?
rect.translate(300, 300) will apply a translation or alternatively rect.transform("t300,300")
Also
rect.transform(['m',mat.a, mat.b, mat.c, mat.d, mat.e, mat.f]);
can be used to apply an existing matrix (mat) to the rect.
I found another reference on Stackoverflow that does an awesome job explaining the matrix. It solved my problem.
StackOverflow Matrix clarification

Categories