Accumulate rotation with snapping

It turns out that just using floor for the minutes does work after all.

I did try that, it gets janky when changing directions. There might be a way to make that work but it becomes unnecessarily clunky as far as I can see.

I was able to get this idea to work, too, but it doesn’t seem to work differently than just using floor for what I’m trying to do right now.

I might try something with this but its not what I was trying to solve at the moment.

Thanks for the help, it seems like this is pretty much working the way I wanted now.

// clock face
PVector c, c2;
float r, r2;
int divisions;

// mouse
PVector m, pm, mo, po;
float offset = -HALF_PI, heading, cross;
boolean cwDrag, ccwDrag, measuring, measureBegan;

// math
float snapAngle, dragAngle, dragAngleSnap;
float startAngleOffset, startAngleSnap;
float accDragAngle, accAngleSnap, accMinAngle;
int startSec, endSec, accSec, accMin;

PFont mono, sans;
void setup() {
  size(400, 600);
  mono = createFont("Monospaced", 18);
  sans = createFont("Helvetica", 18);
  //pixelDensity(2);
  ellipseMode(RADIUS);
  r = width/2 - 70;
  c = new PVector(width/2, height - r - 70);
  c2 = new PVector(50, height-50);
  r2 = 30;
  m = new PVector();
  pm = new PVector();
  divisions = 12;
  snapAngle = TWO_PI/divisions;
}
void draw() {
  background(127);
  checkMouse();

  dragAngle = heading;
  dragAngleSnap = snapRound(dragAngle);

  if (mousePressed && !measureBegan) {
    measureBegan = true;

    startAngleSnap = dragAngleSnap;
    startAngleOffset = startAngleSnap - dragAngle;

    startSec = round(startAngleSnap/snapAngle);
    startSec *= 60/divisions;

    endSec = startSec;

    accDragAngle = 0;
    accDragAngle -= startAngleOffset;

    accAngleSnap = 0;
    accSec = 0;
    accMin = 0;
  }

  if (measuring) {
    endSec = round(dragAngleSnap/snapAngle);
    endSec *= 60/divisions;

    accDragAngle += calcDragAngleDiff();

    accAngleSnap = snapRound(accDragAngle);

    accSec = round(accAngleSnap/snapAngle);
    accSec *= 60/divisions;

    accMin = floor(accSec/60);
    accMinAngle = accMin*TWO_PI/60;
  }

  showNeedles();
  showFace(c, r);
  showFace(c2, r2);
  showTextLeft();
  showTextRight();
}
void mouseDragged() {
  measuring = true;
}
void showTextLeft() {
  String startAngleSnapStr = nfs(startAngleSnap, 3, 2) + " " + "startAngleSnap";
  String dragAngleStr = nfs(dragAngle, 3, 2) + " " + "dragAngle";
  String dragAngleSnapStr = nfs(dragAngleSnap, 3, 2) + " " + "dragAngleSnap";
  String accDragAngleStr = nfs(accDragAngle, 3, 2) + " " + "accDragAngle";
  String accAngleSnapStr = nfs(accAngleSnap, 3, 2) + " " + "accAngleSnap";
  String accMinAngleStr = nfs(accMinAngle, 3, 2) + " " + "accMinAngle";

  String[] strings = {
    startAngleSnapStr, 
    dragAngleStr, 
    dragAngleSnapStr, 
    accDragAngleStr, 
    accAngleSnapStr, 
    accMinAngleStr
  };

  float textH = 18;
  textFont(mono);
  textSize(textH);
  textAlign(LEFT, TOP);
  fill(0);
  for (int i=0; i<strings.length; i++) {
    text(strings[i], 5, 5+i*textH);
  }
}
void showTextRight() {
  String newLine = "";
  String startSecStr = "startSec" + " " + nfs(startSec, 2);
  String endSecStr = "endSec" + " " + nfs(endSec, 2);
  String accSecStr = "accSec" + " " + nfs(accSec, 2);
  String accMinStr = "accMin" + " " + nfs(accMin, 2);
  String accTimeSign = accMin<0 || accSec<0 ? "-" : " ";
  String accTimeStr = "accTime" + " " + accTimeSign + nf(abs(accMin), 2) + ":" + nf(abs(accSec%60), 2);

  String[] strings = {
    startSecStr, 
    newLine, 
    endSecStr, 
    newLine, 
    accSecStr, 
    accMinStr, 
    newLine, 
    accTimeStr
  };

  float textH = 18;
  textFont(mono);
  textSize(textH);
  textAlign(RIGHT, TOP);
  fill(0);
  for (int i=0; i<strings.length; i++) {
    text(strings[i], width-5, 5+i*textH);
  }
}
float calcDragAngleDiff() {
  float diff = PVector.angleBetween(mo, po);
  if (ccwDrag) diff = -diff;
  return diff;
}
float snapRound(float a) {
  a /= snapAngle;
  a = round(a);
  a *= snapAngle;
  return a;
}
void checkMouse() {
  m.set(mouseX, mouseY);
  mo = PVector.sub(m, c);
  mo.rotate(-offset);

  pm.set(pmouseX, pmouseY);
  po = PVector.sub(pm, c);
  po.rotate(-offset);

  cross = mo.cross(po).z;
  cwDrag = cross < 0;
  ccwDrag = cross > 0;

  heading = mo.heading();
  heading = (heading > 0) ? heading : heading + TWO_PI;

  if (!mousePressed) {
    measuring = false;
    measureBegan = false;
  }
}
void showNeedles() {
  showNeedle(c, r, dragAngle, color(255), 7);
  showNeedle(c, r, startAngleSnap, color(50, 200, 65), 5);
  showNeedle(c, r, dragAngleSnap, color(200, 35, 40), 3);

  showNeedle(c2, r2, accMinAngle, color(0), 3);
  showNeedle(c2, r2, dragAngleSnap, color(200, 35, 40), 3);
}
void showNeedle(PVector loc, float radius, float angle, color needleColor, int needleSize) {
  strokeWeight(needleSize);
  stroke(needleColor);
  angle += offset;
  lineStartRA(loc, radius+radius/5.5, angle);
}
void circleCR(PVector loc, float radius) {
  ellipse(loc.x, loc.y, radius, radius);
}
void lineStartRA(PVector start, float r, float a) {
  PVector end = new PVector(cos(a), sin(a));
  end.mult(r);
  end.add(start);
  linePts(start, end);
}
void linePts(PVector start, PVector end) {
  line(start.x, start.y, end.x, end.y);
}
void showFace(PVector loc, float radius) {
  PVector tickLoc = new PVector();
  PVector textLoc = new PVector();

  strokeWeight(1);
  noStroke();
  fill(127, 200);
  circleCR(loc, radius);

  for (int i=0; i<divisions; i++) {
    float inc = TWO_PI/divisions;
    float angle = i*inc;
    angle += offset;
    float x = cos(angle);
    float y = sin(angle);

    tickLoc.set(x, y);
    tickLoc.mult(radius);
    tickLoc.add(loc);

    stroke(0);
    noFill();
    circleCR(tickLoc, radius/40);

    textLoc.set(x, y);
    textLoc.mult(radius+radius/3);
    textLoc.add(loc);

    int numVal = (i+divisions-1)%divisions+1;
    numVal *= 60/divisions;
    String numTextStr = nf(numVal, 1);
    fill(0);
    textFont(sans);
    textSize(radius/5.5);
    textAlign(CENTER, CENTER);
    text(numTextStr, textLoc.x, textLoc.y);
  }
}
1 Like