Overblog Suivre ce blog
Editer la page Administration Créer mon blog
/ / /

Ce script découpe l'image du calque courant en tranches verticales, puis dispose les tranches en éventail en les ajustant à la dimension voulue.
Les tranches sont séparées par une marge et possèdent des coins arrondis.
L'image doit plutot etre en format paysage et assez allongée pour pouvoir etre disposée en un bel éventail.

Une interface utilisateur permet de définir les paramètres de cette transformation:
Nombre de morceaux, angle total, taille du cercle, marge entre morceaux, rayon de l'arrondi des coins.

Le script plus bas rassemble trois parties regroupées ici en une seule (PATHS, TRANSFRM et FAN), mais peut etre copié dans un seul fichier FAN.JSX.   

NOTE: Par modularité, il peut-etre utile de séparer ces parties en trois fichiers : PATHS.JSX, TRANSFRM.JSX et FAN.JSX.
Dans ce cas, les lignes "#include ..." au début de FAN.JSX doivent etre activées.
C'est ensuite FAN.JSX qui doit etre exécuté.

fan
venise
montagne
mars1

/************************************************************************
                        Fan
 ************************************************************************
 Build a fan with a wide image.
 ************************************************************************
 13/03/2010 V1r02b Habaki: Improve dialog
 12/03/2010 V1r02  Habaki: Add the fan angle
 09/03/2010 V1r01  Habaki: Creation
 ************************************************************************/
/************************************************************************
   PATHS.JSX
 ************************************************************************
 09/03/2010 Habaki V1r01 : Creation
 ************************************************************************/
/*----------------------------------------------------------------------*
   New Path on a rectangle
                        with Round corners
 *----------------------------------------------------------------------*/
function PathRect(
 Top, Left,Bottom,Right,
 CornerRadius
 )
{
      var idPxl = charIDToTypeID( "#Pxl" );
      var idsetd = charIDToTypeID( "setd" );
      var idnull = charIDToTypeID( "null" );
      var idPath = charIDToTypeID( "Path" );
      var idWrPt = charIDToTypeID( "WrPt" );
      var idT = charIDToTypeID( "T   " );
      //var idT = charIDToTypeID( "TyPa" );
      var idTop = charIDToTypeID( "Top " );
      var idLeft = charIDToTypeID( "Left" );
      var idBtom = charIDToTypeID( "Btom" );
      var idRght = charIDToTypeID( "Rght" );
      var idRds = charIDToTypeID( "Rds " );
      var idRctn = charIDToTypeID( "Rctn" );
     
      var ref4 = new ActionReference();
      ref4.putProperty( idPath, idWrPt );
     
      var desc0 = new ActionDescriptor();
      desc0.putReference( idnull, ref4 );
      var desc1 = new ActionDescriptor();
      desc1.putUnitDouble( idTop, idPxl, Top);
      desc1.putUnitDouble( idLeft, idPxl, Left);
      desc1.putUnitDouble( idBtom, idPxl, Bottom);
      desc1.putUnitDouble( idRght, idPxl, Right);
      desc1.putUnitDouble( idRds, idPxl, CornerRadius);
      desc0.putObject( idT, idRctn, desc1 );
      executeAction( idsetd, desc0, DialogModes.NO );
}
/*----------------------------------------------------------------------*/

/************************************************************************
   TRANSFORM.JSX
    Use Warp
 ************************************************************************
 Perform several transformations of a rectangle :
 Identity
 Trapezium
 First the transform grid must be computed. Then the warp use the last
 computed grid.
 ************************************************************************
 07/03/2010 Habaki V1r01 : Creation
 ************************************************************************/
/*----------------------------------------------------------------------*
   Constructor
 *----------------------------------------------------------------------*/
function Transform()
{
  this.lCur = app.activeDocument.activeLayer;
  var Bounds = this.lCur.bounds;
  this.top = Bounds[1];
  this.left = Bounds[0];
  this.bottom = Bounds[3];
  this.right = Bounds[2];
  // Grid nb of segments
  this.gridHNb = 3;
  this.gridVNb = 3;
  // Grid Unit
  this.gridHU = (this.right  - this.left) / this.gridHNb;
  this.gridVU = (this.bottom - this.top)  / this.gridVNb;
 
  // Warp grip : [V][H] : 0 1 2 3 / 4 5 6 7/ ...
  this.grid;
}
/*----------------------------------------------------------------------*
   Identity Grid
 *----------------------------------------------------------------------*/
Transform.prototype.gridIdentity = function()
{
  var grid = new Array();
  var pt, line;
  var h, v;
 
  for (v = 0;v <= this.gridVNb;v++) {
    line = new Array();
    for (h = 0;h <= this.gridHNb;h++) {
      pt = new Array();
      pt[0] = this.left + h* this.gridHU;
      pt[1] = this.top  + v* this.gridVU;
      line[h] = pt;
    }
    grid[v] = line;
  }
  return(grid);
}
/*----------------------------------------------------------------------*
   Trapezium grid
 *----------------------------------------------------------------------*/
Transform.prototype.gridTrapez = function(
        ScaleBottom  // Scale of bottom edge (%)
 )
{
  var grid = new Array();
  var pt, line;
  var h, v;
  var H, V;
 
  ScaleBottom /= 100.0;
  ScaleBottom = 1.0 - ScaleBottom;
  for (v = 0;v <=  this.gridVNb;v++) {
    line = new Array();
    for (h = 0;h <= this.gridHNb;h++) {
      pt = new Array();
      H = 1.0*h / this.gridHNb;
      V = 1.0*v / this.gridVNb;
      pt[0] = this.left
            + this.gridHU * this.gridHNb*(0.5 -
                                          (0.5 - H)*(1.0 - V*ScaleBottom));
      pt[1] = this.top  + this.gridVU * v;
     
      line[h] = pt;
    }
    grid[v] = line;
  }
  return(grid);
}
/*----------------------------------------------------------------------*
   Execute the warp
 *----------------------------------------------------------------------*/
Transform.prototype.warpExec = function() {
    var h,v;
    var line, pt;
   
    //-------- IDs
    var idTrnf = charIDToTypeID( "Trnf" );
    var idnull = charIDToTypeID( "null" );
    var idOfst = charIDToTypeID( "Ofst" );
    var idHrzn = charIDToTypeID( "Hrzn" );
    var idRlt = charIDToTypeID( "#Rlt" );
    var idVrtc = charIDToTypeID( "Vrtc" );
    var idwarp = stringIDToTypeID( "warp" );
    var idPxl = charIDToTypeID( "#Pxl" );
    var idrationalPoint = stringIDToTypeID( "rationalPoint" );
    var idwarp = stringIDToTypeID( "warp" );
    var idwarpStyle = stringIDToTypeID( "warpStyle" );
    var idwarpCustom = stringIDToTypeID( "warpCustom" );
    var idwarpValue = stringIDToTypeID( "warpValue" );
    var idwarpPerspective = stringIDToTypeID( "warpPerspective" );
    var idwarpPerspectiveOther = stringIDToTypeID( "warpPerspectiveOther" );
    var idwarpRotate = stringIDToTypeID( "warpRotate" );
    var idOrnt = charIDToTypeID( "Ornt" );
    var idbounds = stringIDToTypeID( "bounds" );
    var idTop = charIDToTypeID( "Top " );
    var idLeft = charIDToTypeID( "Left" );
    var idBtom = charIDToTypeID( "Btom" );
    var idRght = charIDToTypeID( "Rght" );
    var idRctn = charIDToTypeID( "Rctn" );
    var iduOrder = stringIDToTypeID( "uOrder" );
    var idvOrder = stringIDToTypeID( "vOrder" );
    var idcustomEnvelopeWarp = stringIDToTypeID( "customEnvelopeWarp" );
    var idmeshPoints = stringIDToTypeID( "meshPoints" );
    var idrationalPoint = stringIDToTypeID( "rationalPoint" );
    var idPath = charIDToTypeID( "Path" );
    var idOrdn = charIDToTypeID( "Ordn" );
    var idTrgt = charIDToTypeID( "Trgt" );
    var idFTcs = charIDToTypeID( "FTcs" );
    var idQCSt = charIDToTypeID( "QCSt" );
    var idQcsa = charIDToTypeID( "Qcsa" );
    //--------
    var desc15 = new ActionDescriptor();
    var ref8 = new ActionReference();
    ref8.putEnumerated( idPath, idOrdn, idTrgt );
    desc15.putReference( idnull, ref8 );
    desc15.putEnumerated( idFTcs, idQCSt, idQcsa );
    var desc16 = new ActionDescriptor();
    desc16.putUnitDouble( idHrzn, idRlt, 0.000000 );
    desc16.putUnitDouble( idVrtc, idRlt, 0.000000 );
    desc15.putObject( idOfst, idOfst, desc16 );
   
    var desc17 = new ActionDescriptor();
    desc17.putEnumerated( idwarpStyle, idwarpStyle, idwarpCustom );
    desc17.putDouble( idwarpValue, 0.00000 );
    desc17.putDouble( idwarpPerspective, 0.000000 );
    desc17.putDouble( idwarpPerspectiveOther, 0.000000 );
    desc17.putEnumerated( idwarpRotate, idOrnt, idHrzn );
   
    // Layer.bounds
    var desc18 = new ActionDescriptor();
    desc18.putUnitDouble( idTop,  idPxl, this.top); // [1] TopLeftY
    desc18.putUnitDouble( idLeft, idPxl, this.left); // [0] TopLeftx
    desc18.putUnitDouble( idBtom, idPxl, this.bottom); // [3] BottomRightY
    desc18.putUnitDouble( idRght, idPxl, this.right); // [2] BottomRightX
    desc17.putObject( idbounds, idRctn, desc18 );
    desc17.putInteger( iduOrder, this.gridHNb+1 );
    desc17.putInteger( idvOrder, this.gridVNb+1 );
   
    // List of mesh points
    // 0 1 2 3
    // 4 5 6 7
    // ...
    var list1 = new ActionList();
    for (v = 0;v <= this.gridVNb;v++) {
      line = this.grid[v];
      for (h = 0;h <= this.gridHNb;h++) {
        pt = line[h];
        var descPt = new ActionDescriptor();
        try{
          descPt.putUnitDouble( idHrzn, idPxl, pt[0]);
          descPt.putUnitDouble( idVrtc, idPxl, pt[1]);
        } catch(ex) {
          alert("==>" + ex.message + "<" + pt[0] + "," + pt[1] + ">");
        }
        list1.putObject( idrationalPoint, descPt );
      }
    }
    var desc19 = new ActionDescriptor();
    desc19.putList( idmeshPoints, list1 );
    desc17.putObject( idcustomEnvelopeWarp, idcustomEnvelopeWarp, desc19 );
    desc15.putObject( idwarp, idwarp, desc17 );
    executeAction( idTrnf, desc15, DialogModes.NO );
}
/************************************************************************
                        Fan
 ************************************************************************
 Build a fan with a wide image.
 ************************************************************************
 13/03/2010 V1r02b Habaki: Improve dialog
 12/03/2010 V1r02  Habaki: Add the fan angle
 09/03/2010 V1r01  Habaki: Creation
 ************************************************************************/
//#include "transfrm.jsx"
//#include "paths.jsx"

/*----------------------------------------------------------------------*
                        Constructor
 *----------------------------------------------------------------------*/
function Fan()
{
  this.FLName="Fan";     // Result layer name
  this.LRef  = app.activeDocument.activeLayer;  // Reference layer
 
  this.FPNb = 7;  // Number of pieces
  this.FRadius = 150;  // Radius of circle: % of image height (> 100)
  this.FCorner = 6.0;  // % of image height
  this.FMargin = 2.0;  // % of image height
  this.FAngle = 180;  // Angle of the fan (0..360dgs)
}
/*----------------------------------------------------------------------*
                        Dialog
 *----------------------------------------------------------------------*/
Fan.prototype.dialogBuild = function(
        )
{
  var Coord = this.LRef.bounds;
  var LHeight = Coord[3] - Coord[1];
  var LWidth  = Coord[2] - Coord[0];
 
  var myResource = "dialog{ \
    orientation: 'column',\
    alignChildren: 'fill',\
    panelLayer: Panel {\
      orientation: 'column',\
      alignChildren: 'fill',\
      text: 'IMAGE to Process',\
      Name: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Layer: '},\
        Val: StaticText {text: ''}\
      },\
      Height: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Height: ', characters: 25},\
        Val: StaticText {text: '?'}\
      },\
      Width: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Width', characters: 25},\
        Val: StaticText {text: '?'}\
      }\
    },\
    panelCircle: Panel  {\
      orientation: 'column',\
      alignChildren: 'fill',\
      text: 'FAN',\
      Nb: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Nb of pieces:', characters: 25},\
        Val: EditText {text: '7', bounds: [0,0,40,20]}\
      },\
      Angle: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Fan angle (dgs):', characters: 25},\
        Val: EditText { text: '180', bounds: [0,0,40,20]}\
      },\
      s00: StaticText {text: 'All % are relative to image Height.', characters: 35 },\
      Rad: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Circle radius (% > 100):', characters: 25},\
        Val: EditText { text: '150', bounds: [0,0,40,20]},\
      },\
      Corn: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Corner radius (%):', characters: 25 },\
        Val: EditText { text: '6', bounds: [0,0,40,20]},\
      },\
      Marg: Group {\
        orientation: 'row',\
        Msg: StaticText {text: 'Margin between pieces (%):' , characters: 25},\
        Val: EditText { text: '2', bounds: [0,0,40,20] }\
      }\
    },\
    sign: StaticText {text: 'Habaki (c) 2010', characters: 15 },\
    groupButtons: Group  {\
      orientation: 'row',\
      buttonOk: Button { text: 'Build', enabled: 'false'},\
      buttonCancel: Button { text: 'Cancel'},\
    }\
  };"
  var myDialog = new Window(myResource, "  Build a FAN");
  myDialog.fanInstance = this;
 
  myDialog.panelLayer.Name.Val.text = this.LRef.name;
  myDialog.panelLayer.Height.Val.text = LHeight;
  myDialog.panelLayer.Width.Val.text = LWidth;
 
  myDialog.panelCircle.Angle.Val.text = "" + this.FAngle;
  myDialog.panelCircle.Rad.Val.text = "" + this.FRadius;
  myDialog.panelCircle.Corn.Val.text = "" + this.FCorner;
  myDialog.panelCircle.Marg.Val.text = "" + this.FMargin*2;
 
  // determines return value and keyboard shortcuts
  myDialog.defaultElement = myDialog.groupButtons.buttonOk; // == return value 1
  myDialog.cancelElement  = myDialog.groupButtons.buttonCancel;
  /*--------------------------------------------------*/
  return myDialog
}
/*----------------------------------------------------------------------*
                Get parameters from User
 *----------------------------------------------------------------------*/
Fan.prototype.getParams = function() 
{
  var Doc = app.activeDocument;
  var LNb;
  var i;
  var LL;
 
  var myDialog = this.dialogBuild();
  var OK = (myDialog.show() == 1) ? true : false;
 
  if (OK) {
    this.FPNb    = parseInt(myDialog.panelCircle.Nb.Val.text);
    this.FAngle  = parseFloat(myDialog.panelCircle.Angle.Val.text);
    this.FRadius = parseFloat(myDialog.panelCircle.Rad.Val.text);
    this.FCorner = parseFloat(myDialog.panelCircle.Corn.Val.text);
    this.FMargin = parseFloat(myDialog.panelCircle.Marg.Val.text)/2;
  }
  return (OK);
}
/*----------------------------------------------------------------------*
                        Build process
 *----------------------------------------------------------------------*/
Fan.prototype.build = function()
{
  try{
    // Config parameters
    var LRef   = this.LRef; // Reference layer
   
    // Computed values
    var FRadE;   // External radius
    var FRadI;   // Internal radius
    var LWE, LWI;  // External,Internal widths for a piece
    var FCntX, FCntY;  // Fan center
    var LAngl0;   // Angle of a piece
    var LHeight, LWidth, LTX, LTY, LW;
    var FL, LL;   // layers
    var Coord;
    var i;
    var pathItem;
    var FMarg, FCorn;
   
    var PI = 3.14159265;
    var Doc = app.activeDocument;
    var Sel = Doc.selection;
   
    // Fan position (Center):
    FCntX = Doc.width /2;  // Horizontal
    FCntY = Doc.height*4/5; // Vertical
   
    // Dimension of the image
    // Coord: TopLeftX,TopLeftY,BottomRightX,BottomRightY
    Coord = LRef.bounds;
    LHeight = Coord[3] - Coord[1];
    LWidth  = Coord[2] - Coord[0];
   
    if (this.FRadius < 100) this.FRadius = 100;
    FRadE = LHeight*this.FRadius/100; // External radius
    FRadI = FRadE - LHeight;  // Internal radius
    LWE = FRadE * 2 * Math.sin(PI*this.FAngle / (this.FPNb * 360));
    LWI = LWE * FRadI / FRadE;
    FMarg = LHeight * this.FMargin / 100;
    FCorn = LHeight * this.FCorner / 100;
   
    var Tf = new Transform();
    Tf.grid = Tf.gridTrapez(LWI * 100.0 / LWE);
   
    // Result layer
    FL = Doc.artLayers.add();
    FL.name = this.FLName;
   
    // Split image in pieces
    LW  = LWidth/ this.FPNb;
    LTX = Coord[0];
    LTY = Coord[1];
    LAngl0 = PI*this.FAngle / (this.FPNb*180);
    for (i=0; i < this.FPNb; i++) {
      //alert("W:" + LW + ", X:" + LTX + ", Y:" + LTY);
     
      // Make a path with rounder corners to cut a piece
      PathRect(LTY, LTX+FMarg, LTY+LHeight, LTX+LW-FMarg, FCorn);
      pathItem = Doc.pathItems[0];
      pathItem.makeSelection();
      pathItem.remove();
     
      // Cut a piece
      LL = LRef.duplicate();
      Doc.activeLayer = LL;
      Sel.invert();
      Sel.clear();
      Sel.deselect();
     
      // Scale
      LL.resize(LWE*100 / LW,
                (FRadE - FRadI) * 100 / LHeight,
                AnchorPosition.TOPCENTER);
      // Trapezium
      Tf.warpExec();
     
      // Move TopCenter
      LL.translate(FCntX - LTX -LW/2
                   + FRadE*(Math.cos(PI - LAngl0*(i)) +
                            Math.cos(PI - LAngl0*(i+1)) )/2
                   ,
                   FCntY - LTY
                   - FRadE*(Math.sin(PI - LAngl0*(i)) +
                            Math.sin(PI - LAngl0*(i+1)) )/2
                   );
     
      // Rotate
      LL.rotate(this.FAngle*(i + 0.5)/this.FPNb -90, AnchorPosition.TOPCENTER);
     
      // Merge with the result
      LL.move(FL, ElementPlacement.PLACEBEFORE);
      LL.merge();
     
      // Another piece
      LTX += LW;
    } // for
   
  } catch(ex){
    alert(ex.message);
  }
}
/*----------------------------------------------------------------------*
                        Execution
 *----------------------------------------------------------------------*/
var RulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;

var fan = new Fan();

if (fan.getParams())
  fan.build();

app.preferences.rulerUnits = RulerUnits;
/*----------------------------------------------------------------------*/

Partager cette page

Repost 0
Published by