Implementation
Implementation
Implementation
#ifdef ANM_OSX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#endif
#include "aoi.h"
// read in name
s >> rhs.name;
do {
// read in AOI coordinates
s >> x; s >> c; s >> y;
rhs.coord[i][0] = x;
rhs.coord[i][1] = y;
i++;
} while( s.get(c) && (c != ’\n’) && ((n = s.peek()) != ’\n’) && (i<4) );
return(s);
}
#include "aoievent.h"
// scanpath implementation
// (c) Andrew T. Duchowski
return s;
}
Sep 01 2009 10:39 cluster.cpp Page 1/3
#include <iostream> for(int i=0; i<2; i++) { mean[i] = x(i); mean(i) = x(i); };
#include <iomanip>
#include <sstream> // if we don’t initialize major, minor axes, cluster rendering bombs
#include <string> major.push_back(0.0);
#include <cmath> major.push_back(0.0);
#include <cstdlib> minor.push_back(0.0);
minor.push_back(0.0);
#ifdef ANM_OSX
#include <OpenGL/gl.h> // set centroid time to incoming point
#include <OpenGL/glu.h> time = x.gettimestamp();
#include <OpenGL/glext.h>
#include <GLUT/glut.h> t_in = t_out = time;
#else }
#include <GL/gl.h>
#include <GL/glu.h> /////////////////////////// operators: copy assignment /////////////////////////
//#include <GL/glext.h> Cluster Cluster::operator=(const Cluster& rhs)
#include <GL/glut.h> {
#endif if(this != &rhs) {
mean = rhs.mean;
#ifndef INFINITY time = rhs.time;
#define INFINITY MAXFLOAT t_in = rhs.t_in;
#endif t_out = rhs.t_out;
// render labels
if(letter>0) {
double offset = sqrt(width*width + height*height)/30.0;
glPushMatrix();
glBegin(GL_LINES);
glVertex2f(mean[0]*width,mean[1]*height);
glVertex2f(mean[0]*width+minor[0]*width+offset,
mean[1]*height+minor[1]*height+offset);
glEnd();
glTranslatef(mean[0]*width+minor[0]*width+offset,
mean[1]*height+minor[1]*height+offset,0.0);
glScalef(0.1,0.1,1.0);
// glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12,letter);
Sep 01 2009 10:39 crand.cpp Page 1/1
#include <cstdlib> *idum=IA*(*idum-k*IQ)-IR*k;
#include <cmath> if(*idum < 0) *idum += IM;
#include <unistd.h> if(j<NTAB) iv[j] = *idum;
#include <sys/types.h> }
#include <sys/time.h> iy = iv[0];
}
#include "crand.h" k=(*idum)/IQ; // start here when not initializing.
*idum = IA*(*idum-k*IQ)-IR*k; // compute idm=(IA*idum) % IM without
double set_range(double rnum, double lo, double hi) if(*idum < 0) *idum += IM; // overflows by Schrage’s method.
{ j = iy/NDIV; // will be in the range 0..NTAB-1.
// returns random number in range [lo,hi] iy = iv[j]; // output previously stored valued
return(rnum*(hi-lo) + lo); iv[j] = *idum; // and refill the shuffle table.
} if((temp=AM*iy) > RNMX) return RNMX; // because users don’t expect
else return temp; // endpoint values.
float crand(int init) }
{
static long seed;
long pid;
struct timeval tp;
struct timezone tzp;
// interface to ran1: call with init=1 to init, init=0 for pseudo-random no.
if(init) {
// initialize generator with current time of day, in microseconds
pid = (long)getpid();
gettimeofday(&tp,&tzp);
seed = -( (tp.tv_sec*1000000 + tp.tv_usec) & pid );
ran1(&seed);
return(0.);
}
else
return(ran1(&seed));
}
b = (2.718281828+shape)/2.718281828; // e = 2.7818281828
for(k=1;k<=500;++k) {
u1 = crand(0);
u2 = crand(0);
q = b*u1;
if(q > 1) {
y = -log((b-q)/shape);
if( u2 <= exp((shape-1)*log(y)) ) return(scale*y);
}
else {
y = exp( log(q)/shape );
if(u2 <= exp(-y)) return(scale*y);
}
}
return(0.); // error, could not find gamma random variate
}
while ( dx < dy ) { // Octant 6 (09:00 - 10:30) with center (c,d) and axes (r,s) [this class uses (h,k) (rx,ry) instead]
PUTPIXEL(xp, yp);
DECREMENT_Y(); coefficients for axis-aligned ellipse:
if ( d > 0 ) INCREMENT_X(); A = s^2
Sep 01 2009 10:39 ellipse.cpp Page 3/4
B = r^2 N2*(r2*c2 + s2*d2) +
C = -2s^2c 2*M*N*c*d*(s2 - r2) +
D = -2r^2d -r2*s2;
E = 0.0; /*
F = s^2c^2 + r^2d^2 - r^2s^2 // translated, no rotation, axis-aligned
A = s2;
coefficient for ellipse rotated by arbitrary angle about origin B = r2;
with M = cos(angle), N = sin(angle): C = -2.0*s2*c;
A = s^2M^2 + r^2N^2 D = -2.0*r2*d;
B = s^2N^2 + r^2M^2 E = 0.0;
C = -2(s^2Mc + r^2Nd) F = s2*c2 + r2*d2 - r2*s2;
D = 2(s^2Nc - r^2Md) */
E = 2MN(r^2 - s^2) /*
F = s^2c^2 + r^2d^2 - r^2s^2 // rotated about origin
A = s2*M2 + r2*N2;
(note that the above rotates ellipse about z-axis, hence not "in place", B = s2*N2 + r2*M2;
or in other words, assuming ellipse is at the origin; thus to draw it, C = -2.0*(s2*M*c + r2*N*d);
don’t shift to origin and then back) D = 2.0*(s2*N*c - r2*M*d);
*/ E = 2.0*M*N*(r2 - s2);
F = s2*c2 + r2*d2 - r2*s2;
double c=h, d=k; */
double r=rx, s=ry; /*
double r2=r*r, s2=s*s; // translated to (c,d), rotated (about origin), then shifted back to origin
double c2=c*c, d2=d*d; // (useless! ellipses are stuck at the origin)
double M2 = M*M, N2 = N*N; A = s2*M2 + r2*N2;
double P,Q,R; B = s2*N2 + r2*M2;
// double one_M = (1.0-M); C = 2.0*(s2*M*c - r2*N*d) - 2.0*c*(r2*N2 + s2*M2) + 2.0*M*N*d*(r2 - s2);
// double one_M2 = one_M*one_M; D = 2.0*(s2*N*c + r2*M*d) - 2.0*d*(r2*M2 + s2*N2) + 2.0*M*N*c*(r2 - s2);
E = 2.0*M*N*(s2 - r2);
/* F = N2*(s2*d2 + r2*c2) + one_M2*(s2*c2 + r2*d2) +
// axis-aligned at origin 2.0*M*N*d*c*(s2 - r2) + 2.0*N*d*c*(r2 - s2) +
A = s2; -r2*s2;
B = r2; */
C = 0.0; // We use
D = 0.0; // Ax^2 + By^2 + Cx + Dy + Exy + F = 0
E = 0.0; // but polynomial used in intersection code assumes
F = -r2*s2; // Ax^2 + Bxy + Cy^2 + Dx + Ey + F = 0
*/ //std::cerr << "creating polynomial with";
/* //std::cerr << " A = " << A;
// rotated at origin //std::cerr << " B = " << B;
A = s2*M2 + r2*N2; //std::cerr << " C = " << C;
B = s2*N2 + r2*M2; //std::cerr << " D = " << D;
C = 0.0; //std::cerr << " E = " << E;
D = 0.0; //std::cerr << " F = " << F;
E = 2.0*M*N*(s2 - r2); //std::cerr << std::endl;
F = -r2*s2; quadratic = Polynomial(F,D,C,B,E,A);
*/
// rotated at origin, translated to (c,d) // determine limits, xmin, xmax, ymin, ymax by solving
// this is what we want! I got some inspiration for this approach // parametric Pt^2 + Qt + R = 0
// from <http://www.3dsoftware.com/Math/PlaneCurves/EllipseAlgebra/> P = 4.0*A*B - E*E;
// which said that you have to evaluate the quadratic at each Q = 4.0*B*C - 2.0*D*E;
// stage of the transformation, hence, start with the above simple R = 4.0*B*F - D*D;
// ellipse, then, rotate, then translate. //std::cerr << "calculating roots of polynomial with";
A = s2*M2 + r2*N2; //std::cerr << " P = " << P;
B = s2*N2 + r2*M2; //std::cerr << " Q = " << Q;
C = -2.0*c*(s2*M2 + r2*N2) - 2.0*M*N*d*(s2 - r2); //std::cerr << " R = " << R;
D = -2.0*d*(s2*N2 + r2*M2) - 2.0*M*N*c*(s2 - r2); //std::cerr << std::endl;
E = 2.0*M*N*(s2 - r2); vector<double> xaxis = Polynomial(P,Q,R).roots();
F = M2*(s2*c2 + r2*d2) + switch((int)xaxis.size()) {
Sep 01 2009 10:39 ellipse.cpp Page 4/4
case 0: xmin = xmax = 0.0; break;
case 1: xmin = xmax = xaxis[0]; break;
case 2: xmin = xaxis[1]; xmax = xaxis[0]; break;
}
P = 4.0*A*B - E*E;
Q = 4.0*A*D - 2.0*C*E;
R = 4.0*A*F - C*C;
//std::cerr << "calculating roots of polynomial with";
//std::cerr << " P = " << P;
//std::cerr << " Q = " << Q;
//std::cerr << " R = " << R;
//std::cerr << std::endl;
vector<double> yaxis = Polynomial(P,Q,R).roots();
switch((int)yaxis.size()) {
case 0: ymin = ymax = 0.0; break;
case 1: ymin = ymax = yaxis[0]; break;
case 2: ymin = yaxis[1]; ymax = yaxis[0]; break;
}
}
#include "event.h"
// scanpath implementation
// (c) Andrew T. Duchowski
s >> rhs.t;
s >> rhs.name;
s >> key;
s >> rhs.x;
s >> rhs.y;
return s;
}
return s;
}
Sep 01 2009 10:39 filter.cpp Page 1/1
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>
#include "filter.h"
// scanpath implementation
// (c) Andrew T. Duchowski
return s;
}
return s;
}
Sep 01 2009 10:39 fixation.cpp Page 1/1
#include <iostream> return s;
#include <iomanip> }
#include <string>
#include <math.h> Fixation::Fixation(const Fixation& rhs) : Point(rhs)
{
#ifdef ANM_OSX number = rhs.number;
#include <OpenGL/gl.h> duration = rhs.duration;
#include <OpenGL/glu.h> amplitude = rhs.amplitude;
#include <OpenGL/glext.h> radius = rhs.radius;
#else type = rhs.type;
#include <GL/gl.h> }
#include <GL/glu.h>
//#include <GL/glext.h> Fixation::Fixation(Fixation* rhs) : Point(rhs)
#endif {
number = rhs->number;
using namespace std; duration = rhs->duration;
amplitude = rhs->amplitude;
#include "point.h" radius = rhs->radius;
#include "fixation.h" type = rhs->type;
}
// fixation class implementation
// (c) Andrew T. Duchowski Fixation Fixation::operator=(const Fixation& rhs)
{
/////////////////////////// friends //////////////////////////////////////////// if(this != &rhs) {
istream& operator>>(istream& s,Fixation& rhs)
{ // copy base class data members (must use base class cast)
int i=0; ((Point &)*this) = rhs;
char c,n;
double f; // copy local data members
number = rhs.number;
// assume we’re reading in Tobii’s FXD (Fixation Data) data file duration = rhs.duration;
// Fix number,Timestamp,Duration,GazepointX,GazepointY amplitude = rhs.amplitude;
radius = rhs.radius;
// read in Fix number type = rhs.type;
s >> rhs.number; }
return *this;
// read timestamp }
s >> rhs.timestamp;
// read duration
s >> rhs.duration;
do {
// read in fixation coordinate
s >> f; rhs[i] = f; i++;
} while( s.get(c) && (c != ’\n’) && ((n = s.peek()) != ’\n’) && (i<2) );
return(s);
}
void FixationFilter::restoreOutPoints(void)
{
int i, ii; // dummy ring indices
// if there were some points that temporarily went out of the fixation region
if(nPresOut > 0) {
// undo the hypothesis that they were outside the fixation and declare
// them now to be part of the fixation
for(i = 1; i <= nPresOut; i++) {
ii = ringIndex - i;
if(ii < 0) ii += RING_SIZE;
// ATD: I think if nPresOut is not (range) limited to RING_SIZE
// BUG! ii goes -ve (way -ve, out to -184)
// std::cerr << "ii = " << ii << std::endl;
if(gaze_found_ring[ii]) {
nPresFixSamples++;
xPresFixSum += x_gaze_ring[ii];
yPresFixSum += y_gaze_ring[ii];
eye_motion_state[ii] = FIXATING;
}
}
// set the number of "out" points to be zero
nPresOut = 0;
}
}
Sep 01 2009 10:39 gltexobj.cpp Page 1/13
/**************************************************************************** #include <matrix.h>
** $Id: qt/gltexobj.cpp 3.1.2 edited Nov 8 2002 $ #include <quaternion.h>
**
** Copyright (C) 1992-2002 Trolltech AS. All rights reserved. #include "gltexobj.h"
** #include "crand.h"
** This file is part of an example program for Qt. This example #include "distn.h"
** program may be used, distributed and modified without limitation.
** const int textures=2;
*****************************************************************************/ ulong texID[] = {GL_TEXTURE0, GL_TEXTURE1};
#ifdef ANM_OSX // looks like font selection under X11 may be limited...
#include <OpenGL/gl.h> textfont.setFamily("Fixed");
#include <OpenGL/glu.h> textfont.setRawMode(true);
#include <OpenGL/glext.h> textfont.setStyleStrategy(QFont::OpenGLCompatible);
#else //textfont.setPixelSize(10);
#include <GL/gl.h> textfont.setPointSize(10);
#include <GL/glu.h> textfont.setFixedPitch(true);
#include <GL/glext.h> textfont.setStyleHint(QFont::AnyStyle,QFont::PreferBitmap);
#endif
QFontInfo textfontinfo(textfont);
#include <qgl.h>
#include <qtimer.h> std::cout << "family: " << textfontinfo.family() << " ";
#include <qevent.h> std::cout << "pointsize: " << textfontinfo.pointSize() << " ";
#include <qstring.h> std::cout << "weight: " << textfontinfo.weight() << " ";
#include <qfont.h> std::cout << "italic: " << textfontinfo.italic() << " ";
#include <qfontinfo.h> std::cout << std::endl;
#include <qimage.h>
#include <qfiledialog.h> srand((unsigned int)time(&tloc));
#include <qmessagebox.h> }
void GLTexobj::label() // print out unique random sequence strings (for position similiarity)
{ std::cerr << "unique random sequences:" << std::endl;
int scanpaths=0; for(Ra_pos=Ra_scanpath.begin(); Ra_pos != Ra_scanpath.end(); Ra_pos++)
int Ra_scanpaths=Ra_scanpath.size(); std::cerr << (*Ra_pos)->printUniqueString() << std::endl;
// now part of GLTexobj
// static int letter(0); // pairwise similarity
set<string,longer> ulcsset; S__S(scanpaths); // between subject scanpaths only
map<string,Scanpath* >::iterator pos,npos; Ra_S(scanpaths); // between subject and random scanpaths
vector<Scanpath* >::iterator Ra_pos, Ra_npos; std::cerr << "sequence similarity (S_s):" << std::endl;
parsingDiagram(sequence,Ra_sequence);
// get each scanpath to grow its kd-tree std::cerr << "position similarity (S_p):" << std::endl;
for(pos=scanpath.begin(); pos != scanpath.end(); pos++, scanpaths++) parsingDiagram(position,Ra_position);
pos->second->growkdtree((float)width(),(float)height());
// same for all random scanpaths // QFileDialog* fd;
for(Ra_pos=Ra_scanpath.begin(); Ra_pos != Ra_scanpath.end(); Ra_pos++) QString qfilename;
(*Ra_pos)->growkdtree((float)width(),(float)height()); std::string strpath,strfull;
std::ofstream ofs;
// if there are no scanpaths and only 1 Random scanpath, std::ostringstream os;
// label random scanpath sequentially
Sep 01 2009 10:39 gltexobj.cpp Page 5/13
/* --- this makes more sense by batch processor, not GUI // if(i!=j) (*position)[i][j] = pos->second->p_similarity(npos->second);
// get directory where to dump stats (*sequence)[i][j] = pos->second->s_similarity(npos->second);
#ifdef ANM_OSX (*position)[i][j] = pos->second->p_similarity(npos->second);
fd = new QFileDialog("../../.././","Choose directory",this); }
#else }
fd = new QFileDialog("./","Choose directory",this); // print lower diagonal (matrix is symmetrical, only need half of it)
#endif std::cerr << "sequence similarity (S_s):" << std::endl << sequence;
fd->setMode(QFileDialog::Directory); std::cerr << "position similarity (S_p):" << std::endl << position;
//fd->setMode(QFileDialog::DirectoryOnly); }
fd->setViewMode(QFileDialog::Detail); // alternative is List }
fd->setFilter("CSV files (*.csv)");
if(fd->exec() == QDialog::Accepted) { void GLTexobj::Ra_S(int n)
qfilename = fd->selectedFile(); {
strpath = qfilename.ascii(); int i,j,r=(int)Ra_scanpath.size();
map<string,Scanpath* >::iterator pos;
os << strpath << "/" << "G_s.csv"; // compose filename vector<Scanpath* >::iterator Ra_pos;
ofs.open(os.str().c_str()); // open file
stats(ofs,sequence,Ra_sequence,G); // pairwise Ra_sequence similarity (Levenshtein)
ofs.close(); // close file if(r > 0 && n > 0) {
os.str(""); // clear out filename // clear out current random position and sequence Y-matrices and resize
// here we make an n x r matrix since we favor the lower-diagonal form
os << strpath << "/" << "L_s.csv"; // compose filename if(Ra_sequence) delete Ra_sequence; Ra_sequence = new YMatrix(n,r);
ofs.open(os.str().c_str()); // open file if(Ra_position) delete Ra_position; Ra_position = new YMatrix(n,r);
stats(ofs,sequence,Ra_sequence,L);
ofs.close(); // close file // compare with random sequences
os.str(""); // clear out filename for(pos = scanpath.begin(), i=0; pos != scanpath.end(); i++, pos++) {
for(Ra_pos = Ra_scanpath.begin(), j=0;
os << strpath << "/" << "G_p.csv"; // compose filename Ra_pos != Ra_scanpath.end(); j++, Ra_pos++) {
ofs.open(os.str().c_str()); // open file (*Ra_sequence)[i][j] = pos->second->s_similarity(*Ra_pos);
stats(ofs,position,Ra_position,G); (*Ra_position)[i][j] = pos->second->p_similarity(*Ra_pos);
ofs.close(); // close file }
os.str(""); // clear out filename }
// print lower diagonal (matrix is symmetrical, only need half of it)
os << strpath << "/" << "L_p.csv"; // compose filename std::cerr << "Ra sequence similarity (S_s):" << std::endl << Ra_sequence;
ofs.open(os.str().c_str()); // open file std::cerr << "Ra position similarity (S_p):" << std::endl << Ra_position;
stats(ofs,position,Ra_position,L); }
ofs.close(); // close file }
os.str(""); // clear out filename
} void GLTexobj::parsingDiagram(YMatrix *m, YMatrix *Ra_m)
*/ {
int n_Ra=0, n_R=0, n_L=0, n_I=0, n_G=0;
updateGL(); float sum_Ra=0.0, sum_R=0.0, sum_L=0.0, sum_I=0.0, sum_G=0.0;
} float rs_Ra=0.0, rs_R=0.0, rs_L=0.0, rs_I=0.0, rs_G=0.0;
float d;
void GLTexobj::S__S(int n)
{ // given d, the difference in rank, Spearman’s rank-order coefficient
int i,j; // is given as
map<string,Scanpath* >::iterator pos,npos; //
// r_s = 1 - \frac{6 \sum{d^2}}{n (n^2 - 1)}
// pairwise sequence similarity (Levenshtein) //
if(n > 1) { // where the resulting correlation is interpreted as:
// clear out current position and sequence Y-matrices and resize //
if(sequence) delete sequence; sequence = new YMatrix(n,n); // very strong, if 0.9 \leq r_s \leq 1.0,
if(position) delete position; position = new YMatrix(n,n); // strong, if 0.7 \leq r_s \leq 0.9,
// moderate, if 0.5 \leq r_s \leq 0.7,
// compare pos with all other subject sequences
for(pos = scanpath.begin(), i=0; pos != scanpath.end(); i++, pos++) { // parsing diagrams---process subject-subject pairings
for(npos = scanpath.begin(), j=0; npos != scanpath.end(); j++, npos++) { for(int i=0;i<m->rows();i++) {
// if(i!=j) (*sequence)[i][j] = pos->second->s_similarity(npos->second); for(int j=0;j<m->cols();j++) {
Sep 01 2009 10:39 gltexobj.cpp Page 6/13
// don’t count entries on the diagonal! }
// (they are trivially 1.00 and skew the mean)
// if(i>=j) { void GLTexobj::stats(ostream& s, YMatrix *m, YMatrix *Ra_m, cmp_t C)
if(i>j) { {
d = (*m)[i][j].cmp.first; vector<double> similarity, Ra_similarity;
switch((*m)[i][j].cmp.second) {
case N: break; // should have no N comparisons // collect up corresponding comparison entries from m Y-matrix
case Ra: break; // should have no random comparisons if(m) {
case R: sum_R += d; n_R++; break; for(int i=0;i<m->rows();i++)
case L: sum_L += d; n_L++; break; for(int j=0;j<m->cols();j++)
case I: sum_I += d; n_I++; break; if(i>=j && (*m)[i][j].cmp.second == C)
case G: sum_G += d; n_G++; break; similarity.push_back((*m)[i][j].cmp.first);
} }
} // collect up all comparison entries from Ra_m Y-matrix
} if(Ra_m) {
} for(int i=0;i<Ra_m->rows();i++)
// parsing diagrams---process subject-random pairings for(int j=0;j<Ra_m->cols();j++)
if(Ra_m) { if(i>=j && i<m->rows() && j<m->cols() && (*m)[i][j].cmp.second == C)
for(int i=0;i<Ra_m->rows();i++) { Ra_similarity.push_back((*Ra_m)[i][j].cmp.first);
for(int j=0;j<Ra_m->cols();j++) { }
d = (*Ra_m)[i][j].cmp.first;
if(i>=j) { // dump out .csv file for subsequent processing via R
switch((*Ra_m)[i][j].cmp.second) { s << "comparison,similarity" << std::endl;
case N: break; // should have no N comparisons for(int i=0;i<(int)similarity.size();i++) {
case Ra: sum_Ra += d; n_Ra++; break; switch(C) {
case R: break; // should have no R comparisons case N: break; // should not be N comparisons
case L: break; // should have no L comparisons case Ra: break; // should not be random comps
case I: break; // should have no I comparisons case R: s << "R,"; break;
case G: break; // should have no G comparisons case L: s << "L,"; break;
} case I: s << "I,"; break;
} case G: s << "G,"; break;
} }
} s << similarity[i] << std::endl;
} }
for(int i=0;i<(int)Ra_similarity.size();i++)
//rs_Ra = n_Ra > 0 ? 1.0 - 6.0 * sum_Ra/(powf((float)n_Ra,3.0) - n_Ra) : 0.0; s << "Ra," << Ra_similarity[i] << std::endl;
//rs_R = n_R > 0 ? 1.0 - 6.0 * sum_R/(powf((float)n_R,3.0) - n_R) : 0.0; }
//rs_L = n_L > 0 ? 1.0 - 6.0 * sum_L/(powf((float)n_L,3.0) - n_L) : 0.0;
//rs_I = n_I > 0 ? 1.0 - 6.0 * sum_I/(powf((float)n_I,3.0) - n_I) : 0.0; void GLTexobj::mousePressEvent(QMouseEvent* e)
{
rs_Ra = n_Ra > 0 ? sum_Ra/(float)n_Ra : 0.0; vector<float> color(4,0.0);
rs_R = n_R > 0 ? sum_R/(float)n_R : 0.0;
rs_L = n_L > 0 ? sum_L/(float)n_L : 0.0; mouseMapCoordinates(e->x(),e->y());
rs_I = n_I > 0 ? sum_I/(float)n_I : 0.0;
rs_G = n_G > 0 ? sum_G/(float)n_G : 0.0; if(e->button() & LeftButton) {
// do nothing
std::cerr << setw(15) << "Reptitive"; } else if(e->button() & RightButton) {
std::cerr << setw(15) << "Local" << "\n"; // do nothing
std::cerr << setw(15) << rs_R; }
std::cerr << setw(15) << rs_L << std::endl;
std::cerr << setw(15) << "Idiosyncratic"; e->accept();
std::cerr << setw(15) << "Global" << "\n"; updateGL();
std::cerr << setw(15) << rs_I; }
std::cerr << setw(15) << rs_G << std::endl;
void GLTexobj::mouseReleaseEvent(QMouseEvent* e)
if(Ra_m) { {
std::cerr << setw(30) << "Random" << "\n"; mouseMapCoordinates(e->x(),e->y());
std::cerr << setw(30) << rs_Ra << std::endl;
} if(e->button() & MidButton) {
Sep 01 2009 10:39 gltexobj.cpp Page 7/13
// std::cerr << "MidButton + ";
if(e->state() & ControlButton) {
// std::cerr << "ControlButton" << std::endl; void GLTexobj::fileGenerateRa()
// on the Mac: press mouse button first, then press CTL button {
} else { Header *h=NULL;
// std::cerr << std::endl; vector<float> color(4,0.3); // set transparency
} Scanpath *scp;
} QString status;
std::ostringstream os;
if(e->button() & RightButton) {
} // reset simulation elapsed time (no. samples)
simSamples = 0;
e->accept();
updateGL(); // set timestamp
} simStart = timestamp();
void GLTexobj::mouseDoubleClickEvent(QMouseEvent* e) // create new scanpath, with new id "R01-R01-" for the Random recording
{ os << "R" << setw(2) << setfill(’0’) << (int)Ra_scanpath.size()+1;
e->accept(); h = new Header("random",os.str().c_str(),"R01");
updateGL();
} // get scanpath id (associative map key)
id = h->id();
void GLTexobj::wheelEvent(QWheelEvent* e) // get random color for the scanpath
{ for(int i=0; i<3; i++) color[i] = (float)rand()/(float)RAND_MAX;
float degrees = ((float)e->delta() / 360.0); // should be WHEEL_DELTA // random scanpaths get stored separately from subject scanpaths
// float radians = degrees * M_PI / 180.0; scp = new Scanpath(h,width(),height(),color);
scp->setRandom();
if(e->state() & ControlButton) { Ra_scanpath.push_back(scp);
std::cerr << "wheelEvent:";
std::cerr << " delta: " << e->delta(); status = QString("Generating random scanpath (%1 s)")
std::cerr << " degrees: " << degrees << std::endl; .arg(simDuration/1000.0,0,’f’,0); // duration in seconds
}
e->accept(); emit messageStatus(status);
updateGL(); emit setProgress(0,(int)(simDuration/simPeriod));
}
simRunning = true;
void GLTexobj::mouseMoveEvent(QMouseEvent* e)
{ // start timer; idea is for timer to call update()
mouseMapCoordinates(e->x(),e->y()); // to simulate random eye movements
timer->start((int)simPeriod, FALSE);
e->accept(); }
updateGL();
} void GLTexobj::update()
{
void GLTexobj::mouseMapCoordinates(int ix, int iy) static double fixation_dt=poisson(500.0);
{ static double fixation_ts=timestamp();
//std::cerr << "(" << ix << "," << iy << ")" << std::endl; static double fixation_te=0.0;
static double fixation_tt=0.0;
// flip y-coordinate to put into GL coordinates static bool saccade=true;
x = ix; y = height() - iy;
static float fx, fy;
// normalize coordinates static Point *pp;
x = x/width(); y = y/height();
// this is the slot called by Timer whenever its timeout period expires
/* // and is meant to be re-entrant; it simulates random eye movements:
// scale coordinates to ([-w,w], [-h,h]) // - whenever this routine is called, it will either re-fixate the
x = width()*(2.0*x - 1.0); y = height()*(2.0*y - 1.0); // gaze point (fx,fy), or will saccade to some new random location
*/ // - the decision to saccade depends on the time since the last fixation,
} // the period being governed by a normal distribution with variance
Sep 01 2009 10:39 gltexobj.cpp Page 8/13
// characteristic of true eye movements, e.g., [150,650] ms
// - the gaze point is repositioned to a new location, governed //std::cerr << pp;
// by saccade amplitude characteristic of true saccades, e.g., ??
// add current point to scanpath
fixation_te = timestamp(); if(!Ra_scanpath.empty())
fixation_tt = fixation_te - fixation_ts; Ra_scanpath[(int)Ra_scanpath.size()-1]->insertPoint(pp);
if(fixation_tt >= fixation_dt) saccade = true;
emit setProgress(simSamples);
if(saccade) {
// simulate saccade (working in display coordinates) // div simDuration by period (in ms) to get number of samples
if(++simSamples > (int)(simDuration/simPeriod)) {
// using built-in random number generator, no model of eye movements,
// just random location based on built-in rand() // analyze eye movements (e.g., via LC Tech’s position-variance scheme)
// fx = (float)rand()/(float)RAND_MAX * (float)width(); Ra_scanpath[(int)Ra_scanpath.size()-1]->position_variance();
// fy = (float)rand()/(float)RAND_MAX * (float)height();
// use k-means as dispersion-based data reduction (clustering) method
// uniform distribution (min, max), better random generator than rand() Ra_scanpath[(int)Ra_scanpath.size()-1]->fixation_mean_shift();
// but still no model of eye movements
// fx = cuniform(0.,(float)width()); // classify fixations
// fy = cuniform(0.,(float)height()); Ra_scanpath[(int)Ra_scanpath.size()-1]->classifyFixations();
// normal distribution (mean, sdev) instead of uniform, here it is // reset duration (how long to simulate for, in ms)
// weighted to the screen center, but is still not a good model of simDuration = 10000.0;
// saccadic amplitude
fx = normal((float)width()/2.0,(float)width()/6.0); emit clearStatus();
fy = normal((float)height()/2.0,(float)height()/6.0);
simRunning = false;
// get new fixation duration, should be [150,650] ms range
// fixation_dt = cuniform(150.0,600); // stop timer
// fixation_dt = normal(375.0,56.25); timer->stop();
// fixation_dt = poisson(500.0); }
fixation_dt = poisson(1300.0);
// draw current point
// get fixaton start timestamp mouseMapCoordinates((int)fx,(int)fy);
fixation_ts = timestamp();
updateGL();
// reset saccade flag }
saccade = false;
} else { void GLTexobj::fileOpen()
// no saccade, maintain fixation, simulating random re-fixation, {
// with (dx,dy) (arbitrarily) close to current location QFileDialog* fd;
QString qfilename;
// uniform distribution in range [-.45,.45] --- what units?
// fx += cuniform((float)-.45,(float).45); #ifdef ANM_OSX
// fy += cuniform((float)-.45,(float).45); fd = new QFileDialog("../../.././",QString::null,this);
#else
// normal distribution in range with mean=0, sdev=0.91 visual angle (30 px) fd = new QFileDialog("./",QString::null,this);
fx += normal(0.,0.91); #endif
fy += normal(0.,0.91); fd->setMode(QFileDialog::ExistingFile); // use AnyFile for writing
} fd->setViewMode(QFileDialog::Detail); // alternative is List
fd->setFilter("Text files (*.txt)");
// generate new point
pp = new Point(fx,fy,timestamp() - simStart); // Tobii ClearView 2.7.1 recording file types (uncomment as implemented)
fd->addFilter("AOI (*AOI.txt)");
// normalize fd->addFilter("AOIL (*AOIL.txt)");
pp->scale(1.0/(float)width(),1.0/(float)height()); //fd->addFilter("CMD (*CMD.txt)");
//fd->addFilter("EVD (*EVD.txt)");
// flip y-coordinate to put into GL coordinates fd->addFilter("FXD (*FXD.txt)");
pp->flip(); fd->addFilter("EFD (*EFD.txt)");
Sep 01 2009 10:39 gltexobj.cpp Page 9/13
//fd->addFilter("GZD (*GZD.txt)");
//fd->addFilter("AOI_ (*AOI_.txt)"); // all file types except AOIL have headers (AOIL file depends on AOI file)
if( (r.type() != AOIL) ) {
// get selected file // read header info
if(fd->exec() == QDialog::Accepted) qfilename = fd->selectedFile(); ifs >> (h = new Header());
// construct id: assumed to be "<Subject>-<Recording>-" which should
// process file if not empty // be a unique identifier for the recording (if that’s how the files
if(!qfilename.isEmpty()) { // are named)
fileRead(qfilename); id = h->id();
updateGL(); // get random color for the scanpath
} for(int i=0; i<3; i++) color[i] = (float)rand()/(float)RAND_MAX;
}
if(h->sd() == "random") {
void GLTexobj::fileOpenAll() // random scanpath, added to array of random scanpaths, not to map
{ Scanpath *scp = new Scanpath(h,width(),height(),color);
QFileDialog* fd; scp->setRandom();
QStringList qfilenames; scp->input(r.type()); // note which file is to be read in
QStringList::iterator it; ifs >> scp; // read in the given file
Ra_scanpath.push_back(scp); // add to list
#ifdef ANM_OSX } else {
fd = new QFileDialog("../../.././",QString::null,this); // scanpath is a map (associative array), with id acting as key
#else if(!scanpath[id]) // new scanpath
fd = new QFileDialog("./",QString::null,this); scanpath[id] = new Scanpath(h,width(),height(),color);
#endif else // already have header, free memory
fd->setMode(QFileDialog::ExistingFiles); delete h;
fd->setViewMode(QFileDialog::Detail); // alternative is List scanpath[id]->input(r.type()); // note which file is to be read in
fd->setFilter("Text files (*.txt)"); ifs >> scanpath[id]; // read in the given file
// Tobii ClearView 2.7.1 recording file types (uncomment as implemented) // set simDuration to duration of scanpath just read in
//fd->addFilter("AOI (*AOI.txt)"); // if user now generates random scanpath, it will match this duration
//fd->addFilter("AOIL (*AOIL.txt)"); // DO NOT call fileGenerateRa() here though, IT WILL NOT WORK because
//fd->addFilter("CMD (*CMD.txt)"); // that routine starts the timer, which is non-blocking
//fd->addFilter("EVD (*EVD.txt)"); if((simDuration = scanpath[id]->duration()) < 1.0) simDuration = 10000;
fd->addFilter("FXD (*FXD.txt)"); }
fd->addFilter("EFD (*EFD.txt)");
//fd->addFilter("GZD (*GZD.txt)"); // automatically search for and read associated AOIL file if it exists
// (so the stimulus info will be known for scanpath just read in)
// get list of selected files std::ifstream aoilifs;
if(fd->exec() == QDialog::Accepted) qfilenames = fd->selectedFiles(); std::ifstream aoiifs;
std::ifstream evdifs;
// process each one std::ostringstream os;
for(it=qfilenames.begin(); it != qfilenames.end(); it++) { std::string strfull,strpath;
fileRead(*it); struct stat buf;
updateGL();
} // begin read EVD
}
// construct filename to open (qfilename.ascii() includes full path)
void GLTexobj::fileRead(QString qfilename) strfull = qfilename.ascii();
{
Header* h=NULL; // strip leading dir name (could be very long, e.g., /home/<user>/.../
QStringList::iterator it; strpath = strfull.substr(0,strfull.rfind(’/’));
vector<float> color(4,0.3); // set transparency // construct "*EVD.txt" filename
os << strpath << "/" << id << "EVD" << ".txt";
std::ifstream ifs(qfilename.ascii()); // std::cerr << "full = " << strfull << std::endl;
if(!ifs) // std::cerr << "path = " << strpath << std::endl;
std::cerr << "Warning: can’t open " << qfilename << std::endl; // std::cerr << "file = " << os.str().c_str() << std::endl;
else {
// establish file type (the Recording obj just looks for EFD, GZD, etc.) // if file exists, open it, read it in, close it, clearing bits
Recording r(qfilename.ascii()); if(!stat(os.str().c_str(),&buf)) {
Sep 01 2009 10:39 gltexobj.cpp Page 10/13
// open EVD file // open AOIL file
evdifs.open(os.str().c_str(),std::ifstream::in); aoiifs.open(os.str().c_str(),std::ifstream::in);
if(evdifs.good()) { if(aoiifs.good()) {
scanpath[id]->input(EVD); // note which file is to be read in scanpath[id]->input(AOI); // note which file is to be read in
evdifs >> scanpath[id]; // read in the given file aoiifs >> scanpath[id]; // read in the given file
} }
evdifs.close(); // close file aoiifs.close(); // close file
evdifs.clear(); // reset status bits aoiifs.clear(); // reset status bits
os.str(""); // clear out filename os.str(""); // clear out filename
} }
// end read EVD // end read AOI file
void GLTexobj::imgSaveAs()
{
glReadBuffer(GL_BACK);
img = QGLWidget::grabFrameBuffer(true);
#ifdef ANM_OSX
QString qfilename = QFileDialog::getSaveFileName("../../.././",QString::null,this);
#else
QString qfilename = QFileDialog::getSaveFileName("./",QString::null,this);
#endif
if(!qfilename.isEmpty()) img.save(qfilename,"PNG");
}
void GLTexobj::afPlot()
{
map<string,Scanpath* >::iterator pos;
for(pos=scanpath.begin(); pos != scanpath.end(); pos++)
pos->second->afplot();
double GLTexobj::timestamp()
{
double s,us,tod;
struct timeval tp;
gettimeofday(&tp,NULL);
s = static_cast<double>(tp.tv_sec);
us = static_cast<double>(tp.tv_usec);
tod = s*1000000.0 + us;
return(tod/1000.0);
}
Sep 01 2009 10:39 glwinobj.cpp Page 1/1
/**************************************************************************** file->insertItem("Save All...",texwin,SLOT(fileSaveAll()),CTRL+Key_L);
** $Id: qt/glwinobj.cpp 3.1.2 edited Nov 8 2002 $ file->insertSeparator();
** file->insertItem("Clear",texwin,SLOT(fileClear()),CTRL+Key_C);
** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. file->insertSeparator();
** file->insertItem("Quit",qApp,SLOT(quit()),CTRL+Key_Q);
** This file is part of an example program for Qt. This example
** program may be used, distributed and modified without limitation. // create the edit menu
** edit = new QPopupMenu(menubar);
*****************************************************************************/ menubar->insertItem("Edit",edit);
view_scanpaths = new QCheckBox("scanpaths",edit,0);
#include <iostream> view_scanpaths->setChecked(true);
#include <vector> edit->insertItem(view_scanpaths);
#include <string> connect(view_scanpaths,SIGNAL(toggled(bool)),texwin,SLOT(viewScanpaths(bool)));
#include <list>
#include <math.h> // create the aoi menu
aoi = new QPopupMenu(menubar);
#include <qpushbutton.h> menubar->insertItem("AOI",aoi);
#include <qslider.h> aoi->insertItem("Open...",texwin,SLOT(aoiOpen()));
#include <qlayout.h>
#include <qframe.h> // create the image menu
#include <qmenubar.h> image = new QPopupMenu(menubar);
#include <qpopupmenu.h> menubar->insertItem("Image",image);
#include <qcheckbox.h> image->insertItem("Open...",texwin,SLOT(imgOpen()));
#include <qprogressbar.h> image->insertItem("Save As...",texwin,SLOT(imgSaveAs()));
#include <qstatusbar.h>
#include <qapplication.h> // create the tools menu
#include <qkeycode.h> tools = new QPopupMenu(menubar);
menubar->insertItem("Tools",tools);
using namespace std; tools->insertItem("AF plot output...",texwin,SLOT(afPlot()));
tools->insertItem("Label",texwin,SLOT(label()));
#include "glwinobj.h" tools->insertItem("Heatmap",texwin,SLOT(mapheat()));
#include "gltexobj.h"
// status and progress bar
GLObjectWindow::GLObjectWindow( QWidget* parent, const char* name ) : statusBar = new QStatusBar(this);
QWidget( parent, name ) connect(texwin,SIGNAL(messageStatus(const QString&)),
{ statusBar,SLOT(message(const QString&)));
GLTexobj* texwin=NULL; connect(texwin,SIGNAL(clearStatus()),
statusBar,SLOT(clear()));
// Create an OpenGL widget: (doubleBuffer | rgba | depth) set globally // progress bar
texwin = new GLTexobj(this,"glarea"); progressBar = new QProgressBar(statusBar);
std::cout << "doubleBuffer: " << texwin->format().doubleBuffer() << " " connect(texwin,SIGNAL(setProgress(int)),
<< "rgba: " << texwin->format().rgba() << " " progressBar,SLOT(setProgress(int)));
<< "depth: " << texwin->format().depth() << " " connect(texwin,SIGNAL(setProgress(int,int)),
<< std::endl; progressBar,SLOT(setProgress(int,int)));
texwin->setMouseTracking(true); connect(texwin,SIGNAL(resetProgress()),
progressBar,SLOT(reset()));
// create a menu bar connect(texwin,SIGNAL(clearStatus()),
menubar = new QMenuBar(this); progressBar,SLOT(reset()));
menubar->setSeparator(QMenuBar::InWindowsStyle); statusBar->addWidget(progressBar,0,true); // must be permanent (,0,true)
// create the file menu // Top level layout (with 0,0 border)
file = new QPopupMenu(menubar); vlayout = new QVBoxLayout(this, 0, 0, "vlayout");
menubar->insertItem("Recording",file); vlayout->setMenuBar(menubar);
file->insertItem("Open",texwin,SLOT(fileOpen()),CTRL+Key_O); vlayout->addWidget(texwin,1);
file->insertItem("Open All..",texwin,SLOT(fileOpenAll()),CTRL+Key_A); vlayout->addWidget(statusBar);
file->insertSeparator(); }
file->insertItem("Generate Random",texwin,SLOT(fileGenerateRa()),CTRL+Key_R);
file->insertSeparator();
file->insertItem("Save As...",texwin,SLOT(fileSaveAs()),CTRL+Key_W);
Sep 01 2009 10:39 header.cpp Page 1/1
#include <iostream> s >> str; s >> str; // "Coordinate unit:"
#include <iomanip> s >> rhs.unit;
#include <fstream>
#include <sstream> return s;
#include <string> }
#include <algorithm>
#include <time.h> ostream& operator<<(ostream& s, const Header& rhs)
#include <sys/time.h> {
s << "Data properties:" << std::endl; s << std::endl;
using namespace std; s << "Recording date: ";
s << rhs.month << "/" << rhs.day << "/" << rhs.year << std::endl;
#include "header.h" s << "Recording time : ";
s << rhs.hour << ":" << rhs.minute << ":" << rhs.second << ":";
// scanpath implementation s << setw(3) << setfill(’0’) << rhs.ms;
// (c) Andrew T. Duchowski s << " (corresponds to time 0)" << std::endl;
Header::Header(string sty, string subj, string rec) : s << "Study: ";
study(sty), s << rhs.study << std::endl;
subject(subj), s << "Subject: ";
recording(rec), s << rhs.subject << std::endl;
w(1280), h(1024), s << "Recording: ";
unit("Pixels") s << rhs.recording << std::endl;
{ s << "Screen resolution: ";
time_t tloc; s << rhs.w << " x " << rhs.h << std::endl;
struct tm *tod=NULL; s << "Coordinate unit: ";
s << rhs.unit << std::endl;
time(&tloc);
tod = localtime(&tloc); return s;
}
month = tod->tm_mon + 1;
day = tod->tm_mday;
year = tod->tm_year + 1900;
hour = tod->tm_hour;
minute = tod->tm_min;
second = tod->tm_sec;
ms = 0;
}
render(node->right,width,height);
}
Sep 01 2009 10:39 main.cpp Page 1/1
/**************************************************************************** GLObjectWindow* w = new GLObjectWindow;
** $Id: qt/main.cpp 3.1.2 edited Nov 8 2002 $
** // set size...
** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. w->resize( 640, 480 );
** //w->resize( 1280, 1024 );
** This file is part of an example program for Qt. This example app.setMainWidget( w );
** program may be used, distributed and modified without limitation. w->show();
** // ... or go full screen
*****************************************************************************/ //w->showFullScreen();
//
// Qt OpenGL example: Texture int result = app.exec();
// delete w;
// File: main.cpp return result;
// }
// The main() function
//
#ifdef ANM_OSX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
#include <GLUT/glut.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <GL/glut.h>
#endif
#include <qapplication.h>
#include <qgl.h>
#include "glwinobj.h"
#include "crand.h"
if(!QGLFormat::hasOpenGL()) {
qWarning( "This system has no OpenGL support. Exiting." );
return -1;
}
// Create window
Sep 01 2009 10:39 point.cpp Page 1/3
#include <iostream> coord = c;
#include <iomanip> mean = c;
#include <string> }
#include <cmath>
Point::Point(const Point& rhs)
#ifdef ANM_OSX {
#include <OpenGL/gl.h> for(int i=0; i<(int)rhs.coord.size(); i++) {
#include <OpenGL/glu.h> coord.push_back(rhs.coord[i]);
#include <OpenGL/glext.h> mean.push_back(rhs.mean[i]);
#else mn.push_back(rhs.mn[i]);
#include <GL/gl.h> }
#include <GL/glu.h> for(int i=0; i<(int)rhs.sigma.size(); i++)
//#include <GL/glext.h> sigma.push_back(rhs.sigma[i]);
#endif timestamp = rhs.timestamp;
md = rhs.md;
using namespace std; }
for(int i=0; i<(*this).dim(); i++) for(int i=0; i<2; i++) mn[i] += (k * rhs.mean[i]);
dist += pow(coord[i] - rhs.coord[i],2.0);
return(sqrt(dist)); md += k;
Sep 01 2009 10:39 point.cpp Page 3/3
return md;
}
double Point::mean_shift()
{
double dx,dy;
double delta = 0.0;
vector<double> oldmean(2,0.0); // keep old mean
return(exp(-g));
}
return(exp(-g));
}
Sep 01 2009 10:39 polynomial.cpp Page 1/4
#include <iostream> Polynomial result(degree() + rhs.degree() + 1);
#include <iomanip>
#include <string> for(int i=0; i<=degree() + rhs.degree(); i++)
#include <math.h> result.coefs.push_back(0.0);
for(int i=0; i<=degree(); i++)
using namespace std; for(int j=0; j<=rhs.degree(); j++)
result.coefs[i+j] += coefs[i]*rhs.coefs[j];
#include "polynomial.h"
return result;
// polynomial implementation (based on java implementation I found on the web) }
// (c) Andrew T. Duchowski
Polynomial Polynomial::operator/(double scalar)
/////////////////////////// friends //////////////////////////////////////////// {
ostream& operator<<(ostream& s,Polynomial& rhs) for(int i=0; i<(int)coefs.size(); i++)
{ coefs[i] /= scalar;
s.setf(ios::fixed,ios::floatfield);
s.precision(2); return *this;
for(int i=rhs.degree(); i>=0; --i) { }
if(rhs[i] < 0.0)
s << " - "; Polynomial Polynomial::simplify()
else {
s << " + "; for(int i=degree(); i>=0; --i) {
s << fabsf(rhs[i]); if(fabsf(coefs[i]) <= TOLERANCE) {
if(i > 1) coefs.pop_back();
s << " x^" << i; }
else if(i==1) else break;
s << " x"; }
}
s << endl; return *this;
return s; }
}
double Polynomial::eval(double x)
Polynomial::Polynomial(vector<double> c) {
{ double result=0;
coefs.clear();
for(int i=c.size()-1; i>=0; --i) coefs.push_back(c[i]); for(int i=coefs.size()-1; i>=0; --i)
} result = result*x + coefs[i];
return result;
Polynomial::Polynomial(const Polynomial& rhs) }
{
coefs.clear(); bool Polynomial::bisection(double min,double max,double *root)
for(int i=0; i<(int)rhs.coefs.size(); i++) coefs.push_back(rhs.coefs[i]); {
} double minValue = eval(min);
double maxValue = eval(max);
Polynomial::Polynomial(Polynomial* rhs) double result = 0.0;
{ bool haveRoot = false;
coefs.clear();
for(int i=0; i<(int)rhs->coefs.size(); i++) coefs.push_back(rhs->coefs[i]); if(fabsf(minValue) <= TOLERANCE) {
} result = min;
haveRoot = true;
Polynomial Polynomial::operator=(const Polynomial& rhs) *root = result;
{ }
coefs.clear(); else if(fabsf(maxValue) <= TOLERANCE) {
for(int i=0; i<(int)rhs.coefs.size(); i++) coefs.push_back(rhs.coefs[i]); result = max;
return *this; haveRoot = true;
} *root = result;
}
Polynomial Polynomial::operator*(Polynomial& rhs) else if(minValue*maxValue <= 0.0) {
{ double tmp1 = log(max-min);
Sep 01 2009 10:39 polynomial.cpp Page 2/4
double tmp2 = log(10.0) * ACCURACY; vector<double> Polynomial::rootsInInterval(double min,double max)
int iters = (int)ceilf((tmp1+tmp2)/log(2.0)); {
for(int i=0; i<iters; i++) { double root;
result = 0.5*(min+max); vector<double> roots;
double value = eval(result);
if(fabsf(value) <= TOLERANCE) if(degree() == 1) {
break; if(bisection(min,max,&root))
if(value*minValue < 0.0) { roots.push_back(root);
max = result; } else {
maxValue = value; Polynomial deriv = derivative();
} else { Polynomial droots = deriv.rootsInInterval(min,max);
min = result; if(droots.coefs.size() > 0) {
minValue = value; if(bisection(min,droots[0],&root))
} roots.push_back(root);
} for(int i=0; i<=(int)droots.coefs.size()-2; i++) {
haveRoot = true; if(bisection(droots[i],droots[i+1],&root))
*root = result; roots.push_back(root);
} }
return(haveRoot); if(bisection(droots[droots.coefs.size()-1],max,&root))
} roots.push_back(root);
} else {
Polynomial Polynomial::derivative() if(bisection(min,max,&root))
{ roots.push_back(root);
Polynomial derivative(degree()-1); }
}
derivative.coefs.clear(); return(roots);
for(int i=1; i<(int)coefs.size(); i++) }
derivative.coefs.push_back(i*coefs[i]);
vector<double> Polynomial::linearRoot()
return derivative; {
} vector<double> result;
double a = coefs[1];
vector<double> Polynomial::roots()
{ if(fabsf(a) > 0.000001)
vector<double> result; result.push_back(-coefs[0]/a);
simplify(); return(result);
}
switch(degree()) {
case 0: vector<double> Polynomial::quadraticRoots()
break; {
case 1: vector<double> results;
result = linearRoot();
break; if(degree()==2) {
case 2: double a = coefs[2];
result = quadraticRoots(); double b = coefs[1]/a;
break; double c = coefs[0]/a;
case 3: double d = b*b-4.0*c;
result = cubicRoots(); if(d > 0.0) {
break; double e = sqrt(d);
case 4: results.push_back(0.5*(-b+e));
result = quarticRoots(); results.push_back(0.5*(-b-e));
break; } else if(fabsf(d) < 0.000001) {
default: results.push_back(0.5*-b);
break; }
} }
return(result); return(results);
} }
Sep 01 2009 10:39 polynomial.cpp Page 3/4
vector<double> Polynomial::cubicRoots() double c2=coefs[2]/c4;
{ double c1=coefs[1]/c4;
vector<double> results; double c0=coefs[0]/c4;
Polynomial resolveRoots = Polynomial(1.0,
if(degree()==3) { -c2,
double c3=coefs[3]; c3*c1-4.0*c0,
double c2=coefs[2]/c3; -c3*c3*c0+4.0*c2*c0-c1*c1).cubicRoots();
double c1=coefs[1]/c3; double y=resolveRoots[0];
double c0=coefs[0]/c3; double discrim=c3*c3/4.0-c2+y;
double a=(3.0*c1-c2*c2)/3.0; if(fabsf(discrim) <= TOLERANCE) {
double b=(2.0*c2*c2*c2-9.0*c1*c2+27.0*c0)/27.0; discrim=0.0;
double offset=c2/3.0; double t2 = y*y-4.0*c0;
double discrim=b*b/4.0 + a*a*a/27.0; if(t2 >= -TOLERANCE) {
double halfB=b/2.0; if(t2 < 0.0)
if(fabsf(discrim) <= TOLERANCE) { t2 = 0.0;
discrim = 0.0; t2 = 2.0*sqrt(t2);
double tmp; double t1 = 3.0*c3*c3/4.0-2.0*c2;
if(halfB >= 0.0) if(t1+t2 >= TOLERANCE) {
tmp=-pow(halfB,1.0/3.0); double d=sqrt(t1+t2);
else results.push_back(-c3/4.0 + d/2.0);
tmp=pow(-halfB,1.0/3.0); results.push_back(-c3/4.0 - d/2.0);
results.push_back(2.0*tmp-offset); }
results.push_back( -tmp-offset); if(t1-t2 >= TOLERANCE) {
} else if(discrim > 0.0) { double d = sqrt(t1-t2);
double e=sqrt(discrim); results.push_back(-c3/4.0 + d/2.0);
double tmp; results.push_back(-c3/4.0 - d/2.0);
double root; }
tmp=-halfB+e; }
if(tmp >= 0.0) } else if(discrim > 0.0) {
root=pow(tmp,1.0/3.0); double e=sqrt(discrim);
else double t1=3.0*c3*c3/4.0-e*e-2.0*c2;
root=-pow(-tmp,1.0/3.0); double t2=(4.0*c3*c2-8.0*c1-c3*c3*c3)/(4.0*e);
tmp=-halfB-e; double plus=t1+t2;
if(tmp >= 0.0) double minus=t1-t2;
root+=pow(tmp,1.0/3.0); if(fabsf(plus) <= TOLERANCE)
else plus = 0.0;
root-=pow(-tmp,1.0/3.0); if(fabsf(minus) <= TOLERANCE)
results.push_back(root-offset); minus = 0.0;
} else if(discrim < 0.0) { if(plus >= 0.0) {
double distance=sqrt(-a/3.0); double f = sqrt(plus);
double angle=atan2(sqrt(-discrim),-halfB)/3.0; results.push_back(-c3/4.0 + (e+f)/2.0);
double kos=cos(angle); results.push_back(-c3/4.0 + (e-f)/2.0);
double syn=sin(angle); }
double sqrt3=sqrt(3.0); if(minus >= 0.0){
results.push_back(2.0*distance*kos-offset); double f = sqrt(minus);
results.push_back(-distance*(kos+sqrt3*syn)-offset); results.push_back(-c3/4.0 + (f-e)/2.0);
results.push_back(-distance*(kos-sqrt3*syn)-offset); results.push_back(-c3/4.0 - (f+e)/2.0);
} }
} } else if(discrim < 0.0) { }
return(results); }
} return(results);
}
vector<double> Polynomial::quarticRoots()
{ Polynomial bezout(Polynomial e1,Polynomial e2)
vector<double> results; {
return(Polynomial(e,d,c,b,a));
}
Sep 01 2009 10:39 scanpath.cpp Page 1/10
#include <iostream> string eventName="blank";
#include <iomanip>
#include <fstream> // need to determine scaling factor
#include <sstream> if(rhs.header) {
#include <vector> // stimulus width, height (not display screen!)
#include <string> w = rhs.header->width(); h = rhs.header->height();
#include <list> } else {
#include <algorithm> w = 1280.0; h = 1024.0; // default (HACK!)
#include <utility> }
#include <cmath>
//std::cerr << rhs.header;
#ifdef ANM_OSX
#include <OpenGL/gl.h> // figure out what it is user is trying to read in
#include <OpenGL/glu.h> if(!rhs.formats.empty()) file = rhs.formats[rhs.formats.size()-1];
#include <OpenGL/glext.h> switch(file) {
#else case EFD:
#include <GL/gl.h> // Timestamp,Found,GazepointX,GazepointY
#include <GL/glu.h> s >> str >> str >> str >> str;
//#include <GL/glext.h>
#endif while(!s.eof()) {
// read in gaze point info and eat whitespace at EOL
using namespace std; s >> (pp = new Point()) >> std::ws;
// check for sufficient number of points (can’t cluster 1 point) // radius (from filter)
if(point.size() < 2) { fp->setRadius(filter->radius());
std::cerr << "Not enough points!" << std::endl;
Sep 01 2009 10:39 scanpath.cpp Page 5/10
// number (which fixation; for i/o purposes) maxdm = -HUGE;
fp->setNumber(++n);
// for each point, get it to calculate the weighted sum of distances
insertFixation(fp); // of other means
for(int k=0; k<(int)point.size(); k++)
// debug info; comparing my code with LC Tech’s for(int j=0; j<(int)point.size(); j++)
// std::cerr << " my mean: ("; dm = point[k]->mean_dist(*point[j]);
// std::cerr << mean[0] << ",";
// std::cerr << mean[1] << ")"; // for each point, get it to shift its mean by evaluating num/denom
// std::cerr << " filter: ("; for(int k=0; k<(int)point.size(); k++)
// std::cerr << ffilter.getFixation_x() / w << ","; if((dm = point[k]->mean_shift()) > maxdm) maxdm = dm;
// std::cerr << ffilter.getFixation_y() / h << ")";
// std::cerr << std::endl; } while(maxdm > 0.1); // iterative stopping criterion
// std::cerr << " my duration: "; //std::cerr << "done." << std::endl;
// std::cerr << tt << ",";
// std::cerr << " filter: "; // result above is that each point has shifted its mean to the local
// std::cerr << ffilter.getFixationDuration() * 20.0; // cluster center; hence new clusters are defined by point whose
// std::cerr << std::endl; // means are very close together (less than sigma)
// std::cerr << "prev saccade duration: "; //
// std::cerr << ffilter.getPrevSaccadeDuration() * 20.0; // note that there may be clusters that only contain one point -- these
// std::cerr << std::endl; // should be either considered outliers or should be made into very small
// clusters
// resetting mean for next fixation
mean[0] = 0.0; mean[1] = 0.0; // create clusters s.t. all the points with very close (distance wise)
break; // means get lumped into same cluster clusters should then perform PCA
} // to find ellipse parameters
}
std::cerr << "detected " << n << " fixations." << std::endl; std::cerr << "inserting " << point.size() << " points...";
} for(int k=0; k<(int)point.size(); k++) {
// if no clusters exist yet, make a new one
/*! if(cluster.empty()) cluster.push_back(new Cluster(*point[k]));
mean shift applied to gaze points else {
(as means for position-variance, or dispersion-based, eye movement analysis) // check to see if this point belongs in any cluster
*/ bool accepted = false;
void Scanpath::point_mean_shift(void) for(int r=0; r<(int)cluster.size() && !accepted; r++)
{ accepted = (*cluster[r]).insert(*point[k]);
matrix<double> cov(2,2); // if not, make a new cluster
double maxdm, dm; if(!accepted) cluster.push_back(new Cluster(*point[k]));
}
// remove consecutive duplicates }
point.erase(unique(point.begin(),point.end(),PointDuplicate()),point.end()); std::cerr << "created " << cluster.size() << " clusters." << std::endl;
// check for sufficient number of points (can’t cluster 1 point) // perform PCA on each cluster: this will set up ellipses for each as well
if(point.size() < 2) { //std::cerr << "doing pca...";
std::cerr << "Not enough points!" << std::endl; for(int r=0; r < (int)cluster.size(); r++) cluster[r]->pca();
return; //std::cerr << "pca done." << std::endl;
} }
// check for sufficient number of fixations (can’t cluster 1 fixation) // perform PCA on each cluster: this will set up ellipses for each as well
if(fixation.size() < 2) { //std::cerr << "doing pca...";
std::cerr << "Not enough fixations!" << std::endl; for(int r=0; r < (int)cluster.size(); r++) cluster[r]->pca();
return; //std::cerr << "pca done." << std::endl;
} }
#ifdef ANM_OSX
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
//#include <GL/glext.h>
#endif
#include "ymatrix.h"
switch(rhs.cmp.second) {
case N: s << " ("; break;
case Ra: s << "Ra("; break;
case R: s << " R("; break;
case L: s << " L("; break;
case I: s << " I("; break;
case G: s << " G("; break;
}
if(rhs.cmp.second == N)
s << setw(4) << " -- ) ";
else
s << setw(4) << rhs.cmp.first << ") ";
return s;
}