Leaf Venation - Reducing radius size of ellipse as they branch off

Hey there, I'm working with some code in which ellipses are drawn onto the screen simulating leaf/root structure. I'm looking to add some rule which reduces the size of the ellipses when they 'branch off' from the main (original) branches, so each new branch has a proportionally smaller radius than the one in which it came from.

The term in the code I'd need to write the rule for is '_veinNodeRadius'. I'm pretty sure that the rule would have to take place in the 'void placeVeinNode' section too but I'm not 100%.

Would appreciate if someone was able to offer some advice of how I'd go about doing this.

Thanks,

Ross

PAGE 1

VenationAlgorithm va;
VenationPlotter vp;

void setup() {
  size(1000,500);
  background(30);
  scale(0.2);

  va = new VenationAlgorithm();
  vp = new VenationPlotter(va, this.g, width);

  reset();
  redraw();
}

void draw() {
  scale(0.2);
}

void reset() {
  va = new VenationAlgorithm();
  vp = new VenationPlotter(va, this.g, width);

}

void redraw() {
  background(30);
  vp.draw();
}

void keyReleased() {
  switch (key) {
    case 'r':
    reset();
    redraw();
    break;

    case ' ':
    va.step();
    redraw();
    break;
  }
}

PAGE 2

class VenationAlgorithm {
  ArrayList<PVector> _auxins;
  float _auxinRadius;
  ArrayList<PVector> _veinNodes;
  float _veinNodeRadius;
  float _killRadius;
  float _neighborhoodRadius;

  VenationAlgorithm() {
    _auxins = new ArrayList<PVector>();
    _auxinRadius = 0.025;

    _veinNodes = new ArrayList<PVector>();
    _veinNodeRadius = 0.008;

    _killRadius = 0.055;
    _neighborhoodRadius = 0.1;

    seedVeinNodes();
    seedAuxins();
  }


  float getAuxinRadius() {
    return _auxinRadius;
  }

  float getVeinNodeRadius() {
    return _veinNodeRadius;
  }

  float getKillRadius() {
    return _killRadius;
  }

  ArrayList<PVector> getAuxins() {
    return _auxins;
  }

  int numAuxins() {
    return _auxins.size();
  }

  ArrayList<PVector> getVeinNodes() {
    return _veinNodes;
  }

  int numVeinNodes() {
    return _veinNodes.size();
  }

  PVector getVeinNode(int veinNodeIndex) {
    return _veinNodes.get(veinNodeIndex);
  }


  ArrayList<PVector> getNeighborAuxins(int veinNodeIndex) {
    ArrayList<Integer> neighborAuxinIndices = getNeighborAuxinIndices(veinNodeIndex);
    ArrayList<PVector> neighborAuxins = new ArrayList<PVector>();
    for (int i : neighborAuxinIndices) {
      neighborAuxins.add(_auxins.get(i));
    }
    return neighborAuxins;
  }

  ArrayList<PVector> getInfluencerAuxins(int veinNodeIndex) {
    ArrayList<Integer> influencerAuxinIndices = getInfluencerAuxinIndices(veinNodeIndex);
    ArrayList<PVector> influencerAuxins = new ArrayList<PVector>();
    for (int i : influencerAuxinIndices) {
      influencerAuxins.add(_auxins.get(i));
    }
    return influencerAuxins;
  }

  PVector getAuxinInfluenceDirection(PVector veinNode, ArrayList<PVector> auxinInfluencers) {
    PVector result = null;
    for (PVector p : auxinInfluencers) {
      p = p.get();
      p.sub(veinNode);
      p.normalize();

      if (result == null) {
        result = new PVector();
      }
      result.add(p);
    }

    if (result != null) {
      result.normalize();
    }
    return result;
  }


  private ArrayList<Integer> getNeighborAuxinIndices(int veinNodeIndex) {
    float dx, dy, r = 4.0 * _neighborhoodRadius * _neighborhoodRadius;
    PVector p, veinNode = _veinNodes.get(veinNodeIndex);
    ArrayList<Integer> neighborAuxinIndices = new ArrayList<Integer>();
    for (int i = 0; i < _auxins.size(); i++) {
      p = _auxins.get(i);
      dx = p.x - veinNode.x;
      dy = p.y - veinNode.y;
      if ((dx * dx) + (dy * dy) < r) {
        neighborAuxinIndices.add(i);
      }
    }
    return neighborAuxinIndices;
  }

  private ArrayList<Integer> getInfluencerAuxinIndices(int veinNodeIndex) {
    ArrayList<Integer> neighborAuxinIndices = getNeighborAuxinIndices(veinNodeIndex);
    ArrayList<Integer> influencerAuxinIndices = new ArrayList<Integer>();
    for (int auxinIndex : neighborAuxinIndices) {
      if (veinNodeIndex == getInfluencedVeinNodeIndex(auxinIndex)) {
        influencerAuxinIndices.add(auxinIndex);
      }
    }
    return influencerAuxinIndices;
  }

  int getInfluencedVeinNodeIndex(int auxinIndex){
    PVector auxin = _auxins.get(auxinIndex);
    return getNearestVeinNodeIndex(auxin.x, auxin.y);
  }

  int getNearestVeinNodeIndex(float x, float y) {
    int candidateIndex = -1;
    PVector p;
    float dx, dy, distSq, candidateDistSq = 0;
    for (int i = 0; i < _veinNodes.size(); i++) {
      p = _veinNodes.get(i);
      dx = p.x - x;
      dy = p.y - y;
      distSq = dx * dx + dy * dy;
      if (candidateIndex < 0 || distSq < candidateDistSq) {
        candidateIndex = i;
        candidateDistSq = distSq;
      }
    }
    return candidateIndex;
  }




  void step() {
    placeVeinNodes();
    killAuxins();
  }


  void seedVeinNodes() {
    float x, y;
    for (int i = 0; i < 3; i++) {
      x = random(1);
      y = random(1);
      _veinNodes.add(new PVector(x, y));
    }
  }

  void seedAuxins() {
    float x, y;
    for (int i = 0; i < 5000 && _auxins.size() < 2000; i++) {
      x = random(5);
      y = random(5);
      if (!hitTestPotentialAuxin(x, y)) {
        _auxins.add(new PVector(x, y));
      }
    }
  }

  void placeVeinNodes(){
    int count = _veinNodes.size();
    for (int i = 0; i < count; i++) {
      placeVeinNode(i);
    }
  }

  void placeVeinNode(int seedVeinNodeIndex) {
    PVector veinNode = _veinNodes.get(seedVeinNodeIndex);
    ArrayList<PVector> influencerAuxins = getInfluencerAuxins(seedVeinNodeIndex);
    PVector p = getAuxinInfluenceDirection(veinNode, influencerAuxins);
    if (p != null) {
      if (p.mag() <= 0) {
        p.x = 1;
        p.y = 0;
        p.rotate(random(1) * 2 * PI);
      }
      p.mult(2 * _veinNodeRadius);
      p.add(veinNode);
      _veinNodes.add(p);
    }
  }

  void killAuxins() {
    PVector p;
    for (int i = 0; i < _auxins.size(); i++) {
      p = _auxins.get(i);
      if (hitTestExistingAuxin(p.x, p.y)) {
        _auxins.remove(i);
        i--;
      }
    }
  }

  private boolean hitTestExistingAuxin(float x, float y){
    float dx, dy, r;

    r = _killRadius * _killRadius;
    for (PVector p : _veinNodes) {
      dx = p.x - x;
      dy = p.y - y;
      if ((dx * dx) + (dy * dy) < r) {
        return true;
      }
    }
    return false;
  }

  private boolean hitTestPotentialAuxin(float x, float y) {
    float dx, dy, r;

    r = 4.0 * _auxinRadius * _auxinRadius;
    for (PVector p : _auxins) {
      dx = p.x - x;
      dy = p.y - y;
      if ((dx * dx) + (dy * dy) < r) {
        return true;
      }
    }

    r = _killRadius * _killRadius;
    for (PVector p : _veinNodes) {
      dx = p.x - x;
      dy = p.y - y;
      if ((dx * dx) + (dy * dy) < r) {
        return true;
      }
    }
    return false;
  }
}

PAGE 3

class VenationPlotter {
  VenationAlgorithm _va;
  PGraphics _g;
  int _size;

  VenationPlotter(VenationAlgorithm va, PGraphics g, int size) {
    _va = va;
    _g = g;
    _size = size;
  }

  void draw() {
    drawVeinNodes();
  }

  void drawVeinNodes() {
    float r = _va.getVeinNodeRadius() * _size;
    _g.fill(220);
    ArrayList<PVector> veinNodes = _va.getVeinNodes();
    for (PVector p : veinNodes) {
      _g.ellipse(_size * p.x, _size * p.y, 2*r, 2*r);
    }
  }
}
Sign In or Register to comment.