Java 形状从一个仿射变换到另一个仿射变换的最大边界框 总结

Java 形状从一个仿射变换到另一个仿射变换的最大边界框 总结,java,math,image-processing,geometry,interpolation,Java,Math,Image Processing,Geometry,Interpolation,我试图找到在到变换锚之间变换的形状的边界框。目标是封装矩形的动画,以使任何矩形都不会超出渲染区域。我正在使用以下三个java类: java.awt.Rectangle java.awt.geom.AffineTransform 锚点(自定义类) 目前我所拥有的 以下是锚类的定义方式: public class Anchor { public double deltaX = 0, deltaY = 0; public double scaleX = 1, scaleY = 1; p

我试图找到在到变换锚之间变换的形状的边界框。目标是封装矩形的动画,以使任何矩形都不会超出渲染区域。我正在使用以下三个java类:

  • java.awt.Rectangle
  • java.awt.geom.AffineTransform
  • 锚点(自定义类)
目前我所拥有的 以下是锚类的定义方式:

public class Anchor {
  public double deltaX = 0, deltaY = 0;
  public double scaleX = 1, scaleY = 1;
  public double degrees = 0;
}
在我的实际应用程序中,我有一组固定在时间上的锚,随着动画的进行,形状在两个相邻的“锚”之间插值。但是对于这个问题,我们只需要担心两个锚

这是我想要的一个粗略的、基本上可以工作的实现,但我觉得必须有一个更准确、更有效的数学方法。多亏了走了很多步,它才慢下来。显然,我迭代的步骤越多,结果就越好

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
}
公共静态矩形getBounds(锚定左锚定、锚定右锚定、int形宽、int形高){
矩形baseShape=新矩形(0,0,shapeWidth,shapeHeight);//我们在动画中绘制的形状
矩形globalBounds=null,localBounds;//全局边界是整个动画的边界

对于(double time=0;time从迭代解中,您可以导出一步解(但它可能高估了边界框,因为如果选择了旋转的最极值,这将计算可能覆盖的最大面积):


假设原始形状不能覆盖由原始矩形对角线描述的圆之外的任何东西,则无论如何旋转,它都不会离开圆。通过找到应用的最大比例,可以调整圆的比例。

通过凸度,矩形的边界框与它的四个角的边界框。因此,您可以对单个角的边界框进行推理,然后“合并”四个角的结果

当你在一个角上应用你的变换时,你同时改变了中心,大小和方向。换句话说,轨迹是复杂的曲线,一个变形的阿基米德螺旋

一种简单的方法是通过计算给定时间间隔内的多个位置来找到近似边界框,并保持极值(最好是针对每一帧,给定帧速率)

严格的方法是通过分析找到曲线的极值

看一看变换后的横坐标,我们有
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)
。这是一个众所周知的线性三角方程。你可以在时间间隔中找到它的根。需要仔细讨论。

我曾将此堆栈溢出发送给一位朋友征求一些建议。他当时没有时间查看它,但他确实发送了此消息:。可能可以解决此数学问题,但我发现了很多问题更好的解决方法


我知道动画会发生多少帧(生成GIF)因此,我可以简单地预先计算每个帧的变换-找到所有帧的边界框,然后正确生成输出图像。

我不完全确定目标是什么。您想找到形状可能覆盖的最大边界框吗?还是需要找到一组变换的精确边界框?精度如何是否需要(超调可接受?/欠调可接受?)。是的-我想找到在两个仿射变换之间插值时形状可能覆盖的最大边界框。精确边界更好,但超调不是一个太大的问题。这是一个好主意。我想我知道你在做什么(但你的代码中有一些错误)。这可能是我必须走的路。如果你不同时更改所有参数,两种情况是可以处理的。如果不更改旋转,轨迹是一条直线段,非常容易处理。仅更改旋转即可生成圆弧(不太困难)。
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);
}