/************************************************/ /* File: fittscalc.cpp */ /* Author: Andrew R. Freed */ /* Modified date: 5/6/03 */ /* */ /* This file computes the 10^10 telephone */ /* number analysis. The methods contained here */ /* can be easily modified and expanded */ /************************************************/ #include #include struct digit{ int x; int y; int width; int height; }; struct phone{ int filenum; int startx; int starty; digit buttons[11]; //digit[0-9] is 0-9, digit[10] is X }; void readDataFile(); double calcFittsTime(int phonenum, int frombutton, int tobutton, int mode); double computeTable(int phonenum, int frombutton, int tobutton, int mode); double overall[] = {0., 0.,0.,0.,0.,0.,0.,0.,0.,0.,0.}; //phones 1-10, start+0-9, end 0-9 double all[11][11][10]; void dialAll(); void dialGeneric(int, int, int, int, int, int, int, int, int, int); const int XPOS = 10; const int START = -1; const int SCREEN_WIDTH = 1024; const int SCREEN_HEIGHT = 768; const double FITTS_A = 100.0; const double FITTS_B = 150.0; const double FITTS_K = 100.0; const enum {TALL, WIDE}; bool printHeaders = false; phone phones[10+1]; //will index 1 to 10, skipping phone[0] void main() { readDataFile(); for(int a=1;a<=10;a++){ for(int b=0;b<=9;b++){ for(int c=0;c<=9;c++) { computeTable(a,b,c,TALL); } computeTable(a,START,c,TALL); } } for(int i=1; i<=10; i++) cout << i << "\t"; cout << endl; for(i=1; i<=10; i++) cout << phones[i].filenum << "\t"; cout << endl; dialAll(); for(i=1;i<=10;i++) cout << overall[i] << "\t"; cout << endl; } void dialAll() { int a,b,c,d,e,f,g,h,i,j; for(a=0;a<=9;a++){ for(b=0;b<=9;b++){ for(c=0;c<=9;c++){ for(d=0;d<=9;d++){ for(e=0;e<=9;e++){ for(f=0;f<=9;f++){ for(g=0;g<=9;g++){ for(h=0;h<=9;h++){ for(i=0;i<=9;i++){ for(j=0;j<=9;j++){ dialGeneric(a,b,c,d,e,f,g,h,i,j); }}}}}}}}}} for(a=1;a<=10;a++){ for(b=1;b<=10;b++){ overall[a] /= 10.0; }} } //Computes movement time from a source to destination button on a given phone //Last parameter TALL or WIDE indicates whether the movement is mostly //vertical or horizontal. It is OK to assume TALL movement, then the function //will fix this if the movement is purely lateral double calcFittsTime(int phonenum, int frombutton, int tobutton, int mode) { //First calculate distance of movement by looking up button locations double distance, xdist, ydist, width, time; if(frombutton == START) { xdist = ( (((double) SCREEN_WIDTH)/2.0) - ( phones[phonenum].startx + phones[phonenum].buttons[tobutton].x + (((double) phones[phonenum].buttons[tobutton].width)/2.0) ) ); ydist = ( (((double) SCREEN_HEIGHT)/2.0) - ( phones[phonenum].starty + phones[phonenum].buttons[tobutton].y + (((double) phones[phonenum].buttons[tobutton].height)/2.0) ) ); } else { xdist = ( (phones[phonenum].buttons[frombutton].x + (((double) phones[phonenum].buttons[frombutton].width)/2.0) ) - (phones[phonenum].buttons[tobutton].x + (((double) phones[phonenum].buttons[tobutton].width)/2.0) ) ); ydist = ( (phones[phonenum].buttons[frombutton].y + (((double) phones[phonenum].buttons[frombutton].height)/2.0) ) - ( phones[phonenum].buttons[tobutton].y + (((double) phones[phonenum].buttons[tobutton].height)/2.0) ) ); } distance = sqrt(xdist * xdist + ydist * ydist); if(distance < 0.1) return 0.0; // if already on target, no time! if(mode == WIDE) { width = distance * ((double) phones[phonenum].buttons[tobutton].width) / abs(xdist); } else { //If the movement is horizontal we should compute it as such if(ydist < 0.1) return calcFittsTime(phonenum, frombutton, tobutton, WIDE); else width = distance * ((double) phones[phonenum].buttons[tobutton].height) / abs(ydist); } //*** Feel free to insert a different Fitts' Law equation //convert to log 2 by doing log10(x)/log10(2) time = FITTS_A + FITTS_B * (log(2.0 * distance / width)/ log(2.0)); //time = FITTS_K * (log(0.5 + distance / width)/log(2.0)); if(time < 100.0) time = 100.0; return time; } //For the 10^10 number analysis, I first populate a table with all the //possible movements. This table is 1100 entries. It is more convenient //to do 10^10 lookups on this table than to recompute 10^10 times! double computeTable(int phonenum, int frombutton, int tobutton, int mode) { double time=calcFittsTime(phonenum,frombutton,tobutton,mode); if(frombutton != START) all[phonenum][frombutton][tobutton] = time; else all[phonenum][10][tobutton] = time; return time; } void readDataFile() { fstream fin; //Fix this pathname with your own! //fin.open("c:\\data\\phone-posititons.txt",ios::in); fin.open("phone-positions.txt",ios::in); int phonenum, filenum; int i,j; //x position for(i=1; i<=10; i++) // over all phones { fin >> phonenum >> filenum; phones[phonenum].filenum = filenum; for(j=0; j<=10; j++) // over all buttons fin >> phones[phonenum].buttons[j].x; } //y position for(i=1; i<=10; i++) // over all phones { fin >> phonenum >> filenum; for(j=0; j<=10; j++) // over all buttons fin >> phones[phonenum].buttons[j].y; } //Width for(i=1; i<=10; i++) // over all phones { fin >> phonenum >> filenum; for(j=0; j<=10; j++) // over all buttons fin >> phones[phonenum].buttons[j].width; } //Height for(i=1; i<=10; i++) // over all phones { fin >> phonenum >> filenum; for(j=0; j<=10; j++) // over all buttons fin >> phones[phonenum].buttons[j].height; } //Read in phone starting positions for(i=1; i<=10; i++) { fin >> phonenum >> filenum; fin >> phones[phonenum].startx >> phones[phonenum].starty; } } void dialGeneric(int first, int second , int third, int fourth, int fifth, int sixth, int seventh, int eighth, int ninth, int tenth) { double time=0.0; for(int phonenum=1; phonenum<=10; phonenum++) { time = all[phonenum][START][first] + all[phonenum][first][second] + all[phonenum][second][third] + all[phonenum][third][fourth] + all[phonenum][fourth][fifth] + all[phonenum][fifth][sixth] + all[phonenum][sixth][seventh] + all[phonenum][seventh][eighth] + all[phonenum][eighth][ninth] + all[phonenum][ninth][tenth]; //time is in ms, convert to s time /= 1000.0; overall[phonenum] += time; //cout << time << "\t"; } }