desc: Vmorph v1.01 (Edit for more info)
Requested, tested and perfected by Reflected aka Dagan Israeli.
Note : always add Jeffo's MIDItoReaControlPath after Vmorph.

1) XY-PAD :
-----------
* Left click to drag the target.
* Right click to make it follow the mouse.
* CTRL click to create/delete patch dots.
* Right click selects/unselects dots.
* SHIFT drag a dot to increase its size (radius of influence).
* ALT left/right click to zoom in/out.

2) CC MENU :
------------
Vmorph sends midi CC to control your plugins :
* CTRL click in the CC menu to add a row. 
* Click a knob, use midi learn, and drag the CC value.
* Right click to switch to graphic values and set names.
Vmorph stores values from plugins via parameter modulation :
* Select Vmorph "param 1" modulation : link from the knob.
* Select a dot, tweak the knob and click the "store" button.
* (v1.01) CTRL click a CC row to activate "snap" mode :
value snaps to the nearest dot (thanks caseyjames)
* SHIFT click a CC value to send current value to all dots.
* ALT click a CC value to randomize this CC for all dots.

3) PATCH DOTS MENU :
--------------------
* CTRL click in the menu creates a new patch.
* Drag and drop a patch from the menu to the pad.
* Right click selects/unselects patches.
* CTRL click a dot to delete it from the surface.
* CTRL click on color area to change the dot's color.
* ALT click on color area to randomize the current dot.
* ALT click on unselected dots to randomize them.
(CTRL+ALT click on a CC row to prevent from randomization)

4) TIME MARKERS :
-----------------
The target can follow vectors set by time markers.
* select "edition : time markers"
* CTRL click to add a marker at Reaper's cursor position.
* Shift click a marker to set a loop from the first marker.
* Shift right click a marker to set a ping-pong loop.
* To select time-based or beat-based markers, modify "time_mode=0" (line 135).
Save and recompile. Always save your work before you recompile !
* You can set the maximum number of markers : change "flag_max=100" (line 140).
* CTRL SHIFT click to create random markers in the view from the first marker.
To set the min and max steps between random markers, modify "time_randmin"
and "time_randmax" (line 136).
Please note that random markers will overwrite all markers !

5) AUTOMATION :
---------------
* You can record the target position with X-Y envelopes.
* Set "edition : read envelopes" to read or render your project.
Please note that you must not change the view to read the correct coordinates !

OTHER :
* "weight" slider : 1 = smooth interpolation, 5 = abrupt.


slider1:64<0,127,1>- param 1
slider2:64<0,127,1>- param 2
slider3:64<0,127,1>- param 3
slider4:64<0,127,1>- param 4
slider5:64<0,127,1>- param 5
slider6:64<0,127,1>- param 6
slider7:64<0,127,1>- param 7
slider8:64<0,127,1>- param 8
slider9:64<0,127,1>- param 9
slider10:64<0,127,1>- param 10
slider11:64<0,127,1>- param 11
slider12:64<0,127,1>- param 12
slider13:64<0,127,1>- param 13
slider14:64<0,127,1>- param 14
slider15:64<0,127,1>- param 15
slider16:64<0,127,1>- param 16
slider17:64<0,127,1>- param 17
slider18:64<0,127,1>- param 18
slider19:64<0,127,1>- param 19
slider20:64<0,127,1>- param 20
slider21:64<0,127,1>- param 21
slider22:64<0,127,1>- param 22
slider23:64<0,127,1>- param 23
slider24:64<0,127,1>- param 24
slider25:64<0,127,1>- param 25
slider26:64<0,127,1>- param 26
slider27:64<0,127,1>- param 27
slider28:64<0,127,1>- param 28
slider29:64<0,127,1>- param 29
slider30:64<0,127,1>- param 30
slider31:64<0,127,1>- param 31
slider32:64<0,127,1>- param 32
slider33:64<0,127,1>- param 33
slider34:64<0,127,1>- param 34
slider35:64<0,127,1>- param 35
slider36:64<0,127,1>- param 36
slider37:64<0,127,1>- param 37
slider38:64<0,127,1>- param 38
slider39:64<0,127,1>- param 39
slider40:64<0,127,1>- param 40
slider41:64<0,127,1>- param 41
slider42:64<0,127,1>- param 42
slider43:64<0,127,1>- param 43
slider44:64<0,127,1>- param 44
slider45:64<0,127,1>- param 45
slider46:64<0,127,1>- param 46
slider47:64<0,127,1>- param 47
slider48:64<0,127,1>- param 48
slider49:64<0,127,1>- param 49
slider50:64<0,127,1>- param 50
slider51:64<0,127,1>- param 51
slider52:64<0,127,1>- param 52
slider53:64<0,127,1>- param 53
slider54:64<0,127,1>- param 54
slider55:64<0,127,1>- param 55
slider56:64<0,127,1>- param 56
slider57:64<0,127,1>- param 57
slider58:64<0,127,1>- param 58
slider59:64<0,127,1>- param 59
slider60:64<0,127,1>- param 60
slider61:0<0,2,1{patch dots,time markers,read envelopes}>edition
slider62:2<1,5,0.01>weight
slider63:64<0,1600,0.01>- X
slider64:64<0,1000,0.01>- Y

in_pin:none
out_pin:none

@init
ext_noinit=1;

time_mode=0; // time marker position : 0=time based  1=beat based
time_randmin=0.25; // minimum time or beat value between random time markers
time_randmax=8; // maximum time or beat value between random time markers

// MAX VALUES //
flag_max=100;     // time markers
cc_max=60;       // parameters
cc_namesize=9;
dot_namesize=16;
zoom=1.015;

// ARRAY DEFINITION //
flag_x=0; flag_y=flag_max;
flag_time=flag_max*2; flag_min=flag_max*3; flag_sec=flag_max*4;
cc_inter=flag_max*5;
cc_snap=flag_max*5+cc_max;
cc_num=flag_max*5+cc_max*2;
cc_channel=flag_max*5+cc_max*3;
cc_name=flag_max*5+cc_max*4;
slider_prev=flag_max*5+cc_max*(4+cc_namesize); // slider1 - slider60
OS=flag_max*5+cc_max*(4+cc_namesize)+60; // memory offset
dot_bypass=OS;
dot_x=OS+1; dot_y=OS+2;
dot_r=OS+3; dot_g=OS+4; dot_b=OS+5;
dot_dist=OS+6;
dot_ratio=OS+7;
dot_name=OS+8;
cc_val=OS+8+dot_namesize;
M=8+dot_namesize+cc_max; // memory per dot //

dd=8; // default cursor size

@slider

j=0; loop(cc_N, slider(j+1)!=slider_prev[j] ? store=1; j+=1);

@block

//// AUTOMATION READ ////
slider61==2 ? (x0=slider63; y0=slider64) : 

//// RMB ////
mouse_cap==2 ? (
 mouse_y>16 && mouse_x>w2 ? (
  slider61==0 && dot_focus!=0 ? (
   rmb==0 ? (
    cc_name_edit=0;
    keys=(sign(dot_focus)==1);
    dot_focus=-1*dot_focus;
    dot_focus<0 ? (
     (dot_focus+scroll2+hmax-1)<0 ? scroll2=1-hmax-dot_focus :
     (scroll2+dot_focus+2)>0 ? scroll2=(dot_focus==-1)-dot_focus-2;
    );
    dd=8*dot_ratio[M*(abs(dot_focus)-1)];
    follow=0;
    x0=dot_x[M*(abs(dot_focus)-1)]; y0=dot_y[M*(abs(dot_focus)-1)];
    send=1;
   );
  ) : follow=1;
 ) :
 // DOT MENU SELECT //
 mouse_x>w1 && rmb==0 ? (
  dot_focus<0 ? dot_focus=0 :
  dot_focus>0 ? (
   cc_name_edit=0; follow=0;
   dd=8*dot_ratio[M*(dot_focus-1)]; dot_focus=-dot_focus;
   dot_bypass[M*(abs(dot_focus)-1)]<0 ? (
    x0=dot_x[M*(abs(dot_focus)-1)]; y0=dot_y[M*(abs(dot_focus)-1)];
   ) : (
    j=0; loop(cc_N,slider(j+1)=cc_inter[j]=cc_val[M*(abs(dot_focus)-1) + j]; sliderchange(2 ^ (j+1)); midisend(0,11*16+cc_channel[j]-1,cc_num[j]|(cc_inter[j]*256)); j+=1);
   );
  );
 ) : mouse_x<w1 && rmb==0 ? name=!name; // switch param view
 rmb=1;
 store=0;
) :

//// CTRL + LMB ////
mouse_cap==5 && lmb==0 ? (
 follow=0;
 lmb=1;
 // CHANGE COLOR //
 dot_focus<0 && mouse_y<16 && mouse_x<76 && (name || mouse_x>48) ? (
  dot_r[M*(abs(dot_focus)-1)]=rand(100)/100; dot_g[M*(abs(dot_focus)-1)]=0.2+rand(80)/100; dot_b[M*(abs(dot_focus)-1)]=1-dot_r[M*(abs(dot_focus)-1)];
 );
 mouse_x<w1 ? (
  // SWITCH MODE CC ROW //
  mouse_y<=16*(cc_N-floor(scroll1))+15 ? (
   kk=num_focus+channel_focus+val_focus; kk>0 ? cc_snap[kk-1]*=-1;
  ) :
  // ADD CC ROW //
  mouse_y>16*(cc_N-floor(scroll1))+15 && cc_N<cc_max ? (
   name=0;
   cc_num[cc_N]=1+max(cc_num[cc_N-1],0);
   cc_channel[cc_N]=max(cc_channel[cc_N-1],1);
   cc_name[cc_N*cc_namesize]=7; cc_name[cc_N*cc_namesize+1]=$'n'; cc_name[cc_N*cc_namesize+2]=$'e'; cc_name[cc_N*cc_namesize+3]=$'w';
   cc_snap[cc_N]=-1;
   cc_val[M*(abs(dot_focus)-1)+cc_N]=64;
   cc_N+=1;
   hmax<cc_N-floor(scroll1)+1 && cc_N>1 ? scroll1=floor(scroll1)+1;
  );
 ) :
 mouse_x<w2 ? (
  dot_focus>0 ? dot_bypass[M*(abs(dot_focus)-1)]*=-1 : 
  mouse_y>16*(dot_N-floor(scroll2))+15 ? (
   // ADD PATCH DOT IN MENU //
   dot_r[M*dot_N]=rand(100)/100; dot_g[M*dot_N]=0.2+rand(80)/100; dot_b[M*dot_N]=1-dot_r[M*dot_N];
   ratio=dot_ratio[M*dot_N]=1; dot_bypass[M*dot_N]=1;
   dot_x[M*dot_N]=w2+20; dot_y[M*dot_N]=36;
   j=0; loop(cc_N,cc_val[M*dot_N+j]=cc_inter[j]; j+=1);
   dot_N+=1; dot_focus=-dot_N; keys=1; cc_name_edit=0;
   store ? (store=0; j=0; loop(cc_N, cc_val[M*(abs(dot_focus)-1)+j]=slider(j+1); j+=1));
   hmax<dot_N-floor(scroll2)+1 && dot_N>1 ? scroll2=floor(scroll2)+1;
  );
 ) :
 mouse_y>16 && mouse_x>w2 ? (
  slider61==1 ? (
   flag_focus>0 ? (
    // DELETE TIME MARKER //
    flag_focus<=abs(loop_end) ? loop_end-=sign(loop_end); abs(loop_end)==1 ? loop_end=0;
    kk=flag_focus-1; loop(flag_N-flag_focus+1,
     flag_time[kk]=flag_time[kk+1];
     flag_min[kk]=flag_min[kk+1];
     flag_sec[kk]=flag_sec[kk+1];
     flag_x[kk]=flag_x[kk+1];
     flag_y[kk]=flag_y[kk+1];
     kk+=1;
    );
    flag_N-=1;
   ) : (
    // ADD TIME MARKER // 
    time_mode ? position=beat_position : position=play_position;
    flag_N==0 ? (
     flag_time[0]=position;
     flag_min[0]=floor(play_position/60); flag_sec[0]=play_position-60*flag_min[flag_N];
     flag_x[0]=mouse_x; flag_y[0]=mouse_y;     
     flag_N=1;
    ) : (
     k=-2; while(
      k+=1;
      position>flag_time[k+1] && k<flag_N-1;
     );
     k+=1;
     position==flag_time[k] ? (flag_x[k]=mouse_x; flag_y[k]=mouse_y) : 
     flag_N<flag_max ? (
      abs(loop_end)>k ? loop_end+=sign(loop_end);
      kk=flag_N; loop(flag_N-k,
       flag_time[kk]=flag_time[kk-1];
       flag_min[kk]=flag_min[kk-1];
       flag_sec[kk]=flag_sec[kk-1];
       flag_x[kk]=flag_x[kk-1];
       flag_y[kk]=flag_y[kk-1];
       kk-=1;
      );
      flag_time[k]=position;
      flag_min[k]=floor(play_position/60);
      flag_sec[k]=play_position-60*flag_min[k];
      flag_x[k]=mouse_x;
      flag_y[k]=mouse_y;     
      flag_N+=1;
     );
    );
   );
  ) : (
   dot_focus>0 ? (
    // BYPASS PATCH DOT //
    dot_bypass[M*(dot_focus-1)]*=-1;
   ) : (
    // ADD PATCH DOT //
    dot_r[M*dot_N]=rand(100)/100; dot_g[M*dot_N]=0.2+rand(80)/100; dot_b[M*dot_N]=1-dot_r[M*dot_N];
    ratio=dot_ratio[M*dot_N]=1;
    x0=dot_x[M*dot_N]=mouse_x; y0=dot_y[M*dot_N]=mouse_y;
    dot_bypass[M*dot_N]=-1;
    j=0; loop(cc_N,cc_val[M*dot_N+j]=cc_inter[j]; j+=1);
    dot_N<1 ? (dot_menu=1; dot_x[M*dot_N]<264 ? x0=dot_x[M*dot_N]=264);
    dot_N+=1; dot_focus=-dot_N; keys=1; cc_menu=1; dd=8; cc_name_edit=0;
    store ? (store=0; j=0; loop(cc_N, cc_val[M*(abs(dot_focus)-1)+j]=slider(j+1); j+=1));
    dot_N>2 && (dot_focus+scroll2+hmax-1)<0 ? scroll2=1-hmax-dot_focus;
   );
  );
 );
) :

//// LMB ////
mouse_cap==1 ? (
 val_focus>0 ? (
  lmb==0 ? (
   dot_focus>=0 && val_focus<=cc_N && name && mouse_y>16 ? (keys=1; cc_name_edit=!cc_name_edit);
   // KEYBOARD EDITION FOR CC NAMES //
   keys && dot_focus==0 && cc_name_edit && mouse_y<16 && mouse_x>76 ? (
    mouse_x<90 ? (keys=!keys; cc_name_edit=0) : (
     cc_name[cc_namesize*(val_focus-1)]==7 ? (jj=0; loop(4,cc_name[cc_namesize*(val_focus-1)+jj]=0; jj+=1); jj=0) : (
      jj=-1; while(
       jj+=1;
       cc_name[cc_namesize*(val_focus-1)+jj]!=0 && jj<cc_namesize;
      );
     );
     mouse_x<428 ? (
      jj<cc_namesize ? cc_name[cc_namesize*(val_focus-1)+jj]=97+floor((mouse_x-90)/13);
     ) : mouse_x>440 ? (
      jj<cc_namesize ? cc_name[cc_namesize*(val_focus-1)+jj]=21+floor((mouse_x-90)/13);
     ) : jj>0 ? cc_name[cc_namesize*(val_focus-1)+jj-1]=0;
    );
   );
   mouse_ystart=mouse_y; cc_edit=cc_val[M*(abs(dot_focus)-1)+val_focus-1];
  ) :
  // DRAG CC VALUE //
  cc_edit>-1 ? (
   slider(val_focus)=cc_inter[val_focus-1]=cc_val[M*(abs(dot_focus)-1)+val_focus-1]=min(127,max(0,cc_edit+floor((mouse_ystart-mouse_y)/3)));
   sliderchange(2 ^ val_focus);
   dot_focus<0 ? (midisend(0,11*16+cc_channel[val_focus-1]-1,cc_num[val_focus-1]|(cc_val[M*(abs(dot_focus)-1)+val_focus-1]*256)); store=0) : (
    midisend(0,11*16+cc_channel[val_focus-1]-1,cc_num[val_focus-1]|(cc_inter[val_focus-1]*256)); store=0;
   );
  );
 ) :
 // DRAG CC CHANNEL //
 channel_focus>0 ? (
  lmb==0 ? (mouse_ystart=mouse_y; cc_edit=cc_channel[channel_focus-1]) :
  cc_edit>-1 ? cc_channel[channel_focus-1]=min(16,max(1,cc_edit+floor((mouse_ystart-mouse_y)/12)));
 ) :
 // DRAG CC NUMBER //
 num_focus>0 ? (
  lmb==0 ? (mouse_ystart=mouse_y; cc_edit=cc_num[num_focus-1]) :
  cc_edit>-1 ? cc_num[num_focus-1]=min(127,max(0,cc_edit+floor((mouse_ystart-mouse_y)/10)));
 ) :
 mouse_y<16 ? (
  mouse_x>76 ? (
   // KEYBOARD EDITION FOR DOT NAMES //
   mouse_x<90 ? (
    lmb==0 ? keys=!keys;
   ) : (
    slider61==0 && lmb==0 && keys && dot_focus<0 ? (
     jj=-1; while (
      jj+=1;
      dot_name[M*(abs(dot_focus)-1)+jj]!=0 && jj<dot_namesize;
     );
     mouse_x<428 ? (
      jj<dot_namesize ? dot_name[M*(abs(dot_focus)-1)+jj]=97+floor((mouse_x-90)/13);
     ) : mouse_x>440 ? (
      jj<dot_namesize ? dot_name[M*(abs(dot_focus)-1)+jj]=21+floor((mouse_x-90)/13);
     ) : jj>0 ? dot_name[M*(abs(dot_focus)-1)+jj-1]=0;
    );
   );
  ) : 
  // STORE VST VALUES (LINK) //
  lmb==0 && dot_focus<0 && store ? (store=0; j=0; loop(cc_N, cc_inter[j]=cc_val[M*(abs(dot_focus)-1)+j]=slider(j+1); j+=1);
  );
 ) :
 // EXPAND COLLAPSE MENUS //
 mouse_y>gfx_h-15 && mouse_x<(29+62*cc_menu+62*dot_menu) ? (
  mouse_x<15 ? (lmb==0 ? (lmb=1; cc_menu=!cc_menu)) :
  mouse_x<15+60*cc_menu ? (1=1) :
  mouse_x<29+62*cc_menu ? (lmb==0 ? (lmb=1; dot_menu=!dot_menu));
 ) : (
  // CC SCROLL BUTTONS //
  mouse_x<w1 ? (
   scroll1>=1 && mouse_y>15 && mouse_y<32 ? (lmb==0 ? (scroll_speed=0; scroll1=floor(scroll1)-0.001) : (scroll1-=scroll_speed; scroll_speed=min(scroll_speed+0.002,0.5))) :
   hmax<cc_N-floor(scroll1)+1 && mouse_y>gfx_h-32 && mouse_y<gfx_h-15 ? (lmb==0 ? (scroll_speed=0; scroll1=floor(scroll1)+1) : (scroll1+=scroll_speed; scroll_speed=min(scroll_speed+0.002,0.5)));
  ) :    
  // DOT SCROLL BUTTONS //
  mouse_x<w2 ? (
   scroll2>=1 && mouse_y>15 && mouse_y<32 ? (lmb==0 ? (scroll_speed=0; scroll2=floor(scroll2)-0.001) : (scroll2-=scroll_speed; scroll_speed=min(scroll_speed+0.002,0.5))) :
   hmax<dot_N-floor(scroll2)+1 && mouse_y>gfx_h-32 && mouse_y<gfx_h-15 ? (lmb==0 ? (scroll_speed=0; scroll2=floor(scroll2)+1) : (scroll2+=scroll_speed; scroll_speed=min(scroll_speed+0.002,0.5)));
  );
  mouse_x>w2 ? (
   dx=mouse_x-mouse_xprev; dy=mouse_y-mouse_yprev;
   cc_name_edit=0;
   slider61==1 ? (
    // DRAG TIME MARKERS //
    flag_focus>0 && !inter_focus ? (
     flag_x[flag_focus-1]+=dx; flag_y[flag_focus-1]+=dy;
    ) : (
     lmb==0 ? inter_focus=(abs(x0-mouse_x)<30 && abs(y0-mouse_y)<50);
     // DRAG CURSOR //
     inter_focus ? (x0+=dx; y0+=dy) : (
      // DRAG ALL //
      x0+=dx; y0+=dy;
      i=0; loop(dot_N,dot_x[M*i]+=dx; dot_y[M*i]+=dy; i+=1);
      k=0; loop(flag_N,flag_x[k]+=dx; flag_y[k]+=dy; k+=1);
     );
     flag_focus=0;
    );
   ) : (
    // DRAG PATCH DOTS //
    dot_focus>0 && !inter_focus ? (
     dot_x[M*(dot_focus-1)]+=dx; dot_y[M*(dot_focus-1)]+=dy; send=1;
    ) : (
     lmb==0 ? (dot_focus=0; inter_focus=(abs(x0-mouse_x)<16+dd && abs(y0-mouse_y)<16+dd));
     // DRAG CURSOR //
     inter_focus ? (
      x0+=dx; y0+=dy;
      dot_N>0 && dot_focus>-1 ? (
       i=0; while(
        dot_bypass[M*i]<0 ? dot_focus=(i+1)*(abs(dot_x[M*i]-x0)<8+8*dot_ratio[M*i] && abs(dot_y[M*i]-y0)<8+8*dot_ratio[M*i]);
        i+=1;
        (dot_focus==0 && i<dot_N);
       );
      );
     ) : (
      // DRAG ALL //
      x0+=dx; y0+=dy;
      i=0; loop(dot_N,dot_x[M*i]+=dx; dot_y[M*i]+=dy; i+=1);
      k=0; loop(flag_N,flag_x[k]+=dx; flag_y[k]+=dy; k+=1);
     );
     inter_focus==0 ? dot_focus=0;
    );
   );
  ) : slider61==0 && mouse_x>w1 && dot_focus>0 && dot_bypass[M*(dot_focus-1)]>0 ? drop=1; // DRAG AND DROP //
 );
lmb=1;
follow=0;
) :

//// NO MOUSE BUTTON ////
mouse_cap==0 ? (
 // DROP DOT //
 drop ? (
  mouse_x>w2 ? (dot_ratio[M*(dot_focus-1)]=1; dot_bypass[M*(dot_focus-1)]=-1; dot_x[M*(dot_focus-1)]=mouse_x; dot_y[M*(dot_focus-1)]=mouse_y); 
  drop=0;
 );
 // VARIABLES INIT //
 cc_edit=-1; ratio_edit=-1; inter_focus=0; flag_focus=0;
 cc_name_edit==0 ? (num_focus=0; channel_focus=0; val_focus=0);
 dot_focus>0 ? dot_focus=0;
 lmb=O; rmb=0;
 mouse_y>16 && mouse_x>w2 ? (
  // TIME MARKER FOCUS //
  slider61==1 ? (
   flag_N>0 ? (
    k=0; while(
     flag_focus=(k+1)*(abs(flag_x[k]-mouse_x)<16 && abs(flag_y[k]-mouse_y)<16);
     k+=1;
     (flag_focus==0 && k<flag_N);
    );
   );
  ) : 
  // PATCH DOT FOCUS //
  dot_N>0 && dot_focus>-1 ? (
   i=0; while(
    dot_bypass[M*i]<0 ? dot_focus=(i+1)*(abs(dot_x[M*i]-mouse_x)<8+8*dot_ratio[M*i] && abs(dot_y[M*i]-mouse_y)<8+8*dot_ratio[M*i]);
    i+=1;
    (dot_focus==0 && i<dot_N);
   );
  );
 ) : 
 // CC NUM CHANNEL VAL FOCUS //
 mouse_x<w1 ? (
  mouse_y>16*(1+(scroll1>=1)) && mouse_y<gfx_h-32? (
   kk=floor((mouse_y-1)/16)+floor(scroll1);
   cc_name_edit==0 ? (name || mouse_x>48 ? val_focus=kk : mouse_x>28 ? channel_focus=kk : num_focus=kk);
  );
 ) : 
 // DOT MENU FOCUS //
 mouse_x<w2 ? (
  mouse_y>16*(1+(scroll2>=1)) && mouse_y<gfx_h-32 && dot_focus>=0 ? (
   kk=floor((mouse_y-1)/16)+floor(scroll2); kk<=dot_N ? dot_focus=kk;
  );
 );
) :

//// ALT + LMB ////
mouse_cap==17 ? (
 // ZOOM IN //
 mouse_x>w2 && mouse_y>16 ? (
  x0=mouse_x; y0=mouse_y;
  i=0; loop(dot_N,dot_x[M*i]*=zoom; dot_x[M*i]-=(zoom-1)*mouse_x; dot_y[M*i]*=zoom; dot_y[M*i]-=(zoom-1)*mouse_y; i+=1);
  k=0; loop(flag_N,flag_x[k]*=zoom; flag_x[k]-=(zoom-1)*mouse_x; flag_y[k]*=zoom; flag_y[k]-=(zoom-1)*mouse_y; k+=1);
 ) : 
 // RANDOMIZE DOT (ON COLOR OR ON SNAPSHOT) //
 (dot_focus<0 && mouse_y<16 && mouse_x<76 && (name || mouse_x>48)) || dot_focus>0 && mouse_x<w2 ? (
  lmb==0 ? (
   j=0; loop(cc_N,
    abs(cc_snap[j])==1 ? (
     slider(j+1)=cc_inter[j]=cc_val[M*(abs(dot_focus)-1)+j]=floor(rand(127.99)); sliderchange(2 ^ abs(dot_focus));
     midisend(0,11*16+cc_channel[j]-1,cc_num[j]|(cc_inter[j]*256));
    );
    j+=1;
   );
  );
  store=0;
 ) :
 // RANDOMIZE ALL DOTS (ON CC VALUE) //
 lmb==0 && dot_focus==0 && val_focus>0 && mouse_y>16 ? (
  i=0; loop(dot_N,cc_val[M*i+val_focus-1]=floor(rand(127.99)); i+=1);
  send=1;
 );
 lmb=1;
) :

//// ALT + RMB ////
mouse_cap==18 ? (
 // ZOOM OUT //
 mouse_x>w2 && mouse_y>16 ? (
  x0=mouse_x; y0=mouse_y;
  i=0; loop(dot_N,dot_x[M*i]/=zoom; dot_x[M*i]+=(1-1/zoom)*mouse_x; dot_y[M*i]/=zoom; dot_y[M*i]+=(1-1/zoom)*mouse_y; i+=1);
  k=0; loop(flag_N,flag_x[k]/=zoom; flag_x[k]+=(1-1/zoom)*mouse_x; flag_y[k]/=zoom; flag_y[k]+=(1-1/zoom)*mouse_y; k+=1);
 );
 rmb=1;
) : 

//// SHIFT + LMB ////
mouse_cap==9 ? (
 slider61==1 ? (
  // SET LOOP MARKER //
  lmb==0 && flag_focus>0 ? (loop_end==flag_focus || flag_focus==1 ? loop_end=0 : loop_end=flag_focus);
 ) : (
  // SEND CC VAL TO ALL //
  dot_focus<0 && val_focus>0 ? (
   i=0; loop(dot_N, cc_val[M*i+val_focus-1]=cc_val[M*(abs(dot_focus)-1)+val_focus-1]; i+=1);
  );
  // DRAG DOT RATIO //
  lmb==0 && dot_focus>0 ? (
   mouse_ystart=mouse_y; ratio_edit=abs(dot_focus); ratio=dot_ratio[M*(dot_focus-1)];
  ) :
  ratio_edit>-1 ? (
   dot_ratio[M*(dot_focus-1)]=min(4,max(1,ratio+(mouse_ystart-mouse_y)/16)); send=1;
  );
 );
 lmb=1;
) :

//// SHIFT + RMB ////
mouse_cap==10 ? (
 slider61==1 ? (
  // SET PING PONG LOOP MARKER //
  rmb==0 && flag_focus>0 ? (loop_end==-flag_focus || flag_focus==1 ? loop_end=0 : loop_end=-flag_focus);
 );
 rmb=1;
) : 

//// CTRL + SHIFT + LMB ////
mouse_cap==13 ? (
 slider61==1 ? (
  // SET RANDOM LOOP MARKERS //
  time_mode ? position=beat_position : position=play_position;
  lmb==0 && flag_N>0 && position>flag_time[0] ? (
   k=0; time_temp=flag_time[0]; loop_end=0;
   while(
    time_temp+=time_randmin+rand(min(position-time_temp,time_randmax-time_randmin));
    k+=1;
    time_temp<position-time_randmin && k<flag_max ? (
     flag_time[k]=time_temp;
     time_mode ? (flag_min[k]=floor(time_temp/tempo); flag_sec[k]=time_temp*60/tempo-60*flag_min[k]) : (flag_min[k]=floor(time_temp/60); flag_sec[k]=time_temp-60*flag_min[k]);
     flag_x[k]=w2+rand(gfx_w-w2); flag_y[k]=16+rand(gfx_h-16);
    ) : (
     flag_time[k]=position;
     flag_min[k]=floor(play_position/60); flag_sec[k]=play_position-60*flag_min[k];
     flag_x[k]=mouse_x; flag_y[k]=mouse_y;
    );
    flag_N=k+1;
    time_temp<position-time_randmin && k<flag_max;
   );
  );
 );
 lmb=1;
) : 

//// CTRL + ALT + LMB ////
mouse_cap==21 ? (
 // BYPASS CC ROW //
 lmb==0 && mouse_x<w1 && mouse_y<=16*cc_N+15 ? (
  kk=num_focus+channel_focus+val_focus; kk>0 ? (abs(cc_snap[kk-1])==2 ? cc_snap[kk-1]*=0.5 : cc_snap[kk-1]*=2);
 );
 lmb=1;
);

// RIGHT MOUSE FOLLOW //
follow ? (x0=mouse_x; y0=mouse_y) :

// FOLLOWING TIME MARKERS //
slider61==1 ? (
 time_mode ? position=beat_position : position=play_position;
 loop_end>1 ? (
  position>flag_time[loop_end-1] ? (kk=flag_time[loop_end-1]-flag_time[0]; position-=flag_time[0]; position=flag_time[0]+position-kk*floor(position/kk));
 ) :
 loop_end<-1 ? (
  position>flag_time[abs(loop_end)-1] ? (
   kk=flag_time[abs(loop_end)-1]-flag_time[0]; position-=flag_time[0];
   position=position-2*kk*floor(position/(kk*2)); 
   position>kk ? position=2*kk-position;
   position+=flag_time[0];
  );
 );
 flag_N>1 && position>flag_time[0] && position!=play_prev ? (
  k=-2; while(
   k+=1;
   position>flag_time[k+1] && k<flag_N-1;
  );
  k<flag_N-1 ? (
   kk=(position-flag_time[k])/(flag_time[k+1]-flag_time[k]);
   x0=(1-kk)*flag_x[k]+kk*flag_x[k+1];
   y0=(1-kk)*flag_y[k]+kk*flag_y[k+1];
  );   
 );
);

slider61<2 ? (
// CURSOR LIMITS //
 play_state==0 && dot_focus>-1 ? (
  x0<w2+20 ? x0=w2+20; x0>gfx_w-20 ? x0=gfx_w-20;
  y0<34 ? y0=34; y0>gfx_h-20 ? y0=gfx_h-20;
 );
 // AUTOMATION WRITE //
 slider63=x0 ; slider_automate(slider63);
 slider64=y0 ; slider_automate(slider64);
);

// INTERPOLATION //
send || x0!=x0_prev || y0!=y0_prev ? (
 dist_invtotal=0;
 i=0;
 nearest=-1; dist_min=10000; // variables to find the nearest dot  //////////////
 loop(dot_N,
  dist_invtotal>-1 ? (
   dot_bypass[M*i]<0 ? (
    dot_dist[M*i]=((dot_x[M*i]-x0)^2 + (dot_y[M*i]-y0)^2)^(slider62/2)/dot_ratio[M*i];
    dot_dist[M*i]==0 ? dist_invtotal=-1 : dist_invtotal+=1/dot_dist[M*i];
    dot_dist[M*i]<dist_min ? (dist_min=dot_dist[M*i]; nearest=i);
   );
   i+=1;
  );
 );
 j=0; i-=1;
 dist_invtotal==-1 ? (
  loop(cc_N,slider(j+1)=cc_inter[j]=cc_val[M*i + j]; sliderchange(2 ^ (j+1)); midisend(0,11*16+cc_channel[j]-1,cc_num[j]|(cc_inter[j]*256)); j+=1);
  store=0;
 ) : (
  loop(cc_N,
   cc_snap[j]>0 ? cc_inter[j]=cc_val[M*nearest + j] : (
    cc_inter[j]=0;
    i=0;
    loop(dot_N,
     dot_bypass[M*i]<0 ? (
      cc_inter[j]+=cc_val[M*i + j]/(dot_dist[M*i]*dist_invtotal);
     );
     i+=1;
    );
   );
   // MIDI SEND //
   slider(j+1)=cc_inter[j]=floor(cc_inter[j]+0.5); sliderchange(2 ^ (j+1));
   midisend(0,11*16+cc_channel[j]-1,cc_num[j]|(cc_inter[j]*256));
   j+=1;
  );
  store=0;
 );
);

// INIT VALUES //
w1=76*cc_menu;
w2=w1+menu_w*dot_menu;
mouse_xprev=mouse_x ; mouse_yprev=mouse_y;
x0_prev=x0; y0_prev=y0;
play_prev=position;
send=0;
j=0; loop(cc_N, slider_prev[j]=slider(j+1); j+=1);

@serialize

file_var(0,x0); file_var(0,y0);
file_var(0,dot_N);
file_var(0,cc_N);
file_var(0,flag_N);
file_var(0,loop_end);
file_var(0,scroll1); file_var(0,scroll2);
file_var(0,cc_menu); file_var(0,dot_menu);
file_mem(0,0,OS+dot_N*M);

@gfx 256 320

gfx_a=1;

// FIELD BACKGROUND //
gfx_r=gfx_b=gfx_g=0.86;
gfx_x=1;gfx_y=1; gfx_rectto(gfx_w-1,gfx_h-1);

// DOTS //
dot_N>0 ? (
 nearest>-1 && dot_bypass[M*nearest]<0 ? (
  d=8*dot_ratio[M*nearest]+4;
  gfx_x=dot_x[M*nearest]-d; gfx_y=dot_y[M*nearest]-d;
  gfx_r=gfx_g=gfx_b=0.7;
  gfx_rectto(dot_x[M*nearest]+d,dot_y[M*nearest]+d);
 );
 dot_focus!=0 && dot_bypass[M*(abs(dot_focus)-1)]<0 ? (
  d=8*dot_ratio[M*(abs(dot_focus)-1)]+4;
  gfx_x=dot_x[M*(abs(dot_focus)-1)]-d; gfx_y=dot_y[M*(abs(dot_focus)-1)]-d;
  gfx_r=gfx_g=gfx_b=1-sign(dot_focus);
  gfx_rectto(dot_x[M*(abs(dot_focus)-1)]+d,dot_y[M*(abs(dot_focus)-1)]+d);
 );
 i2=0;
 loop(dot_N,
  dot_bypass[M*i2]<0 ? (
   gfx_r=gfx_b=gfx_g=0.6; gfx_a=0.5;
   gfx_x=dot_x[M*i2]; gfx_y=15; gfx_lineto(gfx_x,gfx_h-2,0);
   gfx_x=1; gfx_y=dot_y[M*i2]; gfx_lineto(gfx_w-2,gfx_y,0);
   gfx_a=1;
   d=8*dot_ratio[M*i2];
   gfx_x=dot_x[M*i2]-d-1; gfx_y=dot_y[M*i2]-d-1;
   gfx_r=gfx_g=gfx_b=0;
   gfx_rectto(dot_x[M*i2]+d+1,dot_y[M*i2]+d+1);
   gfx_x=dot_x[M*i2]-d; gfx_y=dot_y[M*i2]-d;
   gfx_r=dot_r[M*i2]; gfx_g=dot_g[M*i2]; gfx_b=dot_b[M*i2];
   gfx_rectto(dot_x[M*i2]+d,dot_y[M*i2]+d);
   gfx_y=dot_y[M*i2]-9; gfx_x+=5;
   gfx_r=gfx_g=gfx_b=0;
   gfx_drawnumber(i2+1,0); gfx_x+=3;
   jj2=0; while (
    gfx_drawchar(dot_name[M*i2+jj2]);
    jj2+=1;
    dot_name[M*i2+jj2]!=0 && jj2<dot_namesize;
   );
  );
  i2+=1;
 );
);

// TIME MARKERS (FLAG) //
flag_N>0 ? (
 flag_focus>0 ? (
  gfx_r=gfx_g=gfx_b=1;
  gfx_x=flag_x[flag_focus-1]-8; gfx_y=flag_y[flag_focus-1]-8;
  gfx_rectto(flag_x[flag_focus-1]+8,flag_y[flag_focus-1]+8);  
 );
 k2=0;
 gfx_x=flag_x[k2]; gfx_y=flag_y[k2];
 loop(flag_N,
  gfx_r=gfx_b=gfx_g=0;
  k2<abs(loop_end) ? (gfx_r=0.5*(1+sign(loop_end)); gfx_b=1);
  gfx_lineto(flag_x[k2],flag_y[k2],1);
  gfx_x=flag_x[k2]-3; gfx_y=flag_y[k2]-3;
  gfx_rectto(flag_x[k2]+3,flag_y[k2]+3);
  gfx_x+=10;
  gfx_r=gfx_g=gfx_b=0;
  gfx_drawnumber(flag_min[k2],0);
  gfx_drawchar($'.');
  gfx_drawnumber(flag_sec[k2],3);
  gfx_x=flag_x[k2]; gfx_y=flag_y[k2];
  k2+=1;
 );
);

// MAIN CURSOR //
slider61<1 ? (
 follow || inter_focus ? (dot_focus>0 ? dd=8*dot_ratio[M*(dot_focus-1)] : dd=8);
) : dd=8;
gfx_r=gfx_g=gfx_b=0.5;
gfx_x=x0-dd-4; gfx_y=y0-4;
gfx_rectto(gfx_x-4,y0-dd-4); gfx_rectto(x0-4,gfx_y-4);
gfx_x+=8; gfx_rectto(x0+dd+8,gfx_y+4); gfx_rectto(gfx_x-4,y0-4);
gfx_y+=8; gfx_rectto(gfx_x+4,y0+dd+4); gfx_rectto(x0+4,gfx_y+4);
gfx_x-=8; gfx_rectto(x0-dd-8,gfx_y-4); gfx_rectto(gfx_x+4,y0+4);

// KEYBOARD //
gfx_r=gfx_b=gfx_g=0; gfx_x=gfx_w; gfx_y=0; gfx_rectto(0,15); 
dot_focus<0 || cc_name_edit ? (
 keys ? (
  gfx_r=gfx_b=1;gfx_g=0;
  mouse_x<1064 && mouse_x>75 && mouse_y<16 ? (
   gfx_y=1; gfx_x=76+floor((mouse_x-76)/13)*13; gfx_rectto(gfx_x+13,14);
  );
  gfx_y=4; gfx_r=gfx_b=gfx_g=0.86;
  jj2=0; loop(26,gfx_x=92+jj2*13; gfx_drawchar(97+jj2); jj2+=1);
  jj2=0; loop(min(48,floor((gfx_w-450)/13)),gfx_x=92+(jj2+27)*13; gfx_drawchar(48+jj2); jj2+=1);
  gfx_x=430; gfx_drawchar(27);
 );
 gfx_y=4; gfx_x=79; gfx_r=gfx_b=gfx_g=0.86; gfx_drawchar(16+keys);
);

// CC MENU //
cc_menu ? (
 hmax=floor(gfx_h/16)-2;
 gfx_r=gfx_b=gfx_g=0; gfx_x=0; gfx_y=15; gfx_rectto(76,gfx_h);
 name==0 ? (
  gfx_r=gfx_b=gfx_g=0.5; gfx_x=28; gfx_y=15; gfx_rectto(47,gfx_h-15); // CHANNEL BACKGROUND //
  gfx_r=gfx_b=gfx_g=0.86; gfx_x=48; gfx_y=15; gfx_rectto(75,gfx_h-15); // CC VALUES BACKGROUND //
 );
 // PINK CURSOR //
 dot_focus<0 || cc_name_edit ? (
  gfx_r=gfx_b=1;gfx_g=0;
  name==0 ? (
   gfx_y=15+16*(num_focus+channel_focus+val_focus-1-floor(scroll1));
   num_focus!=0 ? (gfx_x=1; gfx_rectto(27,gfx_y+15)) :
   channel_focus!=0 ? (gfx_x=28; gfx_rectto(47,gfx_y+15)) :
   val_focus!=0 ? (gfx_x=48; gfx_rectto(75,gfx_y+15));
  );
 );
 // CC VALUES - NAMES //
 gfx_r=gfx_b=gfx_g=0.86;
 name ? (
  j2=floor(scroll1);
  loop(min(hmax,cc_N-floor(scroll1)),
   gfx_x=3; gfx_y=16+(j2-floor(scroll1))*16; gfx_r=gfx_b=gfx_g=0.7; val_focus==(j2+1) ? gfx_g=0;
   kk2=0; loop(9,gfx_drawchar(cc_name[j2*cc_namesize+kk2]); kk2+=1);
   cc_snap[j2]>0 ? gfx_a=0.2;
   gfx_r=gfx_b=gfx_g=0.4; gfx_x=6; gfx_y+=9; gfx_rectto(70,gfx_y+4);
   dot_focus<0 ? kk=cc_val[M*(abs(dot_focus)-1)+j2] : kk=cc_inter[j2];
   gfx_r=gfx_b=gfx_g=1; val_focus==j2+1 && dot_focus<0 ? gfx_g=0 : abs(cc_snap[j2])==2 ? gfx_b=0 ; gfx_x=6; gfx_rectto(7+kk/2,gfx_y-4);
   gfx_a=1;
   j2+=1;
  );
 ) : (
  gfx_x=3; gfx_y=4; loop(2,gfx_drawchar($'C')); gfx_x=30; gfx_drawchar($'C'); gfx_drawchar($'H');
  j2=floor(scroll1);
  loop(min(hmax,cc_N-floor(scroll1)),
   abs(cc_snap[j2])==2 ? (
   gfx_x=48; gfx_y=15+(j2-floor(scroll1))*16; gfx_r=gfx_g=1; gfx_b=0; gfx_rectto(75,gfx_y+15);
   );
   gfx_y=19+(j2-floor(scroll1))*16; gfx_r=gfx_b=gfx_g=0.86;
   gfx_x=3; gfx_drawnumber(cc_num[j2],0);
   gfx_x=30; gfx_drawnumber(cc_channel[j2],0);
   gfx_x=50; gfx_r=gfx_b=gfx_g=0;
   cc_snap[j2]>0 ? gfx_a=0.2;
   dot_focus<0 ? gfx_drawnumber(cc_val[M*(abs(dot_focus)-1) + j2],0) : gfx_drawnumber(cc_inter[j2],0);
   gfx_a=1;
   j2+=1;
  );
 );
// SCROLL BUTTONS //
 scroll1>=1 ? ( // UP //
  gfx_r=gfx_g=gfx_b=0; gfx_x=0; gfx_y=14; gfx_rectto(w1,31); 
  gfx_r=gfx_g=gfx_b=0.8; gfx_x=1; gfx_y=15; gfx_rectto(w1-1,30);
  gfx_r=gfx_g=gfx_b=0; gfx_x=35; gfx_y=19; gfx_drawchar(30);
 ); 
 hmax<cc_N-floor(scroll1)+1 ? ( // DOWN //
  gfx_r=gfx_g=gfx_b=0; gfx_x=0; gfx_y=gfx_h-14; gfx_rectto(w1,gfx_h-32); 
  gfx_r=gfx_g=gfx_b=0.8; gfx_x=1; gfx_y=gfx_h-15; gfx_rectto(w1-1,gfx_h-31);
  gfx_r=gfx_g=gfx_b=0; gfx_x=35; gfx_y=gfx_h-27; gfx_drawchar(31);
 );
);

// DOT MENU //
dot_menu ? (
 hmax=floor(gfx_h/16)-2;
 gfx_r=gfx_b=gfx_g=0; gfx_x=76*cc_menu; gfx_y=15; gfx_rectto(gfx_x+menu_w,gfx_h);
 dot_N>0 ? (
  dot_focus<0 && abs(dot_focus)>floor(scroll2) && abs(dot_focus)<floor(scroll2)+hmax+1 ? (
   gfx_r=gfx_b=1;gfx_g=0;
   gfx_x=1+76*cc_menu; gfx_y=15+(abs(dot_focus)-1-floor(scroll2))*16; gfx_rectto(gfx_x+menu_w-2,gfx_y+17);
   gfx_r=gfx_b=gfx_g=0;
   gfx_x-=1; gfx_y-=1; gfx_rectto(gfx_x-menu_w+4,gfx_y-15); 
  );
  menu_w=32;
  i2=floor(scroll2);
  loop(min(hmax,dot_N-floor(scroll2)),
   gfx_x=12+76*cc_menu+9*dot_bypass[M*i2]; gfx_y=17+(i2-floor(scroll2))*16;
   gfx_r=dot_r[M*i2]; gfx_g=dot_g[M*i2]; gfx_b=dot_b[M*i2];
   gfx_rectto(gfx_x+13,gfx_y+13);
   gfx_y-=8; gfx_x+=4;
   gfx_r=gfx_g=gfx_b=0.7+0.3*(abs(dot_focus)==i2+1);
   gfx_drawnumber(i2+1,0); gfx_x+=3;
   jj2=0; while(
    gfx_drawchar(dot_name[M*i2+jj2]);
    jj2+=1;
    dot_name[M*i2+jj2]!=0 && jj2<dot_namesize;
   );
   menu_w<gfx_x-76*cc_menu ? menu_w=gfx_x-76*cc_menu+4;
   i2+=1;
  );
 );
// SCROLL BUTTONS //
 scroll2>=1 ? ( // UP //
  gfx_r=gfx_g=gfx_b=0.8; gfx_x=1+75*cc_menu; gfx_y=15; gfx_rectto(gfx_x+menu_w-2+cc_menu,30); 
  gfx_r=gfx_g=gfx_b=0; gfx_x-=menu_w/2+3; gfx_y=19; gfx_drawchar(30); 
 ); 
 hmax<dot_N-floor(scroll2)+1 ? ( // DOWN //
  gfx_r=gfx_g=gfx_b=0; gfx_x=75*cc_menu; gfx_y=gfx_h-15; gfx_rectto(gfx_x+menu_w-1+cc_menu,gfx_h-32);
  gfx_r=gfx_g=gfx_b=0.8; gfx_x=1+75*cc_menu; gfx_y=gfx_h-15; gfx_rectto(gfx_x+menu_w-2+cc_menu,gfx_h-31);
  gfx_r=gfx_g=gfx_b=0; gfx_x-=menu_w/2+3; gfx_y=gfx_h-27; gfx_drawchar(31);
 );
);

dot_focus<0 ? (
 // STORE BUTTON //
 store ? (
  gfx_r=gfx_b=1;gfx_g=0;
  gfx_x=gfx_y=1; gfx_rectto(75,14);
  gfx_r=gfx_b=gfx_g=0;
  gfx_x=gfx_y=2; gfx_rectto(74,13);
  gfx_r=gfx_b=1;gfx_g=0;
  gfx_x=gfx_y=4; gfx_drawchar($'s'); gfx_drawchar($'t'); gfx_drawchar($'o'); gfx_drawchar($'r'); gfx_drawchar($'e');
 ) : (
 // COLOR BUTTON//
  gfx_r=dot_r[M*(abs(dot_focus)-1)]; gfx_g=dot_g[M*(abs(dot_focus)-1)]; gfx_b=dot_b[M*(abs(dot_focus)-1)];
  gfx_x=1+(1-name)*47; gfx_y=1; gfx_rectto(75,14);
 );
) :
dot_focus>0 ? (
// mouse_x<w2 && mouse_cap==1 ? (
 drop ? (
  gfx_r=gfx_b=gfx_g=0; gfx_x=mouse_x-9; gfx_y=mouse_y-9; gfx_rectto(gfx_x+18,gfx_y+18);
  gfx_x=mouse_x-8; gfx_y=mouse_y-8;
  gfx_r=dot_r[M*(dot_focus-1)]; gfx_g=dot_g[M*(dot_focus-1)]; gfx_b=dot_b[M*(dot_focus-1)];
  gfx_rectto(gfx_x+16,gfx_y+16);
 );
);

// EXPAND COLLAPSE //
gfx_r=gfx_b=gfx_g=0;
gfx_x=29+61*cc_menu; gfx_y=gfx_h-15; gfx_rectto(1,gfx_h-1);
gfx_r=gfx_b=gfx_g=0.86;
gfx_rectto(14,gfx_h-14); gfx_x+=1+61*cc_menu; gfx_rectto(gfx_x+13,gfx_h-1);
gfx_r=gfx_b=gfx_g=0;
gfx_x=4; gfx_y-=10; gfx_drawchar(26+cc_menu); gfx_x+=6+61*cc_menu; gfx_drawchar(26+dot_menu);