package advanced.touchTail;
import java.awt.Polygon;
import java.io.File;
import java.util.HashMap;
import javax.media.opengl.GL;
import org.mt4j.components.visibleComponents.AbstractVisibleComponent;
import org.mt4j.input.inputProcessors.IGestureEventListener;
import org.mt4j.input.inputProcessors.MTGestureEvent;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.DragEvent;
import org.mt4j.input.inputProcessors.componentProcessors.dragProcessor.MultipleDragProcessor;
import org.mt4j.util.MT4jSettings;
import org.mt4j.util.MTColor;
import org.mt4j.util.math.Plane;
import org.mt4j.util.math.Ray;
import org.mt4j.util.math.Vector3D;
import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PImage;
import processing.opengl.PGraphicsOpenGL;
/**
* The Class TouchTailComponent.
*
* Yellowtail by Golan Levin (www.flong.com)
* Yellowtail (1998-2000) is an interactive software system for the gestural creation
* and performance of real-time abstract animation. Yellowtail repeats a user's strokes end-over-end,
* enabling simultaneous specification of a line's shape and quality of movement.
* Each line repeats according to its own period,
* producing an ever-changing and responsive display of lively, worm-like textures.
*/
public class TouchTailComponent extends AbstractVisibleComponent {
private TailGesture[] gestureArray;
private final int nGestures = 30; // Number of gestures
private final int minMove = 3; // Minimum travel for a new point
private int currentGestureID;
private PApplet app;
private HashMap<Long, TailGesture> idToGesture;
private Plane plane;
private PImage a;
private boolean useTexture = false;
public TouchTailComponent(PApplet applet) {
super(applet);
idToGesture = new HashMap<Long, TailGesture>();
this.app = applet;
currentGestureID = -1;
gestureArray = new TailGesture[nGestures];
for (int i = 0; i < nGestures; i++) {
gestureArray[i] = new TailGesture(app.width, app.height);
}
clearTails();
this.registerInputProcessor(new MultipleDragProcessor(app));
this.addGestureListener(MultipleDragProcessor.class, new DragListener());
Vector3D norm = new Vector3D(0,0,1);
Vector3D pointInPlane = new Vector3D(0,0,0);
plane = new Plane(pointInPlane, norm);
// /*
if (useTexture){
this.setNoStroke(true);
a = applet.loadImage(System.getProperty("user.dir") + File.separator + "examples" + File.separator +"advanced"+ File.separator+ File.separator + "touchTail" + File.separator + "data" + File.separator +
"brush_cr3.png");
}else{
this.setNoStroke(false);
this.setStrokeWeight(0.8f);
}
// */
}
private class DragListener implements IGestureEventListener{
public boolean processGestureEvent(MTGestureEvent ge) {
DragEvent de = (DragEvent)ge;
Vector3D to = de.getTo();
switch (de.getId()) {
case DragEvent.GESTURE_DETECTED:{
currentGestureID = (currentGestureID+1) % nGestures;
//System.out.println("New current gesture ID => " + currentGestureID);
TailGesture G = gestureArray[currentGestureID];
idToGesture.put(de.getDragCursor().getId(), G);
G.clear();
G.clearPolys();
G.addPoint(to.x, to.y);
}break;
case DragEvent.GESTURE_UPDATED:{
TailGesture G = idToGesture.get(de.getDragCursor().getId());
if (G.distToLast(to.x, to.y) > minMove) {
G.addPoint(to.x, to.y);
G.smooth();
G.compile();
}
}break;
case DragEvent.GESTURE_ENDED:{
idToGesture.remove(de.getDragCursor().getId());
}break;
default:
break;
}
return true;
}
}
@Override
public void drawComponent(PGraphics g) {
//FIXME TEST
if (MT4jSettings.getInstance().isOpenGlMode()){
GL gl = ((PGraphicsOpenGL)g).gl;
if (useTexture){
// gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);
// gl.glBlendFunc(GL.GL_ONE, GL.GL_ONE);
gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
}
// gl.glDisable(GL.GL_DEPTH_TEST);
// gl.glColorMask(true, true, false, false);
}
if (useTexture){
g.textureMode(g.NORMALIZED);
}
/*
g.textureMode(g.NORMALIZED);
g.beginShape(PApplet.QUADS);
g.texture(a); //FIXME TEST
g.vertex(0, 0, 0,0);
g.vertex(100, 0, 1,0);
g.vertex(100, 100, 1,1);
g.vertex(0, 100, 0,1);
g.endShape();
*/
updateGeometry();
for (int i = 0; i < nGestures; i++) {
renderGesture(g, gestureArray[i], g.width, g.height);
}
}
private void renderGesture(PGraphics g, TailGesture gesture, int w, int h) {
if (gesture.exists) {
if (gesture.nPolys > 0) {
Polygon polygons[] = gesture.polygons;
int crosses[] = gesture.crosses;
if (this.isNoStroke())
g.noStroke();
else{
MTColor strokeCol = gesture.getColor();
g.strokeWeight(this.getStrokeWeight());
g.stroke(strokeCol.getR(), strokeCol.getG(), strokeCol.getB(), strokeCol.getAlpha());
}
if (this.isNoFill())
g.noFill();
else{
MTColor fillCol = gesture.getColor();
g.fill(fillCol.getR(), fillCol.getG(), fillCol.getB(), fillCol.getAlpha());
}
int xpts[];
int ypts[];
Polygon p;
int cr;
//FIXME TEST
if (useTexture){
MTColor c = gesture.getColor();
g.tint(c.getR(), c.getG(), c.getB(), c.getAlpha());
}
g.beginShape(PApplet.QUADS);
if (useTexture){
g.texture(a); //FIXME TEST
}
int gnp = gesture.nPolys;
for (int i=0; i < gnp; i++) {
p = polygons[i];
xpts = p.xpoints;
ypts = p.ypoints;
if (useTexture){
//FIXME TEST
g.vertex(xpts[0], ypts[0], 0,0);
g.vertex(xpts[1], ypts[1], 1,0);
g.vertex(xpts[2], ypts[2], 1,1);
g.vertex(xpts[3], ypts[3], 0,1);
}else{
g.vertex(xpts[0], ypts[0]);
g.vertex(xpts[1], ypts[1]);
g.vertex(xpts[2], ypts[2]);
g.vertex(xpts[3], ypts[3]);
}
// /*
if ((cr = crosses[i]) > 0) {
if ((cr & 3)>0) {
g.vertex(xpts[0]+w, ypts[0]);
g.vertex(xpts[1]+w, ypts[1]);
g.vertex(xpts[2]+w, ypts[2]);
g.vertex(xpts[3]+w, ypts[3]);
g.vertex(xpts[0]-w, ypts[0]);
g.vertex(xpts[1]-w, ypts[1]);
g.vertex(xpts[2]-w, ypts[2]);
g.vertex(xpts[3]-w, ypts[3]);
}
if ((cr & 12)>0) {
g.vertex(xpts[0], ypts[0]+h);
g.vertex(xpts[1], ypts[1]+h);
g.vertex(xpts[2], ypts[2]+h);
g.vertex(xpts[3], ypts[3]+h);
g.vertex(xpts[0], ypts[0]-h);
g.vertex(xpts[1], ypts[1]-h);
g.vertex(xpts[2], ypts[2]-h);
g.vertex(xpts[3], ypts[3]-h);
}
// I have knowingly retained the small flaw of not
// completely dealing with the corner conditions
// (the case in which both of the above are true).
}
// */
}
g.endShape();
}
}
}
private void updateGeometry() {
TailGesture J;
for (int g = 0; g < nGestures; g++) {
if ((J = gestureArray[g]).exists) {
if (!idToGesture.containsValue(J)){
advanceGesture(J); //FIXME ENABLE
}
}
}
}
private void advanceGesture(TailGesture gesture) {
// Move a Gesture one step
if (gesture.exists) { // check
int nPts = gesture.nPoints;
int nPts1 = nPts-1;
Vector3D path[];
float jx = gesture.jumpDx;
float jy = gesture.jumpDy;
if (nPts > 0) {
path = gesture.path;
for (int i = nPts1; i > 0; i--) {
path[i].x = path[i-1].x;
path[i].y = path[i-1].y;
}
path[0].x = path[nPts1].x - jx;
path[0].y = path[nPts1].y - jy;
gesture.compile();
}
}
}
public void clearTails() {
for (int i = 0; i < nGestures; i++) {
gestureArray[i].clear();
}
}
@Override
protected boolean componentContainsPointLocal(Vector3D testPoint) {
return plane.componentContainsPointLocal(testPoint);
}
@Override
public Vector3D getIntersectionLocal(Ray ray) {
return plane.getIntersectionLocal(ray);
}
/*
void keyPressed() {
if (key == '+' || key == '=') {
if (currentGestureID >= 0) {
float th = gestureArray[currentGestureID].thickness;
gestureArray[currentGestureID].thickness = min(96, th+1);
gestureArray[currentGestureID].compile();
}
} else if (key == '-') {
if (currentGestureID >= 0) {
float th = gestureArray[currentGestureID].thickness;
gestureArray[currentGestureID].thickness = max(2, th-1);
gestureArray[currentGestureID].compile();
}
} else if (key == ' ') {
clearGestures();
}
}
*/
}