Laser Sensing Display for UI interfaces in the real world
Fork of skinGames_forktest by
Diff: WrapperFunctions.cpp
- Revision:
- 40:3ba2b0ea9f33
- Child:
- 42:5f21a710ebc5
diff -r 7c54b6bca0e2 -r 3ba2b0ea9f33 WrapperFunctions.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WrapperFunctions.cpp Wed Oct 16 16:14:27 2013 +0000 @@ -0,0 +1,494 @@ +#include "WrapperFunctions.h" +#include "textData.h" // this just contains a special switch/case function to load an array of data, so as to store data in FLASH. + +using namespace std; + +extern LaserRenderer lsr; +extern Scene scene; +extern laserSensingDisplay lsd; + +bool previousLsdState; //local scope - only used in this file. + +// ================================= BASIC "OPENGL-like" object/scene builders and query functions ("sense") ========================================== + +void clearScene() // THIS SHOULD BE CALLED WHENEVER we want to create a new scene from scratch. +{ + // This is delicate: first, we have to STOP the displaying engine, then we clear the scene and we are ready to add objects (begin/end, or primitive builders) + // BUT WE NEED TO LET THE DISPLAYING ENGINE FINISH THE WORK IT'S DOING on the current buffer. TWO WAYS: DOUBLE BUFFERING, or wait: + // Check in which point of the drawing we are: + if (lsd.isRunning()) { + lsd.startDisplayCheck(); + while(!lsd.isDisplayingOver()); + stopDisplay(); + } else { //do nothing, the display engine is not working (we don't need to wait for the display to end!) + } + scene.clear(); +} + +void deleteObject(int _id) { + if (lsd.isRunning()) { + lsd.startDisplayCheck(); + while(!lsd.isDisplayingOver()); + stopDisplay(); + } else { //do nothing, the display engine is not working (we don't need to wait for the display to end!) + } + // DELETE THE OBJECT and update the scene: + scene.deleteObject(_id); + updateScene(); +} + +void updateScene() // THIS SHOULD BE CALLED WHENEVER we ended CREATING or MODIFYING a scene +{ + // In fact, the only thing this routine does is to indicate to the (dormant!) displaying engine that the scene STRUCTURE has changed. + lsd.setSceneToDisplay(&scene); // this will compute the number of objects, points, etc, to prepare the displaying engine + resumeDisplay();// this will resume display IF it was displaying before. +} + + +// Object creation ("openGL-style"). +// NOTE: returning the pointer will make it possible to create COLLECTIONS of objects (by creating a vector<BaseObject*> firstCollection, which is a SUBSET of the +// scene collection. In the future, I will make a "collection" class with specific sensing methods, but for the time being this can be done by hand. +BaseObject* begin(unsigned char _id=0) +{ + // This will add a new object. In the future, we can have modes - lines, bezier, and points - as well as specification of the type of object (enum) + // TO DO interesting feature: MERGING OF OBJECTS if they have the same ID? Note that if we DON'T specify an ID, the index will grow automatically starting from 0. + // for (int i=0; i<scene.totalObjects(); i++) if ((scene.objectArray[i]->ID())==_id) ptr_object=scene.objectArray[i]; + + BaseObject* ptr_newObject=new BaseObject(_id); // note: I cannot simply do: BaseObject newObject(_id) because this would be a LOCAL object, deallocated at the exit of this function. + // NOTE: by allocating the memory for the object pointed by ptr_newObject using new BaseObject, we lost the possibility of intantiating CHILD objects. + // One possibility is to have a parameter in the begin() method to indicate the type of object (for instance: GENERIC_OBJECT, LETTER, etc...). This way, + // we can properly instantiate the object. Another possiblity is to + + if (ptr_newObject) { // check to see if we could allocate memory + scene.ptr_currentObject=ptr_newObject; // type casting "slices" the child object (but in principle, "scene" WON'T use derived methods) + ptr_newObject->setColor(lsr.color); // use current color in state machine + //pc.printf("Could allocate object: %d", _id); + } else { + //pc.printf("Could not allocate object: %d", _id); + } + return(ptr_newObject); +} + +void vertex(V3& _v3) +{ + // Add a 3d point to the "current" object (ie, the latest added object in principle!), but do not RENDER it yet (no final projection computed). + // NOTE: the point WILL however be added by transforming it with the current MODELVIEW matrix. This way, we can create a whole 3d "scene" before rendering, + // that can be rotated again without the need to rebuilt it. + + scene.ptr_currentObject->addVertex(_v3, lsr.RT); + // Or, to avoid using ptr_currentObject: scene.objectArray.back()->addVertex(_v3, lsr.RT); + + // Another way: ptr_currentObject->vertexArray.push_back(lsr.RT*_v3); + // yet another way: use a method of lsr that "transform" a 3d point. Too heavy... +} + +void vertex(float x, float y, float z) +{ + V3 v(x, y, z); + vertex(v); // why I cannot do: vertex(V3(x, y, z)) ? because vertex(V3& _v3) needs a REFERENCE to an object that has larger scope that this method itself! +} + +void vertexArray(vector<V3>& _vertexArray) { + for (int i=0; i<_vertexArray.size(); i++) { + vertex(_vertexArray[i]); + } +} + +void end() // add the object to the current scene (we could add to a specific scene in the future...) +{ +// note: the current object pointerd by ptr_newObject can be BaseObject or any child class. This is not a problem as long as the methods applied to the +// scene vector array don't use child methods (then we can do a dynamic cast before including in the array: pb = dynamic_cast<CBase*>(&d); ). IF we want +// the scene class to be able to use child methods, then we need to make BaseObject polymorphic, by declaring all usable methods VIRTUAL. + scene.addCurrentObject(); // add the currently tracked object to the vector array + //scene.addObject(scene.ptr_newObject); // use Scene method to add object to the scene (global). + // NOTE: scene structure changed, BUT we don't need to inform the displaying engine if we don't want (the new object won't be displayed though) +} + +// Sensing methods (query and process sensed data for all objects): +// Objects: we can query the object by their ID or by just giving the object pointer. Now, since we are using begin/end to create objects (at least for now), it will +// be better to query by ID (and assume also that their IDs are different). Now, note that matching the ID may be costly (well, I assume they are not more than ten or so objects). +bool senseObject(int _id) +{ + BaseObject* ptr_object=NULL; + for (int i=0; i<scene.totalObjects(); i++) if ((scene.objectArray[i]->ID())==_id) ptr_object=scene.objectArray[i]; + if (ptr_object!=NULL) return(ptr_object->sense()); + else return(false); // if there is no object with this ID, return false (convention) +} + +bool senseScene() +{ + return(scene.sense()); +} + +// ================================= "GLOBAL" TRANSFORMATIONS (i.e. "viewing transforms") ON OBJECTS and SCENE without rebuilding them ============================================================ +// NOTE: There are two ways of doing this: (1) one is to apply the required transformation on the object/scene 3d points themselves; (2) the other is to mantain the 3d points values, but apply +// the transformation FOR rendering only. The advantage of this last technique is that there will not be any drifting of the values because +// of approximations after a while; the disadvantage is that we cannot easily track an "incremental" interactive transformation (say, scrolling a sphere). But this can be solved by using +// an "accumulator" tranformation matrix (or a quaternion). Also, the second methodology closely matches the OpenGL philosophy: defining the "modelview transformation" and the "viewing transformation", +// even though these transformations apply to the same modelview matrix. +// I will implement both, because even the first option can be useful (to create more complex scenes by rotating already created objects for instance - but it will be trickier to understand though) + +// (1) applying a transformation on the 3d points (NOTE: we could also apply transformations on the projected points! this is however a work for the "viewport"). +// NOTE: I am not going to create new transform methods (rotation and translations), but instead use the lsr stack. So, I assume the user has set RT to what she wants. Then, we will +// just apply RT to the object or whole scene. This method can be used to RESIZE the object (in particular when it is centered). +//(a) Objects: +void trasformObject(int _id) // use maps in the future!!! +{ + BaseObject* ptr_object=NULL; + for (int i=0; i<scene.totalObjects(); i++) if ((scene.objectArray[i]->ID())==_id) ptr_object=scene.objectArray[i]; + if (ptr_object!=NULL) { + // Apply the current RT transformation: + ptr_object->transform(lsr.RT); + // Them RENDER the object? no, this will be done only when drawing: the reason is that ON TOP of this transform, we may have the GLOBAL MODELVIEW matrix to apply + //lsr.renderObject(ptr_object); + } +} +// If we know the object already (this is the output of "begin" method for instance), we can apply the transformation without having to match the ID: +void transformObject(BaseObject* ptr_object) +{ + ptr_object->transform(lsr.RT); + // lsr.renderObject(ptr_object); +} +// (b) Whole scene: +void transformScene() +{ + scene.transform(lsr.RT); // or equialently: for (int i=0; i<scene.totalObjects(); i++) scene.objectArray[i]->transform(lsr.RT); + // lsr.renderScene(&scene); +} + +//(2) Apply current RT transformation and render but KEEPING the original values of the 3d points: +//(a) Objects: +void drawObject(int _id) // use maps in the future!!! +{ + BaseObject* ptr_object=NULL; + for (int i=0; i<scene.totalObjects(); i++) if ((scene.objectArray[i]->ID())==_id) ptr_object=scene.objectArray[i]; + if (ptr_object!=NULL) { + // We don't apply the current RT transformation (ptr_object->transform(lsr.RT)), but instead we directly RENDER it with a pre-transformation: + lsr.renderObject(ptr_object, lsr.RT); + } +} +// If we know the object already (this is the output of "begin" method for instance), we can apply the transformation without having to match the ID: +void drawObject(BaseObject* ptr_object) +{ + //ptr_object->transform(lsr.RT); + lsr.renderObject(ptr_object, lsr.RT); +} +// (b) Whole scene: +// ATTENTION: we may need START the display engine the FIRST time or nothing will be seeing (this is done by calling startDisplay()). +void drawScene() +{ + //NOTE: there is no need, AND IT IS better NOT to stop/resume the display engine (detaching and reattaching the ISR). + // There is no need in principle, because we only call drawScene AFTER the scene to draw has been passed to the laserSensingDisplay object. This means + // that the number of objects and points per object did NOT change. + //scene.transform(lsr.RT); // or equialently: for (int i=0; i<scene.totalObjects(); i++) scene.objectArray[i]->transform(lsr.RT); + lsr.renderScene(&scene, lsr.RT); +} + +// Color attribute transformation (for the time being, only to the whole scene): +void changeColorScene(unsigned char _color) { + for (int i=0; i<scene.totalObjects(); i++) scene.objectArray[i]->setColor(_color); +} + +// ================================= OBJECT PRIMITIVES (this could be in a different file) ============================================================ +// NOTE: these methods will not create new objects, unless we explicitly say so (by preceding them with "begin" and ending by "end". +// Instead, they will add the vertices to the "current object" being created. + +// A line: +void line(V3& v0, V3& v1, int npoints) +{ + V3 stepVector=(v1-v0)/(npoints-1); + for (int i=0; i<npoints; i++) { + V3 v(v0+stepVector*i); // vertex added to the current object + vertex(v); + } +} +void line(float x0, float y0, float z0, float x1, float y1, float z1, int npoints) +{ + // NOTE: we don't clear the current modelview, but use it's current value. + V3 A(x0, y0, z0), B(x1, y1, z1); + line(A, B, npoints); +} +// A line in the z=0 plane: +void line(float x0, float y0, float x1, float y1, int npoints) +{ + // NOTE: we don't clear the current modelview, but use it's current value. + V3 A(x0, y0, 0), B(x1, y1, 0); + line(A, B, npoints); +} + +// A square in the z=0 plane +void square(float sideSize, int npointsSide) +{ + V3 A(0,0, 0), B(sideSize, 0, 0); + line(A, B, npointsSide); + A.x = sideSize; + A.y = sideSize; + line(B, A, npointsSide); + B.x = 0; + B.y = sideSize; + line(A, B, npointsSide); + A.x = 0; + A.y = 0; + line(B, A, npointsSide); +} + +// A rectangle in the z=0 plane +void rectangle(float sideSizeX, float sideSizeY, int interPointDistance) +// note: interPointDistance (degrees per point) would be the equivalent of "pixels" separation for the laser projector +{ + V3 A(0,0, 0), B(sideSizeX, 0, 0); + line(A, B, sideSizeX/interPointDistance); + A.x = sideSizeX; + A.y = sideSizeY; + line(B, A, sideSizeY/interPointDistance); + B.x = 0; + B.y = sideSizeY; + line(A, B, sideSizeX/interPointDistance); + A.x = 0; + A.y = 0; + line(B, A, sideSizeY/interPointDistance); +} + + + +void circle(float radius, int numpoints) +{ + float angleStep=360.0/numpoints; + lsr.pushPoseMatrix(); + for (int i=0; i<numpoints; i++) { + vertex(radius, 0, 0); + lsr.rotateZ(angleStep); + } + // Redo the first point to close the cirlce: + vertex(radius, 0, 0); + lsr.popPoseMatrix(); +} + +// A "cube" (with one corner at the origin): +void cube(float sideSize, int nbpointsSide) +{ + lsr.pushPoseMatrix(); + square(sideSize, nbpointsSide); + line(0,0,0,0,0,sideSize, nbpointsSide); + lsr.translate(0,0,sideSize); + square(sideSize, nbpointsSide); + lsr.popPoseMatrix(); +} + +// LETTERS and STRINGS: +// NOTE: for the time being, these letters are not special object. I will just add the vertex of the letter to a normal BaseObject. +void letter3d(char _letter, float width, float height) +{ + unsigned char numpoints=fillAuxBuffer(_letter); + lsr.pushPoseMatrix(); + lsr.resize(0.1*width, 1.0/15.0*height, 1); // note: letter size as defined in textData.h is: width = 10, height = 15 + for (int i=0; i<numpoints; i++ ) vertex(auxbuffer[2*i], auxbuffer[2*i+1], 0); + lsr.popPoseMatrix(); +} + +// To create a string: either as a single object, or separate objects. In the later case, we should NOT call to "begin/end", because this will be done +// in THIS wrapping function... how to indicate this? and which index? So, for the time being, I will consider the FIRST case only. If we want to have +// separate objects, we will need to do this explicitly, basically copying this code and interspeeding it with "begin/end" and numbering the objects (cool +// for detecting individual touched letters). +void string3d(string _text, float totalWidth, float height) +{ + float stepX=1.0*totalWidth/_text.length(); // this is just fontWidth, or some percentage of it + lsr.pushPoseMatrix(); + for (unsigned short i=0; i<_text.length(); i++) { + char ch=_text.at(i); + lsr.translate(1.3*stepX,0,0); // slightly larger than the fontWidth... + if (ch!=' ') letter3d(ch, stepX, height); + } + lsr.popPoseMatrix(); +} + + + +// ========= MULTI OBJECTS ================================================================================== + +// A simple "normalized" grid on the z=0 plane (with points, repeated to get them more "clear"). +// Also, it may be interesting to make each group of points a separate object (for switching off laser, but also querying touch) +// Corner of the grid at the origin. +// NOTE: this is an example of a "multi-object" that does NOT need "begin/end" in the main +void grid(int nx, int ny, int repeatpoint) +{ + float px=1.0/(nx-1), py=1.0/(ny-1); + //lsr.pushPoseMatrix(); + for (int i=0; i<ny; i++) { + // pc.printf("\n"); + if (i%2==0) { // to do zig-zag... + for (int j=0; j<nx; j++) { + begin(ny*i+j); + for (int k=0; k<repeatpoint; k++) + //vertex(1.0*j*px,1.0*i*py,0); // faster than using translations... + vertex(1.0*i*py,1.0*j*px,0); + end(); + //pc.printf("%4.2f ", 1.0*j*px); + } + } else { // odd line: + for (int j=nx-1; j>=0; j--) { + begin(ny*i+j); + for (int k=0; k<repeatpoint; k++) + // vertex(1.0*j*px,1.0*i*py,0); // faster than using translations... + vertex(1.0*i*py,1.0*j*px,0); + end(); + //pc.printf("%4.2f ", 1.0*j*px); + } + } + } + //lsr.popPoseMatrix(); +} + +// A simple "muti-object" grid, not normalized (this is just for convenience): +void grid(float sizeX, float sizeY, int nx, int ny, int repeatpoint) +{ + lsr.pushPoseMatrix(); + lsr.resize(sizeX, sizeY, 1); + grid(nx, ny, repeatpoint); + lsr.popPoseMatrix(); +} + +//Normalized grid of circles: +void gridCircles(int nx, int ny, float radius, int nbpointsCircle) +{ + float px=1.0/(nx-1), py=1.0/(ny-1); + lsr.pushPoseMatrix(); + for (int i=0; i<ny; i++) { + if (i%2==0) { // to do zig-zag... + for (int j=0; j<nx; j++) { + begin(ny*i+j); + circle(radius, nbpointsCircle); + end(); + lsr.translate(px, 0, 0); + } + } else { // odd line: + for (int j=nx-1; j>=0; j--) { + begin(ny*i+j); + circle(radius, nbpointsCircle); + end(); + lsr.translate(-px, 0, 0); + } + } + lsr.translate(0, py, 0); + } + lsr.popPoseMatrix(); +} + +// Not normalized grid of circles: +void gridCircles(float sizeX, float sizeY, int nx, int ny, float radius, int nbpointsCircle) +{ + float px=sizeX/(nx-1), py=sizeY/(ny-1); + lsr.pushPoseMatrix(); + for (int i=0; i<ny; i++) { + if (i%2==0) { // to do zig-zag... + for (int j=0; j<nx; j++) { + begin(ny*i+j); + circle(radius, nbpointsCircle); + end(); + lsr.translate(px, 0, 0); + } + } else { // odd line: + for (int j=nx-1; j>=0; j--) { + lsr.translate(-px, 0, 0); + begin(ny*i+j); + circle(radius, nbpointsCircle); + end(); + } + } + lsr.translate(0, py, 0); + } + lsr.popPoseMatrix(); +} + +// WRAPPERS TO CREATE ARBITRARY OBJECTS FROM SENT DATA POINTS (V3 array) ============================================= +void createShapeObject(int idobject, vector<V3> &arrayVertices) { + begin(idobject); + vertexArray(arrayVertices); + end(); +} + +// WRAPPERS TO LOAD OBJECTS FROM SYSTEM FILE ========================================================================= +// ... to do + +// WRAPPERS TO LOAD MATRICES FROM SYSTEM FILE ========================================================================== +// ...to do + +// ================================= WRAPPERS FOR MORE BASIC IO FUNCTIONS ================================= +void showLimitsMirrors(unsigned short pointsPerLine, unsigned short durationSecs) +{ + // Stop the display engine and lasers: + stopDisplay(); + // but we need to ensure that the DISPLAYING lasers are ON: + IO.setRGBPower(0x07); + IO.showLimitsMirrors(pointsPerLine, durationSecs); + resumeDisplay(); +} + +void scanSerial(unsigned short pointsPerLine) +{ + // Stop the display engine and lasers: + stopDisplay(); + // ...but we need to ensure that the sensing laser is ON: + IO.setLaserLockinPower(1); + IO.scan_serial(pointsPerLine); + resumeDisplay(); +} + +void recomputeLookUpTable() +{ + // Stop the display engine and lasers: + stopDisplay(); + // but we need to ensure that the sensing laser is ON: + IO.setLaserLockinPower(1); + IO.scanLUT(); // this recreates and SAVES the LUT table + resumeDisplay(); +} + +// This is a special function that needs to be called at least once to set the displaying engine ON, thus setting the current state of the engine: +void startDisplay() { + IO.setLaserLockinPower(1); + lsd.run(); +} + +void stopDisplay() // save previous displaying status, stops the displaying engine, and switch off lasers +{ + previousLsdState=lsd.isRunning(); + lsd.stop(); + // also, switch off lasers: + IO.setLaserLockinPower(0); + IO.switchOffDisplayLasers(); +} + +void resumeDisplay() // go back to previous state of displaying engine +// and set the sensing laser ON (no need to explicitly switch the DISPLAYING lasers ON - this is done in the displaying engine ISR). +{ + if (previousLsdState) { + //switch on sensing lasers: + IO.setLaserLockinPower(1); + lsd.run(); + // rem: no need to set the displaying lasers: this is done per-object basis + } +} + +// =============================== HARDWARE KNOBS: switches, potentiometers... ================================================= +void hardwareKnobs() +{ + // Potentiometer, switches, etc: + //(1) Check for change of threshold mode button (switch one): + //!!! ATTENTION: this does not work very well to say the truth: bouncing+adc settings problems... + bool stateswitch; + if (IO.switchOneCheck(stateswitch)) { + if (stateswitch) pc.printf("Set: AUTO threshold mode\n"); + else pc.printf("Set: FIXED threshold mode\n"); + for (int i=0; i< scene.totalObjects(); i++) scene.objectArray[i]->displaySensingBuffer.setThresholdMode((stateswitch? 1 : 0)); + } + //(2) Check the current pot value and update the "fixed threshold" when pressing second switch (I don't care if the threshold mode + // is fixed or not - this will set the VALUE of the fixed threshold) + // NOTE: as thresholding mode CAN be object dependent though, but this is a simple hardware command that set the fixed threshold for all the objects. + if (IO.switchTwoCheck(stateswitch)) { + IO.updatePotValue(); + for (int i=0; i< scene.totalObjects(); i++) scene.objectArray[i]->displaySensingBuffer.setFixedThreshold(IO.potValue); + pc.printf("Fixed Threshold :%d\n", IO.potValue); + } +} +