Skip to main content

[JAVA2D] AffineTransform question

3 replies [Last post]
Anonymous

Hello,

I have a question relating to using the AffineTransform's preconcatenate
method to increase and decrease scaling.

------------------------------------------------
Background:

I have a panel that presents a buffered image to the user. The user can
increase the scale (zoom in) by dragging
the mouse up in the panel.

The user can decrease the scale by dragging down.

I want to be able to set a min and a max zoom limit.

What I do on mouse drag is to convert the mouse event's delta-y value to
a zoom/scale increment.

Then, if I have not yet reached the min/max scale values, I create a new
AffineTransform, based
on the delta-Y val of the mouse event, like this:

double zoomIncrement = // some value based on mouse delta-Y

AffineTransform scaleIncrementAtx =
AffineTransform.getScaleInstance(zoomIncrement, zoomIncrement);

Then I preconcatenate this atx with the existing atx, like this:

currentTransform.preConcatenate(scaleIncrementAtx);

So my current logic is like this:

if(currentScaleY < SCALE_MAX)
{
currentTransform.preConcatenate(scaleIncrementAtx);
}

This works very well. I get nice smooth zoom in/out as the user is
dragging the mouse.

However, the problem is that when the user gets close to either the min
or max limits,
the last preconcatenation can put the above the max or below the min.

This is because I can't predict what the scaleY value will be
following the preConcatenation operation. I can only test it
after the fact.

So my logic should be like this

if(currentScaleY < SCALE_MAX)
{
AffineTransform tempAtx = new AffineTransform(currentTransform);
tempAtx.preConcatenate(scaleIncrementAtx);

if(getScaleYElement(getScaleYElement(tempAtx)) < SCALE_MAX)
{
currentTransform.preConcatenate(scaleIncrementAtx);
}
else // calc the increment that will result in max scale
value
{
// ?? how to calc zoomIncrement in this case ??
zoomIncrement = ??

AffineTransform adjustedIncrementAtx =
AffineTransform.getScaleInstance(zoomIncrement, zoomIncrement);

currentTransform.preConcatenate(adjustedIncrementAtx);
}
}

-------------------------------------------------
Question:

Is there a way to calculate the zoomIncrement value in:

AffineTransform scaleIncrementAtx =
AffineTransform.getScaleInstance(zoomIncrement, zoomIncrement);

Such that we can control the value that will be returned by
getScaleYElement
following the preconcatenation as shown above and in the snippet below?

For example:
------------------ start snippet -----------------

static final double SCALE_MAX = 10;

double currentScaleY = getScaleYElement(currentTransform);

// ?? how to code calculateScaleIncrement(double, double) ??
double scaleIncrement = calculateScaleIncrement(currentScaleY,
SCALE_MAX);

AffineTransform scaleIncrementAtx =
AffineTransform.getScaleInstance(scaleIncrement, scaleIncrement);

currentTransform.preConcatenate(scaleIncrementAtx);

// !! now getScaleYElement() should return a value very close to
SCALE_MAX
double newScaleY = getScaleYElement(currentTransform);

------------------ end snippet ---------------------

---------------------------------
Other methods referred to above:

/**
* Calculate and return the y scale factor for the specified
AffineTransform.
*
* @param atx the AffineTransform whose Y scale element we want.
*
* @return the Y scale value.
*/
public static double getScaleYElement(final AffineTransform atx)
{
double scaleYElement = 0;

final double scaleY = atx.getScaleY();
final double shearY = atx.getShearY();

scaleYElement = Math.sqrt(Math.pow(scaleY, 2) + Math.pow(shearY,
2));

return scaleYElement;
}

/**
* Concatenate the user's current action (pan, zoom, rotate) to the
current
* image transform state and repaint the image.
* Note use of preconcatenate here: apply the user's atx *after*
applying
* the existing mouseBaseTransform.
* @param atx the 'delta' affine transform, i.e. pan or scale
'increment'.
*/
protected void updateImageTransform(final AffineTransform atx) //
see FusionPanel
{
userTransform = new AffineTransform(mouseBaseTransform);
userTransform.preConcatenate(atx);
repaint();
}

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Jim Graham

Another way of managing this is to keep various decomposed values around
and then compose the matrix only at render time.

For example, if you want to independently control scaling and rotation and
set limits on them:

On mouse motion:

if (scaling motion) {
scale = scale +*/- motion delta;
if (scale out of range) {
set scale to range limit;
}
} else {
// rotation motion
rotation = rotation +*/- motion delta;
if (rotation out of range) {
set rotation to range limit;
}
}

In paint routine:

g2d.scale(scale, scale);
g2d.rotate(rotation);
// Or similar operations on a fresh AffineTransform object

That makes it easier to manage limits on the relative constituent
transformation components without having to worry about the mathematics of
how they got combined into a single transformation matrix...

...jim

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Ted Hill

-----Original Message-----
From: Ted Hill
Sent: Thursday, June 09, 2005 11:38 AM
To: 'Alexey Ushakov'
Subject: RE: [JAVA2D] AffineTransform question

Hi Alexey,

The value that I am looking for is the double value to pass to

AffineTransform scaleAtx = AffineTransform.getScaleInstance(double,
double)

So that when scaleAtx is preconcatenated with myCurrentTransform, we can
precisely control the resulting value

from passing myCurrentTransform to this method:

public static double getScaleYElement(final AffineTransform atx)
{
double scaleYElement = 0;

final double scaleY = atx.getScaleY();
final double shearY = atx.getShearY();

scaleYElement = Math.sqrt(Math.pow(scaleY, 2) + Math.pow(shearY,
2));

return scaleYElement;
}

-----Original Message-----
From: Discussion list for Java 2D API
[mailto:JAVA2D-INTEREST@JAVA.SUN.COM] On Behalf Of Alexey Ushakov
Sent: Thursday, June 09, 2005 10:56 AM
To: JAVA2D-INTEREST@JAVA.SUN.COM
Subject: Re: [JAVA2D] AffineTransform question

Hello Ted,

Applying scaling means simply multiplication diagonal coefficients of
the transform matrix to the specified scaling values.

> So my logic should be like this
>
> if(currentScaleY < SCALE_MAX)
> {
> AffineTransform tempAtx = new
AffineTransform(currentTransform);
> tempAtx.preConcatenate(scaleIncrementAtx);
>
> if(getScaleYElement(getScaleYElement(tempAtx)) < SCALE_MAX)
> {
> currentTransform.preConcatenate(scaleIncrementAtx);
> }
> else // calc the increment that will result in max scale
> value
> {
> // ?? how to calc zoomIncrement in this case ??
> zoomIncrement = ??

So, if I understood your task correctly zoomIncrement should be
SCALE_MAX/currentScaleY.

>
> AffineTransform adjustedIncrementAtx =
> AffineTransform.getScaleInstance(zoomIncrement, zoomIncrement);
>
> currentTransform.preConcatenate(adjustedIncrementAtx);
> }
> }

BTW, you could use following check

currentScaleY + scaleIncrementAtx < SCALE_MAX/currentScaleY

instead of

> if(getScaleYElement(getScaleYElement(tempAtx)) < SCALE_MAX)
> {

Best Regards,
Alexey

========================================================================
===
To unsubscribe, send email to listserv@java.sun.com and include in the
body of the message "signoff JAVA2D-INTEREST". For general help, send
email to listserv@java.sun.com and include in the body of the message
"help".

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".

Alexey Ushakov

Hello Ted,

Applying scaling means simply multiplication diagonal coefficients of
the transform matrix to the specified scaling values.

> So my logic should be like this
>
> if(currentScaleY < SCALE_MAX)
> {
> AffineTransform tempAtx = new AffineTransform(currentTransform);
> tempAtx.preConcatenate(scaleIncrementAtx);
>
> if(getScaleYElement(getScaleYElement(tempAtx)) < SCALE_MAX)
> {
> currentTransform.preConcatenate(scaleIncrementAtx);
> }
> else // calc the increment that will result in max scale
> value
> {
> // ?? how to calc zoomIncrement in this case ??
> zoomIncrement = ??

So, if I understood your task correctly zoomIncrement should be
SCALE_MAX/currentScaleY.

>
> AffineTransform adjustedIncrementAtx =
> AffineTransform.getScaleInstance(zoomIncrement, zoomIncrement);
>
> currentTransform.preConcatenate(adjustedIncrementAtx);
> }
> }

BTW, you could use following check

currentScaleY + scaleIncrementAtx < SCALE_MAX/currentScaleY

instead of

> if(getScaleYElement(getScaleYElement(tempAtx)) < SCALE_MAX)
> {

Best Regards,
Alexey

===========================================================================
To unsubscribe, send email to listserv@java.sun.com and include in the body
of the message "signoff JAVA2D-INTEREST". For general help, send email to
listserv@java.sun.com and include in the body of the message "help".