我正在尝试找到在变换锚点之间转换的形状的边界框。目标是封装矩形的动画,使得矩形都不在渲染区域之外。我正在使用这三个java类:
以下是定义锚类的方法:
public class Anchor {
public double deltaX = 0, deltaY = 0;
public double scaleX = 1, scaleY = 1;
public double degrees = 0;
}
在我的实际应用程序中,我有一组附加到时间的锚点,随着动画的进行,形状在两个相邻的“锚点”之间进行插值。但对于这个问题,我们只需要担心2个锚点。
这是一个原始的,主要是我想要的工作实现,但我觉得必须有一个更准确,有效的数学方法。由于必须采取许多措施,这是懒散的。我迭代的步骤越多,显然效果越好。
public static Rectangle getBounds(Anchor leftAnchor, Anchor rightAnchor, int shapeWidth, int shapeHeight) {
Rectangle baseShape = new Rectangle(0, 0, shapeWidth, shapeHeight); // the shape we draw in the animation
Rectangle globalBounds = null, localBounds; // global bounds is the bounds of the whole animation
for(double time = 0;time <= 1;time += 0.001) { // interpolate from one anchor to the next (1000 steps)
// Create the transformation and find the finds of the resultant shape
AffineTransform transformation = getInterpolatedTransformation(leftAnchor, rightAnchor, shapeWidth, shapeHeight, time);
localBounds = transformation.createTransformedShape(baseShape).getBounds();
if(globalBounds == null) // if it's the first step, create the initial bounds
globalBounds = localBounds;
else // otherwise continue adding bounds
globalBounds.add(localBounds);
}
return globalBounds; // return the global bounds
}
public static AffineTransform getInterpolatedTransformation(Anchor left, Anchor right, int width, int height, double time) {
// get the interpolated values from the two anchors
double deltaX = linearInterpolation(left.deltaX, right.deltaX, time);
double deltaY = linearInterpolation(left.deltaX, right.deltaX, time);
double scaleX = linearInterpolation(left.scaleX, right.scaleX, time);
double scaleY = linearInterpolation(left.scaleY, right.scaleY, time);
double degrees = linearInterpolation(left.degrees, right.degrees, time);
// Create the AffineTransformation based on the two interpolated acnhors
AffineTransform transform = new AffineTransform();
transform.translate(deltaX, deltaY);
transform.scale(scaleX, scaleY);
transform.rotate(Math.toRadians(degrees),
scaleX*width/2.0+deltaX,
scaleY*height/2.0+deltaY);
return transform; // return it
}
是否有一种有效的方法来查找包含在两个仿射变换之间插值的矩形的每个可能变换的边界框。
最初我的动画锚没有定义学位。我可以通过简单地添加左锚点的AffineTransform和右锚点的AffineTransform的结果边界框来找到边界框。但是,通过旋转,动画矩形的点可以逃脱这些更容易找到的边界框。
PS:对你们C程序员,我为我的代码中的冗长而道歉......答案 0 :(得分:0)
从您的迭代解决方案中,您可以得出一步式解决方案(它可能会高估边界框,因为如果选择了最极端的旋转值,这将计算可能的最大面积):
public static Rectangle getBounds(Anchor leftAnchor, Anchor rightAnchor, int shapeWidth, int shapeHeight) {
double size = Math.sqrt((shapeWidth * shapeWidth) + (shapeHeight * shapeHeight));
// find the maximum scale
double maxScale = Math.max(leftAnchor.scaleX, leftAnchor.scaleY);
maxScale = Math.max(rightAnchor.scaleX);
maxScale = Math.max(rightAnchor.scaleY);
int maxSize = (int) Math.ceil(maxScale * size);
return new Rectangle(0, 0, maxSize, maxSize);
}
这可以使用原始形状不能覆盖原始矩形对角线所描述的圆圈之外的任何东西 - 无论你如何旋转它,它都不会离开圆圈。通过查找最大应用比例,可以调整圆以进行缩放。
答案 1 :(得分:0)
通过凸度,矩形的边界框与其四个边角的边界框相同。因此,您可以在单个角落的边界框上进行推理,然后&#34;合并&#34;四个角落的结果。
在角上应用变换时,同时更改中心,大小和方向。换句话说,轨迹是复杂的曲线,一个变形的阿基米德螺旋。
一种简单的方法是通过计算给定时间间隔内的多个位置来找到近似边界框并保持极值(理想情况下,对于每帧,给定帧速率)。
严谨的方法是通过分析找到曲线的极值。
看一下转换后的横坐标,我们得到X = X0 + t.DX + (Sx + t.DSx) cos(A+t.Da) - (Sy + t.DSy) sin(A+t.Da)
。
导出一次并找到衍生物的零:0 = DX + DSx cos(A+t.Da) - Da.(Sx + t.DSx) sin(A+t.Da) - DSy sin(A+t.Da) - Da.(Sy+t.DSy) cos(A+t.Da)
。
遗憾的是,这是一个超越的等式,因为它具有t.sin(t)
形式的术语,因此无法使用分析解决方案,并且您必须采用数值方法。
我建议在计算成本和准确度之间进行折衷:计算合理数量的帧的角位置,并保持Xmin, Xmax, Ymin, Ymax
的极值。
如果您观察到其中一个边界上的行为发生了变化,请说Xmin
减少然后增加,您可以使用较小的步骤来优化搜索(直到您进入单个帧的步骤);或者,使用最小值附近的三个值并执行抛物线插值。
更新:
具有恒定比例的情况可以通过分析处理。 (要么比例非常稳定,要么通过保持比例的最大值来保持安全。)
例如,横坐标的等式具有类似A + B.t + C.cos(t) + D.sin(t)
的形式。导出,0 = B - C.sin(t) + D.cos(t)
。这是众所周知的线性三角方程。您可以在时间间隔中找到它的根。需要仔细讨论。
答案 2 :(得分:0)
我已将此Stack OVerflow发送给朋友以获取一些建议。他当时和那里没有时间去看它,但他确实给我发了这个:The X & Y Problem。有可能解决这个数学问题,但我找到了一个更好的解决方案。
我知道动画将会发生多少帧(生成GIF),因此我只需预先计算每个帧即可。转换 - 找到所有帧的边界框,然后正确生成输出图像。