Multi-touch gear for navigation

Hello to everyone,

I’m a student and not very expert in coding but I’m trying to experiment with touchscreens using the smartphone’s screen as prototype, using conductive objects as interactive gears.

I’d like to put on the screen a three-point of contact object, where each point is positioned as if they where on each of an isosceles triangle’s vertices.
When I rotate the tool, something should happen, like scrolling a page or something.

Now, as a first step I think I should define an orientation, that’s why I thought of an isosceles triangle (acute), so I need to find the apex.

I think I should calculate the sides to find the longer, so I thought about calculating point distances.
Should I calculate the major sides, confronting each distances?
Am I in the right direction?

The next problem is to keep the apex tracked when rotating the object, to have the orientation and tracking the angle of rotation.
What could I use to track the angle of rotation?

Do you have some clues on the algorithm?

thank you in advance,

Al

Hi.
Maybe this link could be a starting point.
Edit: I forgot to link the reference.
https://android.processing.org/reference/multitouch/touches.html

Thanks I’ve been looking to your example but I don’t know how to fit it to my needs.

Here is the code I’ve written.

import controlP5.*;


double dist1, dist2, dist3;

float XG, YG;

float centreX;
float centreY;
float angle;
float rotation0 = 0;
int lineLength = 1000;
float degree;

int apix;

ControlP5 cp5;
int slider = 100;

void setup() {
  fullScreen();
  orientation(LANDSCAPE);
  textFont(createFont("SansSerif", 24 * displayDensity));
  textAlign(CENTER, CENTER);

  cp5 = new ControlP5(this);
  rotate(PI/2);
  cp5.addSlider("slider").setPosition(100, 400).setRange(0, 360).setSize(1000, 100);
}    

void draw() {
  background(255);

  for (int i = 0; i < touches.length; i++) {
    //diameter
    float d = (70 + 70 * touches[i].area) * displayDensity;

    fill(0, 255 * touches[i].pressure);
    ellipse(touches[i].x, touches[i].y, d, d);

    //ellipse text - touches ID
    fill(255, 0, 0);
    text(touches[i].id, touches[i].x + d/2, touches[i].y - d/2);    

    text("T"+ i + ": " + touches[i].x + ", " + touches[i].y, 500, (100+i*100));

    stroke(0);
    strokeWeight(4);


    if (touches.length == 2) {
      line(touches[0].x, touches[0].y, touches[1].x, touches[1].y);
    }    

    if (touches.length == 3) {
      line(touches[0].x, touches[0].y, touches[1].x, touches[1].y);
      line(touches[0].x, touches[0].y, touches[2].x, touches[2].y);
      line(touches[1].x, touches[1].y, touches[2].x, touches[2].y);

      // distance from points
      dist1 = Math.sqrt(Math.pow((touches[0].x - touches[1].x), 2) + Math.pow((touches[0].y - touches[1].y), 2));
      dist2 = Math.sqrt(Math.pow((touches[0].x - touches[2].x), 2) + Math.pow((touches[0].y - touches[2].y), 2));
      dist3 = Math.sqrt(Math.pow((touches[1].x - touches[2].x), 2) + Math.pow((touches[1].y - touches[2].y), 2));

      // center of mass
      XG = (touches[0].x + touches[1].x + touches[2].x)/3;
      YG = (touches[0].y + touches[1].y + touches[2].y)/3;

      centreX = XG;
      centreY = YG;

      float dx = touches[0].x - centreX;
      float dy = touches[0].y - centreY;
      angle = atan2(dy, dx);
      // calculate the end point
      float newX = centreX + cos(angle) * lineLength;
      float newY = centreY + sin(angle) * lineLength;

      // map to the 0-360 range
      degree = degrees(floorMod(angle + HALF_PI, TWO_PI));

      noFill();
      strokeWeight(6);
      stroke(0);
      line(centreX, centreY, newX, newY);
    }
  }


  fill(255);
  stroke(0);
  strokeWeight(6);
  pushMatrix();
  translate(400, 800);
  rotate(rotation0 + angle);
  triangle(0, -100, 100, 100, -100, 100);
  popMatrix();

  cp5.getController("slider").setValue(degree);
}

static float floorMod(float a, float b) {
  return a - b * floor(a / b);
}

As you can see the problem I need to solve is that this shows the real angle, and of course it changes if you first touch the screen with a different foot of the tripoid.
I instead want a relative difference in degrees, so I want that any time you touch with the tripoid, no matter the foot, the 0 is set at that angle, and then to visualize the angle of which it has been rotated.

Another problem is to “map” the numbers to find the angle especially when the range is exceeded, like when the 0 is set to 300° and I move the tripoid of 200°, so the effective 200° movement will be at the original 140° angle, if you follow me.
What formula can I use to calculate this difference in angle?

thank you for anyone’s help!

Maybe I’m not understanding your question, but if your goal is to always point your “center to top line” to the angle where the longest lines crosses, why dont you pick the midle of the triangle base and calculate from there the angle to the top.
The part of your code below will always take the first toutch in consideration.

float dx = touches[0].x - centreX;
      float dy = touches[0].y - centreY;
      angle = atan2(dy, dx);
      // calculate the end point
      float newX = centreX + cos(angle) * lineLength;
      float newY = centreY + sin(angle) * lineLength;

Yes exactly, that is my problem, in fact whenever the “tripod” is not recognized for an instant, the first touch point may change, and so would the angle.

Your idea could work, I’ll test it thanks, but maybe I would still occur into the other issues?

(Hope I make this explanation clear)
Assuming that the case is when the touches get untouched for an instant and the first touch changes, the position of the tripod on the screen, and the middle point pointing to the up vertice, would be the same as the moment before.
How can I calculate a delta of angle? So that at whatever angle I put the tripod on the screen, I calculate the midpoint and the relative angle, but I don’t want to use that relative angle but the amount of angle resulted by the rotation of the tripod itself from moment 0 to moment 1 (maybe I didn’t explain it, but the tripod has to be used as a gear of increment to navigate in the infographics).

Then other problem, in my code I have confined angles range between 0-360. How can I calculate the amount of rotated angle if I come across the case when the angle gets from 360 to 0 and start the rotation again?

This is my main issue, because I use the angle as a reference to change stuff, like the year of which the infographics are displayed. And is bad that whenever the touches change order, I skip from year 2000, to 2018…

As a next step, but this is really secondary, what if the user take off the tripod and put it on again, at a different angle? As for the experience I would like the year to stay the same. Is there a way to keep trace of the “amount of angle increment” and sum it the new amount from the new starting angle?

Thank you very much for your help, I hope to have explained my self well!
Have a nice day!

@Alhugen
If you place code below above the snippet of your code I posted above; the arrow will point to the top of your isosceles triangle, no matter the sequence of the touches.
Of course you also have to change to
float dx = touches[top].x - centreX;
float dy = touches[top].y - centreY;

      md = min((int)dist1, (int)dist2, (int)dist3);
      if((int) dist1 > md) b_dist1 = true;
      if((int) dist2 > md) b_dist2 = true;
      if((int) dist3 > md) b_dist3 = true;
      if(b_dist1 && b_dist2) top = 0;
      if(b_dist1 && b_dist3) top = 1;
      if(b_dist2 && b_dist3) top = 2;
      b_dist1 = false;
      b_dist2 = false;
      b_dist3 = false;
/*
Of course you need to declare the global vaiables
boolean b_dist1, b_dist2, b_dist3;
int top;
float md;
*/

@Alhugen
As for the second questiont; try code below.
It is a tricky solution because mouseDragged() is far less fluent then I expected; so I had to build-in a small timer. Put the code right below background()

fill(0);
  if (mouse_drag) delta_degree = int(first_degree-degree);
  if (!mouse_drag) lock = false;
  mouse_drag = false;
  text("delta_degree = "+delta_degree, 150, 200);
  text("degree = "+int(degree), 150, 100);

And,


void mouseDragged() {
  if (!lock && millis()-start_time > 150) {
    first_degree = degree;   
    lock = true;
  }
  mouse_drag = true;
  start_time = millis();
}
/*
And of course declare variables
boolean mouse_drag, lock;
float start_time, first_degree;
int delta_degree;
*/

I have ideas about solving the other problems, but first I have to know if this is working for you.