How do I properly declare an array of PVectors inside a Class?

edited November 2014 in How To...

Hi there.

I'm trying to use PVectors to store coordinates that will be input in a class constructor, and getting NullPointerExceptions. As in below:

Coords[] crds = new Coords[20]
int CleanArray[] = {0, 0, 0, 0, 0, 0, 0, 0};   // Meant for testing

void setup() {
crds[0] = new Coords(CleanArray);


}

class Coords {

  PVector[] q = new PVector[8];
  int j = 0;

  Coords(int[] coords) {

    int lim = coords.length;

    for (int i = 0; i < lim; i= i++) {
      q[i].set(coords[j], coords[j+1]);   // This is the line that gets a NullPointerException.
      j+=2;
    }
  }
}

I might be misusing PVectors and Class.

What I am trying to do:

1) Whenever a Coords object is created, it receives an array as a parameter.

2) The array gets distributed to PVectors via set() -- so q[0].set(coords[j], coords[j+1]) would set q[0].x to coords[0] and q[0].y to coords[1], and subsequently.

Could anyone shed a light?

If there's a better way to distribute the array values to use later, I'm all ears.

Thanks!

Answers

  • edited November 2014 Answer ✓

    Hmm... How about this 1? ~O)

    // forum.processing.org/two/discussion/7942/
    // how-do-i-properly-declare-an-array-of-pvectors-inside-a-class
    
    static final int QTY = 20;
    Coords[] crds = new Coords[QTY];
    
    void setup() {
      int[] test = {
        1, 2, -1, -2, 4, 3, 100, 50
      };
    
      crds[0] = new Coords(test);
      println(crds[0].q);
    
      crds[1] = new Coords(15, 20, -9, 7, 0400, 0x100, MAX_INT);
      println(crds[1].q);
    
      exit();
    }
    
    static final class Coords {
      static final int QTY = 8;
      final PVector[] q = new PVector[QTY];
    
      Coords(int... coords) {
        int len = min(coords.length>>1<<1, QTY);
        for (int i = 0; i < len; i += 2)
          q[i>>1] = new PVector(coords[i], coords[i+1]);
      }
    }
    
  • Whoa

    Nice working answer. Lots of new things to learn! I liked the "..." parameter and the bit shifting on the q[i>>1].

    I didn't undestand the shifting back and forth in line 26 though. Could you ellaborate? I understand that len is meant to not let the "for" overflow (it checks if coords.length is over QTY, and if it does, its value becomes QTY). But not the >>1<<1 part.

    Would the "final" before PVector in line 22 prevent me from changing crds[1].q contents?

    Thank you for your time! (I'm new here, would accepting the answer lock the discussion?)

  • accepting the answer is a bad idea...

    question looks solved then

  • Answer ✓

    PVector[] q = new PVector[8];

    This creates an array that can hold 8 PVectors, it does not create 8 PVectors so in line 20 when you try to set the values of a PVector in the array there is none - hence NPE

    In GoToLoop(s) code

    coords.length>>1<<1

    finds the largest number <= coords.length that is a multiple of 2

    Would the "final" before PVector in line 22 prevent me from changing crds[1].q contents?

    No - you can change the PVectors in the array but it would prevent you changing the array, i.e.

    q = new PVector[50]; // is not OK
    q[0] = new PVector(); // is OK
    q[0].set(0,0); // is OK if element 0 already has a PVector
    
  • edited November 2014

    @quark's already answered @XKuei. Nonetheless, gonna append my own complementary explanations too: :-@

    But not the >>1<<1 part.

    >>1 <<1 is analogous to /2 *2. In Java and many C-derived languages, except JS, if both operands are of whole type, their division's result cuts any fractional part off. That's why 3/4 yields 0 rather than .75! @-)

    In short, it's the quickest way I've found to force an even # of elements, so they're paired up for PVector. :-B
    Otherwise, that odd pairless MAX_INT argument in line #15 would throw an NPE too! 8-X

    But why >>1 instead of regular /2 you might ask? Well, >> is a bit tiny faster than /! \m/
    Another reason is >>1 works correctly in JS, while /2 would bug there b/c there's no whole division in JS!
    So right behavior is kept whether Java or JS modes are used! =P~

  • edited November 2014

    Would the final before PVector in line 22 prevent me from changing crds[1].q contents?

    Absolutely not! Keyword final only affects the variable itself, locking up its value.
    It doesn't affect any object's members it may reference to: https://processing.org/reference/final.html

    In final PVector[] q = new PVector[QTY];, field q is "forever" bound to the newly created array's reference.
    Therefore, q can't be reassigned to another array reference. Not even null is possible.
    Actually, it can't even be assigned to itself: q = q! :))

  • edited November 2014

    Another very common doubt is the fact that new PVector[QTY]; only instantiates the [] array itself.
    All of their indexed "slots" or elements are still null. Or 0, 0.0 or false, depending on array's type.
    That's why we still need to instantiate PVector objects and assign them to the array's "slots".
    Once those "slots" are properly initialized, we can use set() method instead to change PVector's values. :-bd

  • edited November 2014

    I'm new here, would accepting the answer lock the discussion?

    Nope! Actually most of us answerers still check on "answered" threads no matter what! #:-S

Sign In or Register to comment.