我通过将立方贝塞尔曲线拼接在一起创建了“blob”形状(截图如下)。我希望能够检测出曲线越过自身或其他曲线的情况,并且想知道是否有推荐的方法或已知的算法来做到这一点?
我的一个想法是使用FlatteningPathIterator
将形状分解为直线段,然后检测给定的段是否与另一个段相交,但我对是否有更好的方法感兴趣(如这将具有二次性能)。如果我确实追求这种方法,那么Java中的库函数是否可以检测两个线段是否重叠?
感谢。
无交叉
No Crossover http://www.freeimagehosting.net/uploads/7ad585414d.png
跨接
Crossover http://www.freeimagehosting.net/uploads/823748f8bb.png
答案 0 :(得分:4)
我实际上找到了一个使用内置Java2D功能的工作解决方案,而且非常快......
只需从曲线中创建一个Path2D,然后从Path2D中创建一个区域并调用方法Area.isSingular();
它有效......请参阅这个小例子。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.CubicCurve2D;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
@SuppressWarnings("serial")
public static void main(String[] args) {
JFrame f = new JFrame("Test");
JPanel c = new JPanel() {
Area a;
Path2D p;
{
p = new Path2D.Double();
p.append(new CubicCurve2D.Double(0, 0, 100, 0, 150, 50, 200, 100), true);
p.append(new CubicCurve2D.Double(200, 100, 200, 150, 150,0, 50, 100), true);
p.append(new CubicCurve2D.Double(100, 100, 100, 50, 50, 50, 0, 0), true);
a = new Area(p);
setPreferredSize(new Dimension(300, 300));
}
@Override
protected void paintComponent(Graphics g) {
g.setColor(Color.black);
((Graphics2D)g).fill(p);
System.out.println(a.isSingular());
}
};
f.setContentPane(c);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
}
答案 1 :(得分:2)
您可以采取Bezier curve的矢量函数:
将成对曲线的不同贝塞尔曲线等同起来,看看[0,1]中是否有解。当然,在重叠的部分是不同曲线的一部分的情况下,这将有所帮助。在一条曲线与自身相交的情况下,它不会有帮助......
编辑:
我引用了二次曲线函数,所以这是立方体:
确实难以解决这个问题。作为替代方案,我建议使用更松散的方法。你可以做的是将每条曲线“分割”成n个点并使用上面的函数计算它们的位置。然后,对于每个点,您将考虑任意半径的磁盘(取决于曲线的大小)并搜索这些磁盘的交叉点。您需要忽略顺序磁盘的交叉点,因为相交只是因为它们在单个曲线段上彼此靠得太近。
此方法应该非常快,但如果选择“错误”参数(n尺寸和半径),则可能会失去准确性,但您可以进行实验。
答案 2 :(得分:1)
我不确定这是否有帮助,但它类似于多边形渲染中的问题,对于每条扫描线Y,您有一个(X,flag)值对的数组,其中线穿过该扫描线。
跟随形状中的每条曲线,以及它穿过每条扫描线Y的位置,记录(X,标志),其中flag = 1,如果“north”,则flag = -1,如果“向南”。
如果在每条扫描线上您按X顺序考虑对,并保持标志值的运行总和,那么如果曲线是顺时针,则两个X值的跨度之间的总和将为正,如果是曲线则为负是逆时针的。
如果所有跨距均为+1或所有跨距均为-1,则曲线不会自行交叉。
编辑:这需要时间与图形越过的扫描线数呈线性关系。然后,结果数据结构可用于渲染图形。
答案 3 :(得分:1)
我认为你可以通过
得到一个不错的近似值FlatteningPathIterator
获取近似于blob的多边形。这非常简单,避免了您担心的O( n 2 )性能。对于您的平均基本blob,如图中的那些,只有两条向下的路径。
您可以通过保持向下路径水平排序(可能是TreeSet
)来进一步减少比较次数。
仅比较 y 维度中重叠的线段的另一种方法是使用interval tree。
答案 4 :(得分:0)
INTERSECT(b_0,...,b_m;c_0,...,c_n, EPSILON)
if [min b_i, max b_i] AND [min c_i, max c_i] != EMPTY { // check bounding boxes
if m*(m-1)*max||delta^2(b_i)|| > EPSILON) { // check flatness
Calculate b'_0, ..., b'_2m over [0, 0.5, 1] with the deCasteljau algorithm;
INTERSECT(b'_0,...,b'_m;c_0,...,c_n;EPSILON);
INTERSECT(b'_m,...,b'_2m;c_0,...,c_n;EPSILON);
}
}
else {
if (n*n-1)*max||delta^2(c_i)|| > EPSILON then {
Calculate c'_0, ..., c'_2m over [0, 0.5, 1] with the deCasteljau algorithm;
INTERSECT(b_0,...,b_m;c'_0,...,c'_n;EPSILON);
INTERSECT(b_0,...,b_m;c'_n,...,c'_2n;EPSILON);
}
else {
Intersect line segments b_0b_m and c_0c_n;
}
}
其中delta^2(b_i)
定义为b_{i+2} - 2*b_{i+1} + b_i
。