Class inheritance with toxiclibs (Python)

edited February 2018 in Python Mode

Hi everyone !

I'm really excited to start using the toxiclibs library following this Daniel Shifmann tutorial but i'm having hard times implementing the following snippet in Python:

class Particle extends VerletParticle2D {

  Particle(float x, float y) {
    super(x, y);
  }

  void display() {
    fill(255);
    ellipse(x, y, 10, 10);
  }
}

This is supposed to get the x and y coordinates from the main .pde file and make them "move"/'"change" according to the toxiclibs VerletPhysics engine.

I believe the corresponding Python code would be:

import toxi.physics2d.VerletParticle2D as VerletParticle2D

class Particle(VerletParticle2D):
    def __init__(self, x, y):
        super(Particle, self).__init__(x, y)
        self.x = x
        self.y = y


    def display(self):
        fill(255)
        ellipse(self.x, self.y, 10, 10)

But I must have missed something because the coordinates don't change and all my points stay still on the screen. Do you have any idea what I'm doing wrong here ?

Answers

  • edited February 2018
    """
     Toxic VerletParticle2D (v1.2)
     GoToLoop (2018-Feb-14)
    
     Forum.Processing.org/two/discussion/26346/
     class-inheritance-with-toxiclibs-pyhton#Item_1
    """
    
    from toxi.physics2d import VerletParticle2D
    
    class Particle(VerletParticle2D):
        W, H, C = 10, 10, 0xff
    
        def display(p):
            fill(p.C)
            ellipse(p.x(), p.y(), p.W, p.H)
            return p
    
    
    
    noLoop()
    colorMode(RGB)
    strokeWeight(1.5)
    stroke(0)
    
    background(int(random(PImage.ALPHA_MASK)))
    
    p = Particle(width>>1, height>>1).display()
    
    x, y = p.x(), p.y()
    print p, x, y # {x:50.0, y:50.0} 50.0 50.0
    
  • edited February 2018

    Thanks for your answer GoToLoop. I must admit I don't really understand how this code could help me. I need to get the coordinates from the main .pyde file so I think this part is mendatory (?) :

    class Particle(VerletParticle2D):
        def __init__(self, x, y): #must specify 'x' and 'y'
            super(Particle, self).__init__(x, y)
    

    Now, following that logic I should then write:

    def display(self): #'self' is important right ?

    Below the main .pyde file:

    add_library('toxiclibscore')
    add_library('verletphysics')
    from particle import Particle
    
    def setup():
        size(600, 600)
        global p, particles, physics
        particles = []
    
        physics = VerletPhysics2D()
        physics.addBehavior(GravityBehavior(Vec2D(0, 4)))
    
        for e in range(0, 40):
            p = Particle(100 + (e * 10), 10)
            particles.append(p)
            physics.addParticle(p)
    
    def draw():
        background(0)
    
        physics.update()
    
    
        for e in particles:
            e.display()
    
  • edited February 2018

    AFAIK, the way I've refactored your Particle class should work alright. :ar!

    The console output from: print Particle(width>>1, height>>1).display()
    as {x:50.0, y:50.0} proves that the class had been properly initialized. :-B

    Why don't you test it the way it is now, then make your own changes to it later? :>

  • I did ! I'm still fiddling with your code right now, trying to figure this out.

  • edited February 2018 Answer ✓

    Version 1.2 now: x, y = p.x(), p.y(); print p, x, y prints out: {x:50.0, y:50.0} 50.0 50.0 B-)

  • edited February 2018

    Sorry still don't get it, I must be dumb. How can I specify x and y IN the inheritance part ?

  • edited February 2018

    ToxicVerletParticle2D.pyde:

    """
     Toxic VerletParticle2D (v1.3)
     GoToLoop (2018-Feb-14)
    
     Forum.Processing.org/two/discussion/26346/
     class-inheritance-with-toxiclibs-pyhton#Item_7
    """
    
    from Particle import Particle
    
    noLoop()
    colorMode(RGB)
    strokeWeight(1.5)
    stroke(0)
    
    background(int(random(PImage.ALPHA_MASK)))
    
    p = Particle(width>>1, height>>1).display()
    
    x, y = p.x(), p.y()
    print p, x, y # {x:50.0, y:50.0} 50.0 50.0
    

    Particle.py:

    from toxi.physics2d import VerletParticle2D
    
    class Particle(VerletParticle2D):
        W, H, C = 10, 10, 0xff
    
        def display(p):
            fill(p.C)
            ellipse(p.x(), p.y(), p.W, p.H)
            return p
    
  • edited February 2018

    If I can get away w/ it, I very much prefer not to implement my own __init__() when extending a class. 8-|

    In particular, b/c parent class VerletParticle2D got both fields x & y, plus their corresponding getters x() & y(), that forces Jython to have to choose either the fields or the getter methods. :-SS

    It is clear to me that Jython picked the getters x() & y() over the fields x & y. :-\"

  • Your code surely works but whenever I try to apply to my sketch it doesn't work at all. Thanks for your time anyway.

  • Well, could you at least post your attempt to use my own Particle class version? L-)

  • Also, I don't think the Toxic library can be imported via add_library()! :-&

  • The thing is my code initialize the class but nothing moves. At first I thought it had to do with my initialization but now I come to think it has something to do with the Python mode not fully working with the toxiclibs library

  • I'm still curious to know what would have been your version of the extension using __init()__

  • Ru doing more than passing fields x & y inside your __init__()? :-/
    In case you're not, there's no advantage on implementing your own __init__(). :-@

    Like I've already asked, w/o seeing how you would attempt to use my own Particle implementation, it's hard to have an exact idea what's going wrong! #-o

  • Got it after an hour messing with your answer:

    class Particle(VerletParticle2D):
    
        def display(p):
            fill(255)
            ellipse(p.x(), p.y(), 10, 10)
    

    Just that simple ! I feel really dumb tbh.

    Thank you GoToLoop !

  • edited February 2018 Answer ✓

    I've merged the code you've provided w/ my own subclass Particle. :-bd

    Also, I've added an __init__() constructor w/ super() for my subclass Particle too: :bz
    def __init__(p, *args): super(Particle, p).__init__(*args)

    It's been posted commented out b/c it's actually redundant & unnecessary. :-\"
    But you can remove the # there if you wish to check that out for yourself. :>

    ToxicVerletParticle2D.pyde:

    """
     Toxic VerletParticle2D (v2.0.2)
     GoToLoop (2018-Feb-14)
    
     Forum.Processing.org/two/discussion/26346/
     class-inheritance-with-toxiclibs-pyhton#Item_16
    """
    
    from Particle import Particle
    
    from toxi.physics2d import VerletPhysics2D
    from toxi.physics2d.behaviors import GravityBehavior2D
    
    from toxi.geom import Vec2D
    
    def setup():
        size(800, 600)
        frameRate(2)
    
        colorMode(RGB)
        ellipseMode(CENTER)
    
        strokeWeight(Particle.BOLD)
        stroke(Particle.STROKE)
        strokeCap(ROUND)
        fill(Particle.FILL)
    
        global particleEngine
        particleEngine = createParticles()
    
    
    def draw():
        background(0)
        particleEngine.update()
        for p in particleEngine.particles: p.display()
    
    
    def createParticles(QTY=30, OFF=75):
        physics = VerletPhysics2D()
        physics.addBehavior(GravityBehavior2D(Vec2D(0, 4)))
    
        w, h = Particle.W, Particle.H
        gap = (w>>1) + w
    
        for p in range(QTY):
            particle = Particle(p*gap + OFF, h)
            physics.addParticle(particle)
            print particle, particle.x(), particle.y(), particle.weight
    
        return physics
    

    Particle.py:

    from toxi.physics2d import VerletParticle2D
    
    class Particle(VerletParticle2D):
        W, H = 15, 15
        FILL, STROKE, BOLD = 0xff, 0xffFF0000, 1.5
    
        def display(p):
            ellipse(p.x(), p.y(), p.W, p.H)
            return p
    
    
        # def __init__(p, *args): super(Particle, p).__init__(*args)
    
  • edited February 2018

    Beautiful ! I'm going to test this right now. Many thanks !

  • edited July 2018

    Latest "Toxic VerletParticle2D" version 3.0; now based on the original Daniel Shifman's "Cloth2d" Java Mode sketch from Coding Train Challenge YouTube tutorial: \m/

    https://GitHub.com/CodingTrain/website/tree/master/CodingChallenges/CC_020_Cloth2D

    ToxicVerletParticle2D.pyde:

    """
     Toxic VerletParticle2D (v3.1.1)
     GoToLoop (2018-Feb-14)
    
     Forum.Processing.org/two/discussion/26346/
     class-inheritance-with-toxiclibs-pyhton#Item_18
    
     GitHub.com/CodingTrain/website/tree/master/CodingChallenges/CC_020_Cloth2D
    """
    
    add_library('toxiclibs')
    
    from toxi.physics2d import VerletPhysics2D
    from toxi.physics2d.behaviors import GravityBehavior2D
    from toxi.geom import Vec2D
    
    from Particle import Particle
    from Spring import Spring
    
    ROWS, COLS, GRAV = 40, 40, GravityBehavior2D(Vec2D(0, 1))
    LAST_ROW, LAST_COL = ROWS - 1, COLS - 1
    RANGE_ROWS, RANGE_COLS = tuple(range(ROWS)), tuple(range(COLS))
    
    W, H, OFFSET, FORCE = Particle.W, Particle.H, 6, .8
    OFFW, OFFH = W*OFFSET + (W>>1), H + (H>>1)
    GAPW, GAPH = (W>>2) + W, (H>>2) + H
    LENGTH, WEIGHT, ITERS = GAPW, 1, 30
    
    RENDERER, FPS, BG = FX2D, 60, 0100
    
    showParticles, showSprings = False, True
    
    def setup():
        size(600, 700, RENDERER)
        noSmooth()
        frameRate(FPS)
    
        colorMode(RGB)
        ellipseMode(CENTER)
        strokeCap(ROUND)
        fill(Particle.FILL)
    
        global cloth
        cloth = createClothParticles()
        insertSpringsToParticles(cloth)
        lockCornerParticles(cloth)
    
        cloth.addBehavior(GRAV)
        cloth.numIterations = ITERS
    
    
    def draw():
        background(BG)
        cloth.update()
    
        showParticles and displayParticles(cloth)
        showSprings and displaySprings(cloth)
    
        displayTitle()
    
    
    def mousePressed():
        global showParticles, showSprings
        btn = mouseButton
    
        if btn == LEFT: showParticles = not showParticles
        elif btn == RIGHT: showSprings = not showSprings
        else: resetParticles(cloth)
    
    
    def displayTitle(TITLE='FPS: %02.f'):
        this.surface.title = TITLE % frameRate
    
    
    def displayParticles(phy):
        strokeWeight(Particle.BOLD)
        stroke(Particle.STROKE)
        for p in phy.particles: p.display()
    
    
    def displaySprings(phy):
        strokeWeight(Spring.BOLD)
        stroke(Spring.STROKE)
        for s in phy.springs: s.display()
    
    
    def lockCornerParticles(phy):
        particles = phy.particles
    
        particles[0].lock() # UP-LEFT
        particles[LAST_COL].lock() # UP-RIGHT
        particles[-ROWS].lock() # DOWN-LEFT
        particles[-1].lock() # DOWN-RIGHT
    
    
    def createClothParticles(phy = VerletPhysics2D()):
        phy.clear()
    
        for r in RANGE_ROWS:
            y = r*GAPH + OFFH
    
            for c in RANGE_COLS:
                x = c*GAPW + OFFW
                phy.addParticle(Particle(x, y, WEIGHT))
    
        return phy
    
    
    def insertSpringsToParticles(phy):
        phy.springs.clear()
        particles = phy.particles
    
        for r in RANGE_ROWS:
            row = r*COLS
    
            for c in RANGE_COLS:
                idx = row + c
                a = particles[idx]
    
                if c != LAST_COL:
                    b = particles[idx + 1]
                    phy.addSpring(Spring(a, b, LENGTH, FORCE))
    
                if r != LAST_ROW:
                    b = particles[idx + COLS]
                    phy.addSpring(Spring(a, b, LENGTH, FORCE))
    
    
    def resetParticles(phy):
        particles = phy.particles
    
        for r in RANGE_ROWS:
            row = r*COLS
            y = r*GAPH + OFFH
    
            for c in RANGE_COLS:
                idx = row + c
                x = c*GAPW + OFFW
                particles[idx].set(x, y)
    

    Particle.py:

    # add_library('toxiclibs')
    
    from toxi.physics2d import VerletParticle2D
    
    class Particle(VerletParticle2D):
        W, H = 10, 10
        FILL, STROKE, BOLD = 0350, 0xffFF0000, 1
    
        def display(p):
            ellipse(p.x(), p.y(), p.W, p.H)
            return p
    
    
        # def __init__(p, *args): super(Particle, p).__init__(*args)
    

    Spring.py:

    # add_library('toxiclibs')
    
    from toxi.physics2d import VerletSpring2D
    
    class Spring(VerletSpring2D):
        STROKE, BOLD = 0xff, 2
    
        def display(s):
            line(s.a.x(), s.a.y(), s.b.x(), s.b.y())
            return s
    
    
        # def __init__(s, *args, **kw): super(Spring, s).__init__(*args, **kw)
    
  • edited February 2018

    That's neat.

    I'd like to add a mouseEvent (to drag the particles with the mouse and see the distortion) in the Particle class of this sketch (my version) but can't figure how to make it work. Should I post a new question or keep asking here ?

  • Yea, you may choose to start a new forum thread for it.
    But given it's related to the stuff here, also post a link to this thread there.

  • Will do thanks.

  • edited December 2018

    Given Toxiblibs is also available under JavaScript as well: ~O)

    http://Haptic-Data.com/toxiclibsjs
    https://GitHub.com/hapticdata/toxiclibsjs

    I've converted "Toxic VerletParticle2D" to p5.js too: https://p5js.org/ B-)

    And you all can check it online here: :bz
    https://Bl.ocks.org/GoSubRoutine/f4e383cfc4de8b063df3ee1c3fc66419

    index.html:

    <!DOCTYPE html>
    
    <meta charset=utf-8>
    <meta name=viewport content=width=device-width,initial-scale=1>
    
    <script async src=https://CDN.JSDelivr.net/npm/p5></script>
    
    <script defer 
      src=https://CDN.JSDelivr.net/gh/hapticdata/toxiclibsjs/build/toxiclibs.min.js>
    </script>
    
    <script defer src=Particle.js></script>
    <script defer src=Spring.js></script>
    
    <script defer src=sketch.js></script>
    

    Particle.js:

    "use strict";
    
    class Particle extends toxi.physics2d.VerletParticle2D {
      static get FILL() {
        delete this.FILL;
        return this.prototype.FILL = this.FILL = color(0o350);
      }
    
      static get STROKE() {
        delete this.STROKE;
        return this.prototype.STROKE = this.STROKE = color('red');
      }
    
      display() {
        ellipse(this.x, this.y, this.W, this.H);
        return this;
      }
    }
    
    Particle.prototype.H = Particle.H = 10;
    Particle.prototype.W = Particle.W = 10;
    Particle.prototype.BOLD = Particle.BOLD = 1;
    
    Particle.prototype.FILL = 0o350;
    Particle.prototype.STROKE = 'red';
    

    Spring.js:

    "use strict";
    
    class Spring extends toxi.physics2d.VerletSpring2D {
      static get STROKE() {
        delete this.STROKE;
        return this.prototype.STROKE = this.STROKE = color(0xff);
      }
    
      display() {
        line(this.a.x, this.a.y, this.b.x, this.b.y);
        return this;
      }
    }
    
    Spring.prototype.BOLD = Spring.BOLD = 2;
    Spring.prototype.STROKE = 0xff;
    

    sketch.js:

    /**
     * Toxic VerletParticle2D (v3.2)
     * GoToLoop (2018-Feb-16)
     *
     * Forum.Processing.org/two/discussion/26346/
     * class-inheritance-with-toxiclibs-pyhton#Item_22
     *
     * GitHub.com/CodingTrain/website/tree/master/CodingChallenges/CC_020_Cloth2D
     *
     * Bl.ocks.org/GoSubRoutine/f4e383cfc4de8b063df3ee1c3fc66419
     */
    
    "use strict";
    
    const { VerletPhysics2D } = toxi.physics2d,
          { GravityBehavior } = toxi.physics2d.behaviors,
          { Vec2D } = toxi.geom;
    
    const ROWS = 40, COLS = 40,
          LAST_ROW = ROWS - 1, LAST_COL = COLS - 1,
          GRAV = new GravityBehavior(new Vec2D(0, 1));
    
    const { W, H } = Particle, OFFSET = 6, FORCE = .8,
          OFFW = W*OFFSET + (W>>1), OFFH = H + (H>>1),
          GAPW = (W>>2) + W, GAPH = (H>>2) + H,
          LENGTH = GAPW, WEIGHT = 1, ITERS = 30;
    
    const RENDERER = 'p2d', FPS = 60, BG = 0o100;
    
    let showParticles = false, showSprings = true, cloth, bg;
    
    function setup() {
      createCanvas(600, 600, RENDERER).mousePressed(clothDisplayMode);
      frameRate(FPS).noSmooth();
    
      ellipseMode(CENTER).colorMode(RGB);
      fill(Particle.FILL).strokeCap(ROUND);
      bg = color(BG);
    
      insertSpringsToParticles( cloth = createClothParticles() );
      lockCornerParticles(cloth);
    
      cloth.addBehavior(GRAV);
      cloth.numIterations = ITERS;
    }
    
    function draw() {
      background(bg);
      cloth.update();
    
      showParticles && displayParticles(cloth);
      showSprings && displaySprings(cloth);
    
      displayTitle();
    }
    
    function clothDisplayMode(evt) {
    /*
      _onmousedown(evt); // workaround for mouseButton, mouseX & mouseY!
    
      evt.preventDefault();
      evt.stopPropagation();
    */
    
      switch (mouseButton) {
        case LEFT: return showParticles = !showParticles;
        case RIGHT: return showSprings = !showSprings;
        default: resetParticles(cloth);
      }
    }
    
    function displayTitle() {
      return top.document.title = round(frameRate());
    }
    
    function displayParticles({ particles }) {
      strokeWeight(Particle.BOLD).stroke(Particle.STROKE);
      for (const p of particles)  p.display();
    }
    
    function displaySprings({ springs }) {
      strokeWeight(Spring.BOLD).stroke(Spring.STROKE);
      for (const s of springs)  s.display();
    }
    
    function lockCornerParticles({ particles }) {
      particles[0].lock(); // UP-LEFT
      particles[LAST_COL].lock(); // UP-RIGHT
      particles[particles.length - ROWS].lock(); // DOWN-LEFT
      particles[particles.length - 1].lock(); // DOWN-RIGHT
    }
    
    function createClothParticles(phy = new VerletPhysics2D) {
      phy.clear();
    
      for (let r = 0; r < ROWS; ++r) {
        const y = r*GAPH + OFFH;
    
        for (let c = 0; c < COLS; ++c) {
          const x = c*GAPW + OFFW;
          phy.addParticle(new Particle(x, y, WEIGHT));
        }
      }
    
      return phy;
    }
    
    function insertSpringsToParticles(phy) {
      phy.springs.length = 0;
      const { particles } = phy;
    
      for (let r = 0; r < ROWS; ++r) {
        const row = r*COLS;
    
        for (let c = 0; c < COLS; ++c) {
          const idx = row + c, a = particles[idx];
    
          if (c !== LAST_COL) {
            const b = particles[idx + 1];
            phy.addSpring(new Spring(a, b, LENGTH, FORCE));
          }
    
          if (r !== LAST_ROW) {
            const b = particles[idx + COLS];
            phy.addSpring(new Spring(a, b, LENGTH, FORCE));
          }
        }
      }
    }
    
    function resetParticles({ particles }) {
      for (let r = 0; r < ROWS; ++r) {
        const row = r*COLS, y = r*GAPH + OFFH;
    
        for (let c = 0; c < COLS; ++c) {
          const idx = row + c, x = c*GAPW + OFFW;
          particles[idx].set(x, y);
        }
      }
    }
    
Sign In or Register to comment.