maxkueng / victor

A JavaScript 2D vector class with methods for common vector operations
http://victorjs.org/
MIT License
830 stars 108 forks source link

Immutable version #18

Open neojski opened 9 years ago

neojski commented 9 years ago

It was very annoying for me that methods like add or divide modified the vector. My temporary solution is my immutable-vector2d library. Do you have any ideas as how to merge my project into this one?

maxkueng commented 9 years ago

Hi. Yes, the vectors are mutable on purpose. The idea behind it is that,.. let's say you have a space ship that moves. So the ship has a position vector and a and a velocity vector. To move the ship you add the velocity vector to the position vector:

var ship = {
  position: new Victor(100, 200),
  velocity: new Victor(1, 0),
  move: function () {
    this.position.add(this.velocity);
  }
};

setInterval(function () {
  ship.move();
  draw();
}, 1000 / 16);

instead of doing:

// let's assume Victor is immutable
var ship = {
  ...
  move: function () {
    this.position = this.position.add(this.velocity);
  }
};

...

Do you think this is a bad idea?

You can use the .clone() method if you want to create a new vector based on another.

var ship = {
  ...
  move: function () {
    // add only half the velocity without modifying the velocity
    this.position.add(this.velocity.clone().divide(new Victor(2, 2)));
  }
};

...

What are your thoughts?

neojski commented 9 years ago

Sure, I'm not saying mutability is inherently bad. My scenario where I prefer things to be immutable is more math style:

var a = Vector(23, 12);
var b = Vector(25, 13);
var c = a.normalize().add(b.normalize());

to calculate angle bisector. With mutable stuff I need

var a = Vector(23, 12);
var b = Vector(25, 13);
var c = a.clone().normalize().add(b.clone().normalize());

With more calculations it's clone everywhere. Most likely we cannot do anything about that except for introducing API for both mutable and immutable operations. In that case, I think, I prefer them to be separate libraries.

ahvonenj commented 9 years ago

I would also prefer if the methods such as add or divide would not modify the vector. I think they should be immutable by default as every library with vector that I have ever used behaves like that.

yukulele commented 9 years ago

I made a Immutable vector library https://github.com/yukulele/Vector.js

huiwang commented 8 years ago

i agree to have an immutable version. this just makes things much more simpler to reason about.

this is extremely useful when you want to simulate something. immutability allows to travel back to the time.

philj0st commented 8 years ago

+1 for immutability feature. Will be awesome now that functional Javascript is becoming more and more popular. see Redux and co. there are also a lot of games using functional programming now .. and i think games are definitly one of your library's core audiences :smile:

chinoto commented 6 years ago

Shouldn't this be closed? Victor has been mutable for years, so changing it would break functionality for existing users. Besides, @neojski, @yukulele, and @graue have filled the need for immutable vectors.

YarekTyshchenko commented 3 years ago

Gah, just been bitten by this. I was really surprised that operation are mutable. Should clearly mention this on the website

rmkane commented 1 year ago

They are mutable, because of the ability to chain.

It would be nice to state that the library is not immutable on the README or the docs; up-front.

If you check out the docs e.g. the add method, it states that the vector is added in-place:

.add(vector) [chainable]

Adds another vector to itself.

My opinion

I will side with the author and say that this:

this.position.add(this.velocity);

Is easier to read than:

this.position = this.position.clone().add(this.velocity); // or
this.position = Victor.fromObject(this.position).add(this.velocity);
chinoto commented 1 year ago

The ability to chain has nothing to do with mutability, it's just a matter of whether the methods return this (or a new object in the case of an immutable version). If the methods returned nothing, then they would have to mutate this, or do a weird C-like thing like this:

let a = new Victor(1, 2);
let b = new Victor(3, 4);
let ret = new Victor(0, 0);
a.add(b, ret);
// ret has been modified by the add method

Though I think in C, the ret value is generally uninitialized, which we can't do in JavaScript due to the lack of pointers, but objects are shared by reference, so ret could be initialized as {value: null} and then a method can set value to whatever it wants.

Totally agree with your opinion examples. Would be nice if JavaScript supported operator overloading, then you could do this.position += this.velocity.