最近写蒙版工具的时候,需要将蒙版中心点限制在视频可视区域内,可是视频又可以旋转缩放和平移,因此就转换成判断中心点在任意矩形内,研究了几种方案,仅供参考。
通过Path构建Region判断
首先根据提供的矩形的四个顶点生成Path:
public void setBounds(List<PointF> points) {
mPath.moveTo(points.get(0).x, points.get(0).y);
mPath.lineTo(points.get(1).x, points.get(1).y);
mPath.lineTo(points.get(2).x, points.get(2).y);
mPath.lineTo(points.get(3).x, points.get(3).y);
mPath.close();
}
然后根据Path构建对应的Region对象:
mRegion = new Region();
RectF rect = new RectF();
mPath.computeBounds(rect, true);
this.mRegion.setPath(mPath, new Region((int) rect.left, (int) rect.top, (int) rect.right, (int) rect.bottom));
最后判断点是否包含在区域中:
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mTouchX = event.getX();
mTouchY = event.getY();
} else if (action == MotionEvent.ACTION_MOVE) {
float x = event.getX();
float y = event.getY();
float dx = x - mTouchX;
float dy = y - mTouchY;
mTouchX = x;
mTouchY = y;
if (mRegion.contains((int) (mCenterPoint.x + dx), (int) (mCenterPoint.y + dy))) {
mCenterPoint.offset(dx, dy);
invalidate();
}
}
return true;
}
反旋转
将点反向旋转矩形的旋转角度,然后与未旋转的矩形进行判断。
下面的方法是一个点绕另一个点旋转以后得到新的点:
private PointF rotateByAngle(PointF point, PointF center, float angle) {
PointF rotatedPoint = new PointF();
double radians = Math.toRadians(angle);
double cos = Math.cos(radians);
double sin = Math.sin(radians);
rotatedPoint.x = (float) ((point.x - center.x) * cos - (point.y - center.y) * sin + center.x);
rotatedPoint.y = (float) ((point.x - center.x) * sin + (point.y - center.y) * cos + center.y);
return rotatedPoint;
}
首先将矩形的四个点进行反转:
public void setBounds(List<PointF> points, float rotation) {
mRotation = rotation;
mPivot.set((points.get(0).x + points.get(2).x) / 2f, (points.get(0).y + points.get(2).y) / 2f);
mCenterPoint.set(mPivot);
PointF leftTop = rotateByAngle(points.get(0), mPivot, -rotation);
PointF leftBottom = rotateByAngle(points.get(1), mPivot, -rotation);
PointF rightBottom = rotateByAngle(points.get(2), mPivot, -rotation);
PointF rightTop = rotateByAngle(points.get(3), mPivot, -rotation);
mRotatedRect = new RectF();
mRotatedRect.left = (leftTop.x + leftBottom.x) / 2f;
mRotatedRect.top = (leftTop.y + rightTop.y) / 2f;
mRotatedRect.right = (rightTop.x + rightBottom.x) / 2f;
mRotatedRect.bottom = (leftBottom.y + rightBottom.y) / 2f;
mBoundSize = new SizeF(rightBottom.x - leftTop.x, rightBottom.y - leftTop.y);
}
然后判断点是否在区域内:
@Override
public boolean onTouchEvent(MotionEvent event) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mTouchX = event.getX();
mTouchY = event.getY();
} else if (action == MotionEvent.ACTION_MOVE) {
float x = event.getX();
float y = event.getY();
float dx = x - mTouchX;
float dy = y - mTouchY;
mTouchX = x;
mTouchY = y;
mCenterPoint.offset(dx, dy);
PointF rc = rotateByAngle(mCenterPoint, mPivot, -mRotation);
if (rc.x < mRotatedRect.left) {
rc.x = mRotatedRect.left;
} else if (rc.x > mRotatedRect.right) {
rc.x = mRotatedRect.right;
}
if (rc.y < mRotatedRect.top) {
rc.y = mRotatedRect.top;
} else if (rc.y > mRotatedRect.bottom) {
rc.y = mRotatedRect.bottom;
}
PointF point = rotateByAngle(rc, mPivot, mRotation);
mCenterPoint.set(point);
invalidate();
}
return true;
}
向量
只需要判断该点是否在四条边之间就行。利用向量叉乘的方向性,来判断两叉乘向量是否同向,效率更高。
判断两叉乘向量是否同向可以用点乘实现,如果点乘大于0,则两向量同向,如果点乘小于0,则两向量反向,如果点乘等于0,则有一叉乘向量为0向量。
( p 1 p 2 ⃗ \vec{p1p2} p1p2 X p 1 p ⃗ \vec{p1p} p1p) * ( p 3 p 4 ⃗ \vec{p3p4} p3p4 X p 3 p ⃗ \vec{p3p} p3p) >= 0
说明p在p1p2,p3p4中间夹着, 同理计算另两边
( p 1 p 2 ⃗ \vec{p1p2} p1p2 X p 1 p ⃗ \vec{p1p} p1p) * ( p 3 p 4 ⃗ \vec{p3p4} p3p4 X p 3 p ⃗ \vec{p3p} p3p) >= 0 && ( p 2 p 3 ⃗ \vec{p2p3} p2p3 X p 2 p ⃗ \vec{p2p} p2p) * ( p 4 p 1 ⃗ \vec{p4p1} p4p1 X p 4 p ⃗ \vec{p4p} p4p) >= 0
public class Point {
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
}
public static double getCross(Point p, Point p1, Point p2) {
return (p2.x - p1.x) * (p.y - p1.y) - (p.x - p1.x) * (p2.y - p1.y);
}
public static boolean isPointInMatrix(Point p, Point p1, Point p2, Point p3, Point p4) {
boolean h = getCross(p, p1, p2) * getCross(p, p3, p4) >= 0;
boolean v = getCross(p, p2, p3) * getCross(p, p4, p1) >= 0;
return h && v;
}
感谢大家的支持,如有错误请指正,如需转载请标明原文出处!