-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Regarding the p5.Vector functions random2D() and random3D() using Math.random() #6141
Comments
I'm not sure that it's possible to do this on the static vector methods, since the class is shared across potentially many p5 instances if used in instance mode. For non-static methods, I think to do this, the Lines 38 to 44 in 3b0d132
If we instead just pass the p5 instance, maybe storing it internally as _random() {
if (this._pInst) {
return this._pInst.random();
} else {
return Math.random();
}
} ...and then use |
We can't use p5.js functions inside static functions... ok. I tried making a patch. Should I pass the instance like this and get _toRadians() and _fromRadians() from it? p5.prototype.createVector = function(x, y, z) {
if (this instanceof p5) {
return new p5.Vector(
this,
...arguments
);
} else {
return new p5.Vector(x, y, z);
}
};
p5.Vector = function Vector() {
let x, y, z;
// This is how it comes in with createVector()
// This check if the first argument is a function
if (arguments[0] instanceof p5) {
// In this case the vector have an associated p5 instance
this.isPInst = true;
this._pInst = arguments[0];
this._fromRadians = this._pInst._fromRadians.bind(this._pInst);
this._toRadians = this._pInst._toRadians.bind(this._pInst);
x = arguments[1] || 0;
y = arguments[2] || 0;
z = arguments[3] || 0;
// This is what we'll get with new p5.Vector()
} else {
x = arguments[0] || 0;
y = arguments[1] || 0;
z = arguments[2] || 0;
}
/**
* The x component of the vector
* @property x {Number}
*/
this.x = x;
/**
* The y component of the vector
* @property y {Number}
*/
this.y = y;
/**
* The z component of the vector
* @property z {Number}
*/
this.z = z;
};
function setup() {
createCanvas(400, 400);
v=createVector(1,1,1);
console.log(v._pInst);
angleMode(DEGREES);
console.log(v._fromRadians(1));
console.log(v._toRadians(1));
} output:
And _random(), setRandom2D(), setRandom3D() are prepared like this. _random() {
if (this._pInst) {
return this._pInst.random();
} else {
return Math.random();
}
}
setRandom2D () {
this.set(this.fromAngle(this._random() * constants.TWO_PI));
return this;
}
setRandom3D () {
const angle = this._random() * constants.TWO_PI;
this.z = this._random() * 2 - 1;
const vzBase = Math.sqrt(1 - this.z * this.z);
this.x = vzBase * Math.cos(angle);
this.y = vzBase * Math.sin(angle);
return this;
} How about something like this...? |
I think that looks good to me! @limzykenneth does this also make sense to you? |
I'm sorry, random2D is better this way... (since it can't call fromAngle()) setRandom2D () {
const angle = this._random() * constants.TWO_PI;
this.x = Math.cos(angle);
this.y = Math.sin(angle);
this.z = 0;
return this;
} |
This one is better...sorry. p5.Vector = class {
// This is how it comes in with createVector()
// This check if the first argument is p5 instance
constructor(...args) {
let x, y, z;
if (args[0] instanceof p5) {
this.isPInst = true;
this._pInst = args[0];
this._fromRadians = this._pInst._fromRadians.bind(this._pInst);
this._toRadians = this._pInst._toRadians.bind(this._pInst);
x = args[1] || 0;
y = args[2] || 0;
z = args[3] || 0;
// This is what we'll get with new p5.Vector()
} else {
x = args[0] || 0;
y = args[1] || 0;
z = args[2] || 0;
}
/**
* The x component of the vector
* @property x {Number}
*/
this.x = x;
/**
* The y component of the vector
* @property y {Number}
*/
this.y = y;
/**
* The z component of the vector
* @property z {Number}
*/
this.z = z;
}
} |
It's a small detail, but I thought it would be more consistent to use this.isPInst for the internal determination of _random(). _random() {
if (this.isPInst) {
return this._pInst.random();
} else {
return Math.random();
}
} |
This has been done before for To give a bit of context to So in this case, my recommendation is to deal with it in the same way as |
There must have been a good reason why you couldn't pass the p5 instance directly... Thanks for letting me know. I thought of an implementation like this. First pass random() directly. p5.prototype.createVector = function(x, y, z) {
if (this instanceof p5) {
return new p5.Vector(
this._fromRadians.bind(this),
this._toRadians.bind(this),
this.random.bind(this), // random function.
...arguments
);
} else {
return new p5.Vector(x, y, z);
}
};
p5.Vector = class {
// This is how it comes in with createVector()
// This check if the first argument is a function
constructor(...args) {
let x, y, z;
if (typeof args[0] === 'function') {
this.isPInst = true;
this._fromRadians = args[0];
this._toRadians = args[1];
this._random = args[2]; // random function.
x = args[3] || 0;
y = args[4] || 0;
z = args[5] || 0;
// This is what we'll get with new p5.Vector()
} else {
x = args[0] || 0;
y = args[1] || 0;
z = args[2] || 0;
}
/**
* The x component of the vector
* @property x {Number}
*/
this.x = x;
/**
* The y component of the vector
* @property y {Number}
*/
this.y = y;
/**
* The z component of the vector
* @property z {Number}
*/
this.z = z;
}
} And use this if this.isPInst is true. // setRandom2D
p5.Vector.prototype.setRandom2D = function(){
const rdmForAngle = (this.isPInst ? this._random() : Math.random());
const angle = rdmForAngle * TWO_PI;
this.x = Math.cos(angle);
this.y = Math.sin(angle);
this.z = 0;
return this;
}
// setRandom3D
p5.Vector.prototype.setRandom3D = function(){
const rdmForAngle = (this.isPInst ? this._random() : Math.random());
const rdmForZ = (this.isPInst ? this._random() : Math.random());
const angle = rdmForAngle * TWO_PI;
this.z = rdmForZ * 2 - 1;
const vzBase = Math.sqrt(1 - this.z * this.z);
this.x = vzBase * Math.cos(angle);
this.y = vzBase * Math.sin(angle);
return this;
} How about this..? |
And I have to fix the copy() and cross() as well... copy () {
if (this.isPInst) {
return new p5.Vector(
this._fromRadians,
this._toRadians,
this._random,
this.x,
this.y,
this.z
);
} else {
return new p5.Vector(this.x, this.y, this.z);
}
}
cross(v) {
const x = this.y * v.z - this.z * v.y;
const y = this.z * v.x - this.x * v.z;
const z = this.x * v.y - this.y * v.x;
if (this.isPInst) {
return new p5.Vector(this._fromRadians, this._toRadians, this._random, x, y, z);
} else {
return new p5.Vector(x, y, z);
}
} |
It's a small detail, but I feel like cross() should be written like this... cross(v) {
const x = this.y * v.z - this.z * v.y;
const y = this.z * v.x - this.x * v.z;
const z = this.x * v.y - this.y * v.x;
const result = this.copy();
result.set(x, y, z);
return result;
} I think it's better to do this so that we only have to change copy() when we have to increase the number of arguments. That's what I thought, but add(), sub(), set() use the same logic, but if there is such a function, I feel like I can organize the processing. function getValues(x, y, z){
const values = {};
if (x instanceof p5.Vector) {
values.x = x.x || 0;
values.y = x.y || 0;
values.z = x.z || 0;
} else if (Array.isArray(x)) {
values.x = x[0] || 0;
values.y = x[1] || 0;
values.z = x[2] || 0;
} else {
values.x = x || 0;
values.y = y || 0;
values.z = z || 0;
}
return values;
} Using this, for example set() can be written as: set (x, y, z) {
const values = getValues(x, y, z);
this.x = values.x;
this.y = values.y;
this.z = values.z;
return this;
} And cross() can also be applied like cross(1, 1, 1) using this. |
Alternatively, if you define it like this, you may be able to write mult() and div() more conveniently (in that case, set defaultValue to 1). function getValidatedValue(value, defaultValue){
if (value === undefined) {
return defaultValue;
} else if (Number.isFinite(value) && typeof value === 'number') {
return value;
} else {
console.warn(
'The value must be either undefined or a defined finite number.'
);
}
return defaultValue;
}
function getValues(x, y, z, defaultValue = 0){
const values = {};
if (x instanceof p5.Vector) {
values.x = getValidatedValue(x.x, defaultValue);
values.y = getValidatedValue(x.y, defaultValue);
values.z = getValidatedValue(x.z, defaultValue);
} else if (Array.isArray(x)) {
values.x = getValidatedValue(x[0], defaultValue);
values.y = getValidatedValue(x[1], defaultValue);
values.z = getValidatedValue(x[2], defaultValue);
} else {
values.x = getValidatedValue(x, defaultValue);
values.y = getValidatedValue(y, defaultValue);
values.z = getValidatedValue(z, defaultValue);
}
return values;
} In the case of mult() and div(), unlike add() etc., if there is one argument, the remaining two values are set to that value by default, but it can be handled by simply dividing it. It feels much easier than writing similar code for each of the three cases. |
@inaridarkfox4231 I think you can go ahead with filing a PR and we can work out the implementation details in the PR (much easier to review changes/suggestions in a PR as well 😉). Any implementation details you are not sure to include or not you can mention in the PR post or comments itself. Thanks. |
Thank you for your reply! I would like to create a pull request. I'll put the rewrite idea on hold and just implement the function. |
Sorry, I can't maintain my motivation, so I would like to close this issue. |
@inaridarkfox4231 That is totally fine, we really appreaciate any effort you put in to contributing to p5.js in any way. Do work at your own pace. I would like to keep this issue open for now as I think it would be good to have and someone else can pick up the implementation. If you like you can unsubscribe from this issue with the button on the sidebar to the right so you don't receive notifications for it. |
I got it. thank you very much. |
Thank you all for working on this issue. I am inviting the current Math stewards to this discussion @limzykenneth, @ericnlchen, @ChihYungChang, @bsubbaraman, @albertomancia, @JazerUCSB, @tedkmburu, @perminder-17, @Obi-Engine10, @jeanetteandrews. Would love to hear what y'all think. Thanks! |
Topic
The p5.Vector functions random2D() and random3D() now use Math.random() to get random numbers.
random3D
But for example, the p5.js functions shuffle() and randomGaussian() use p5.js random().
shuffle
The difference between random() and Math.random() is whether they are affected by randomSeed(). With random(), you can get the same set of values if you use the same seed value.
For example, with the following sketch, if you get random3D() values every frame, Math.random() won't work because the values will be different each time.
(latest version)
In the following video, the modified random3D() is used in the second half. The same vector can be obtained because it is affected by randomSeed().
2023-05-16.18-47-26.mp4
There is a way to prefetch and use the same set of values, but it is more convenient to have it affected by randomSeed(). I think there is no problem because both methods can be used.
However, I am not very familiar with how to rewrite it, so I would appreciate it if someone could tell me...
How should I rewrite Math.random()? random()? p5.random()? p5.prototype.random()? something else...
The text was updated successfully, but these errors were encountered: