001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.demos.touchtable; 031 032import java.io.IOException; 033import java.io.PrintWriter; 034import java.util.ArrayList; 035import java.util.Scanner; 036 037import org.openimaj.image.MBFImage; 038import org.openimaj.image.colour.RGBColour; 039import org.openimaj.image.processing.transform.PiecewiseMeshWarp; 040import org.openimaj.math.geometry.point.Point2d; 041import org.openimaj.math.geometry.point.Point2dImpl; 042import org.openimaj.math.geometry.shape.Rectangle; 043import org.openimaj.math.geometry.shape.Shape; 044import org.openimaj.math.geometry.shape.Triangle; 045import org.openimaj.util.pair.Pair; 046 047public class TriangleCameraConfig implements CameraConfig { 048 049 private int gridWidth; 050 private int gridHeight; 051 private Rectangle visibleArea; 052 private ArrayList<Point2d> touchArray; 053 private ArrayList<Point2d> screenArray; 054 private ArrayList<Pair<Shape>> trianglePairs; 055 private PiecewiseMeshWarp<Float[], MBFImage> nlp; 056 057 public TriangleCameraConfig(ArrayList<Point2d> touchArray, int gridx,int gridy, Rectangle visibleArea) { 058 this.gridWidth = gridx; 059 this.gridHeight = gridy; 060 this.visibleArea = visibleArea; 061 this.touchArray = touchArray; 062 createScreenArray(); 063 createTriangles(); 064 createNonLinearWarp(); 065 } 066 067 private void createScreenArray() { 068 this.screenArray = new ArrayList<Point2d>(); 069 int yInc = (int) (this.visibleArea.height/gridHeight); 070 int xInc = (int) (this.visibleArea.width/gridWidth); 071 for(int row = 0; row < gridHeight+1; row++){ 072 for(int col = 0; col < gridWidth + 1; col++){ 073 this.screenArray.add(new Point2dImpl(col * xInc, row * yInc)); 074 } 075 } 076 } 077 078 public TriangleCameraConfig() { 079 080 } 081 082 private void createTriangles() { 083 this.trianglePairs = new ArrayList<Pair<Shape>>(); 084 for (int row = 0; row < gridHeight; row++) { 085 for(int col = 0; col < gridWidth; col++){ 086 Point2d p1 = this.touchArray.get(col + row * (gridWidth+1)); 087 Point2d p2 = this.touchArray.get((col+1) + row * (gridWidth+1)); 088 Point2d p3a = this.touchArray.get(col + (row+1) * (gridWidth+1)); 089 Point2d p3b = this.touchArray.get((col+1) + (row+1) * (gridWidth+1)); 090 091 Point2d p1Screen = this.screenArray.get(col + row * (gridWidth+1)); 092 Point2d p2Screen = this.screenArray.get((col+1) + row * (gridWidth+1)); 093 Point2d p3aScreen = this.screenArray.get(col + (row+1) * (gridWidth+1)); 094 Point2d p3bScreen = this.screenArray.get((col+1) + (row+1) * (gridWidth+1)); 095 096 Triangle ct1 = new Triangle(p1,p2,p3a); 097 Triangle ct2 = new Triangle(p2,p3a,p3b); 098 099 Triangle st1 = new Triangle(p1Screen,p2Screen,p3aScreen); 100 Triangle st2 = new Triangle(p2Screen,p3aScreen,p3bScreen); 101 102 this.trianglePairs.add(new Pair<Shape>(st1,ct1)); 103 this.trianglePairs.add(new Pair<Shape>(st2,ct2)); 104 } 105 } 106 107 } 108 109 @Override 110 public Touch transformTouch(Touch point) { 111 int matching = this.nlp.getMatchingShapeIndex(point.calculateCentroid()); 112 if(matching == -1) return null; 113 float ptx = point.getX(); 114 float pty = point.getY(); 115 Point2d[] cameraTriangle = ((Triangle)(this.trianglePairs.get(matching).secondObject())).vertices; 116 Point2d[] screenTriangle = ((Triangle)(this.trianglePairs.get(matching).firstObject())).vertices; 117 Point2dImpl A = (Point2dImpl) cameraTriangle[0]; // Place camera vector triangle points 118 Point2dImpl B = (Point2dImpl) cameraTriangle[1]; // into some local vectors 119 Point2dImpl C = (Point2dImpl) cameraTriangle[2]; 120 float total_area = (A.x - B.x) * (A.y - C.y) - (A.y - B.y) * (A.x - C.x); // Calculate the total area of the triangle 121 // pt,B,C 122 float area_A = (ptx - B.x) * (pty - C.y) - (pty - B.y) * (ptx - C.x); // and find the area enclosed by the 123 124 // A,pt,C 125 float area_B = (A.x - ptx) * (A.y - C.y) - (A.y - pty) * (A.x - C.x); // three camera vector triangle points 126 127 float bary_A = area_A / total_area; // so we can find three fractions of the total area 128 float bary_B = area_B / total_area; 129 float bary_C = 1.0f - bary_A - bary_B; // bary_A + bary_B + bary_C = 1 130 131 Point2dImpl sA = (Point2dImpl) screenTriangle[0]; // Place screen vector triangle points 132 Point2dImpl sB = (Point2dImpl) screenTriangle[1]; // into some local vectors 133 Point2dImpl sC = (Point2dImpl) screenTriangle[2]; 134 135 136 float transformedPosx = (sA.x*bary_A) + (sB.x*bary_B) + (sC.x*bary_C); 137 float transformedPosy = (sA.y*bary_A) + (sB.y*bary_B) + (sC.y*bary_C); 138 139 return new Touch(transformedPosx,transformedPosy,point.getRadius(), point.touchID, point.motionVector); 140 } 141 142 @Override 143 public String asciiHeader() { 144 return "triangleconfig"; 145 } 146 147 @Override 148 public void readASCII(Scanner in) throws IOException { 149 this.gridWidth = in.nextInt(); 150 this.gridHeight = in.nextInt(); 151 this.visibleArea = new Rectangle(in.nextFloat(),in.nextFloat(),in.nextFloat(),in.nextFloat()); 152 this.touchArray = new ArrayList<Point2d>(); 153 while(in.hasNext()){ 154 this.touchArray.add( 155 new Point2dImpl(in.nextFloat(),in.nextFloat()) 156 ); 157 } 158 this.createScreenArray(); 159 this.createTriangles(); 160 this.createNonLinearWarp(); 161 162 } 163 164 private void createNonLinearWarp() { 165 this.nlp = new PiecewiseMeshWarp<Float[], MBFImage>(this.trianglePairs); 166 } 167 168 @Override 169 public void writeASCII(PrintWriter out) throws IOException { 170 out.format("%d %d\n",this.gridWidth,this.gridHeight); 171 out.format("%f %f %f %f\n",this.visibleArea.x,this.visibleArea.y,this.visibleArea.width,this.visibleArea.height); 172 for (Point2d t : this.touchArray) { 173 out.format("%f %f\n", 174 t.getX(),t.getY() 175 ); 176 } 177 } 178 179 public void drawTriangles(MBFImage image) { 180 for (Pair<Shape> t : this.trianglePairs) { 181 image.drawShape(t.firstObject(), RGBColour.RED); 182 } 183 } 184 185}