How do I scale real motion (e.g., miles/hour) to pixels/second?

edited December 2016 in Questions about Code

Daniel Shiffman made a tutorial on how to draw a moving ball.

In it I commented (reproduced below) on the following lines of code:

  float circleX;
  float xspeed = 0.2;

  void draw(){
  ...
  ellipse(circleX,height/2,32,32);
  circleX = circleX + xspeed;
  }

We're using floating arithmetic to implement motion that occurs in discrete steps in our program. Correct me if I'm wrong: There is no notion of ".2" of a pixel (a fraction of a pixel) and that the ellipse function, in its implementation, is overloaded so that the location of the ellipse is drawn at Floor(x_position, y_position) for float variables x_position, y_position.

By using floating values, we force processing to implicitly reference a step function to determine where to draw an ellipse. So if we have a step size of .2, the variable that stores position must increment by the step size 5 times before we actually see the ellipse move 1 pixel unit over. The delay in motion that we perceive corresponds to the delay resulting from incrementing the float to the next highest whole number that processing interprets as a new coordinate.


If the above bolded statement is true and the delay is a function of the time required to increment a float, is it then necessary to measure this delay in order to perform unit conversion (mi/hr to px/sec)?

The end goal is to map some real-world rate to a rate represented in processing such that the rate in processing isn't affected by variables that a programmer can't control (cpu scheduling, for instance). Basically, if I wanted a mapping of 70mi/hr to 70px/sec, I'd like for the processing rate to be a consistent 70px/sec and not 67px/sec or 80px/sec.

Answers

  • edited December 2016

    There is no notion of ".2" of a pixel (a fraction of a pixel)

    AFAIK you can achieve a similar effect with blending, related to antialiasing.
    For example, let's say you have to draw a line with coordinates (1.2, 0) and (1.2, 100). Since half a pixel does not exist, you instead reduce the alpha and draw with first pixel at 80% alpha and second pixel at 20% alpha (I think, but perhaps my calculations are wrong) and so one for the whole line. But I don't know if that is implemented in OpenGL/Processing.
    Lines that are not horizontal/vertical are also drawn with similar processes - example.
    EDIT: The algorithm mentioned in my example is also capable of drawing lines whose endpoints aren't integer values.

  • edited December 2016 Answer ✓

    Correct me if I'm wrong: There is no notion of ".2" of a pixel (a fraction of a pixel) and that the ellipse function, in its implementation, is overloaded so that the location of the ellipse is drawn at Floor(x_position, y_position) for float variables x_position, y_position.

    @GoToLoop already answered your question, but to expand on @Lord_of_the_Galaxy 's response about how pixel/sub-pixel values work:

    I believe this idea of taking a floor is a major simplification to the point of being mistaken. Float coordinates, including sub-pixel anti-aliasing, is built into almost everything in Processing all-the-way-down. This has all kinds of implications for 3D and for scaling.

    For example, consider "point()" and "scale()". It doesn't matter that there is no pixel between 1 and 2 -- you can still draw a point to any arbitrary floating-point location, e.g. 0.25, 0.5, 0.75 etc.

    When we use scale to zoom in on a size 200x200 sketch to a factor of 200, we see a point rendered as a 1 pixel-wide circle. we also see that it is not taking the floor of anything with respect to pixels -- it is being rendered with a floating point location to whatever the frame of reference happens to be.

    /**
     * A Floating Point
     * 2016-12-14 Jeremy Douglass Processing 3.2.3
     * When we zoom in on a single point in a single pixel,
     * we see floating point locations between pixel steps being rendered.
     */
    
    float offset;
    void setup(){
      size(200,200);
    }
    
    void draw(){
      background(192);
      offset = millis()%1000/1000.0;
      pushMatrix();
        scale(200);
        point(0.5+offset, 0.5+offset);
      popMatrix();
      text(offset,10,10);
    }
    

    AFloatingPoint--screenshot

    If you want to see how this actually works in your case, check out the ellipse() source code and follow it through the implementation, to drawShape() in PGraphics etc. Note that at a certain point it drawing becomes renderer-specific (Java2D, P3D etc.)

  • edited December 2016

    This post discusses how to achieve framerate independent motion

  • Your bold went off, @quark.

  • Scale, unfortunately, works in a way different from antialiasing/using the alpha channel to handle non-integral (is that the word?) endpoints.
    Scale just multiplies the transformation matrix by the specified value.
    AFAIK, that's different from what I was talking about, i.e. an algorithm for rasterisation or whatever it is called. Scale happens much before rasterisations do.

  • Agreed, both scale and anti-aliasing are different aspects of what I described as the general paradigm: "float coordinates [are] built into almost everything in Processing all-the-way-down."

    So: scale multiplies the transformation matrix -- with floats, not by mapping stepped pixel units to other stepped units. The matrix then embeds points as (conceptually) circles, not a 1x1 pixel -- and the radius of those circles is again expressed in floats. Those circles are then anti-aliased to the screen with an inter-pixel edge that expressed in floats. & etc. So in the example sketch you can see that the primitives, the matrix, and the anti-aliased edges are all "floats all-the-way-down."

  • Precisely.
    Ignoring the fact that floats aren't quite as precise as doubles and Processing uses floats (does anyone know why?), or that floats/doubles have a limit to their precision, computer graphics as a whole use floating point arithmetic and not integers (at least as of now, maybe previously it was different or sometime later this may change).
    Let's not discuss further unless someone has a problem with this reasoning.

Sign In or Register to comment.