/examples/advanced/touchTail/TailGesture.java

http://mt4j.googlecode.com/ · Java · 292 lines · 221 code · 45 blank · 26 comment · 18 complexity · 3a542657b800af9e9d9db3992bc41c89 MD5 · raw file

  1. package advanced.touchTail;
  2. import java.awt.Polygon;
  3. import org.mt4j.util.MTColor;
  4. import org.mt4j.util.math.ToolsMath;
  5. import org.mt4j.util.math.Vector3D;
  6. import processing.core.PApplet;
  7. /**
  8. * The Class TailGesture.
  9. *
  10. * Yellowtail by Golan Levin (www.flong.com)
  11. * Yellowtail (1998-2000) is an interactive software system for the gestural creation
  12. * and performance of real-time abstract animation. Yellowtail repeats a user's strokes end-over-end,
  13. * enabling simultaneous specification of a line's shape and quality of movement.
  14. * Each line repeats according to its own period,
  15. * producing an ever-changing and responsive display of lively, worm-like textures.
  16. */
  17. public class TailGesture {
  18. // private float damp = 5.0f; //ORIGINAL
  19. private float damp = 5.0f;
  20. private float dampInv = 1.0f / damp;
  21. private float damp1 = damp - 1;
  22. private int w;
  23. private int h;
  24. private int capacity;
  25. private float INIT_TH = 14; //ORIGINAL
  26. // private float INIT_TH = 24;
  27. private float thickness = INIT_TH;
  28. Vector3D path[];
  29. int crosses[];
  30. Polygon polygons[];
  31. int nPoints;
  32. int nPolys;
  33. float jumpDx;
  34. float jumpDy;
  35. boolean exists;
  36. private MTColor color;
  37. public TailGesture(int mw, int mh) {
  38. w = mw;
  39. h = mh;
  40. capacity = 600;
  41. path = new Vector3D[capacity];
  42. polygons = new Polygon[capacity];
  43. crosses = new int[capacity];
  44. for (int i=0;i<capacity;i++) {
  45. polygons[i] = new Polygon();
  46. polygons[i].npoints = 4;
  47. path[i] = new Vector3D();
  48. crosses[i] = 0;
  49. }
  50. nPoints = 0;
  51. nPolys = 0;
  52. exists = false;
  53. jumpDx = 0;
  54. jumpDy = 0;
  55. // this.color = new MTColor(Tools3D.getRandom(50, 255), Tools3D.getRandom(50, 255), Tools3D.getRandom(50, 255), 255);
  56. this.color = new MTColor(ToolsMath.getRandom(0, 255), ToolsMath.getRandom(0, 255), ToolsMath.getRandom(0, 255), 255);
  57. }
  58. public void clear() {
  59. nPoints = 0;
  60. exists = false;
  61. thickness = INIT_TH;
  62. }
  63. public void clearPolys() {
  64. nPolys = 0;
  65. }
  66. public void addPoint(float x, float y) {
  67. if (nPoints >= capacity) {
  68. // there are all sorts of possible solutions here,
  69. // but for abject simplicity, I don't do anything.
  70. }
  71. else {
  72. float v = distToLast(x, y);
  73. float p = getPressureFromVelocity(v);
  74. path[nPoints++].setXYZ(x,y,p);
  75. if (nPoints > 1) {
  76. exists = true;
  77. jumpDx = path[nPoints-1].x - path[0].x;
  78. jumpDy = path[nPoints-1].y - path[0].y;
  79. }
  80. }
  81. }
  82. private float getPressureFromVelocity(float v) {
  83. final float scale = 18;
  84. final float minP = 0.02f;
  85. final float oldP = (nPoints > 0) ? path[nPoints-1].z : 0;
  86. return ((minP + PApplet.max(0, 1.0f - v/scale)) + (damp1*oldP))*dampInv;
  87. }
  88. private void setPressures() {
  89. // pressures vary from 0...1
  90. float pressure;
  91. Vector3D tmp;
  92. float t = 0;
  93. float u = 1.0f / (nPoints - 1)*PApplet.TWO_PI;
  94. for (int i = 0; i < nPoints; i++) {
  95. pressure = PApplet.sqrt((1.0f - PApplet.cos(t)) * 0.5f);
  96. path[i].z = pressure;
  97. t += u;
  98. }
  99. }
  100. public float distToLast(float ix, float iy) {
  101. if (nPoints > 0) {
  102. Vector3D v = path[nPoints-1];
  103. float dx = v.x - ix;
  104. float dy = v.y - iy;
  105. return PApplet.mag(dx, dy);
  106. }
  107. else {
  108. return 30;
  109. }
  110. }
  111. public void compile() {
  112. // compute the polygons from the path of Vector3D's
  113. if (exists) {
  114. clearPolys();
  115. Vector3D p0, p1, p2;
  116. float radius0, radius1;
  117. float ax, bx, cx, dx;
  118. float ay, by, cy, dy;
  119. int axi, bxi, cxi, dxi, axip, axid;
  120. int ayi, byi, cyi, dyi, ayip, ayid;
  121. float p1x, p1y;
  122. float dx01, dy01, hp01, si01, co01;
  123. float dx02, dy02, hp02, si02, co02;
  124. float dx13, dy13, hp13, si13, co13;
  125. float taper = 1.0f;
  126. int nPathPoints = nPoints - 1;
  127. int lastPolyIndex = nPathPoints - 1;
  128. float npm1finv = 1.0f / PApplet.max(1, nPathPoints - 1);
  129. // handle the first point
  130. p0 = path[0];
  131. p1 = path[1];
  132. radius0 = p0.z * thickness;
  133. dx01 = p1.x - p0.x;
  134. dy01 = p1.y - p0.y;
  135. hp01 = PApplet.sqrt(dx01*dx01 + dy01*dy01);
  136. if (hp01 == 0) {
  137. hp02 = 0.0001f;
  138. }
  139. co01 = radius0 * dx01 / hp01;
  140. si01 = radius0 * dy01 / hp01;
  141. ax = p0.x - si01;
  142. ay = p0.y + co01;
  143. bx = p0.x + si01;
  144. by = p0.y - co01;
  145. int xpts[];
  146. int ypts[];
  147. int LC = 20;
  148. int RC = w-LC;
  149. int TC = 20;
  150. int BC = h-TC;
  151. float mint = 0.618f;
  152. float tapow = 0.4f;
  153. // handle the middle points
  154. int i = 1;
  155. Polygon apoly;
  156. for (i = 1; i < nPathPoints; i++) {
  157. taper = PApplet.pow((lastPolyIndex-i) * npm1finv, tapow);
  158. p0 = path[i-1];
  159. p1 = path[i ];
  160. p2 = path[i+1];
  161. p1x = p1.x;
  162. p1y = p1.y;
  163. radius1 = Math.max(mint, taper * p1.z * thickness);
  164. // assumes all segments are roughly the same length...
  165. dx02 = p2.x - p0.x;
  166. dy02 = p2.y - p0.y;
  167. hp02 = (float) Math.sqrt(dx02*dx02 + dy02*dy02);
  168. if (hp02 != 0) {
  169. hp02 = radius1/hp02;
  170. }
  171. co02 = dx02 * hp02;
  172. si02 = dy02 * hp02;
  173. // translate the integer coordinates to the viewing rectangle
  174. axi = axip = (int)ax;
  175. ayi = ayip = (int)ay;
  176. axi=(axi<0)?(w-((-axi)%w)):axi%w;
  177. axid = axi-axip;
  178. ayi=(ayi<0)?(h-((-ayi)%h)):ayi%h;
  179. ayid = ayi-ayip;
  180. // set the vertices of the polygon
  181. apoly = polygons[nPolys++];
  182. xpts = apoly.xpoints;
  183. ypts = apoly.ypoints;
  184. xpts[0] = axi = axid + axip;
  185. xpts[1] = bxi = axid + (int) bx;
  186. xpts[2] = cxi = axid + (int)(cx = p1x + si02);
  187. xpts[3] = dxi = axid + (int)(dx = p1x - si02);
  188. ypts[0] = ayi = ayid + ayip;
  189. ypts[1] = byi = ayid + (int) by;
  190. ypts[2] = cyi = ayid + (int)(cy = p1y - co02);
  191. ypts[3] = dyi = ayid + (int)(dy = p1y + co02);
  192. // keep a record of where we cross the edge of the screen
  193. crosses[i] = 0;
  194. if ((axi<=LC)||(bxi<=LC)||(cxi<=LC)||(dxi<=LC)) {
  195. crosses[i]|=1;
  196. }
  197. if ((axi>=RC)||(bxi>=RC)||(cxi>=RC)||(dxi>=RC)) {
  198. crosses[i]|=2;
  199. }
  200. if ((ayi<=TC)||(byi<=TC)||(cyi<=TC)||(dyi<=TC)) {
  201. crosses[i]|=4;
  202. }
  203. if ((ayi>=BC)||(byi>=BC)||(cyi>=BC)||(dyi>=BC)) {
  204. crosses[i]|=8;
  205. }
  206. //swap data for next time
  207. ax = dx;
  208. ay = dy;
  209. bx = cx;
  210. by = cy;
  211. }
  212. // handle the last point
  213. p2 = path[nPathPoints];
  214. apoly = polygons[nPolys++];
  215. xpts = apoly.xpoints;
  216. ypts = apoly.ypoints;
  217. xpts[0] = (int)ax;
  218. xpts[1] = (int)bx;
  219. xpts[2] = (int)(p2.x);
  220. xpts[3] = (int)(p2.x);
  221. ypts[0] = (int)ay;
  222. ypts[1] = (int)by;
  223. ypts[2] = (int)(p2.y);
  224. ypts[3] = (int)(p2.y);
  225. }
  226. }
  227. public void smooth() {
  228. // average neighboring points
  229. final float weight = 18;
  230. final float scale = 1.0f / (weight + 2);
  231. int nPointsMinusTwo = nPoints - 2;
  232. Vector3D lower, upper, center;
  233. for (int i = 1; i < nPointsMinusTwo; i++) {
  234. lower = path[i-1];
  235. center = path[i];
  236. upper = path[i+1];
  237. center.x = (lower.x + weight*center.x + upper.x)*scale;
  238. center.y = (lower.y + weight*center.y + upper.y)*scale;
  239. }
  240. }
  241. public MTColor getColor() {
  242. return color;
  243. }
  244. public void setColor(MTColor color) {
  245. this.color = color;
  246. }
  247. }