假设我有150个节点/顶点的路径。如果是这样,我怎么能简化,例如一条带有3个顶点的直线,将删除中间的一条,因为它不会添加到路径中。我怎么能避免破坏尖角?我怎样才能消除微小的变化并保持平滑的曲线。
由于
答案 0 :(得分:4)
对于每3个顶点,选择中间的顶点和其他两个顶点之间的calculate its distance to the line segment。如果距离小于您愿意接受的容差,请将其删除。
如果中间顶点非常靠近其中一个端点,则应该收紧公差以避免移除圆角。
答案 1 :(得分:3)
如果这样,我可以简化,例如一条带有3个顶点的直线,将删除中间的一条,因为它不会添加到路径中。
对于每组三个连续顶点,测试它们是否都在一条直线上。如果是,则删除中间顶点。
另外,我怎么能避免破坏尖角?
如果你只是删除掉在另外两个之间的直线上的顶点,那么你就不会有这个问题了。
答案 2 :(得分:3)
更简单的方法。取3个顶点a,b和c并计算:顶点之间的角度/倾斜度。
std::vector<VERTEX> path;
//fill path
std::vector<int> removeList;
//Assure that the value is in the same unit as the return of angleOf function.
double maxTinyVariation = SOMETHING;
for (int i = 0; i < quantity-2; ++i) {
double a = path[i], b = path[i + 1] , c = path[i + 2]
double abAngle = angleOf(a, b);
double bcAngle = angleOf(b, c);
if (abs(ab - bc) <= maxTinyVariation) {
//a, b and c are colinear
//remove b later
removeList.push_back(i+1);
}
}
//Remove vertecies from path using removeList.
答案 3 :(得分:2)
使用 Douglas-Peucker 方法简化路径。
epsilon
参数定义&#34;简单&#34;:
private List<Point> douglasPeucker (List<Point> points, float epsilon){
int count = points.size();
if(count < 3) {
return points;
}
//Find the point with the maximum distance
float dmax = 0;
int index = 0;
for(int i = 1; i < count - 1; i++) {
Point point = points.get(i);
Point lineA = points.get(0);
Point lineB = points.get(count-1);
float d = perpendicularDistance(point, lineA, lineB);
if(d > dmax) {
index = i;
dmax = d;
}
}
//If max distance is greater than epsilon, recursively simplify
List<Point> resultList;
if(dmax > epsilon) {
List<Point> recResults1 = douglasPeucker(points.subList(0,index+1), epsilon);
List<Point> recResults2 = douglasPeucker(points.subList(index, count), epsilon);
List<Point> tmpList = new ArrayList<Point>();
tmpList.addAll(recResults1);
tmpList.remove(tmpList.size()-1);
tmpList.addAll(recResults2);
resultList = tmpList;
} else {
resultList = new ArrayList<Point>();
resultList.add(points.get(0));
resultList.add(points.get(count-1));
}
return resultList;
}
private float perpendicularDistance(Point point, Point lineA, Point lineB){
Point v1 = new Point(lineB.x - lineA.x, lineB.y - lineA.y);
Point v2 = new Point(point.x - lineA.x, point.y - lineA.y);
float lenV1 = (float)Math.sqrt(v1.x * v1.x + v1.y * v1.y);
float lenV2 = (float)Math.sqrt(v2.x * v2.x + v2.y * v2.y);
float angle = (float)Math.acos((v1.x * v2.x + v1.y * v2.y) / (lenV1 * lenV2));
return (float)(Math.sin(angle) * lenV2);
}
答案 4 :(得分:1)
让A,B,C成为一些观点。
检查它们位于同一行的最简单方法是计算向量的交叉积 B-A,C-A。
如果等于零,则它们位于同一行:
// X_ab, Y_ab - coordinates of vector B-A.
float X_ab = B.x - A.x
float Y_ab = B.y - A.y
// X_ac, Y_ac - coordinates of vector C-A.
float X_ac = C.x - A.x
float Y_ac = C.y - A.y
float crossproduct = Y_ab * X_ac - X_ab * Y_ac
if (crossproduct < EPS) // if crossprudct == 0
{
// on the same line.
} else {
// not on the same line.
}
当你知道A,B,C位于同一条线上之后,很容易知道B是否位于A和C之间,而是抛出了矢量B-A和C-A的内部产物。如果B位于A和C之间,则(B-A)具有与(C-A)相同的方向,并且内部产物&gt; 0,否则&lt; 0:
float innerproduct = X_ab * X_ac + Y_ab * Y_ac;
if (innerproduct > 0) {
// B is between A and C.
} else {
// B is not between A and C.
}