// (c) Copyright 2009.  Adobe Systems, Incorporated.  All rights reserved.//// Straighten.jsx - Straighten and crop a photo, based on the ruler position.//// John Peterson, Adobe Systems, 2009///*@@@BUILDINFO@@@ Straighten.jsx 3.0.0.2*//*// BEGIN__HARVEST_EXCEPTION_ZSTRING<javascriptresource><name>$$$/JavaScripts/Straighten/Menu=Straighten to Ruler</name><menu>automate</menu></javascriptresource>// END__HARVEST_EXCEPTION_ZSTRING*/// on localized builds we pull the $$$/Strings from a .dat file$.localize = true;var g_StackScriptFolderPath = app.path + "/"+ localize("$$$/ScriptingSupport/InstalledScripts=Presets/Scripts") + "/"										+ localize("$$$/Private/Exposuremerge/StackScriptOnly=Stack Scripts Only/");$.evalFile( g_StackScriptFolderPath + "Geometry.jsx");$.evalFile(g_StackScriptFolderPath + "Terminology.jsx");// Create a base object to scope the rest of the functions in the filefunction Straightener(){	this.pluginName = "Straighten";}straighten = new Straightener();straighten.getUnitPoint = function( desc ){	var x = desc.getUnitDoubleValue( kxStr );	var y = desc.getUnitDoubleValue( kyStr );	return new TPoint( x, y );}// Special version of crop supporting the hidePixels option (DOM FIX)straighten.hideCrop = function( cropRect, hidePixels ){	if (typeof hidePixels == "undefined")		hidePixels = true;			// Hiding  pixels does not work on simple documents	if ((app.activeDocument.layers.length == 1)		&& (app.activeDocument.activeLayer.isBackgroundLayer))		hidePixels = false;		    var desc = new ActionDescriptor();	var cropDesc = new ActionDescriptor();	cropDesc.putUnitDouble( enumTop, unitPixels, cropRect.fTop );	cropDesc.putUnitDouble( enumLeft, unitPixels, cropRect.fLeft );	cropDesc.putUnitDouble( keyBottom, unitPixels, cropRect.fBottom );	cropDesc.putUnitDouble( enumRight, unitPixels, cropRect.fRight );    desc.putObject( keyTo, classRectangle, cropDesc );    desc.putUnitDouble( enumAngle, unitAngle, 0.0 );	if  (hidePixels)		desc.putBoolean( eventHide, true );	executeAction( eventCrop, desc, DialogModes.NO );}// Remove the ruler and update the display (DOM FIX)straighten.clearRuler = function(){	desc = new ActionDescriptor();	var eventClearRuler = app.stringIDToTypeID("clearRuler");	executeAction( eventClearRuler, desc, DialogModes.NO );}// Get the endpoints from the ruler (DOM FIX)straighten.getRulerEndpoints = function(){	var desc1 = new ActionDescriptor();	var ref1 = new ActionReference();	ref1.putProperty( classProperty, krulerPointsStr );	ref1.putEnumerated( classDocument, typeOrdinal, enumTarget );	desc1.putReference( typeNULL, ref1 );	var result = executeAction( eventGet, desc1, DialogModes.NO );	if  (result.hasKey( kpointsStr ))	{		var i, ptList = result.getList( kpointsStr );		// The middle item in the list is an unused "midpoint" (currently == p0)		var p0 = this.getUnitPoint( ptList.getObjectValue(0) );		var p1 =  this.getUnitPoint( ptList.getObjectValue(2) );		if (p0.fX < p1.fX)			return [p0, p1];		else			return [p1, p0];	}	else		return [];}// Given two ruler endpoints, compute the rotation (in radians)// needed to make the ruler horizontal or vertical.straighten.getRotationAngle = function( p0, p1 ){	// Perfectly horizontal or vertical - no rotation	if ((p0.fX == p1.fX) || (p0.fY == p1.fY))		return 0.0;			var a, t, v = p1 - p0;	if (Math.abs(v.fY) > Math.abs(v.fX))	{		// If the line is mostly vertical, adjust the angle to		// straighten to the vertical axis.		t = v.fX;		v.fX = v.fY;		v.fY = t;		if (v.fX < 0) 		{			v.fX = - v.fX;			a = v.vectorAngle();		}		else			a = -v.vectorAngle();	}	else 		a = v.vectorAngle();			return -a;}// Rotate point around angle (in radians)TPoint.prototype.rotate = function( angle ){	var ca = Math.cos(angle);	var sa = Math.sin(angle);		var nx = this.fX * ca - this.fY * sa;	var ny = this.fY * ca + this.fX * sa;	this.fX = nx;	this.fY = ny;}// Given an rectange and a rotation angle, find a new rectangle with the// same proportions that fits inside the original.straighten.getCropRect = function( angle, rect ){	// Rotate rect about the origin, and find the bounds of the rotated rectangle.	rect.setCenter( TPoint.kOrigin );	var i, rotatedPoints = [ rect.getTopLeft(), rect.getTopRight(), rect.getBottomRight(), rect.getBottomLeft() ];		var rotatedBounds = new TRect(0,0,0,0);	for (i in rotatedPoints)	{		rotatedPoints[i].rotate(angle);		rotatedBounds.extendTo( rotatedPoints[i] );	}	// Move the origin back to top left corner of the enclosing rect,	// the new center is based on that.	rotatedBounds.offset( -rotatedBounds.getTopLeft() );	var newCenter = rotatedBounds.getCenter();	for (i in rotatedPoints)		rotatedPoints[i] += newCenter;			rect.setCenter( newCenter );	var cropPts = [];		// So sign test works below	if (angle < -Math.PI)		angle += 2*Math.PI;			var off1 = (angle > 0) ? 3 : 0;	var off2 = (angle > 0) ? 0 : 1;		// Draw lines from the center through the corners of the original rect.	// Record where those lines intersect the rotated rect.	var ii, rectPoints = [rect.getTopLeft(), rect.getTopRight(), rect.getBottomRight(), rect.getBottomLeft()];	for (ii in rectPoints)	{		i = Number(ii);	// Whacky Javascript uses strings, not numbers, for index		cropPts[i] = TPoint.lineSegmentIntersect( rotatedPoints[(i + off1) % 4], rotatedPoints[(i + off2) % 4], 		                                          newCenter, rectPoints[i] );	}				// The new crop is based on the minimum bounds of the intersections found above.	var cropRect = new TRect( Math.max(cropPts[0].fX, cropPts[3].fX), Math.max(cropPts[0].fY, cropPts[1].fY), 	                          Math.min(cropPts[1].fX, cropPts[2].fX), Math.min(cropPts[2].fY, cropPts[3].fY) );	return cropRect;}// Straighten the image to the ruler.  Returns the rotation angle,// or zero if there was no rotation (or no ruler).straighten.rotateToRuler = function(){	var rulerPts = this.getRulerEndpoints();	if (rulerPts.length == 0)		return 0.0;			var angle = this.getRotationAngle( rulerPts[0], rulerPts[1] );	app.activeDocument.rotateCanvas( angle * 180.0 / Math.PI );	return angle;}// Crop the image to the bounds, rotated by angle (in radians)straighten.cropRotatedImage = function( angle, bounds ){	var newBounds = this.getCropRect( angle, bounds );		// Ideally we'd report an error here, but since it's past RC, we'll just silently fail.	if (newBounds.isValid())		this.hideCrop( newBounds, true );//	app.activeDocument.crop( [UnitValue(newBounds.fLeft, 'px'), UnitValue(newBounds.fTop, 'px'), //							UnitValue(newBounds.fRight, 'px'), UnitValue(newBounds.fBottom, 'px')] );}// Main interactive entry.straighten.doInteractive = function(){	var bounds = new TRect( 0, 0, app.activeDocument.width.as('px'), app.activeDocument.height.as('px') );	var angle = straighten.rotateToRuler()	if (angle != 0.0)	{		// Don't crop if the alt/option key is down		if (! ScriptUI.environment.keyboardState['altKey'])			straighten.cropRotatedImage( angle, bounds );		straighten.clearRuler();	}		}// Set runstraightenFromScript if you want to load this script w/o running it.if ((typeof(runstraightenFromScript) == 'undefined')	|| (runstraightenFromScript==false))	straighten.doInteractive();