Skip to main content
Participating Frequently
February 25, 2018
Answered

Stretch image in one dimension

  • February 25, 2018
  • 2 replies
  • 6594 views

Hi, I need to create a slice of an image and then stretch it either horizontally or vertically by a few millimetres. This would be the same as holding down the Command or Control key while dragging on a handle. Any help would be much appreciated.

Stuart

This topic has been closed for replies.
Correct answer Peter Kahrel

Just set the horizontalScale of the image's container (a rectangle).

P.

2 replies

Marc Autret
Legend
March 3, 2018

Hi all,

Not sure I clearly understood the goal, but it was really exciting to play with the idea.

Here is a first try, and first draft of my AutoFeedSlug script.

function nonZero(/*num*/x,  e)

//----------------------------------------------------------

// EPSILON-aware nonzero test.

{

    e = Math.pow(2,-51);

    return ( 0 > x ? -x : x ) > e;

};

function autoFeedSlug(/*?Image[0]*/img,  pge,rec,side,dpg,snap,edge,a,i,t,s,o)

//----------------------------------------------------------

// Given an Image that should extend up to the slug but has not enough material,

// try to feed the missing area by fitting and rescaling a slice of the picture.

// [REM] The code autodetects the edge of interest, provided the image presents any

// border close to a page edge. (Also, make sure the container has no stroke.)

{

    // Settings and constants.

    // ---

    const SNAP = .05;            // 5% of the lowest page dim.

    const FIT_FACTOR = 2;        // Should be 2 (IMHO) to optimize the joint.

    const PGE=0, REC=1, IMG=2;   // Simple enum.

    // ---

    const CS_SPREAD = +CoordinateSpaces.SPREAD_COORDINATES;

    const BB_GEO = +BoundingBoxLimits.GEOMETRIC_PATH_BOUNDS;

    const RM_MULT = +ResizeMethods.MULTIPLYING_CURRENT_DIMENSIONS_BY;

    const RC_KEEP = +ResizeConstraints.KEEP_CURRENT_VALUE;

    const PS_LEFT = +PageSideOptions.LEFT_HAND;

    const PS_RIGHT = +PageSideOptions.RIGHT_HAND;

    // Checkpoints.

    // ---

    if( FIT_FACTOR <= 1 )

    {

        alert("The FIT_FACTOR setting must be greater than 1.");

    }

    while( (!img) || (1!=img.length) || !((img=img[0]) instanceof Image) )

    {

        if( img.hasOwnProperty('images') && (img=img.images).length && ((img=img[0]) instanceof Image) ) break;

        alert("Select an Image.");

        return;

    }

    if( !(pge=img.properties.parentPage) )

    {

        alert("The Image must be on a page.");

        return;

    }

    if( !((rec=img.parent) instanceof Rectangle) || (0 < rec.properties.strokeWeight) )

    {

        alert("The Image must belong to a rectangular box having no stroke.");

        return;

    }

    // Metrics.

    // => `a` :: [ pageTLBR, recTLBR, imgTLBR ]

    // ---

    for( a=[pge,rec,img], i=PGE ; i <= IMG ; ++i )

    {

        t = a.transformValuesOf(CS_SPREAD)[0].matrixValues;

        if( nonZero(t[0]*t[1]) || nonZero(t[2]*t[3]) )

        {

            alert("No weird rotation should be applied to the "+a.constructor.name+".");

            return;

        }

        a = (t=a.properties[i===PGE?'bounds':'geometricBounds']);

        a.width = t[3]-t[1];

        a.height = t[2]-t[0];

    }

    // Page side.

    // => `side` :: -1_left  |  0_single  |  +1_right

    // ---

    side = +(0 < pge.documentOffset && pge.side);

    side = (side==PS_RIGHT)-(side==PS_LEFT);

    // Bleed.

    // => `dpg` :: [-top,-left, +bot,+right]

    // ---

    t = resolve(pge.toSpecifier().replace(/\/\/.+/,''));

    if( !(t instanceof Document) ) throw "Unable to find the parent document."

    t = t.documentPreferences.properties;

    dpg = t.documentBleedUniformSize ?

        ( (t=t.documentBleedTopOffset), [-t,-t,t,t] ) :

        [

            -t.documentBleedTopOffset,

            -t.documentBleedInsideOrLeftOffset,

            t.documentBleedBottomOffset,

            t.documentBleedOutsideOrRightOffset

        ];

    // Identify the most relevant edge.

    // => edge :: { 0:TLBR-index, dist:num }, -BLEED < dist < SNAP

    // ---

    snap = SNAP*Math.min(a[PGE].width, a[PGE].height);

    for( (edge=[i=-1]).dist=1/0 ; ++i < 4 ; )

    {

        // i :: 0_top ; 1_left ; 2_bot ; 3_right

        // Calculate the algebraic distance.

        // ---

        s = 2 > i ? 1 : -1;

        t = s*(Math[0<s?'max':'min'](a[REC],a[IMG])-a[PGE]);

        // Skip this edge if its distance to page > snap zone.

        // ---

        if( t > snap ) continue;

       

        // Skip this edge if it already reaches the bleed.

        // ---

        if( t <= s*dpg ) continue;

        // Skip this edge if it regards the spine.

        // ---

        if( side && side==2-i ) continue;

        t < edge.dist && (edge[0]=i,edge.dist=t);

    }

   

    if( 0 > (i=edge[0]) )

    {

        alert("Unable to find the relevant edge. Make sure the image is in the snap zone.");

        return;

    }

   

    // ---

    // Process.

    // ---

   

    // First, duplicate the rec applying the proper shift.

    // ---

    edge[0] = edge.dist*(2 > i ? -1 : 1) + dpg;

    edge[(1&i)?'push':'unshift'](0);

    t = rec.duplicate(void 0,edge);

   

    // Make sure autofit is off.

    // ---

    t.hasOwnProperty('frameFittingOptions') && t.clearFrameFittingOptions();

   

    // Adjust the geometric bounds.

    // ---

    o = t.geometricBounds;

    o = a[PGE]+dpg;

    o[2^i] = o-FIT_FACTOR*edge[1-(1&i)];

    t.geometricBounds = o;

   

    // Resize the inner image to fit.

    // ---

    img = t.images[0].getElements()[0];

   

    // Ok this step is a bit hacky, but basically

    // we want an anchor point in the form [u,v].

    // i==0  -->  [u,v]==[.5,0]  i.e center-TOP

    // i==1  -->  [u,v]==[0,.5]  i.e LEFT-center

    // i==2  -->  [u,v]==[.5,1]  i.e center-BOTTOM

    // i==3  -->  [u,v]==[1,.5]  i.e RIGHT-center

    // ---

    // It is important to resolve the location of

    // of that anchor relative to the container `t`,

    // not relative to the image bounding box, for

    // the image area may exceed the area of its

    // container! We must use the correct origin

    // in the resize transformation.

    // ---

    (ap=[.5])[(1&i)?'unshift':'push'](+(i>1));

    ap = t.resolve([ap,BB_GEO,CS_SPREAD],CS_SPREAD)[0];

    // Why do I use `resize()`? Because I'm not definitely

    // sure that 90 or 180 angles and/or reflections

    // applied to either the frame or the image couldn't

    // make fail naive methods. Resizing is reliable.

    // ---

    (a=[RC_KEEP])[(1&i)?'unshift':'push'](FIT_FACTOR);

    img.resize(

        CS_SPREAD,        // In-spread bounding box of the Image.

        [ap,CS_SPREAD],   // Origin as computed above (in the spread space)

        RM_MULT,          // Multiply the size by FIT_FACTOR...

        a);               // ...along the desired dimension.

    app.select(null);

};

app.doScript("autoFeedSlug(app.properties.selection)",

    ScriptLanguage.JAVASCRIPT,void 0,UndoModes.ENTIRE_SCRIPT,"AutoFeedSlug");

And here is a quick demo (not sure the link works):

AutoFeedSlug - YouTube

Best,

Marc

Marc Autret
Legend
March 3, 2018

As you have noted I constantly confuse the words 'bleed' and 'slug.'

So FeedBleed would probably be a better name for that script…

Community Expert
March 5, 2018

Hi Stuart,

(…) I haven't been able to figure out how to stretch the image at the same time.

The end of the code treats this part of the question:

// . . .

img.resize(CS_SPREAD, origin, RM_MULT, a);

I prefer using resize over other approaches because that's a very flexible method. In particular, we have to be very careful about the origin of the transformation. Although resizing is applied to the image itself, the origin is calculated with respect to the block edge at this specific moment, keeping in mind that the image bounds may exceed the bounds of the frame. So we cannot blindly perform the transformation from the image edge coordinates, we need to find the exact origin inside the area to get a clean joint. The below animation illustrates this point:

@+

Marc


Hi Marc,

thank you for that first draft!

If you are willing to go on with this I'd say: First check if an image could perhaps reach to fill the bleed without transformation.

That would require to move the path points of the container frame to the edge of the bleed area.

But perhaps this is better up to the user to decide. The script is running on a selection so the user should be responsible to first check if a "natural" bleed is possible from the start before running any bleed filling script.

Thanks,
Uwe

Peter Kahrel
Community Expert
Peter KahrelCommunity ExpertCorrect answer
Community Expert
February 26, 2018

Just set the horizontalScale of the image's container (a rectangle).

P.

stuarth64Author
Participating Frequently
February 26, 2018

Thank you Peter.

stuarth64Author
Participating Frequently
March 3, 2018

Hi Peter,

I finally got round to trying this but it's not the solution I need. What I'm trying to do is create missing bleed by duplicating a thin slice of the image, just in from each edge and then have the script replicate the action of dragging the centre outer handle out by a couple of millimetres while holding down the Command key.

I would really appreciate any help you or anyone else can offer.