缩放和缩放后屏幕坐标和图像坐标之间的转换

时间:2016-07-08 14:55:53

标签: java translation scaling zooming

我有一个课程,负责管理从屏幕坐标到图像坐标的转换 但是,我有一个"关闭一个错误"。

以下给出318,198,而不是319,199:

@Test
public void test6rightScreenCornerToImageCoOrdAfterZoomingAndScaling() {
    PointTranslation pointTranslation = new PointTranslation();
    pointTranslation.setOriginalSize(0, 0, 320, 200); // original image
    pointTranslation.zoomIn(9, 9, 310, 190); // zoomed image starting at 9,9
    pointTranslation.scale(0, 0, 800, 800);

    Point translatedPoint = pointTranslation.transformPoint(799,799);
    System.out.println(testName.getMethodName() + " : " + translatedPoint.toString());
    assertTrue(translatedPoint.x == 319);
    assertTrue(translatedPoint.y == 199);
}

=============================================== ==============

完整清单:

package gtx;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.internal.gdip.PointF;
import org.eclipse.swt.internal.gdip.RectF;

@SuppressWarnings("restriction")
public class PointTranslation {
RectF originalSize = null;
RectF currentSize = null;
RectF scaledSize = null;

public void setOriginalSize(int originX, int originY, int width, int height) {
    originalSize = getRectangle(originX, originY, width, height);
    currentSize = originalSize;
}

public void zoomIn(int originX, int originY, int width, int height) {
    // System.out.println("addTranslation: " + originX + " " + originY + " "
    // + width + " " + height);
    currentSize = getRectangle(originX, originY, width, height);
}

public void scale(int originX, int originY, int width, int height) {
    // System.out.println("addTranslation: " + originX + " " + originY + " "
    // + width + " " + height);
    scaledSize = getRectangle(originX, originY, width, height);
}

public boolean isPointWithinBounds(Point point) {
    return isPointWithinBounds(point.x, point.y);
}

public boolean isPointWithinBounds(int xPos, int yPos) {
    boolean ret = false;

    if (scaledSize != null) {
        RectF sourceRec = scaledSize;
        int xBounds = (int) (sourceRec.Width + sourceRec.X);
        int yBounds = (int) (sourceRec.Height + sourceRec.Y);
        ret = (xPos < xBounds) && (yPos < yBounds) && (xPos > sourceRec.X) && (yPos > sourceRec.Y);
    }

    return ret;
}

public Point transformPoint(Point point) {
    return transformPoint(point.x, point.y);
}

public Point transformPoint(int xPos, int yPos) {
    Point sourcePoint = new Point((int) xPos, (int) yPos);
    Point retPoint = sourcePoint;

    if (this.scaledSize != null) {
        retPoint = transformPoint(this.scaledSize, this.currentSize, sourcePoint);
    }
    return retPoint;
}

/*
 * Rectangle 1 has (x1, y1) origin and (w1, h1) for width and height, and
 * Rectangle 2 has (x2, y2) origin and (w2, h2) for width and height, then
 * 
 * Given point (x, y) in terms of Rectangle 1 co-ords, to convert it to
 * Rectangle 2 co-ords: xNew = ((x-x1)/w1)*w2 + x2; yNew = ((y-y1)/h1)*h2 +
 * y2;
 */
private Point transformPoint(RectF source, RectF destination, Point intPoint) {
    PointF point = new PointF();
    point.X = intPoint.x;
    point.Y = intPoint.y;
    return transformPoint(source, destination, point);
}

private Point transformPoint(RectF source, RectF destination, PointF point) {
    return new Point((int) ((((point.X - source.X) / source.Width) * destination.Width + destination.X)),
            (int) ((((point.Y - source.Y) / source.Height) * destination.Height + destination.Y)));
}

private RectF getRectangle(int x, int y, int width, int height) {
    RectF rect = new RectF();
    rect.X = x;
    rect.Y = y;
    rect.Height = height;
    rect.Width = width;
    return rect;
}

private PointF getPoint(int x, int y) {
    PointF retPoint = new PointF();
    retPoint.X = x;
    retPoint.Y = y;
    return retPoint;
}

public void reset() {
    this.originalSize = null;
    this.currentSize = null;
    this.scaledSize = null;
}
}

更新

我的问题似乎与四舍五入有关。奇怪的是,对于一些测试用例我需要Round Up来获得正确的Point,有时我需要向上舍入。我遗漏了像缩放因子之类的东西。有关如何正确翻译两个矩形之间的任何建议吗?

更新2:

我尝试了以下方法,但仍然没有快乐:

    private Point transformPoint(RectF source, RectF destination, PointF point) {   
        float xPercent = normalize(point.X,source.X,source.Width);
        float destX = xPercent*(Math.abs(destination.Width - destination.X)) + destination.X;

        float yPercent = normalize(point.Y,source.Y,source.Height);
        float destY = yPercent*(Math.abs(destination.Height - destination.Y)) + destination.Y;

        System.out.println("Float x,y: " + destX + ", " + destY);
        System.out.println("Ceil Float x,y: " + Math.floor(destX) + ", " + Math.floor(destY) );

        return new Point((int)Math.floor(destX), (int)Math.floor(destY));
    }

    private float normalize(float value, float min, float max) {
        return Math.abs((value - min) / (max - min));
    }

3 个答案:

答案 0 :(得分:2)

In running the test case and stepping through the code, my debugging shows the following substitutions...

transformPoint(RectF source, RectF destination, PointF point) {
    return new Point (
        (int) (((( 799 - 0 ) / 800 ) * 310 ) + 9 )),
        (int) (((( 799 - 0 ) / 800 ) * 190 ) + 9 ))
    );
}

The first half of the equation returns 318.6125. The second half of the equation returns 198.7625.

You need to either

  1. modify the equation so that the int transformation truncates to the desired value (such as + 1 at the end)
  2. round up before conversion to int
  3. live with the result

And as Mathew noted, multiple translations in a row distort and magnify the problem, much like averaging averages.

答案 1 :(得分:1)

如Reenactor所述,您需要在转换为int之前对点进行舍入:

private Point transformPoint(RectF source, RectF destination, PointF point) {
   final int ptx = Math.round((((point.X - source.X) / source.Width) * destination.Width + destination.X));
   final int pty = Math.round((((point.Y - source.Y) / source.Height) * destination.Height + destination.Y));
   return new Point(ptx, pty);    
}

答案 2 :(得分:0)

Is it possible you are converting the interim results after each translation to integer points?

Remember, org.eclipse.swt.graphics.Point stores x and y as int, so any fractions in your calculations are dropped.

That is:

1) First translation:

x1 = ((799 - 0) / 800) * 301 + 9 = 309.62375
y1 = ((799 - 0) / 800) * 181 + 9 = 189.77375

If you were storing those in a Point object before going into the next translation, they would be truncated to (309, 189) and you would get

2) Second translation

x2 = ((309 - 9) / 301) * 320 + 0 = 318.93...
y2 = ((189 - 9) / 181) * 200 + 0 = 198.89...

Which would, in turn, be truncated to (318, 198).