Reputation: 23
Given is a rotated rectangle which is inscribed into another rectangle.
Both rectangles have their own coordinate systems.
In the inscribed & rotated rectangle there is a point P with the coordinates relative to this rectangle (red).
Wanted are the coordinates of this points relative to the outer rectangle (green):
EDIT: Given are also the width and height of both rectangles + the angle of the rotation
Image of an overview:
My attempts with a transform matrix (setRotate() & setScale()) failed and with Trigonometry I neither got it working.
How can I calculate the position of point P relative to the outer rectangle?
Thanks in advance!
Upvotes: 1
Views: 1670
Reputation: 23
EDIT2: A friend of mine pointed out a much more elegant solution (Thanks!). Here it is:
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint)
{
shadowSize.set((int) width + 1, (int) height + 1);
float x = offsetX - w / 2, y = offsetY - h / 2;
shadowTouchPoint.x = Math.round(x * c - y * s * sFac + w / 2 + (width - w) / 2);
shadowTouchPoint.y = Math.round(x * s * sFac + y * c + h / 2 + (height - h) / 2);
}
sFac is defined as:
float sFac = (float) Math.signum(rotationRad);
Alright, I managed to solve it with Trigonometry.
For anyone interested, here is the source code for my custom DragShadowBuilder which is used in Android to drag and drop rotated and scaled View objects:
public class TargetDragShadowBuilder extends View.DragShadowBuilder
{
ImageView view;
float offsetX, offsetY;
double rotationRad;
float w;
float h;
double s;
double c;
float width;
float height;
public TargetDragShadowBuilder(final ImageView view, float offsetX, float offsetY)
{
super(view);
this.view = view;
this.offsetX = offsetX * view.getScaleX();
this.offsetY = (int) (offsetY * view.getScaleY());
rotationRad = Math.toRadians(view.getRotation());
w = view.getWidth() * view.getScaleX();
h = (int) (view.getHeight() * view.getScaleY());
s = Math.abs(Math.sin(rotationRad));
c = Math.abs(Math.cos(rotationRad));
width = (int) (w * c + h * s);
height = (int) (w * s + h * c);
}
@Override
public void onDrawShadow(Canvas canvas)
{
canvas.scale(view.getScaleX(), view.getScaleY(), width / 2, height / 2);
canvas.rotate(view.getRotation(), width / 2, height / 2);
canvas.translate((width - view.getWidth()) / 2, (height - view.getHeight()) / 2);
super.onDrawShadow(canvas);
}
@Override
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint)
{
shadowSize.set((int) width + 1, (int) height + 1);
double x = offsetX, y = offsetY;
if(rotationRad < 0)
{
final double xC = offsetX / c;
x = xC + s * (offsetY - xC * s);
final double yC = offsetY / c;
y = yC + s * (w - offsetX - yC * s);
}
else if(rotationRad > 0)
{
final double xC = offsetX / c;
x = xC + s * (h - offsetY - xC * s);
final double yC = offsetY / c;
y = yC + s * (offsetX - yC * s);
}
shadowTouchPoint.x = (int) Math.round(x);
shadowTouchPoint.y = (int) Math.round(y);
}
}
It is valid for rotations from -90° to +90°.
If anyone has a cleaner or easier solution I am still interested in it.
EDIT: And here is the code for how I handle the drop of the View object.
private class TargetDragListener implements OnDragListener
{
@Override
public boolean onDrag(View v, DragEvent e)
{
switch(e.getAction())
{
case DragEvent.ACTION_DRAG_STARTED:
break;
case DragEvent.ACTION_DRAG_ENTERED:
break;
case DragEvent.ACTION_DRAG_EXITED:
break;
case DragEvent.ACTION_DROP:
if(e.getLocalState() instanceof TargetItem)
{
TargetItem target = (TargetItem) e.getLocalState();
dropTarget(target, e.getX(), e.getY());
}
break;
case DragEvent.ACTION_DRAG_ENDED:
((DragableItem) e.getLocalState()).setVisibility(View.VISIBLE);
default:
break;
}
return true;
}
}
private void dropTarget(TargetItem target, float x, float y)
{
target.setDragged(false);
target.setVisibility(View.VISIBLE);
target.bringToFront();
final float scaleX = target.getScaleX(), scaleY = target.getScaleY();
double rotationRad = Math.toRadians(target.getRotation());
final float w = target.getWidth() * scaleX;
final float h = target.getHeight() * scaleY;
float s = (float) Math.abs(Math.sin(rotationRad));
float c = (float) Math.abs(Math.cos(rotationRad));
float sFac = (float) -Math.signum(rotationRad);
target.offsetX *= scaleX;
target.offsetY *= scaleY;
x += -target.offsetX * c - target.offsetY * s * sFac;
y += target.offsetX * s * sFac - target.offsetY * c;
float[] pts = { x, y };
float centerX = x + c * w / 2f + sFac * s * h / 2f;
float centerY = y - sFac * s * w / 2f + c * h / 2f;
transform.setRotate(-target.getRotation(), centerX, centerY);
transform.mapPoints(pts);
target.setX(pts[0] + (w - target.getWidth()) / 2);
target.setY(pts[1] + (h - target.getHeight()) / 2);
}
Upvotes: 0