/**
   * Creates a spring between the two given nodes with the given strength. If the two nodes not
   * directly connected by an edge already have a spring between them, it will be replaced by this
   * one.
   *
   * @param node1 First of the two nodes to have a spring between them.
   * @param node2 Second of the two nodes to have a spring between them.
   * @param length The length of this spring (natural rest distance at which the two nodes would
   *     sit).
   * @param strength The strength of this new spring.
   * @return True if the viewer contains the two nodes and a spring between them has been created.
   */
  public boolean addSpring(N node1, N node2, float length, float strength) {
    Particle p1 = nodes.get(node1);
    if (p1 == null) {
      return false;
    }
    Particle p2 = nodes.get(node2);
    if (p2 == null) {
      return false;
    }

    // We may have to remove existing spring if it exists between these two nodes.
    for (int i = 0; i < physics.getNumSprings(); i++) {
      Spring spring = physics.getSpring(i);
      if ((((spring.getOneEnd() == p1) && (spring.getTheOtherEnd() == p2))
              || ((spring.getOneEnd() == p2) && (spring.getTheOtherEnd() == p1)))
          && (spring.strength() != EDGE_STRENGTH)) {
        physics.removeSpring(spring);
        break;
      }
    }

    // Add the new force.
    physics.makeSpring(p1, p2, strength, DAMPING, length);
    return false;
  }
Example #2
0
 @Override
 public void prepare(final ParticleSystem system) {
   if (_wanderTargets.size() != system.getNumParticles()) {
     _wanderTargets = new ArrayList<Vector3>(system.getNumParticles());
     for (int x = system.getNumParticles(); --x >= 0; ) {
       _wanderTargets.add(new Vector3(system.getEmissionDirection()).normalizeLocal());
     }
   }
 }
Example #3
0
  /**
   * Cause this particle to reset it's color, age and size per the parent's settings. status is set
   * to Status.Available. Location, velocity and lifespan are set as given. Actual geometry data is
   * not affected by this call, only particle params.
   *
   * @param velocity new initial particle velocity
   * @param position new initial particle position
   * @param lifeSpan new particle lifespan in ms
   */
  public void init(
      final ReadOnlyVector3 velocity, final ReadOnlyVector3 position, final double lifeSpan) {
    this.lifeSpan = lifeSpan;
    _velocity.set(velocity);
    _position.set(position);

    currColor.set(parent.getStartColor());
    currentAge = 0;
    status = Status.Available;
    values[VAL_CURRENT_SIZE] = parent.getStartSize();
  }
Example #4
0
  public void killParticle() {
    setStatus(Status.Dead);

    final Vector3 tempVec3 = Vector3.fetchTempInstance();
    final FloatBuffer vertexBuffer = parent.getParticleGeometry().getMeshData().getVertexBuffer();
    BufferUtils.populateFromBuffer(tempVec3, vertexBuffer, startIndex);
    final int verts = ParticleSystem.getVertsForParticleType(type);
    for (int x = 1; x < verts; x++) {
      BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + x);
    }
    Vector3.releaseTempInstance(tempVec3);
  }
Example #5
0
  /**
   * update position (using current position and velocity), color (interpolating between start and
   * end color), size (interpolating between start and end size), spin (using parent's spin speed)
   * and current age of particle. If this particle's age is greater than its lifespan, it is set to
   * status DEAD.
   *
   * <p>Note that this only changes the parameters of the Particle, not the geometry the particle is
   * associated with.
   *
   * @param secondsPassed number of seconds passed since last update.
   * @return true if this particle is not ALIVE (in other words, if it is ready to be reused.)
   */
  public boolean updateAndCheck(final double secondsPassed) {
    if (status != Status.Alive) {
      return true;
    }
    currentAge += secondsPassed * 1000; // add ms time to age
    if (currentAge > lifeSpan) {
      killParticle();
      return true;
    }

    final Vector3 temp = Vector3.fetchTempInstance();
    _position.addLocal(_velocity.multiply(secondsPassed * 1000f, temp));
    Vector3.releaseTempInstance(temp);

    // get interpolated values from appearance ramp:
    parent.getRamp().getValuesAtAge(currentAge, lifeSpan, currColor, values, parent);

    // interpolate colors
    final int verts = ParticleSystem.getVertsForParticleType(type);
    for (int x = 0; x < verts; x++) {
      BufferUtils.setInBuffer(
          currColor, parent.getParticleGeometry().getMeshData().getColorBuffer(), startIndex + x);
    }

    // check for tex animation
    final int newTexIndex = parent.getTexAnimation().getTexIndexAtAge(currentAge, lifeSpan, parent);
    // Update tex coords if applicable
    if (currentTexIndex != newTexIndex) {
      // Only supported in Quad type for now.
      if (ParticleType.Quad.equals(parent.getParticleType())) {
        // determine side
        final float side = (float) Math.sqrt(parent.getTexQuantity());
        int index = newTexIndex;
        if (index >= parent.getTexQuantity()) {
          index %= parent.getTexQuantity();
        }
        // figure row / col
        final float row = side - (int) (index / side) - 1;
        final float col = index % side;
        // set texcoords
        final float sU = col / side, eU = (col + 1) / side;
        final float sV = row / side, eV = (row + 1) / side;
        final FloatBuffer texs =
            parent.getParticleGeometry().getMeshData().getTextureCoords(0).getBuffer();
        texs.position(startIndex * 2);
        texs.put(eU).put(sV);
        texs.put(eU).put(eV);
        texs.put(sU).put(eV);
        texs.put(sU).put(sV);
        texs.clear();
      }
      currentTexIndex = newTexIndex;
    }

    return false;
  }
    @Override
    protected void onDraw(Canvas canvas) {

      Paint paint = new Paint();
      paint.setColor(Color.RED);
      paint.setTextSize(40);

      /*
       * draw the background
       */
      canvas.drawBitmap(mPool, 0, 0, null);

      /*
       * compute the new position of our object, based on accelerometer
       * data and present time.
       */

      final ParticleSystem particleSystem = mParticleSystem;
      final long now = mSensorTimeStamp + (System.nanoTime() - mCpuTimeStamp);
      final float sx = mSensorX;
      final float sy = mSensorY;

      particleSystem.update(sx, sy, now);

      final float xc = mXOrigin;
      final float yc = mYOrigin;
      final float xs = mMetersToPixelsX;
      final float ys = mMetersToPixelsY;

      String scor = "score: " + score.toString();

      // canvas.rotate(90);
      canvas.drawText(scor, xc, yc, paint);

      int i = 0;
      for (Particle p : particleSystem.mBalls) {
        /*
         * We transform the canvas so that the coordinate system matches
         * the sensors coordinate system with the origin in the center
         * of the screen and the unit is the meter.
         */

        i++;
        final float x = xc + particleSystem.getPosX(i) * xs;
        final float y = yc - particleSystem.getPosY(i) * ys;
        canvas.drawBitmap(p.bitmap, x, y, null);
      }

      // and make sure to redraw asap
      invalidate();
    }
Example #7
0
  /**
   * Reset particle conditions. Besides the passed lifespan, we also reset color, size, and spin
   * angle to their starting values (as given by parent.) Status is set to Status.Available.
   *
   * @param lifeSpan the recreated particle's new lifespan
   */
  public void recreateParticle(final double lifeSpan) {
    this.lifeSpan = lifeSpan;

    final int verts = ParticleSystem.getVertsForParticleType(type);
    currColor.set(parent.getStartColor());
    for (int x = 0; x < verts; x++) {
      BufferUtils.setInBuffer(
          currColor, parent.getParticleGeometry().getMeshData().getColorBuffer(), startIndex + x);
    }
    values[VAL_CURRENT_SIZE] = parent.getStartSize();
    currentAge = 0;
    values[VAL_CURRENT_MASS] = 1;
    status = Status.Available;
  }
  /**
   * Adds the given edge to those to be displayed in the viewer. Note that the edge must connect
   * nodes that have already been added to the viewer. This version will use the locations of the
   * two nodes to calculate their distance of separation.
   *
   * @param edge Edge to add to the display.
   * @return True if edge was added successfully. False if edge contains nodes that have not been
   *     added to the viewer.
   */
  public boolean addEdge(E edge) {
    Particle p1 = nodes.get(edge.getNode1());
    if (p1 == null) {
      System.err.println("Warning: Node1 not found when creating edge.");
      return false;
    }
    Particle p2 = nodes.get(edge.getNode2());
    if (p2 == null) {
      System.err.println("Warning: Node2 not found when creating edge.");
      return false;
    }

    // Only add edge if it does not already exist in the collection
    if (!edges.containsKey(edge)) {
      float x1 = p1.position().x();
      float y1 = p1.position().y();
      float x2 = p2.position().x();
      float y2 = p2.position().y();
      // Strength, damping, reset length
      edges.put(
          edge,
          physics.makeSpring(
              p1,
              p2,
              EDGE_STRENGTH,
              DAMPING,
              (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2))));
    }
    return true;
  }
  public void draw() {
    background(255);
    lights();
    smooth();

    ps.run();
  }
    @Override
    protected void onDraw(Canvas canvas) {

      /*
       * draw the background
       */

      // canvas.drawBitmap(mWood, 0, 0, null);

      /*
       * compute the new position of our object, based on accelerometer
       * data and present time.
       */

      final ParticleSystem particleSystem = mParticleSystem;
      final long now = mSensorTimeStamp + (System.nanoTime() - mCpuTimeStamp);
      final float sx = mSensorX;
      final float sy = mSensorY;

      particleSystem.update(sx, sy, now);

      final float xc = mXOrigin;
      final float yc = mYOrigin;
      final float xs = mMetersToPixelsX;
      final float ys = mMetersToPixelsY;
      final Bitmap bitmap = mBitmap;
      final int count = particleSystem.getParticleCount();
      for (int i = 0; i < count; i++) {
        /*
         * We transform the canvas so that the coordinate system matches
         * the sensors coordinate system with the origin in the center
         * of the screen and the unit is the meter.
         */

        final float x = xc + particleSystem.getPosX(i) * xs;
        final float y = yc - particleSystem.getPosY(i) * ys;
        canvas.drawBitmap(bitmap, x, y, mPaint);
      }

      // and make sure to redraw asap
      invalidate();
    }
  /**
   * Creates a attractive or repulsive force between the two given nodes. If the two nodes already
   * have a force between them, it will be replaced by this one.
   *
   * @param node1 First of the two nodes to have a force between them.
   * @param node2 Second of the two nodes to have a force between them.
   * @param force Force to create between the two nodes. If positive, the nodes will attract each
   *     other, if negative they will repulse. The larger the magnitude the stronger the force.
   * @param minDistance Minimum distance within which no force is applied.
   * @return True if the viewer contains the two nodes and a force between them has been created.
   */
  public boolean addForce(N node1, N node2, float force, float minDistance) {
    Particle p1 = nodes.get(node1);
    if (p1 == null) {
      return false;
    }
    Particle p2 = nodes.get(node2);
    if (p2 == null) {
      return false;
    }

    // We may have to remove existing force if it exists between these two nodes.
    for (int i = 0; i < physics.getNumAttractions(); i++) {
      Attraction a = physics.getAttraction(i);
      if (((a.getOneEnd() == p1) && (a.getTheOtherEnd() == p2))
          || ((a.getOneEnd() == p2) && (a.getTheOtherEnd() == p1))) {
        physics.removeAttraction(a);
        break;
      }
    }
    // Add the new force.
    physics.makeAttraction(p1, p2, force, minDistance);
    return false;
  }
  /** Centres the particle view on the currently visible nodes. */
  private void updateCentroid() {
    float xMax = Float.NEGATIVE_INFINITY,
        xMin = Float.POSITIVE_INFINITY,
        yMin = Float.POSITIVE_INFINITY,
        yMax = Float.NEGATIVE_INFINITY;

    for (int i = 0; i < physics.getNumParticles(); ++i) {
      Particle p = physics.getParticle(i);
      xMax = Math.max(xMax, p.position().x());
      xMin = Math.min(xMin, p.position().x());
      yMin = Math.min(yMin, p.position().y());
      yMax = Math.max(yMax, p.position().y());
    }

    float xRange = xMax - xMin;
    float yRange = yMax - yMin;

    if ((xRange == 0) && (yRange == 0)) {
      xRange = Math.max(1, xMax);
      yRange = Math.max(1, yMax);
    }
    float zScale = (float) Math.min(height / (yRange * 1.2), width / (xRange * 1.2));
    centroid.setTarget(xMin + 0.5f * xRange, yMin + 0.5f * yRange, zScale);
  }
  /**
   * Tethers the given node to its location with the given strength.
   *
   * @param node The node to be tethered.
   * @param strength Strength of the tether.
   * @return True if the viewer contains the given node and it was tethered successfully.
   */
  public boolean tether(N node, float strength) {
    Particle p1 = nodes.get(node);
    if (p1 == null) {
      return false;
    }

    // Grab the tethering stake if it has already been created, otherwise create a new one.
    Particle stake = stakes.get(node);
    if (stake == null) {
      stake = physics.makeParticle(1, node.getLocation().x, node.getLocation().y, 0);
      stake.makeFixed();
      stakes.put(node, stake);
    }

    // Grab the tether if it has already been created, otherwise create a new one.
    Spring tether = tethers.get(stake);
    if (tether == null) {
      tether = physics.makeSpring(stake, p1, strength, DAMPING, Float.MIN_VALUE);
      tethers.put(stake, tether);
    } else {
      tether.setStrength(strength);
    }
    return true;
  }
  /**
   * Adds the given edge to those to be displayed in the viewer. Note that the edge must connect
   * nodes that have already been added to the viewer. This version will fix the distance of
   * separation between nodes to the given value
   *
   * @param edge Edge to add to the display.
   * @return True if edge was added successfully. False if edge contains nodes that have not been
   *     added to the viewer.
   */
  public boolean addEdge(E edge, float distance) {
    Particle p1 = nodes.get(edge.getNode1());
    if (p1 == null) {
      System.err.println("Warning: Node1 not found when creating edge.");
      return false;
    }
    Particle p2 = nodes.get(edge.getNode2());
    if (p2 == null) {
      System.err.println("Warning: Node2 not found when creating edge.");
      return false;
    }

    // Only add edge if it does not already exist in the collection
    if (!edges.containsKey(edge)) {
      // Strength, damping, reset length
      edges.put(edge, physics.makeSpring(p1, p2, EDGE_STRENGTH, DAMPING, distance));
    }
    return true;
  }
  public void setup() {

    mFont = createFont("ArialRoundedMTBold-36", 48);
    size(1440, 900, OPENGL);
    background(0);
    frameRate(120);
    //  model   = new OBJModel(this, "alyson_laugh.obj", "absolute", TRIANGLES);
    model = new OBJModel(this, "alyson_scared.obj", "absolute", TRIANGLES);
    //  model   = new OBJModel(this, "alyson_crying.obj", "absolute", TRIANGLES);

    smooth();
    colorMode(HSB);
    strokeWeight(4);

    model.scale(800);
    model.translateToCenter();

    detailValue = 3;
    scaleValue = 10;
    vertices = new ArrayList();
    ps = new ParticleSystem(this, scaleValue);

    PVector averagePosition = new PVector(0, 0, 0);

    for (int i = 0; i < model.getVertexCount(); i += detailValue) {
      PVector destinationPoint = model.getVertex(i);

      vertices.add(destinationPoint);

      averagePosition.add(destinationPoint);
    }

    averagePosition.div(vertices.size());

    for (int i = 0; i < vertices.size(); i++) {
      PVector destination = (PVector) vertices.get(i);
      Particle p = new Particle(this, averagePosition, destination);
      ps.addParticle(p);
    }
  }
  /**
   * Attempts to space out non-connected nodes from one another. This is achieved by adding a strong
   * repulsive force between non-connected nodes. Note that this produces n-squared forces so can be
   * slow for large networks where many nodes are not connected to each other.
   */
  public void spaceNodes() {
    ArrayList<Particle> pList = new ArrayList<Particle>(nodes.values());
    for (int i = 0; i < pList.size(); i++) {
      for (int j = 0; j < pList.size(); j++) {
        if (i > j) {
          Particle p1 = pList.get(i);
          Particle p2 = pList.get(j);

          // See if we have a connection between nodes
          for (Spring spring : edges.values()) {
            if (((spring.getOneEnd() == p1) && (spring.getTheOtherEnd() == p2))
                || ((spring.getOneEnd() == p2) && (spring.getTheOtherEnd() == p1))) {
              // Do nothing as we already have an edge connecting these two particles
            } else {
              // Add a small repulsive force
              physics.makeAttraction(p1, p2, -1000, 0.1f);
            }
          }
        }
      }
    }
  }
 /**
  * Updates the positions of nodes and edges in the viewer. This method does not normally need to
  * be called as update happens every time draw() is called. Calling this method can be useful if
  * you wish to speed up the movement of nodes and edges by updating their position more than once
  * every draw cycle.
  */
 public void updateParticles() {
   physics.tick(0.3f); // Advance time in the physics engine.
 }
 /**
  * Adds a node to those to be displayed in the viewer.
  *
  * @param node Node to add to the viewer.
  */
 public void addNode(N node) {
   Particle p = physics.makeParticle(1, node.getLocation().x, node.getLocation().y, 0);
   nodes.put(node, p);
 }
 public void draw() {
   background(0);
   frame.setTitle(str((int) frameRate));
   ps.update();
 }
Example #20
0
  /**
   * Update the vertices for this particle, taking size, spin and viewer into consideration. In the
   * case of particle type ParticleType.GeomMesh, the original triangle normal is maintained rather
   * than rotating it to face the camera or parent vectors.
   *
   * @param cam Camera to use in determining viewer aspect. If null, or if parent is not set to
   *     camera facing, parent's left and up vectors are used.
   */
  public void updateVerts(final Camera cam) {
    final double orient = parent.getParticleOrientation() + values[VAL_CURRENT_SPIN];
    final double currSize = values[VAL_CURRENT_SIZE];

    if (type == ParticleSystem.ParticleType.GeomMesh
        || type == ParticleSystem.ParticleType.Point) {; // nothing to do
    } else if (cam != null && parent.isCameraFacing()) {
      final ReadOnlyVector3 camUp = cam.getUp();
      final ReadOnlyVector3 camLeft = cam.getLeft();
      final ReadOnlyVector3 camDir = cam.getDirection();
      if (parent.isVelocityAligned()) {
        bbX.set(_velocity).normalizeLocal().multiplyLocal(currSize);
        camDir.cross(bbX, bbY).normalizeLocal().multiplyLocal(currSize);
      } else if (orient == 0) {
        bbX.set(camLeft).multiplyLocal(currSize);
        bbY.set(camUp).multiplyLocal(currSize);
      } else {
        final double cA = MathUtils.cos(orient) * currSize;
        final double sA = MathUtils.sin(orient) * currSize;
        bbX.set(camLeft)
            .multiplyLocal(cA)
            .addLocal(camUp.getX() * sA, camUp.getY() * sA, camUp.getZ() * sA);
        bbY.set(camLeft)
            .multiplyLocal(-sA)
            .addLocal(camUp.getX() * cA, camUp.getY() * cA, camUp.getZ() * cA);
      }
    } else {
      bbX.set(parent.getLeftVector()).multiplyLocal(0);
      bbY.set(parent.getUpVector()).multiplyLocal(0);
    }

    final Vector3 tempVec3 = Vector3.fetchTempInstance();
    final FloatBuffer vertexBuffer = parent.getParticleGeometry().getMeshData().getVertexBuffer();
    switch (type) {
      case Quad:
        {
          _position.subtract(bbX, tempVec3).subtractLocal(bbY);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 0);

          _position.subtract(bbX, tempVec3).addLocal(bbY);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 1);

          _position.add(bbX, tempVec3).addLocal(bbY);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 2);

          _position.add(bbX, tempVec3).subtractLocal(bbY);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 3);
          break;
        }
      case GeomMesh:
        {
          final Quaternion tempQuat = Quaternion.fetchTempInstance();
          final ReadOnlyVector3 norm = triModel.getNormal();
          if (orient != 0) {
            tempQuat.fromAngleNormalAxis(orient, norm);
          }

          for (int x = 0; x < 3; x++) {
            if (orient != 0) {
              tempQuat.apply(triModel.get(x), tempVec3);
            } else {
              tempVec3.set(triModel.get(x));
            }
            tempVec3.multiplyLocal(currSize).addLocal(_position);
            BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + x);
          }
          Quaternion.releaseTempInstance(tempQuat);
          break;
        }
      case Triangle:
        {
          _position
              .subtract(3 * bbX.getX(), 3 * bbX.getY(), 3 * bbX.getZ(), tempVec3)
              .subtractLocal(bbY);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 0);

          _position.add(bbX, tempVec3).addLocal(3 * bbY.getX(), 3 * bbY.getY(), 3 * bbY.getZ());
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 1);

          _position.add(bbX, tempVec3).subtractLocal(bbY);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 2);
          break;
        }
      case Line:
        {
          _position.subtract(bbX, tempVec3);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex);

          _position.add(bbX, tempVec3);
          BufferUtils.setInBuffer(tempVec3, vertexBuffer, startIndex + 1);
          break;
        }
      case Point:
        {
          BufferUtils.setInBuffer(_position, vertexBuffer, startIndex);
          break;
        }
    }
    Vector3.releaseTempInstance(tempVec3);
  }
Example #21
0
 /**
  * Cause this particle to reset it's lifespan, velocity, color, age and size per the parent's
  * settings. status is set to Status.Available and location is set to 0,0,0. Actual geometry data
  * is not affected by this call, only particle params.
  */
 public void init() {
   init(parent.getRandomVelocity(_velocity), Vector3.ZERO, parent.getRandomLifeSpan());
 }
Example #22
0
 /**
  * Normal use constructor. Sets up the parent and particle type for this particle.
  *
  * @param parent the particle collection this particle belongs to
  */
 public Particle(final ParticleSystem parent) {
   this.parent = parent;
   type = parent.getParticleType();
 }
Example #23
0
 @Override
 public void prepare(final ParticleSystem system) {
   super.prepare(system);
   _swarmPoint.set(system.getOriginCenter()).addLocal(_swarmOffset);
 }
 /**
  * Sets the drag on all particles in the system. By default drag is set to 0.75 which is enough to
  * allow particles to move smoothly.
  *
  * @param drag Drag effect (larger numbers slow down movement).
  */
 public void setDrag(float drag) {
   physics.setDrag(drag);
 }