// stroke_3D_spiral.js
// Sunabe kazumichi 2010.2
// http://dp48069596.lolipop.jp/

//scriptographer.2.9.073-arrived/
//      PopupDialog  of "PLEASE !! UN-Group(Ctrl+Shift+G), UN-CompoundPath" appears in "Zaxis Go !!" button.

var excessive_transform_threshold = 2.0;
// ----------------------------------------------
function Vector3d(x,y,z){
  this.x = x; this.y = y; this.z = z;
  return this;
}
// ----------------------------------------------
function Vector3d_clone(){
  return new Vector3d(this.x, this.y, this.z);
} Vector3d.prototype.clone =Vector3d_clone;
// ----------------------------------------------
function Vector3d_add(vt){
  this.x += vt.x; this.y += vt.y; this.z += vt.z;
  return this;
} Vector3d.prototype.add =Vector3d_add;
// ----------------------------------------------
function Vector3d_sub(vt){
  this.x -= vt.x; this.y -= vt.y; this.z -= vt.z;
  return this;
} Vector3d.prototype.sub =Vector3d_sub;
// ----------------------------------------------
function Vector3d_xrot(t){
  var c = Math.cos(t); var s = Math.sin(t);
  var y2 = this.y*c - this.z*s;
  var z2 = this.y*s + this.z*c;
  this.y = y2; this.z = z2;
  return this;
} Vector3d.prototype.xrot =Vector3d_xrot;
// ----------------------------------------------
function Vector3d_yrot(t){
  var c = Math.cos(t); var s = Math.sin(t);
  var x2 = this.x*c + this.z*s;
  var z2 = - this.x*s + this.z*c;
  this.x = x2; this.z = z2;
  return this;
} Vector3d.prototype.yrot =Vector3d_yrot;
// ----------------------------------------------
function Vector3d_zrot(t){
  var c = Math.cos(t); var s = Math.sin(t);
  var x2 = this.x*c - this.y*s;
  var y2 = this.x*s + this.y*c;
  this.x = x2; this.y = y2;
  return this;
} Vector3d.prototype.zrot =Vector3d_zrot;
// ----------------------------------------------
function Vector3d_rot(xr, yr, zr){
  this.xrot(xr); this.yrot(yr); this.zrot(zr);
  return this;
} Vector3d.prototype.rot =Vector3d_rot;
// ----------------------------------------------
function Vector3d_to2dz(f){
  this.x = f*this.x/(f-this.z);
  this.y = f*this.y/(f-this.z);
  return this;
} Vector3d.prototype.to2dz =Vector3d_to2dz;
// ----------------------------------------------
var sel, sr, so, so2, inv_radius, vts;
var fpnt, global_depth, min_radius;
var last_pnt, updated;
var hpi = Math.PI / 2;
var wpi = Math.PI * 2;
var mpi = Math.PI;
var p0 = new Point(0,0);
var v0 = new Vector3d(0,0,0);
// ----------------------------------------------
var min_radius =100;   //Fixed :  set max( this value, max(selection.width, selection.height)) to virtual sphere's radius
var last_pnt = null;
var updated = false;
var sel = [];
var vts = [];
// ----------------------------------------------
function onMouseDown(event){
  with( activeDocument.activeLayer ){
    if( locked || hidden ){
      Dialog.alert("please unlock and show the active layer");
      return; }
  }
  //activeDocument.redraw(); 	
var flen =400  // focus distance
var gdepth=0  // z-axis depth
var sheight=6	
  sel = activeDocument.getItems({
	type: Path, 
	selected: true
	});  
	sr = getArtSetRect(sel);
  if(sr == null) return;
  if(so == null) so = sr.center;
  so2 = -so;
  
  inv_radius = 1 / (Math.max(Math.min(sr.width, sr.height) / 2, min_radius));
  var ar, j, vt;
  var opts = {z:0, xr:0, yr:0, zr:0, xor:0, yor:0, zor:0, center:null}
  var sname, seg;
  var vectHeight = v0.clone();
  vectHeight.z = - gdepth; 
  for(var i=0; i < sel.length; i++){
    opts.xor = 0;
    opts.yor = 0;
    opts.zor = 0;      
    ar = [];  
	for(j = 0; j < sel[i].segments.length; j++){
      seg = sel[i].segments[j];
      ar.push([new Vector3d(seg.point.x - so.x, seg.point.y - so.y, 0),
               new Vector3d(seg.handleIn.x,  seg.handleIn.y, 0),
               new Vector3d(seg.handleOut.x, seg.handleOut.y, 0)]);
    }   
    getSelfRotationFromName(sel[i], ar, opts);   
  //  getRotationFromName(sel[i], opts);
    if(opts.xor!=0 || opts.yor!=0 || opts.zor!=0){
      rotSegDat(ar, opts.xor, opts.yor, opts.zor);
    }
    for(var j = 0; j < ar.length; j++){ ar[j][0].add( vectHeight ); }
    vts.push(ar);  
 }
  last_pnt = event.point;
  updated = false;
}
// ----------------------------------------------
function getSelfRotationFromName(obj, ar, opts){
  if( obj instanceof Layer || obj.name == null) return;
  
  var sname = obj.name || '';
  	var xaxis=components.x_axis.value;	
	var yaxis=components.y_axis.value;	
	var zaxis=components.z_axis.value;
  
  
  opts.xr = 0;
  opts.yr = 0;
  opts.zr = 0;
  opts.center = obj.bounds.center;
  oz = 0;   
  // xN:  x-axis rotation. uses center of the object itself as origin
  if(sname.match(/S(-?\d+)/))
	{
		if(xaxis){opts.xr += toRad(RegExp.$1); }
		if(yaxis){opts.yr += toRad(RegExp.$1); }
		if(zaxis){opts.zr += toRad(RegExp.$1); } 
	}  
  // actual rotation
  if(opts.xr!=0 || opts.yr!=0 || opts.zr!=0){
    rotSegDat2(ar, opts.xr, opts.yr, opts.zr,
               opts.center-so, oz);
  } 
  // N: height ( - global depth) (must be at the beginning of its name)
  if( sname.match(/^([\d\.-]+)/) && !isNaN(RegExp.$1) ){
    var vectHeight = parseFloat(RegExp.$1);
    for(var j = 0; j < ar.length; j++){
        ar[j][0].z += vectHeight;
   }
  }
  
  if( obj.parent instanceof Item ){
    getSelfRotationFromName(obj.parent, ar, opts);
  }
}
//----------------------------------------------
function toRad(dg){ return (dg-0)/180*Math.PI; }
// ----------------------------------------------
function onMouseDrag(event){
  if(last_pnt == null || last_pnt.getDistance(event.point) < 1) return;
  last_pnt = event.point; 
  
  
var flen =400   // focus distance
var gdepth=0  // z-axis depth
var sheight=6	

  if(sel.length < 1 || sr == null) return;
  var v = (last_pnt - so) * inv_radius;	  
  if(v.getDistance(p0) > 0.999) v = v.normalize() * 0.999;
  // rotation angles
  var t = Math.asin(v.y);
  if(isNaN(t)) return;
  var xr = -t;
  var yr = Math.asin(v.x / Math.cos(t));		
  if(isNaN(yr)) return;
  var zr = 0; 
  var j, vp, seg, pnt;
  for(var i = 0; i< sel.length; i++){
    seg = sel[i].segments;  
    for(j = 0; j < seg.length; j++){
      vp = vts[i][j][0]; //.clone();
      pnt = view3d(vts[i][j][0], xr, yr, zr, v0, p0)
      seg[j].point = pnt + so;
      seg[j].handleIn= view3d(vts[i][j][1], xr, yr, zr, vp, pnt);
      seg[j].handleOut= view3d(vts[i][j][2], xr, yr, zr, vp, pnt);
    }
  } 
  updated = true;
}
// ----------------------------------------------
function view3d(vt, xr, yr, zr, v2, p){
var flen =400;   // focus distance
 var vect = vt.clone().add(v2);
  vect.rot(xr, yr, zr);
  vect.to2dz(flen);
  return new Point(vect.x - p.x, vect.y - p.y);
}
// ----------------------------------------------
function rotSegDat(ar, xr, yr, zr){
  for(var i = 0; i < ar.length; i++){
    ar[i][1].add(ar[i][0]);
    ar[i][2].add(ar[i][0]);
    ar[i][0].rot(xr, yr, zr);
    ar[i][1].rot(xr, yr, zr).sub(ar[i][0]);
    ar[i][2].rot(xr, yr, zr).sub(ar[i][0]);
  }
}
// ----------------------------------------------
function rotSegDat2(ar, xr, yr, zr, o, oz){
  var vo = new Vector3d(o.x, o.y, oz);
  for(var i = 0; i < ar.length; i++){
    ar[i][0].sub(vo);
    ar[i][1].add(ar[i][0]);
    ar[i][2].add(ar[i][0]);
    ar[i][0].rot(xr, yr, zr).add(vo);
    ar[i][1].rot(xr, yr, zr).add(vo).sub(ar[i][0]);
    ar[i][2].rot(xr, yr, zr).add(vo).sub(ar[i][0]);
  }
}
// ----------------------------------------------
function onMouseUp(){
 
  resetValues();
 
}
// ----------------------------------------------
function resetValues(){
  //sel = null;
  vts = [];
  so = null;
  sr = null;
  last_pnt = null;
  updated = false;
}
// ----------------------------------------------
function getArtSetRect(set){
  if(set.length < 1) return null;
  var tmp_rect = null; 
  for(var i = set.length - 1; i >= 0; i--){  
    // if there's a path named "origin" in the selection,
    // set its center as the rotation center   
    if(so == null && set[i].name == "origin"){
      so = set[i].bounds.center;
      if(set.length > 1){
        set.remove(i);
        continue;
      }
    }
    with(set[i].bounds){
      if(tmp_rect == null){
        tmp_rect = [top, right, bottom, left];
      } else {
        if(top    > tmp_rect[0])  tmp_rect[0] = top;
        if(right  > tmp_rect[1])  tmp_rect[1] = right;
        if(bottom < tmp_rect[2])  tmp_rect[2] = bottom;
        if(left   < tmp_rect[3])  tmp_rect[3] = left;
      }
    }
  }
  return new Rectangle(new Point(tmp_rect[3], tmp_rect[0]),
                       new Point(tmp_rect[1], tmp_rect[2]));
}
function dbug(msg){if (true)
		print('::debug: '+msg);
}
// ------------------------  Dialog stuff  ------------------------ 
var values = {
	size: 10,
	scale: 100,
	count: 0,
};

var components = {
ruler1: {
		type: 'ruler', label: 'spiral'
	},
	spC: { type: 'number', label: 'C' ,value: 50, range: [0, 100]},
	spM: { type: 'number', label: 'M' ,value: 0, range: [0, 100]},	
	spY: { type: 'number', label: 'Y' ,value: 0, range: [0, 100]},
	spK: { type: 'number', label: 'K' ,value: 0, range: [0, 100]},
	spW: { type: 'number', label: 'W' ,value: 0.4,fractionDigits: 1},

ruler2: {
		type: 'ruler'
	},
	dist: { type: 'number', label: 'dist' ,value: 1},
	rotate: { type: 'number', label: 'rotate' ,value: 20},	
	length: { type: 'number', label: 'length',value: 4 },
	
ruler3: {
		type: 'ruler', label: 'option'
	},
	x_axis: { type: 'boolean', label: 'x_axis'},
	y_axis: { type: 'boolean', label: 'y_axis'},	
	z_axis: { type: 'boolean', label: 'z_axis',value:true},
	
	 spiraltButton : {
		type: 'button', value: 'Spiral',
		onClick: function() {
			
	var sprotate=components.rotate.value;	
	var splength=components.length.value;	
	var spdist=components.dist.value;
	var spc=components.spC.value;
	var spm=components.spM.value;
	var spy=components.spY.value;
	var spk=components.spK.value;
	var spwidth=components.spW.value;	
	
	var art = new Path();		
	var shapes =  activeDocument.getItems({
	type: Path, 
	selected: true
	});
	var spColor=new CMYKColor(spc/100,spm/100,spy/100,spk/100);			
	for (var j = 0; j <shapes.length; j++) 			
	{		
			var art = shapes[j];		
			art.curvesToPoints(spdist);	
			var count = art.curves.length;		
			for (var k = 0; k <count; k++) {
	var res = new Path();
	res.style.stroke.width = spwidth;
	res.style.stroke.color =  spColor;	
     var bezier = art.curves[k];
      var pt = bezier.getPoint(0);	
	//var ptk = bezier.getPoint(k);
	res.moveTo(pt);
    var n = bezier.getNormal(-1).normalize(splength); 
	if(n.x != 0 || n.y != 0){
  		res.segments.add(pt.add(n));
		var currPos=res.bounds.center;     //    bar center move to character_line
		var move =pt.subtract(currPos);
			res.translate(move);
			res.name="S"+sprotate*k;
	}
	}	
//last point
	var lastp = new Path();
	lastp.style.stroke.width = spwidth;
	lastp.style.stroke.color =  spColor;	
	 var bezier = art.curves[count-1];	    
	 var ptk = bezier.getPoint(1);	    
 	lastp.moveTo(ptk);
    var n = bezier.getNormal(-1).normalize(splength);   	 
   	if(n.x != 0 || n.y != 0){
  		lastp.segments.add(ptk.add(n));
		var currPos=lastp.bounds.center;     //    bar center move to character_line		
		var move =ptk.subtract(currPos);
		lastp.translate(move);
		lastp.name="S"+sprotate*k
}
activeDocument.redraw();
}
		}
		}
};



var palette = new Palette('Stroke 3D_spiral', components, values);
