////////////////////////////////////////////////////////////////////////////////
// Note from the Scriptographer.org Team
//
// In Scriptographer 2.9, we switched to a top-down coordinate system and
// degrees for angle units as an easier alternative to radians.
// 
// For backward compatibility we offer the possibility to still use the old
// bottom-up coordinate system and radians for angle units, by setting the two
// values bellow. Read more about this transition on our website:
// http://scriptographer.org/news/version-2.9.064-arrived/

script.coordinateSystem = 'bottom-up';
script.angleUnits = 'radians';

/*
fiddlehead (fiddlehead fern : zenmai)
  A drawing tool that creates fiddlehead-like shapes.
  Several parameters can be adjusted with the option dialog box.

Scriptographer 2.6 script
2006-07-07 (last modified:10-02-21, changelog is at the end of this file)

Scriptographer is a plugin for Adobe Illustrator(TM)
created by Juerg Lehni
http://scriptographer.org/

This script was written by Hiroyuki Sato
http://lines-about-to-be-generated.blogspot.com/
*/

var hlen = 4 * (Math.sqrt(2) - 1) / 3;
var hpi = Math.PI / 2;
var qpi = hpi / 2;
var po  = new Point(0,0);

var line, lastPnt, gr;
var angleRange, stemLen, spiralRadius, lineWidth;
var interval, growDir, lineCol;
var g_myDialog;
// --------------------------
function onInit(){
  angleRange = 45  / 180 * Math.PI;
  stemLen = 100;
  spiralRadius = 10;
  lineWidth = 0.5;

  interval = spiralRadius / 2;
  growDir = 1; // 1:normal, 2:drawing direction
  lineCol = new GrayColor(1); //or selectedLineCol_or_Black();
  
  stemLen /= 2;
  spiralRadius /= 2;
  
  // show dialog at start
  onOptions();
}
// --------------------------
function onOptions() {
  if(g_myDialog){
    g_myDialog.dialog.destroy();
    g_myDialog = null;
  }
  
  g_myDialog = myDialog("fiddlehead:",[
    {description:"stem length",   value:stemLen*2},
    {description:"spiral radius", value:spiralRadius*2},
    {description:"interval",      value:interval},
    {description:"angle range",   value:angleRange.toDegrees()},
    {description:"direction(1,2)",value:growDir}
    ]);
}
// ---------------------
function getValuesFromDialog(){
  var opt = myDialog_getValues();
  if(opt != null){
    for(var i=0; i<opt.length; i++){
      if(isNaN(opt[i]) || opt[i]<=0){
        Dialog.alert("please input a positive number");
        return false;
      }
    }
    stemLen = opt[0]/2;
    spiralRadius = opt[1]/2;
    interval = opt[2];
    angleRange = opt[3].toRadians();
    if(opt[4]==1 || opt[4]==2) growDir = opt[4];
    lineCol = selectedLineCol_or_Black();
    return true;
  }
  return false;
}
// ----------------------------------------------
function disposeDialog(){
    if(g_myDialog){
        g_myDialog.dialog.destroy();
        g_myDialog = null;
    }
}
// ----------------------------------------------
function onSelect(){ onOptions(); }
// ----------------------------------------------
function onStop(){ disposeDialog(); }
// ----------------------------------------------
function onDeselect(){ disposeDialog(); }
// ----------------------------------------------
function onMouseDown(event){
  with(document.layers[0]){
    if(locked || hidden){
      Dialog.alert("please unlock and show the first layer");
      return; }
  }
  // get values from dialog
  var chk = getValuesFromDialog();
  if(!chk){ return; }
    
  tool.distanceThreshold = interval;
  lastPnt = event.point;
  if(growDir != 0) return;
  makeline();
  zenmai(new Point(0,1), event);
}
// --------------------------
function onMouseDrag(event) {
  lastPnt = event.lastPoint;
  var v = new Point(1,0);
  if(growDir > 0){
    v = (event.delta).normalize();
    if(growDir > 1) v = v.rotate(hpi);
  }
  makeline();
 
  zenmai(v.rotate(hpi), event);
}
// --------------------------
function onMouseUp(event){
  if(gr && gr.children.length<2){
    if(gr.children.length == 1) gr.firstChild.moveAbove(gr);
    gr.remove();
  }
  gr = null;
  lastPnt = null;
  line = null;
}
// --------------------------
function makeline(){
  line = new Path() {
	fillColor: null,
	strokeColor: lineCol,
	strokeWidth: lineWidth
  };
  if(!gr) gr = new Group();
  gr.appendChild(line);
}
// --------------------------
function zenmai(v, event){
  var m = Math.random() + 1;
  var len = stemLen * m;
  var hlen = len/3;
  // 1st point
  line.segments = [new Segment(lastPnt,
                               po,
                               v * hlen)];
  // 2nd point
  v = v.rotate(angleRange * (Math.random()-0.5));
  var p = lastPnt + ( v * len );
  line.add(new Segment(p,
                       v.rotate(angleRange * (Math.random()-0.5)) * (-hlen),
                       po));
  
  addSpiral(line.segments, spiralRadius * m, 0.85, 12, (Math.random() < 0.5));
}
// --------------------------
function addSpiral(segs,
                   r,  // first radius
                   m,  // rate
                   n,  // number of segments
                   rl){ // turn right  (t/f)
  var qp = rl ? -qpi : qpi;
    
  var len = segs.length-1;
  if(len<1) return;
  var p = segs[len].point;
  // define v
  var v;
  if(segs[len].handleIn == po){
    with(segs[len-1]){
      if(p == point) return;
      if(p == (point + handleOut)){
        v = (p - point).normalize();
      } else { v = p - (point + handleOut).normalize(); }
    }
  } else {
    v = (-segs[len].handleIn).normalize();
  }
  // make spiral
  var seg;
  var h = r * hlen;
  segs[len].handleOut = v * h;
  for(var i=0; i<n; i++){
    v = v.rotate(qp);
    p = p + (v * Math.sqrt(2) * r);
    seg = new Segment(p);
    r *= m;
    v = v.rotate(qp);
    seg.handleIn = v * -h;
    h = r * hlen;
    seg.handleOut = v * h;
    segs.add( seg );
  }
}
// --------------------------
function selectedLineCol_or_Black(){
  if(documents.length>0){
    var obj = document.getItems({type: Path, selected: true});
    for(var i=0; i<obj.length; i++){
      if(obj[i].strokeColor) return obj[i].strokeColor;
    }
  }
  return new GrayColor(1);
}


// ==================================================================
// function for floating dialog
function myDialog(title, elems){
  this.dialog = new FloatingDialog("tabbed") {
    title: title
  };
  this.textedits = [];
  this.max_caption_width = 0;

    
  // parts --------------------------------------
  this.addStatic = function(elem, left, top){
    var st = new TextPane(this.dialog) {
      text: elem["description"],
      position: [left, top]
    };
    if(st.size.width > this.max_caption_width){
      this.max_caption_width = st.size.width;
    }
  }

  this.addTextEdit = function(elem, size, left, top){
    var te = new TextEdit(this.dialog) {
      units: "none",
      text: elem["value"],
      size: size,
      position: [left, top]
    };
    this.textedits.push(te);
  }

  // layout --------------------------------------
  // constants
  var vertical_interval = 24;
  var caption_and_textedit_interval = 4;
  var margin = 10;
  var left = margin;
  
  var textedit_size = new Size(50, 20);
  var button_size = new Size(50, 20);
  
  // ------------------------
  for(var i=0; i<elems.length; i++){
    this.addStatic(elems[i], left, vertical_interval * i + margin);
  }
  left += this.max_caption_width + caption_and_textedit_interval;
  for(var i=0; i<elems.length; i++){
    this.addTextEdit(elems[i], textedit_size, left,
                     vertical_interval * i + margin - 2);
  }
  
  var button_height = 0;
  
  this.dialog.size = new Size(left + textedit_size.width + margin*2,
                      vertical_interval * elems.length + margin*2
                      + button_height);
  return this;
}
// ------------------------------------
function myDialog_getValues(){
  var ar = [];
  var txt;
  for(var i=0; i<g_myDialog.textedits.length; i++){
    txt = g_myDialog.textedits[i].stringValue;
    try{
      ar.push(eval(txt) - 0);
    }catch(e){
      Dialog.alert(e);
      return null;
    }
  }
  return ar;
}
// ==================================================================
/*
changelog:
  2006-07-07  correct the title of the dialog
              delete a meaningless line (1st line of zenmai())
  2006-07-09  fix a bug in the drawing process
              modify to release a group if there's only 1 child
              lengthen the default stem length a bit
  2006-07-20  fix and modify to test the input value properly
              shorten the default stem length
              other minor fix and simplifying
  2006-10-29  fix to work with ver.2.0.019 of Scriptographer
              (Grayscale -> GrayColor)
              other minor modifying in onOptions()
  2008-03-26  modified to work with Sg 2.0.025
  2008-03-27  minor modification for dialog
  2008-04-01  minor fix on myDialog_getValues
  2009-?-?  official staff modified this to work with Sg 2.5. Thanks!
  2010-02-21  modified to work with Sg 2.6.036
*/
