Skip to content

[2.0] Stabilize the set() method of p5.Vector #8189

@GregStanton

Description

@GregStanton

[2.0] Stabilize the set() method of p5.Vector

The set() method of p5.Vector behaves confusingly in the context of p5.js 2.x, and this behavior is the result of undocumented breaking changes. General stabilization to align with the rest of 2.x is likely warranted.

Confusing 1.x behavior

The 1.x version of set() actively modifies components that are left unspecified, in contrast with how methods like add() and mult() behave under similar circumstances.

function setup() {
  let v = createVector(1.01, 1.01, 5);
  v.set(1, 1);

  // Plausible output: `p5.Vector Object : [1, 1, 5]`
  // Actual output: `p5.Vector Object : [1, 1, 0]`
  console.log(v.toString());
}

Confusing 2.x behavior

The 2.x version is still documented as maintaining the 1.x behavior, although it does not behave that way. Instead, it mutates the dimension of the vector whenever dimensions do not match. This will often be an unintended side effect, since the name set suggests the method sets existing properties (including x, y, and z), rather than eliminating or defining new properties.

function setup() {
  let v = createVector(1.01, 1.01, 5);
  v.set(1, 1);

  // Plausible output: `[1, 1, 5]`
  // Actual output: `[1, 1]`
  console.log(v.toString());
}

The unintended side effect above is likely to occur, since users accustomed to the behavior of add() in 1.x may assume the unspecified components will be unchanged.

In addition to accidentally cropping the vector in place, it's also possible to accidentally promote the vector to a higher dimension.

function setup() {
  let v = createVector(1.01, 1.01);
  let w = createVector(1, 1, 0);
  v.set(w);

  // Plausible output: `[1, 1]`
  // Actual output: `[1, 1, 0]`
  console.log(v.toString());
}

The unintended side effect above is also reasonably likely, as users may expect only existing components to be set.

Proposed solution: Standard broadcasting rules

A simple, useful, and extensible approach is to have set() adhere to the standard broadcasting rules proposed in #8159 for other elementwise operations (set() may be viewed as an elementwise assignment operation). This will work equally well for vectors, matrices, and tensors.1

function setup() {
  let v = createVector(1.01, 1.01, 5);
  v.set(1, 1);

  /* Error: A 3D vector can only be set in two possible ways:
  (a) with exactly 3 elements (as a list of numbers, an array, or a `p5.Vector` object)
  (b) with a single element (its value will be used for all 3 components). 
  To fix this error, extra elements may be passed directly to `set()`, 
  or if a `p5.Vector` object is passed, it may be extended with `pad()`.
  */
}
function setup() {
  let v = createVector(1.01, 1.01);
  v.set(1, 1, 0);

  /* Error: A 2D vector can only be set in two possible ways:
  (a) with exactly 2 elements (as a list of numbers, an array, or a `p5.Vector` object)
  (b) with a single element (its value will be used for all 2 components). 
  To fix this error, fewer elements may be passed directly to `set()`, 
  or if a `p5.Vector` object is passed, it may be cropped with `crop()`.
  */
}

The examples above prevent unintended side effects, in favor of more useful methods that unambiguously mutate dimension, such as crop() and pad(), which have already been proposed as a convenient "repair toolkit" to help users fix errors.

Standard broadcasting also facilitates a new, very common use case:

function setup() {
  let v = createVector(0, 0, 0, 0, 0);
  v.set(1);

  // Output: `[1, 1, 1, 1, 1]`
  console.log(v.toString());
}

Next steps (blocking issues)

  • Reach a consensus on the general broadcasting policy proposed in #8159.
  • Reach consensus on the API proposed in #8152, as it includes the set() method.
  • Reach a consensus on applying the broadcasting policy to set(), as proposed here.

The error messages indicated above also reference pad() and crop() methods, which do not exist yet. However, that doesn't necessarily need to block progress on set(); a separate issue could be opened to update the error messages at a later time.

Edit: Added "Next steps" section.

Footnotes

  1. This approach is also similar to the set() method of p5.Image, which can set a single pixel or the whole image. However, the single pixel is not broadcast. In a future 3.0 release, we could potentially create a more intuitive API by providing e.g. setPixel() for the one-pixel case to match the setElement() method of vectors, matrices, tensors, series, and sheets. (Here, setElement() is a proposed API for setting an individual element.) The set() method of p5.Image could then support broadcasting like the other set() methods.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions