Feedback

Please leave feedback and comments. I am always interested to hear how people get on using these LScripts!

Monday 16 April 2012

LScript - Generic_LightColor


LScript (Layout) applies colour to selected lights between frame 0 and 1. It enables changing light colour during spinning light tricks and in old school techniques like faking spectrums. The values are repeated correctly each frame by timing them to the motion blur length.

Compatible with Newtek LightWave 9.6 and above.

// LScript Generic - www.StephenCulley.co.uk
//
// web   address: http://www.stephenculley.co.uk
// email address: email@stephenculley.co.uk

/*  
    LScript Generic - LightColor

    Generic_LightColor.ls

*/

    // Title
    sTitle = "*LightColor";

    // Version
    sVersion = "v1.0";

    // Variable
    aColor;
    aIntensity;

    // Requester
    bRequesterUpdate = false; // Update

    ctrl_c0,ctrl_c1,ctrl_c2,ctrl_c3,ctrl_c4,ctrl_c5,ctrl_c6,ctrl_c7,ctrl_c8,ctrl_c9,ctrl_c10,ctrl_c11,ctrl_c12,ctrl_c13,ctrl_c14;

generic
{

    // Item Select
    Items = Scene().getSelect();  

    bLight = false;

    for(i = 1; i <= Items.size(); i++)
      {
      if(Items[i].genus == LIGHT) bLight = true;
      }        

    if(!bLight)
      {
      info("Select a light to apply color to.");
      return;
      }        

    // Recall
    aColor[1] = recall("vColor1",<255.0,255.0,255.0>);
    aIntensity[1] = recall("nIntensity1",1.0);
    aColor[2] = recall("vColor2",<0.0,0.0,0.0>);
    aIntensity[2] = recall("nIntensity2",0.0);
    aColor[3] = recall("vColor3",<0.0,0.0,0.0>);
    aIntensity[3] = recall("nIntensity3",0.0);
    aColor[4] = recall("vColor4",<0.0,0.0,0.0>);
    aIntensity[4] = recall("nIntensity4",0.0);
    aColor[5] = recall("vColor5",<0.0,0.0,0.0>);
    aIntensity[5] = recall("nIntensity5",0.0);
    aColor[6] = recall("vColor6",<0.0,0.0,0.0>);
    aIntensity[6] = recall("nIntensity6",0.0);
    aColor[7] = recall("vColor7",<0.0,0.0,0.0>);
    aIntensity[7] = recall("nIntensity7",1.0);

    reqbegin(sTitle + " " + sVersion);

    // Reset
    ctrl_res0 = ctlbutton("Reset",50,"button_reset"); // Button Reset
    ctlsep();

    // Control
    ctrl_c0 = ctlpopup("Preset",1, @"Gradient",
                                    "RGB",
                                    "Spectrum"@);

    ctlsep();
    ctrl_c1 = ctlcolor("1",aColor[1]);
    ctrl_c2 = ctlpercent("Intensity",aIntensity[1]);
    ctrl_c3 = ctlcolor("2",aColor[2]);
    ctrl_c4 = ctlpercent("Intensity",aIntensity[2]);
    ctrl_c5 = ctlcolor("3",aColor[3]);
    ctrl_c6 = ctlpercent("Intensity",aIntensity[3]);
    ctrl_c7 = ctlcolor("4",aColor[4]);
    ctrl_c8 = ctlpercent("Intensity",aIntensity[4]);
    ctrl_c9 = ctlcolor("5",aColor[5]);
    ctrl_c10 = ctlpercent("Intensity",aIntensity[5]);
    ctrl_c11 = ctlcolor("6",aColor[6]);
    ctrl_c12 = ctlpercent("Intensity",aIntensity[6]);
    ctrl_c13 = ctlcolor("7",aColor[7]);
    ctrl_c14 = ctlpercent("Intensity",aIntensity[7]);

    interpolate();

    // Developer
    ctlsep();
    ctrl_dev0 = ctltext("","developer: Stephen Culley","http://www.stephenculley.co.uk");

    ctlrefresh(ctrl_c0,"refresh_preset"); // Preset
 
    ctlrefresh(ctrl_c1,"refresh");
    ctlrefresh(ctrl_c2,"refresh");
    ctlrefresh(ctrl_c3,"refresh");
    ctlrefresh(ctrl_c4,"refresh");
    ctlrefresh(ctrl_c5,"refresh");
    ctlrefresh(ctrl_c6,"refresh");
    ctlrefresh(ctrl_c7,"refresh");
    ctlrefresh(ctrl_c8,"refresh");
    ctlrefresh(ctrl_c9,"refresh");
    ctlrefresh(ctrl_c10,"refresh");
    ctlrefresh(ctrl_c11,"refresh");
    ctlrefresh(ctrl_c12,"refresh");
    ctlrefresh(ctrl_c13,"refresh");
    ctlrefresh(ctrl_c14,"refresh");

    return if !reqpost();

    aColor[1] = getvalue(ctrl_c1);
    aIntensity[1] = getvalue(ctrl_c2);
    aColor[2] = getvalue(ctrl_c3);
    aIntensity[2] = getvalue(ctrl_c4);
    aColor[3] = getvalue(ctrl_c5);
    aIntensity[3] = getvalue(ctrl_c6);
    aColor[4] = getvalue(ctrl_c7);
    aIntensity[4] = getvalue(ctrl_c8);
    aColor[5] = getvalue(ctrl_c9);
    aIntensity[5] = getvalue(ctrl_c10);
    aColor[6] = getvalue(ctrl_c11);
    aIntensity[6] = getvalue(ctrl_c12);
    aColor[7] = getvalue(ctrl_c13);
    aIntensity[7] = getvalue(ctrl_c14);

    // Store
    store("vColor1",aColor[1]);
    store("nIntensity1",aIntensity[1]);
    store("vColor2",aColor[2]);
    store("nIntensity2",aIntensity[2]);
    store("vColor3",aColor[3]);
    store("nIntensity3",aIntensity[3]);
    store("vColor4",aColor[4]);
    store("nIntensity4",aIntensity[4]);
    store("vColor5",aColor[5]);
    store("nIntensity5",aIntensity[5]);
    store("vColor6",aColor[6]);
    store("nIntensity6",aIntensity[6]);
    store("vColor7",aColor[7]);
    store("nIntensity7",aIntensity[7]);

    // Error
    bError = true;
    for(i = 1; i <= 7; i++)
      {
      if(aIntensity[i] > 0.0){bError = false;}
      }
    if(bError)
      {
      info("Intensity on atleast one color must be above 0.0%");
      return;
      }  

    // Camera Blur Length
    if(Camera().blurLength(0.0) < 1.0)
      {
      nCameraBlurLength = Camera().blurLength(0.0);
      }
    else
      {
      nCameraBlurLength = 1.0;
      }

    for(i = 1; i <= Items.size(); i++)
      {

      if(Items[i].genus == LIGHT)
        {

        Items[i].select();
      
    // Envelope

        AddEnvelope("Color");
        envRed = Envelope("Color.R",CHAN_PERCENT,Items[i].name);
        envGreen = Envelope("Color.G",CHAN_PERCENT,Items[i].name);
        envBlue = Envelope("Color.B",CHAN_PERCENT,Items[i].name);

        // Clear 
        while(envRed.keyCount >= 1)
          {
          envRed.deleteKey(envRed.keys[1]);       
          }
        while(envGreen.keyCount >= 1)
          {
          envGreen.deleteKey(envGreen.keys[1]);       
          }
        while(envBlue.keyCount >= 1)
          {
          envBlue.deleteKey(envBlue.keys[1]);       
          }

        if(aIntensity[1] > 0.0)
          {
          envRed.createKey(0.0,aColor[1].x * (1/255) * aIntensity[1]); 
          envRed.setKeyCurve(0.0,CHAN_LINEAR);        
          envGreen.createKey(0.0,aColor[1].y * (1/255) * aIntensity[1]); 
          envGreen.setKeyCurve(0.0,CHAN_LINEAR);        
          envBlue.createKey(0.0,aColor[1].z * (1/255) * aIntensity[1]); 
          envBlue.setKeyCurve(0.0,CHAN_LINEAR);        
          }
        else
          {
          iColor = 0;
          for(c = 1; c <= 7; c++)
            {
            if(aIntensity[c] > 0.0){iColor = c; break;}
            }
          envRed.createKey(0.0,aColor[iColor].x * (1/255) * aIntensity[iColor]); 
          envRed.setKeyCurve(0.0,CHAN_LINEAR);        
          envGreen.createKey(0.0,aColor[iColor].y * (1/255) * aIntensity[iColor]); 
          envGreen.setKeyCurve(0.0,CHAN_LINEAR);        
          envBlue.createKey(0.0,aColor[iColor].z * (1/255) * aIntensity[iColor]); 
          envBlue.setKeyCurve(0.0,CHAN_LINEAR);   
          }

        if(aIntensity[2] > 0.0)
          {           
          envRed.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 1),aColor[2].x * (1/255) * aIntensity[2]); 
          envRed.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 1),CHAN_LINEAR);        
          envGreen.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 1),aColor[2].y * (1/255) * aIntensity[2]); 
          envGreen.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 1),CHAN_LINEAR);        
          envBlue.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 1),aColor[2].z * (1/255) * aIntensity[2]); 
          envBlue.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 1),CHAN_LINEAR); 
          }

        if(aIntensity[3] > 0.0)
          {           
          envRed.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 2),aColor[3].x * (1/255) * aIntensity[3]); 
          envRed.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 2),CHAN_LINEAR);        
          envGreen.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 2),aColor[3].y * (1/255) * aIntensity[3]); 
          envGreen.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 2),CHAN_LINEAR);        
          envBlue.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 2),aColor[3].z * (1/255) * aIntensity[3]); 
          envBlue.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 2),CHAN_LINEAR); 
          }

        if(aIntensity[4] > 0.0)
          {           
          envRed.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 3),aColor[4].x * (1/255) * aIntensity[4]); 
          envRed.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 3),CHAN_LINEAR);        
          envGreen.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 3),aColor[4].y * (1/255) * aIntensity[4]); 
          envGreen.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 3),CHAN_LINEAR);        
          envBlue.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 3),aColor[4].z * (1/255) * aIntensity[4]); 
          envBlue.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 3),CHAN_LINEAR); 
          }

        if(aIntensity[5] > 0.0)
          {           
          envRed.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 4),aColor[5].x * (1/255) * aIntensity[5]); 
          envRed.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 4),CHAN_LINEAR);        
          envGreen.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 4),aColor[5].y * (1/255) * aIntensity[5]); 
          envGreen.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 4),CHAN_LINEAR);        
          envBlue.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 4),aColor[5].z * (1/255) * aIntensity[5]); 
          envBlue.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 4),CHAN_LINEAR); 
          }

        if(aIntensity[6] > 0.0)
          {           
          envRed.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 5),aColor[6].x * (1/255) * aIntensity[6]); 
          envRed.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 5),CHAN_LINEAR);        
          envGreen.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 5),aColor[6].y * (1/255) * aIntensity[6]); 
          envGreen.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 5),CHAN_LINEAR);        
          envBlue.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 5),aColor[6].z * (1/255) * aIntensity[6]); 
          envBlue.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 5),CHAN_LINEAR); 
          }

        if(aIntensity[7] > 0.0)
          {           
          envRed.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 6),aColor[7].x * (1/255) * aIntensity[7]); 
          envRed.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 6),CHAN_LINEAR);        
          envGreen.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 6),aColor[7].y * (1/255) * aIntensity[7]); 
          envGreen.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 6),CHAN_LINEAR);        
          envBlue.createKey((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 6),aColor[7].z * (1/255) * aIntensity[7]); 
          envBlue.setKeyCurve((1 / Scene().fps) * nCameraBlurLength * ((1 / 6) * 6),CHAN_LINEAR); 
          }

        if(aIntensity[7] <= 0.0 || nCameraBlurLength < 1.0)
          {
          iColor = 0;
          for(c = 7; c >= 1; c--)
            {
            if(aIntensity[c] > 0.0){iColor = c; break;}
            }
          envRed.createKey(1 / Scene().fps,aColor[iColor].x * (1/255) * aIntensity[iColor]); 
          envRed.setKeyCurve(1 / Scene().fps,CHAN_LINEAR);        
          envGreen.createKey(1 / Scene().fps,aColor[iColor].y * (1/255) * aIntensity[iColor]); 
          envGreen.setKeyCurve(1 / Scene().fps,CHAN_LINEAR);        
          envBlue.createKey(1 / Scene().fps,aColor[iColor].z * (1/255) * aIntensity[iColor]); 
          envBlue.setKeyCurve(1 / Scene().fps,CHAN_LINEAR);   
          }
            
          // Repeat
          envRed.preBehavior = CHAN_REPEAT;
          envRed.postBehavior = CHAN_REPEAT;
          envGreen.preBehavior = CHAN_REPEAT;
          envGreen.postBehavior = CHAN_REPEAT;
          envBlue.preBehavior = CHAN_REPEAT;
          envBlue.postBehavior = CHAN_REPEAT;

    //

        }

      }   

}

button_reset
{
    bRequesterUpdate = true;

    setvalue(ctrl_c1,<1.0,1.0,1.0>); // Color 1
    setvalue(ctrl_c2,1.0); // Intensity 1
    setvalue(ctrl_c3,<0.0,0.0,0.0>); // Color 2
    setvalue(ctrl_c4,0.0); // Intensity 2
    setvalue(ctrl_c5,<0.0,0.0,0.0>); // Color 3
    setvalue(ctrl_c6,0.0); // Intensity 3
    setvalue(ctrl_c7,<0.0,0.0,0.0>); // Color 4
    setvalue(ctrl_c8,0.0); // Intensity 4
    setvalue(ctrl_c9,<0.0,0.0,0.0>); // Color 5
    setvalue(ctrl_c10,0.0); // Intensity 5
    setvalue(ctrl_c11,<0.0,0.0,0.0>); // Color 6
    setvalue(ctrl_c12,0.0); // Intensity 6
    setvalue(ctrl_c13,<0.0,0.0,0.0>); // Color 7
    setvalue(ctrl_c14,1.0); // Intensity 7

    // Update
    aColor[1] = getvalue(ctrl_c1);
    aIntensity[1] = getvalue(ctrl_c2);
    aColor[2] = getvalue(ctrl_c3);
    aIntensity[2] = getvalue(ctrl_c4);
    aColor[3] = getvalue(ctrl_c5);
    aIntensity[3] = getvalue(ctrl_c6);
    aColor[4] = getvalue(ctrl_c7);
    aIntensity[4] = getvalue(ctrl_c8);
    aColor[5] = getvalue(ctrl_c9);
    aIntensity[5] = getvalue(ctrl_c10);
    aColor[6] = getvalue(ctrl_c11);
    aIntensity[6] = getvalue(ctrl_c12);
    aColor[7] = getvalue(ctrl_c13);
    aIntensity[7] = getvalue(ctrl_c14); 

    bRequesterUpdate = false;

    interpolate();
}

refresh:value
{
    if(!bRequesterUpdate)
      {
      aColor[1] = getvalue(ctrl_c1);
      aIntensity[1] = getvalue(ctrl_c2);
      aColor[2] = getvalue(ctrl_c3);
      aIntensity[2] = getvalue(ctrl_c4);
      aColor[3] = getvalue(ctrl_c5);
      aIntensity[3] = getvalue(ctrl_c6);
      aColor[4] = getvalue(ctrl_c7);
      aIntensity[4] = getvalue(ctrl_c8);
      aColor[5] = getvalue(ctrl_c9);
      aIntensity[5] = getvalue(ctrl_c10);
      aColor[6] = getvalue(ctrl_c11);
      aIntensity[6] = getvalue(ctrl_c12);
      aColor[7] = getvalue(ctrl_c13);
      aIntensity[7] = getvalue(ctrl_c14);

      interpolate();
      }
}

refresh_preset:value
{

    bRequesterUpdate = true;

    if(value == 1) // Gradient
      {
      setvalue(ctrl_c1,<1.0,1.0,1.0>); // Color 1
      setvalue(ctrl_c2,1.0); // Intensity 1
      setvalue(ctrl_c3,<0.0,0.0,0.0>); // Color 2
      setvalue(ctrl_c4,0.0); // Intensity 2
      setvalue(ctrl_c5,<0.0,0.0,0.0>); // Color 3
      setvalue(ctrl_c6,0.0); // Intensity 3
      setvalue(ctrl_c7,<0.0,0.0,0.0>); // Color 4
      setvalue(ctrl_c8,0.0); // Intensity 4
      setvalue(ctrl_c9,<0.0,0.0,0.0>); // Color 5
      setvalue(ctrl_c10,0.0); // Intensity 5
      setvalue(ctrl_c11,<0.0,0.0,0.0>); // Color 6
      setvalue(ctrl_c12,0.0); // Intensity 6
      setvalue(ctrl_c13,<0.0,0.0,0.0>); // Color 7
      setvalue(ctrl_c14,1.0); // Intensity 7
      }  

    if(value == 2) // RGB
      {
      setvalue(ctrl_c1,<1.0,0.0,0.0>); // Color 1
      setvalue(ctrl_c2,1.0); // Intensity 1
      setvalue(ctrl_c3,<0.0,0.0,0.0>); // Color 2
      setvalue(ctrl_c4,0.0); // Intensity 2
      setvalue(ctrl_c5,<0.0,0.0,0.0>); // Color 3
      setvalue(ctrl_c6,0.0); // Intensity 3
      setvalue(ctrl_c7,<0.0,1.0,0.0>); // Color 4
      setvalue(ctrl_c8,1.0); // Intensity 4
      setvalue(ctrl_c9,<0.0,0.0,0.0>); // Color 5
      setvalue(ctrl_c10,0.0); // Intensity 5
      setvalue(ctrl_c11,<0.0,0.0,0.0>); // Color 6
      setvalue(ctrl_c12,0.0); // Intensity 6
      setvalue(ctrl_c13,<0.0,0.0,1.0>); // Color 7
      setvalue(ctrl_c14,1.0); // Intensity 7
      } 

    if(value == 3) // Spectrum
      {
      setvalue(ctrl_c1,<1.0,0.0,0.0>); // Color 1
      setvalue(ctrl_c2,1.0); // Intensity 1
      setvalue(ctrl_c3,<1.0,0.5,0.0>); // Color 2
      setvalue(ctrl_c4,1.0); // Intensity 2
      setvalue(ctrl_c5,<1.0,1.0,0.0>); // Color 3
      setvalue(ctrl_c6,1.0); // Intensity 3
      setvalue(ctrl_c7,<0.0,1.0,0.0>); // Color 4
      setvalue(ctrl_c8,1.0); // Intensity 4
      setvalue(ctrl_c9,<0.0,1.0,1.0>); // Color 5
      setvalue(ctrl_c10,1.0); // Intensity 5
      setvalue(ctrl_c11,<0.0,0.0,0.6255>); // Color 6
      setvalue(ctrl_c12,1.0); // Intensity 6
      setvalue(ctrl_c13,<0.5,0.0,0.5>); // Color 7
      setvalue(ctrl_c14,1.0); // Intensity 7
      }


    // Update
    aColor[1] = getvalue(ctrl_c1);
    aIntensity[1] = getvalue(ctrl_c2);
    aColor[2] = getvalue(ctrl_c3);
    aIntensity[2] = getvalue(ctrl_c4);
    aColor[3] = getvalue(ctrl_c5);
    aIntensity[3] = getvalue(ctrl_c6);
    aColor[4] = getvalue(ctrl_c7);
    aIntensity[4] = getvalue(ctrl_c8);
    aColor[5] = getvalue(ctrl_c9);
    aIntensity[5] = getvalue(ctrl_c10);
    aColor[6] = getvalue(ctrl_c11);
    aIntensity[6] = getvalue(ctrl_c12);
    aColor[7] = getvalue(ctrl_c13);
    aIntensity[7] = getvalue(ctrl_c14); 

    bRequesterUpdate = false;

    interpolate();
}

interpolate
{

    bRequesterUpdate = true;

    for(i = 1; i <= 7; i++)
      {
      if(aIntensity[i] <= 0.0)
        {


        iColorA = 0;
        for(c = i; c >= 1; c--)
          {
          if(aIntensity[c] > 0.0){iColorA = c; break;}
          }

        iColorB = 0;
        for(c = i; c <= 7; c++)
          {
          if(aIntensity[c] > 0.0){iColorB = c; break;}
          }

        if(iColorA == 0)
          {aColor[i] = aColor[iColorB];}
        else if(iColorB == 0)
          {aColor[i] = aColor[iColorA];}
        else
          {
          aColor[i] = linear1D(aColor[iColorA],aColor[iColorB],maprange01(iColorA,iColorB,i)); 
          } 


        } 
      }

      if(aIntensity[1] <= 0.0){setvalue(ctrl_c1,(1/255) * aColor[1]);} // Color 1
      if(aIntensity[2] <= 0.0){setvalue(ctrl_c3,(1/255) * aColor[2]);} // Color 2
      if(aIntensity[3] <= 0.0){setvalue(ctrl_c5,(1/255) * aColor[3]);} // Color 3
      if(aIntensity[4] <= 0.0){setvalue(ctrl_c7,(1/255) * aColor[4]);} // Color 4
      if(aIntensity[5] <= 0.0){setvalue(ctrl_c9,(1/255) * aColor[5]);} // Color 5
      if(aIntensity[6] <= 0.0){setvalue(ctrl_c11,(1/255) * aColor[6]);} // Color 6
      if(aIntensity[7] <= 0.0){setvalue(ctrl_c13,(1/255) * aColor[7]);} // Color 7

    bRequesterUpdate = false;

}

// MAP RANGE

maprange01: n1,n2,i
{    
    if(n2-n1 == 0.0){return(0.0);}
  else
    {return((1/(n2-n1)) * (i-n1));}
}

// INTERPOLATION

linear1D: n1,n2,i // i = interpolation point (0-1)
{
    return(n1 * (1 - i) + n2 * i);     
}


All scripts available at my Google Drive at
https://drive.google.com/open?id=1cR_q2GVUAJHumic1-A3eXV16acQnVTWs