How can I replace PVector toString()

edited January 2018 in How To...

... so that I can set the output format of println(myPVector)?

Answers

  • edited January 2018

    I wouldn't. Instead, I would just write your own function to print the values in a PVector - however you like. Example:

    PVector pv = new PVector( 10, 20, 30 );
    
    void setup(){
      size(440,440);
      println( pprintPVector( pv ) );
    }
    
    void draw(){
      background(0);
    }
    
    String pprintPVector( PVector p ){
      return ( "BEHOLD! A GLORIOUS PVECTOR DOTH APPEAR: [[ " + p.x + " || " + p.y + " || " + p.z + " ]]" );
    }
    
  • But if you would? :)

  • I would create a class that extends from PVector and override that function.

    Kf

  • That would require you to replace all references to PVector with myPVector.

  • It would, yes.

    It's about now that we ask you why you want to do this...

  • This reminds me of a story:

    "I really wanna change the behavior of a class method without changing the class, or encapsulating it, or extending it with inheritance" said a group of people that included every black hat hacker in history.

    The Java language checked its fundamental security model. "Nope" said Java. "You can never, ever, ever, ever do that. Ever."

    The End

  • edited January 2018

    Although not very recommended for this particular case, it is nevertheless very doable. ;;)

    However, the @Override toString() will kick in only for those PVector objects instantiated by the "hacked" class. L-)

    We can't force 3rd-party code to use our modified class in place of the original 1. [-X

    That's why some utility function which accepts a PVector instance is more guaranteed to work for all cases than having some derived class which attempts to change the original methods' behavior. :-B

    /**
     * PVector Subclass Example (v1.0.1)
     * GoToLoop (2018/Jan/16)
     *
     * https://Forum.Processing.org/two/discussion/25986/
     * how-can-i-replace-pvector-tostring#Item_7
     */
    
    void setup() {
      final PVector orig = PVector.random3D(this).mult(100);
      println(orig);
    
      final PVector hack = new MyVec().set(orig);
      println(hack);
    
      exit();
    }
    
    public static class MyVec extends PVector {
      public MyVec() {
        super();
      }
    
      public MyVec(final float x, final float y) {
        super(x, y);
      }
    
      public MyVec(final float x, final float y, final float z) {
        super(x, y, z);
      }
    
      @Override public String toString() {
        return "{ x: " + x + ", y: " + y + ", z: " + z + " }";
      }
    }
    
  • We can't force 3rd-party code to use our modified class in place of the original 1.

    Fair enough. The bigger issue with this solution is it can't force NON-3rd-party code to use our modified class in place of the original 1. We have to manually replace new PVector by new MyVec.

  • edited January 2018

    Also how to do the same for Python Mode: O:-)

    """
     PVector Subclass Example (v1.1)
     GoToLoop (2018/Jan/17)
    
     https://Forum.Processing.org/two/discussion/25986/
     how-can-i-replace-pvector-tostring#Item_9
    """
    
    # PVector = __pvector__
    
    def setup():
        orig = PVector.random3D(this).mult(100)
        print orig
    
        hack = MyVec().set(orig)
        print hack
    
        exit()
    
    
    class MyVec(PVector):
        def toString(v):
            return '{ x: %f, y: %f, z: %f }' % (v.x, v.y, v.z)
    
  • edited January 2018

    comment deleted

    I'm not interested in answers from people who need to know that.

  • edited January 2018

    comment deleted

    I'm not interested in answers from people who need to know that.

  • kfrajer's solution to extend the class and override the function is the Java / OOP way to do it but

    That would require you to replace all references to PVector with MyPVector.

    is not entirely true ;)

    PVector vec = new MyPVector();

    will work but any method in PVector that returns PVector objects should also be overridden to return MyPVector objects.

    TfGuy44 provided a suitable alternative for Processing.

    You still haven't said why you might want to do this.

  • The core concept here is that, if you recompile Processing (Java) from its source, you can change anything you want, because it is all yours. Redefine PVector and that's it -- done.

    However, if you use the version that is already compiled, there are things that by design you just can't change about it -- easily, or at all (short of a hacked JVM / byte ode hacks). Even if you redefine a class, everything in the original code that accepts an original PVector as an argument still relies on and only accepts that original class/type. This is how Java works -- it isn't anything specific to Processing.

    Python (and Processing.py) is very, very different in this regard. Not only can you modify classes even at runtime, but most things fundamentally don't care about the types of their method signatures -- everything is duck-typed, so if you shadow or extend PVector (and/or use decorators, modify the class itself at runtime, etc.) and pass it to things, nothing complains. All those approaches just work in Python; in Java none of them work.

  • edited January 2018

    Even if you redefine a class, everything in the original code that accepts an original PVector as an argument still relies on and only accepts that original class/type.

    Fine. No problem there.

    What I'd hoped for though is that PVector would work extended in /my/ code.

    Python (and Processing.py) is very, very different in this regard.

    Understood. Next time, in Processing I'll start in Python. But that's /next/ time. :)

  • edited January 2018

    You still haven't said why you might want to do this.

    Correct. That's because I'm not interested in answers from people who need to know that.

  • edited January 2018

    What I'd hoped for though is that PVector would work extended in /my/ code.

    It will "work" if you extend the class, use @Override, and then declare a new PVector by creating a new extended class instance, as per @GoToLoop's example above. Now you have an object, but your class won't interoperate with Processing in the way you want -- specifically, you can't coerce new PVector to mean new myVec. This also means for example you can't coerce createGraphics to generate a myPGraphics, and you often can't coerce a static Class.copy() to copy extended properties of your subclassed object.

    If that isn't a problem for you, you should follow his example.

  • It will "work" if you extend the class, use @Override, and then declare a new PVector by creating a new extended class instance, as per @GoToLoop's example above

    That's not "it". That requires PVector constructors to be manually recoded.

    A usable workaround, but not a solution to the stated problem.

  • That is an additional workaround. You have already been given a solution to the problem as stated: modify PVector in the Processing source code and recompile.

    Review the thread above for the reasons why this is the solution that fits those criteria.

  • edited January 2018

    You now have a thing called a PVector, but nothing in the API of the compiled library will accept it.

    Perhaps I'm misunderstanding you, but I'm afraid that's not how classical subclass inheritance works under Java, Python, JS, C++, etc. :-\"

    Exactly the opposite: Any function parameter of datatype PVector would accept any derived subclass object of that datatype, including my own MyVec subclass. :)>-

    That function would still "think" that parameter is a "pure" PVector instance object though. 3:-O

    However, anything which would trigger the parameter's toString() method inside that function would get the MyVec's @Override customized implementation instead of the original PVector's. :ar!

    Another more tangible example is Processing's PImage parent class.
    PApplet::image() method's 1st parameter is of datatype PImage:
    https://Processing.org/reference/image_.html

    But it's also extremely common to pass a PGraphics instance argument to it instead. @-)
    Heck! Even Movie & Capture objects can be passed to PApplet::image() method too! 8-}

    B/c all of those subclasses extends PImage parent class. \m/
    Or at least extends another subclass which already extends PImage and so on. :-B
    That's what happens to PGraphics, which is always some other derived subclass of it. :\">

    When I stated: "We can't force 3rd-party code to use our modified class in place of the original 1."

    I've just meant that their internal PVector would be the original 1 from package processing.core, not our hacked 1. :(|)

    Yet, if we have direct access permission to their PVector fields, we can easily re-assign them to use our customized subclass: >:)

    Let's say we've got this file named "SomeLibrary.java" w/ the following content:

    package i.use.original.pvector;
    
    import processing.core.PVector;
    
    public class SomeLibrary {
      public PVector vec = new PVector(1e3f, .7f, -30);
    
      @Override public String toString() {
        return vec.toString();
      }
    }
    

    And now PVector Subclass Example version 2, now extended to use that class SomeLibrary: >-)

    /**
     * PVector Subclass Example (v2.0.1)
     * GoToLoop (2018/Jan/17)
     *
     * https://Forum.Processing.org/two/discussion/25986/
     * how-can-i-replace-pvector-tostring#Item_19
     */
    
    import i.use.original.pvector.SomeLibrary;
    
    void setup() {
      final PVector orig = PVector.random3D(this).mult(100);
      println(orig);
    
      final PVector hack = new MyVec().set(orig);
      println(hack, ENTER);
    
      final SomeLibrary lib = new SomeLibrary();
      println(lib); // [ 1000.0, 0.7, -30.0 ]
    
      lib.vec = new MyVec().set(lib.vec);
      println(lib); // { x: 1000.0, y: 0.7, z: -30.0 }
    
      exit();
    }
    
    public static class MyVec extends PVector {
      public MyVec() {
        super();
      }
    
      public MyVec(final float x, final float y) {
        super(x, y);
      }
    
      public MyVec(final float x, final float y, final float z) {
        super(x, y, z);
      }
    
      @Override public String toString() {
        return "{ x: " + x + ", y: " + y + ", z: " + z + " }";
      }
    }
    

    If we access the unmodified SomeLibrary::vec field, it still println(): [ 1000.0, 0.7, -30.0 ]

    But once we re-assign it w/ our customized MyVec instance, it now println(): { x: 1000.0, y: 0.7, z: -30.0 }.

    Hope the subject is much clearer now. ^#(^

  • edited January 2018

    You still haven't said why you might want to do this.

    Correct. That's because I'm not interested in answers from people who need to know that.

    I don't need to know but obviously I am interested in knowing why. Since that is the case you are not interested in anything I might contribute so I won't bother with this discussion anymore. :-q

    BTW When you know why someone whats a solution to a problem it can possibly direct the responder to possible / alternative solutions.

  • @GoToLoop -- thank you for pointing out I was very unclear on that "it" before "interoperate correctly." I just reread it, and it does look confusing -- I'll try to clean that up later. Great summary of subclass examples in Processing, too. I do think (?) that chrisjj already knows how inheritance works in general, and knows how to do it -- he just wants to know if he can redefine PVector as such, and without using inheritance that requires declaring a new instance of a subclass.

    As an aside, some things about Processing can make rolling your own subclassing a pain in certain circumstances. For example, periodically someone (like me) wants to subclass PGraphics, and then discovers why createGraphics makes that more difficult than it needs to be.

  • chrisjj already knows how inheritance works in general, and knows how to do it -- he just wants to know if he can redefine PVector as such, and without using inheritance that requires declaring a new instance of a subclass.

    Correct, correct and correct :)

  • edited January 2018

    And you can't.

    It isn't possible in Java by design, sans hacking JVM, bytecode, possibly classpath(?) etc. as per all above.

  • Then I assume that means it is also not possible in Processing using Java. Thanks.

    I'll try GoToLoop 's MyVec, despite the requirement to redefine all the constructors that's a gross offence to efficiency :)

  • edited January 2018

    ... that's a gross offence to efficiency.

    Those overloaded constructors are the same quantity of those found in the original PVector class: :-@
    https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PVector.java#L121-L145

    The boilerplate can be a lil' discomforting for the eyes, but they don't slowdown the code. [-(

    You may as well place that subclass in another tab. *-:)

  • I'll try GoToLoop 's MyVec

    I hit a snag. A static function doesn't get the override.

    /**
     * PVector Subclass Example (v2.0.2)
     * GoToLoop (2018/Jan/17), chrisjj (2018/Jan/19)
     *
     * https://Forum.Processing.org/two/discussion/25986/
     * how-can-i-replace-pvector-tostring#Item_19
     */
    
    
    public static class MyVec extends PVector {
      public MyVec() {
        super();
      }
    
      public MyVec(final float x, final float y) {
        super(x, y);
      }
    
      public MyVec(final float x, final float y, final float z) {
        super(x, y, z);
      }
    
      @Override public String toString() {
        return "{ x: " + x + ", y: " + y + ", z: " + z + " }";
      }
    }
    
    void setup() {
      final PVector orig = new PVector();
      println(orig); // [ 0.0, 0.0, 0.0 ]
    
      final PVector hack = new MyVec();
      println(hack, ENTER); // { x: 0.0, y: 0.0, z: 0.0 } 
    
      PVector v = new MyVec(); 
      println(v, ENTER); // { x: 0.0, y: 0.0, z: 0.0 } 
    
      PVector v2 = MyVec.add(v, v);
      println(v2, ENTER); // [ 0.0, 0.0, 0.0 ] 
    
    //  MyVec v3 = MyVec.add(v, v); // Error: cannot convert from PVector to[...].MyVec
    
      exit();
    }
    

    What's the fix/workaround?

  • edited January 2018

    You'll need to create your own static add() method that return MyVec inside MyVec class

          public static MyVec add(PVector v1,PVector v2){
            return new MyVec(v1.x+v2.x,v1.y+v2.y,v1.z+v2.z);
          }
    

    and use MyVec.add() instead of PVector.add()

    That way you can pass both PVector and MyVec object

  • Thanks but crikey I hope you're wrong. That would mean digging out and pasting the definition of every static vector function that's needed.

  • edited January 2018

    A static function doesn't get the override.

    • 1st of all, how can you expect method add() to be overridden if there's nothing overridden besides method toString() and those 3 overloaded constructors! =P~
    • It shoulda been very obvious that when we use extends in a class, it means it inherits all of its parent's members unchanged; the exactly way they had been implemented originally. I-)
    • Only then we have the choice to add more members and/or override, overshadow, overload the previous 1s. :)>-
    • Another very important matter is that static methods can't be truly overridden; at most overshadowed! @-)
    • But that's irrelevant for now, b/c you haven't made any attempts to override or overshadow anything, according to the code you had posted above! :-\"
    • The issue is that in Java, we can assign a subclass object to a variable of 1 of its parent datatypes, but not the opposite. :-SS
    • That's why PVector v2 = MyVec.add(v, v); is valid, while MyVec v3 = MyVec.add(v, v); isn't. [-X
    • B/c the static overloaded version of method add() returns datatype PVector, not MyVec. :-B
    • Unless you wanna bother overshadowing all static methods, like @xxMrPHDxx had shown, lemme teach ya a workaround for it: :ar!
      MyVec v3 = (MyVec) MyVec.add(v, v, new MyVec());
    • Or never declare a variable as datatype MyVec. Always go w/ PVector. ;)
  • how can you expect method add() to be overridden if ...

    I don't expect function add() to be overridden.

    I did expect static MyVec.add(v, v) to return a MyVec, inc. the ToString override.

    It didn't. Further example:

    println(MyVec.add(v, v)); // [ 0.0, 0.0, 0.0 ]

    lemme teach ya a workaround for it: MyVec v3 = (MyVec) MyVec.add(v1, v2, new MyVec());

    That's truly horrible! ;)

    But gives the desired output. Thanks.

    PS With added honesty: MyVec v3 = (MyVec) PVector.mult(v, 0, new MyVec());

  • edited January 2018

    I did expect static MyVec.add(v, v) to return a MyVec, ...

    Well, let's take a look at its actual implementation, shall we? O:-)

    https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PVector.java#L501-L508

    static public PVector add(PVector v1, PVector v2, PVector target) {
      if (target == null) {
        target = new PVector(v1.x + v2.x,v1.y + v2.y, v1.z + v2.z);
      } else {
        target.set(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
      }
      return target;
    }
    

    If parameter target is null, make new MyVec? Nope, it's new PVector! $-)

    As much as we woulda wished the contrary, Java doesn't automatically replacenew MyVec w/ new PVector when that method is inherited by MyVec. :-<

    My workaround simply passes a MyVec object as 3rd argument, so the 3rd parameter target isn't null, but contains an actual MyVec instance. :ar!

    And so at the end, an actual MyVec is returned return target; and can be castable to (MyVec). >-)

  • Thanks for the explanation.

Sign In or Register to comment.