Using fisica and geomerative to add physics to typography

Hi guys.

I have this idea for a project but I can’t really figured it out and the documentation for both of these libraries (fisica and geomerative) that I want to use are not that good. Does anyone know how can this be in the easiest way possible?

Thanks! :slight_smile:

1 Like

It depends.

What kind of physics to what kind of typography?

Some very simple collision. I want to have a static text in the bottom of my canvas and lets say I have in every x frames a new FCircle (that is not static) at the top of my canvas. When it comes down I want this circle to collide with the text. Don’t know if I made it clear.

Thanks for quick answer Jeremy! :slight_smile:

1 Like

There are several ways of doing this. The collision detection itself you can do without a physics engine – you just need a vertex list for the letter forms. Do you need additional physics, or just a bouncing ball that reacts to letter shapes?

If you don’t need additional physics, then you can create the effect you want by combining these two things:

  1. This geomerative example: Tutorial_07_HelloWorld_getPoints.pde

  2. polygon-circle collision detection. http://jeffreythompson.org/collision-detection/poly-circle.php

Your list of letter points is the polygon. Run it through the polygon-circle collision algorithm to check whether the circle is colliding. Then bounce the ball (or whatever action you choose).

Here is a simple demo with a mouse.

/**
 * LetterCollisions
 * 2020-05-24 Jeremy Douglass - Processing 3.5.4
 * https://discourse.processing.org/t/using-fisica-and-geomerative-to-add-physics-to-typography/21225/2
 * 
 * Move the mouse over letters to test collision detection.
 *
 * Combines:
 * 1. This geomerative example: Tutorial_07_HelloWorld_getPoints.pde
 * 2. polygon-circle collision detection. http://jeffreythompson.org/collision-detection/poly-circle.php
 */

import geomerative.*;

// Declare the objects we are going to use, so that they are accesible from setup() and from draw()
RFont f;
RShape grp;
RPoint[] points;

void setup() {
  // Initilaize the sketch
  size(600, 400);
  frameRate(24);

  // Choice of colors
  background(255);
  fill(255, 102, 0);
  stroke(0);

  // VERY IMPORTANT: Allways initialize the library in the setup
  RG.init(this);

  //  Load the font file we want to use (the file must be in the data folder in the sketch floder), with the size 60 and the alignment CENTER
  grp = RG.getText("Hello world!", "FreeSans.ttf", 90, CENTER);

  // Enable smoothing
  smooth();
}

void draw() {
  // Clean frame
  background(255);

  // Set the origin to draw in the middle of the sketch
  int xoff = width/2;
  int yoff = 3*height/5;
  translate(xoff, yoff);

  // express mouse in new coordinates
  int mx = mouseX-xoff;
  int my = mouseY-yoff;
  boolean hit = false;
  
  // Draw the group of shapes
  noFill();
  stroke(0, 0, 200, 150);
  RG.setPolygonizer(RG.ADAPTATIVE);
  grp.draw();

  // Get the points on the curve's shape
  //RG.setPolygonizer(RG.UNIFORMSTEP);
  //RG.setPolygonizerStep(map(float(mouseY), 0.0, float(height), 0.0, 1.0));

  RG.setPolygonizer(RG.UNIFORMLENGTH);
  RG.setPolygonizerLength(8);  // set gap in px between points around letter
  
  // Loop over individual letters in group
  // and process their points separately
  for (int c=0; c<grp.children.length; c++) {
    points = grp.children[c].getPoints();
    // If there are any points
    if (points != null) {
      // draw the letter outline
      fill(0, 200, 0, 32);
      stroke(0, 200, 0);
      beginShape();
      for (int i=0; i<points.length; i++) {
        vertex(points[i].x, points[i].y);
      }
      endShape();

      // draw box on each vertex
      stroke(0, 128, 0);
      for (int i=0; i<points.length; i++) {
        rect(points[i].x-1, points[i].y-1, 3, 3);
      }
      
      // check for mouse collision with this letter
      // record a hit if mouse is touching this letter
      // and use it for drawing later
      if(polyCircle(points, mx, my, 10)) hit=true;
    }
  }
  // draw the mouse is hit(red) or not(blue)
  if(hit) fill(255,0,0,64);
  else fill(0,0,255,64);
  ellipse(mx, my, 20, 20);
}


// PolyCircle collision functions adapted from:
// http://jeffreythompson.org/collision-detection/poly-circle.php
// 1. replace "PVector" with "RPPoint"
// 2. remove "closest point" circle display

// POLYGON/CIRCLE
boolean polyCircle(RPoint[] vertices, float cx, float cy, float r) {

  // go through each of the vertices, plus
  // the next vertex in the list
  int next = 0;
  for (int current=0; current<vertices.length; current++) {

    // get next vertex in list
    // if we've hit the end, wrap around to 0
    next = current+1;
    if (next == vertices.length) next = 0;

    // get the RPoints at our current position
    // this makes our if statement a little cleaner
    RPoint vc = vertices[current];    // c for "current"
    RPoint vn = vertices[next];       // n for "next"

    // check for collision between the circle and
    // a line formed between the two vertices
    boolean collision = lineCircle(vc.x,vc.y, vn.x,vn.y, cx,cy,r);
    if (collision) return true;
  }

  // the above algorithm only checks if the circle
  // is touching the edges of the polygon – in most
  // cases this is enough, but you can un-comment the
  // following code to also test if the center of the
  // circle is inside the polygon

  // boolean centerInside = polygonPoint(vertices, cx,cy);
  // if (centerInside) return true;

  // otherwise, after all that, return false
  return false;
}


// LINE/CIRCLE
boolean lineCircle(float x1, float y1, float x2, float y2, float cx, float cy, float r) {

  // is either end INSIDE the circle?
  // if so, return true immediately
  boolean inside1 = pointCircle(x1,y1, cx,cy,r);
  boolean inside2 = pointCircle(x2,y2, cx,cy,r);
  if (inside1 || inside2) return true;

  // get length of the line
  float distX = x1 - x2;
  float distY = y1 - y2;
  float len = sqrt( (distX*distX) + (distY*distY) );

  // get dot product of the line and circle
  float dot = ( ((cx-x1)*(x2-x1)) + ((cy-y1)*(y2-y1)) ) / pow(len,2);

  // find the closest point on the line
  float closestX = x1 + (dot * (x2-x1));
  float closestY = y1 + (dot * (y2-y1));

  // is this point actually on the line segment?
  // if so keep going, but if not, return false
  boolean onSegment = linePoint(x1,y1,x2,y2, closestX,closestY);
  if (!onSegment) return false;

  // optionally, draw a circle at the closest point
  // on the line
  //////fill(255,0,0);
  //////noStroke();
  //////ellipse(closestX, closestY, 20, 20);
  
  // get distance to closest point
  distX = closestX - cx;
  distY = closestY - cy;
  float distance = sqrt( (distX*distX) + (distY*distY) );

  // is the circle on the line?
  if (distance <= r) {
    return true;
  }
  return false;
}


// LINE/POINT
boolean linePoint(float x1, float y1, float x2, float y2, float px, float py) {

  // get distance from the point to the two ends of the line
  float d1 = dist(px,py, x1,y1);
  float d2 = dist(px,py, x2,y2);

  // get the length of the line
  float lineLen = dist(x1,y1, x2,y2);

  // since floats are so minutely accurate, add
  // a little buffer zone that will give collision
  float buffer = 0.1;    // higher # = less accurate

  // if the two distances are equal to the line's
  // length, the point is on the line!
  // note we use the buffer here to give a range, rather
  // than one #
  if (d1+d2 >= lineLen-buffer && d1+d2 <= lineLen+buffer) {
    return true;
  }
  return false;
}


// POINT/CIRCLE
boolean pointCircle(float px, float py, float cx, float cy, float r) {
  
  // get distance between the point and circle's center
  // using the Pythagorean Theorem
  float distX = px - cx;
  float distY = py - cy;
  float distance = sqrt( (distX*distX) + (distY*distY) );

  // if the distance is less than the circle's 
  // radius the point is inside!
  if (distance <= r) {
    return true;
  }
  return false;
}


// POLYGON/POINT
// only needed if you're going to check if the circle
// is INSIDE the polygon
boolean polygonPoint(RPoint[] vertices, float px, float py) {
  boolean collision = false;

  // go through each of the vertices, plus the next
  // vertex in the list
  int next = 0;
  for (int current=0; current<vertices.length; current++) {

    // get next vertex in list
    // if we've hit the end, wrap around to 0
    next = current+1;
    if (next == vertices.length) next = 0;

    // get the RPoints at our current position
    // this makes our if statement a little cleaner
    RPoint vc = vertices[current];    // c for "current"
    RPoint vn = vertices[next];       // n for "next"

    // compare position, flip 'collision' variable
    // back and forth
    if (((vc.y > py && vn.y < py) || (vc.y < py && vn.y > py)) &&
         (px < (vn.x-vc.x)*(py-vc.y) / (vn.y-vc.y)+vc.x)) {
            collision = !collision;
    }
  }
  return collision;
}

2 Likes

You may also be interested in reading the past posts of @lolonulu, who has done a lot of work with the geomerative library and text vertex data.

2 Likes

Thank you @jeremydouglass and @lolonulu, you guys are amazing.

1 Like