commit 18d15f2fb86b6f4d8710b2b9ef581a00eae626e0
Author: Leszek Koltunski <leszek@distorted.org>
Date:   Wed Nov 23 23:45:12 2016 +0000

    Polish up DEFORM.

diff --git a/src/main/res/raw/main_vertex_shader.glsl b/src/main/res/raw/main_vertex_shader.glsl
index a8e703c..782dada 100644
--- a/src/main/res/raw/main_vertex_shader.glsl
+++ b/src/main/res/raw/main_vertex_shader.glsl
@@ -161,64 +161,64 @@ void restrictZ(inout float v)
 //////////////////////////////////////////////////////////////////////////////////////////////
 // DEFORM EFFECT
 //
-// Deform the whole shape of the Object by force V
-// 
-// If the point of application (Sx,Sy) is on the upper edge of the Object, then:
-// a) ignore Vz
-// b) change shape of the whole Object in the following way:
-//    Suppose the upper-left corner of the Object rectangle is point L, upper-right - R, force vector V
-//    is applied to point M on the upper edge, width of the Object = w, height = h, |LM| = Wl, |MR| = Wr,
-//    force vector V=(Vx,Vy). Also let H = h/(h+Vy)
-//
-//    Let now L' and R' be points such that vec(LL') = Wr/w * vec(V) and vec(RR') = Wl/w * vec(V)
-//    now let Vl be a point on the line segment L --> M+vec(V) such that Vl(y) = L'(y)
-//    and let Vr be a point on the line segment R --> M+vec(V) such that Vr(y) = R'(y)
-//    
-//    Now define points Fl and Fr, the points L and R will be moved to under force V, with Fl(y)=L'(y)
-//    and Fr(y)=R'(y) and |VrFr|/|VrR'| = |VlFl|/|VlL'| = H
-//    Now notice that |VrR'| = |VlL'| = Wl*Wr / w   ( a little geometric puzzle! )
-//
-//    Then points L,R under force V move by vectors vec(Fl), vec(Fr) where
-//    vec(Fl) = (Wr/w) * [ (Vx+Wl)-Wl*H, Vy ] = (Wr/w) * [ Wl*Vy / (h+Vy) + Vx, Vy ]
-//    vec(Fr) = (Wl/w) * [ (Vx-Wr)+Wr*H, Vy ] = (Wl/w) * [-Wr*Vy / (h+Vy) + Vx, Vy ]
-//
-//    Lets now denote M+vec(V) = M'. The line segment LMR gets distorted to the curve Fl-M'-Fr. Let's
-//    now arbitrarilly decide that:
-//    a) at point Fl the curve has to be parallel to line LM'
-//    b) at point M' - to line LR
-//    c) at point Fr - to line M'R
-//
-//    Now if Fl=(flx,fly) , M'=(mx,my) , Fr=(frx,fry); direction vector at Fl is (vx,vy) and at M'
-//    is (+c,0) where +c is some positive constant, then  the parametric equations of the Fl--->M'
-//    section of the curve (which has to satisfy (X(0),Y(0)) = Fl, (X(1),Y(1))=M',
-//    (X'(0),Y'(0)) = (vx,vy), (X'(1),Y'(1)) = (+c,0) ) is
-//
-//    X(t) = ( (mx-flx)-vx )t^2 + vx*t + flx                                  (*)
-//    Y(t) = ( vy - 2(my-fly) )t^3 + ( 3(my-fly) -2vy )t^2 + vy*t + fly
-//
-//    Here we have to have X'(1) = 2(mx-flx)-vx which is positive <==> vx<2(mx-flx). We also have to
-//    have vy<2(my-fly) so that Y'(t)>0 (this is a must otherwise we have local loops!)
-//    Similarly for the Fr--->M' part of the curve we have the same equation except for the fact that
-//    this time we have to have X'(1)<0 so now we have to have vx>2(mx-frx).
-//
-//    If we are stretching the left or right edge of the bitmap then the only difference is that we
-//    have to have (X'(1),Y'(1)) = (0,+-c) with + or - c depending on which part of the curve
-//    we are tracing. Then the parametric equation is
-//
-//    X(t) = ( vx - 2(mx-flx) )t^3 + ( 3(mx-flx) -2vx )t^2 + vx*t + flx
-//    Y(t) = ( (my-fly)-vy )t^2 + vy*t + fly
-//
-//    If we are dragging the top edge:    
-//
-//    Then point (x,h/2) on the top edge will move by vector (X(t),Y(t)) where those functions are
-//    given by (*) and t =  x < dSx ? (w/2+x)/(w/2+dSx) : (w/2-x)/(w/2-dSx)    (-w/2 < x < +w/2 !)
-//    (this is 'vec2 time' below in the code).
-//    Any point (x,y) will move by vector (a*X(t),a*Y(t)) where a is (y+h/2)/h
-  
+// Deform the whole shape of the Object by force V. Algorithm is as follows:
+//
+// Suppose we apply force (Vx,Vy) at point (Cx,Cy) (i.e. the center of the effect). Then, first of all,
+// divide the rectangle into 4 smaller rectangles along the 1 horizontal + 1 vertical lines that pass
+// through (Cx,Cy). Now suppose we have already understood the following case:
+//
+// A vertical (0,Vy) force applied to a rectangle (WxH) in size, at center which is the top-left corner
+// of the rectangle.  (*)
+//
+// If we understand (*), then we understand everything, because in order to compute the movement of the
+// whole rectangle we can apply (*) 8 times: for each one of the 4 sub-rectangles, apply (*) twice,
+// once for the vertical component of the force vector, the second time for the horizontal one.
+//
+// Let's then compute (*):
+// 1) the top-left point will move by exactly (0,Vy)
+// 2) we arbitrarily decide that the top-right point will move by (|Vy|/(|Vy|+A*W))*Vy, where A is some
+//    arbitrary constant (const float A below). The F(V,W) = (|Vy|/(|Vy|+A*W)) comes from the following:
+//    a) we want F(V,0) = 1
+//    b) we want lim V->inf (F) = 1
+//    c) we actually want F() to only depend on W/V, which we have here.
+// 3) then the top edge of the rectangle will move along the line Vy*G(x), where G(x) = (1 - (A*W/(|Vy|+A*W))*(x/W)^2)
+// 4) Now we decide that the left edge of the rectangle will move along Vy*H(y), where H(y) = (1 - |y|/(|Vy|+C*|y|))
+//    where C is again an arbitrary constant. Again, H(y) comes from the requirement that no matter how
+//    strong we push the left edge of the rectangle up or down, it can never 'go over itself', but its
+//    length will approach 0 if squeezed very hard.
+// 5) The last point we need to compute is the left-right motion of the top-right corner (i.e. if we push
+//    the top-left corner up very hard, we want to have the top-right corner not only move up, but also to
+//    the left at least a little bit).
+//    We arbitrarily decide that, in addition to moving up-down by Vy*F(V,W), the corner will also move
+//    left-right by I(V,W) = B*W*F(V,W), where B is again an arbitrary constant.
+// 6) combining 3), 4) and 5) together, we arrive at a movement of an arbitrary point (x,y) away from the
+//    top-left corner:
+//    X(x,y) = -B*x * (|Vy|/(|Vy|+A*W)) * (1-(y/H)^2)                               (**)
+//    Y(x,y) = Vy * (1 - |y|/(|Vy|+C*|y|)) * (1 - (A*W/(|Vy|+A*W))*(x/W)^2)         (**)
+//
+// We notice that formulas (**) have been construed so that it is possible to continously mirror them
+// left-right and up-down (i.e. apply not only to the 'bottom-right' rectangle of the 4 subrectangles
+// but to all 4 of them!).
+//
+// Constants:
+// a) A : valid values: (0,infinity). 'Bendiness' if the surface - the higher A is, the more the surface
+//        bends. A<=0 destroys the system,
+// b) B : valid values: <-1,1>. The amount side edges get 'sucked' inwards when we pull the middle of the
+//        top edge up. B=0 --> not at all, B=1: a looot. B=-0.5: the edges will actually be pushed outwards
+//        quite a bit. One can also set it to <-1 or >1, but it will look a bit ridiculous.
+// c) C : valid values: <1,infinity). The derivative of the H(y) function at 0, i.e. the rate of 'squeeze'
+//        surface gets along the force line. C=1: our point gets pulled very closely to points above it
+//        even when we apply only small vertical force to it. The higher C is, the more 'uniform' movement
+//        along the force line is.
+//        0<=C<1 looks completely ridiculous and C<0 destroys the system.
+
 void deform(in int effect, inout vec4 v)
   {
+  const vec2 one = vec2(1.0,1.0);
+
   const float A = 0.5;
-  const float B = 0.3;
+  const float B = 0.2;
+  const float C = 5.0;
 
   vec2 center = vUniforms[effect+1].yz;
   vec2 force  = vUniforms[effect].xy;
@@ -229,12 +229,17 @@ void deform(in int effect, inout vec4 v)
 
   vec2 Aw = A*maxdist;
   vec2 quot = dist / maxdist;
+  quot = quot*quot;                          // ( (x/W)^2 , (y/H)^2 ) where x,y are distances from V to center
+
+  float denomV = 1.0 / (aForce.y + Aw.x);
+  float denomH = 1.0 / (aForce.x + Aw.y);
 
-  float mvXvert =-((B*dist.x*aForce.y)/(aForce.y + Aw.x))*(1.0-quot.y*quot.y);
-  float mvYvert = force.y*( 1.0 - quot.x*quot.x*(Aw.x/(aForce.y+Aw.x)) ) * aForce.y/(aForce.y+aDist.y);
+  vec2 vertCorr= one - aDist / ( aForce+C*aDist + (one-sign(aForce)) );
 
-  float mvXhorz =-force.x*( 1.0 - quot.y*quot.y*(Aw.y/(aForce.x+Aw.y)) ) * aForce.x/(aForce.x+aDist.x);
-  float mvYhorz =-((B*dist.y*aForce.x)/(aForce.x + Aw.y))*(1.0-quot.x*quot.x);
+  float mvXvert = -B * dist.x * aForce.y * (1.0-quot.y) * denomV;    // impact the vertical   component of the force vector has on horizontal movement
+  float mvYhorz = -B * dist.y * aForce.x * (1.0-quot.x) * denomH;    // impact the horizontal component of the force vector has on vertical   movement
+  float mvYvert =  force.y * (1.0-quot.x*Aw.x*denomV) * vertCorr.y;  // impact the vertical   component of the force vector has on vertical   movement
+  float mvXhorz = -force.x * (1.0-quot.y*Aw.y*denomH) * vertCorr.x;  // impact the horizontal component of the force vector has on horizontal movement
 
   v.x -= (mvXvert+mvXhorz);
   v.y -= (mvYvert+mvYhorz);
