// stroke_3D.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 =flenField.text;	   // focus distance
var gdepth=gdepthField.text;   // z-axis depth
var sheight=sheightField.text;	
  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) return;  
  var sname = obj.name;
  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 =flenField.text;	
var gdepth=gdepthField.text;
var sheight=sheightField.text;	
  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 =flenField.text;	
 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 dialog = new FloatingDialog('tabbed') {
	title: "Stroke 3D",
	size: new Size(216, 348),
	visible: true,
	onClose: function() {
		this.destroy();
	}
};
var estep = new TextPane(dialog){
	text:"step:",
	position:new Point(146, 14)
};
var estepField = new TextEdit(dialog){
	bounds: [180,12, 25, 20],
	text:"10"
};
var erotate = new TextPane(dialog){
	text:"rotate:",
	position: new Point(136, 38)
};
var erotateField = new TextEdit(dialog){
	bounds:[180, 36, 25, 20],
	text:"0"
}
var escale = new TextPane(dialog){
	text:"scale:",
	position: new Point(142, 58)
};
var escaleField = new TextEdit(dialog){
	bounds:[180, 56, 25, 20],
	text:"100"
}
var emvx = new TextPane(dialog){
	text:"movex:",
	position:new Point(136, 78)
};
var emvxField = new TextEdit(dialog){
	bounds: [180, 76, 25, 20],
	text:"0"
};
var emvy = new TextPane(dialog){
	text:"movey:",
	position:new Point(136, 98)
};
var emvyField = new TextEdit(dialog){
	bounds: [180,96, 25, 20],
	text:"0"
};
var bcColor = new TextPane(dialog){
	text:"C:",
	position:new Point(15, 22)
};
var bcColorField = new TextEdit(dialog){
	bounds: [30, 20, 25, 20],
	text:"100"
};
var bmColor = new TextPane(dialog){
	text:"M:",
	position: new Point(15, 42)
};
var bmColorField = new TextEdit(dialog){
	bounds:[30, 40, 25, 20],
	text:"0"
}
var byColor = new TextPane(dialog){
	text:"Y:",
	position:new Point(15, 62)
};
var byColorField = new TextEdit(dialog){
	bounds: [30, 60, 25, 20],
	text:"10"
};
var bkColor = new TextPane(dialog){
	text:"K:",
	position: new Point(15, 82)
};
var bkColorField = new TextEdit(dialog){
	bounds:[30, 80, 25, 20],
	text:"0"
}
var bwidth = new TextPane(dialog){
	text:"W:",
	position: new Point(12, 106)
};
var bwidthField = new TextEdit(dialog){
	bounds:[30, 104, 25, 20],
	text:"8"
};		
var fcColor = new TextPane(dialog){
	text:"C:",
	position:new Point(76, 22)
};
var fcColorField = new TextEdit(dialog){
	bounds: [92, 20, 25, 20],
	text:"0"
};
var fmColor = new TextPane(dialog){
	text:"M:",
	position: new Point(76, 42)
};
var fmColorField = new TextEdit(dialog){
	bounds:[92, 40, 25, 20],
	text:"0"
}
var fyColor = new TextPane(dialog){
	text:"Y:",
	position:new Point(76, 62)
};
var fyColorField = new TextEdit(dialog){
	bounds: [92, 60, 25, 20],
	text:"0"
};
var fkColor = new TextPane(dialog){
	text:"K:",
	position: new Point(76, 82)
};
var fkColorField = new TextEdit(dialog){
	bounds:[92, 80, 25, 20],
	text:"0"
}
var fwidth = new TextPane(dialog){
	text:"W:",
	position: new Point(72, 106)
};
var fwidthField = new TextEdit(dialog){
	bounds:[92, 104, 25, 20],
	text:"0.2"
}
//------------------------------------
var spcColor = new TextPane(dialog){
	text:"C:",
	position:new Point(15, 170)
};
var spcColorField = new TextEdit(dialog){
	bounds: [30, 168, 25, 20],
	text:"70"
};
var spmColor = new TextPane(dialog){
	text:"M:",
	position: new Point(15, 190)
};
var spmColorField = new TextEdit(dialog){
	bounds:[30, 188, 25, 20],
	text:"0"
}
var spyColor = new TextPane(dialog){
	text:"Y:",
	position:new Point(15, 210)
};
var spyColorField = new TextEdit(dialog){
	bounds: [30, 208, 25, 20],
	text:"0"
};
var spkColor = new TextPane(dialog){
	text:"K:",
	position: new Point(15, 230)
};
var spkColorField = new TextEdit(dialog){
	bounds:[30, 228, 25, 20],
	text:"0"
}
var spwidth = new TextPane(dialog){
	text:"W:",
	position: new Point(76, 192)
};
var spwidthField = new TextEdit(dialog){
	bounds:[96, 190, 25, 20],
	text:"0.5"
};	
var sprotate = new TextPane(dialog){
	text:"rotate:",
	position: new Point(136, 172)	
};
var sprotateField = new TextEdit(dialog){
	bounds:[180, 170, 25, 20],
	text:"20"
}
var splength = new TextPane(dialog){
	text:"length:",
	position:new Point(136, 192)
};
var splengthField = new TextEdit(dialog){
	bounds: [180, 190, 25, 20],
	text:"5"
};
var spdist = new TextPane(dialog){
	text:"dist:",
	position: new Point(70, 172)
};
var spdistField = new TextEdit(dialog){
	bounds:[96, 170, 25, 20],
	text:"1"
}
//-------------------------------------------------
var flen  = new TextPane(dialog){
	text:"focal length:",
	position:new Point(15, 282)
};
var flenField = new TextEdit(dialog){
	bounds: [88, 280, 25, 20],
	text:"400"
};
var gdepth  = new TextPane(dialog){
	text:"global depth:",
	position:new Point(10, 302)
};
var gdepthField = new TextEdit(dialog){
	bounds: [88, 300, 25, 20],
	text:"0"
};
var sheight = new TextPane(dialog){
	text:"z_height:",
	position:new Point(52, 322)
};
var sheightField = new TextEdit(dialog){
	bounds: [108, 320, 32, 20],
	text:"0.5"
};
var xaxis=false
var xaxisCheck = new CheckBox(dialog){
		position: [188, 282],
		size: [20, 15],
		visible: true,
		checked: xaxis,
		onClick: function () {
			xaxis = xaxisCheck.checked;
}
};
var xaxisText = new TextPane(dialog){
	text:"x-axis:",
	position:new Point(148, 282)
};
var yaxis=false
var yaxisCheck = new CheckBox(dialog){
		position: [188, 302],
		size: [20, 15],
		visible: true,
		checked: yaxis,
		onClick: function () {
			yaxis = yaxisCheck.checked;
}
};
var yaxisText = new TextPane(dialog){
		text:"y-axis:",
		position:new Point(148, 302)
};		
var zaxis=false
var zaxisCheck = new CheckBox(dialog){
		position: [188, 322],
		size: [20, 15],
		visible: true,
		checked: zaxis,
		onClick: function () {
			zaxis = zaxisCheck.checked;
}
};
var zaxisText = new TextPane(dialog){
		text:"z-axis:",
		position:new Point(148, 322)
};	
//--------------------------------------------------------------------
var zoffsetButton = new Button(dialog){
	text:"Zaxis GO !!",                //  offset  to  z -axis
	position:new Point(134,124),
	onClick : function(){	
var estep=estepField.text;
var erotate=erotateField.text;	
var escale=escaleField.text;	
var emvx=emvxField.text;	
var emvy=emvyField.text;
var bc=bcColorField.text;
var bm=bmColorField.text;
var by=byColorField.text;
var bk=bkColorField.text;
var bwidth=bwidthField.text;
var fc=fcColorField.text;
var fm=fmColorField.text;	
var fy=fyColorField.text;	
var fk=fkColorField.text;	
var fwidth=fwidthField.text;	
var sheight=sheightField.text;		
var stepi=0;
		var black,cyan,magenta,yellow,alpha
		var art = new Path();		
		var shapes =  activeDocument.getItems({
	type: Path, 
	selected: true
	});
	
		if (shapes[0].parent instanceof CompoundPath | shapes[0].parent instanceof Group | shapes[0].parent.parent instanceof Group){
		var alertMessage =  'PLEASE !! \n\nUN-Group(Ctrl+Shift+G), \nUN-CompoundPath(Ctrl+Shift+Alt+8)';
		var dialog = new PopupDialog('alert');
		dialog.alert (alertMessage);
	return;
	}	
	
   for (stepi=0; stepi<=estep; stepi++) { 	
		for (var j = 0; j <shapes.length; j++) 			
	{			
	var art = shapes[j];
//
	var mvx=stepi*emvx/estep;
	var mvy=stepi*emvy/estep;
	var scale=(100-stepi*(100-escale)/estep)/100;
	var rotate=stepi*erotate*Math.PI /(180*estep);
	var cyan=bc-stepi*(bc-fc)/estep;
	var magenta=bm-stepi*(bm-fm)/estep;
	var yellow=by-stepi*(by-fy)/estep;
	var black=bk-stepi*(bk-fk)/estep ;
	var width=bwidth-stepi*(bwidth-fwidth) / estep		
	var sdColor=new CMYKColor(cyan/100,magenta/100,yellow/100,black/100,alpha/100);			
	art=art.clone();
	var targetPoint = art.bounds.center; 
	
	art.style.stroke.width=width;
	art.style.stroke.color=sdColor;
	art.translate([mvx, mvy]);
	
	var targetPoint = art.bounds.center; 
	art.scale(scale,scale);
	var scalePos = art.bounds.center;
	var move = targetPoint.subtract(scalePos);
	art.translate(move);
	var targetPoint = art.bounds.center; 
	art.rotate(rotate,targetPoint);
	var rotatePos = art.bounds.center;
	var move = targetPoint.subtract(rotatePos);
	art.translate(move);
	art.name=sheight*stepi
		}
	}	
	stepi=0;width=0;scale=0;rotate=0;
}
};
zoffsetButton.setSize(72,20);
var spiralButton = new Button(dialog){
	text:"Spiral GO !!",    //           rotation around  x,y,z-axis
	position:new Point(115,225),
	onClick : function(){
	var sprotate=sprotateField.text;	
	var splength=parseFloat(splengthField.text);	
	var spdist=parseFloat(spdistField.text);
	var spc=spcColorField.text;
	var spm=spmColorField.text;
	var spy=spyColorField.text;
	var spk=spkColorField.text;
	var spwidth=spwidthField.text;	
	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();
}
}
};
spiralButton.setSize(90,20);
//Stroke division selection
var thewordFrame=new Frame(dialog){
	text:"back",
	size:[60,130],
	position: new Point(3,0)
};
var pathFrame=new Frame(dialog){
	text:"front",
	size:[60,130],
	position:new Point(65,0)
};
var pathFrame=new Frame(dialog){
	text:"",
	size:[84,143],
	position:new Point(128,6)
};
var selectionFrame=new Frame(dialog){
	text:"Stroke spiral",
	size:[210,104],
	position:new Point(3, 148)
};
var selectionFrame=new Frame(dialog){
	text:"3D_options",
	size:[210,86],
	position:new Point(3, 258)
};

