折线轮廓构造/绘制粗折线
Polylines outline construction / Drawing thick polylines
我面临一项任务,我必须使用多边形绘制多段线。
作为输入参数,我有一个点数组和厚度值。见下图。
我有形成黑色折线和厚度的点,例如10px。现在我需要计算点并构造一条蓝色折线以形成多边形然后渲染它。
有一些相关的文章:
- Drawing Polylines by Tessellation
- An algorithm for polylines outline construction
- EFFICIENT RENDERING OF THICK POLYLINES
但我觉得它们有点复杂和难以理解。
是否没有任何现有的库或更简单的算法来实现它。不需要圆形接头。我正在使用 java 和 libGDX.
我不太确定你为什么要在框架中实现一些高级图形算法,首要任务是轻松呈现事物:)
Libgdx 具有内置的 ShapeRenderer,可让您绘制简单的形状。您所要做的就是根据 thickness
计算出新的顶点,并将它们传递给 ShapeRenderer 以绘制圆和连接这些圆的线。
为了超级简单,您甚至可以使用
rectLine(float x1, float y1, float x2, float y2, float width)
允许您绘制给定粗细线条的方法。所以你只需要做的就是遍历点并绘制所有的线,就像这个伪代码一样:
for point in points:
if thereIsANextPoint():
next = getNextPoint()
sr.rectLine(point.x, point.y, next.x, next .y, thickness)
关于如何使用 ShapeRenderer 的精彩描述包含在我上面附上的 reference
中
我想点之间的连接可能会有一点问题(例如因为不同的角度)我认为这个连接上方的渲染圆圈将是一个很好的解决方法:)
算法如下:
for each line:
find the parallel line upwards:
find the perpendicular: has a slope m2 in approximate
check which side is right (compare angles)
find the two points of the parallel line by solving a small equation problem (A, B, C)
if this line is the first one keep it (l1)
else find the intersection with the previous line (l1, l2): this will give the next articulation point
黄线就是你要的;红线是一般平行线。
关节点是绿色的。您可以在某些组件中打印此缓冲图像。
注意:多边形的宽度不能如您所想的那样固定,因为在关节点处距离会更大。保证的是线段之间的距离是恒定的。
int[] approximate(int[] p, int[] p2, double dr, int l) { // l is the distance, dr is 0 for the beginning of the segment and 1 for the end
double d=Math.sqrt(Math.pow(p[0]-p2[0], 2)+Math.pow(p[1]-p2[1], 2));
double ix=p[0]+dr*(p2[0]-p[0]), iy=p[1]+dr*(p2[1]-p[1]);
double x1=0, x2=0, y1=0, y2=0;
if(p2[0]==p[0]) {
x1=ix+l; x2=ix-l; y1=iy; y2=iy;
}
else {
double m=1.0*(p2[1]-p[1])/(p2[0]-p[0]);
if(Math.abs(m)==0) {
x1=ix; x2=ix; y1=iy+l; y2=iy-l;
}
else {
double m2=-1/m;
double c=iy-m2*ix;
double A=1+m2*m2, B=-2*(ix-m2*c+m2*iy), C=ix*ix+iy*iy+c*c-2*c*iy-l*l;
x1=(-B+Math.sqrt(B*B-4*A*C))/(2*A); x2=(-B-Math.sqrt(B*B-4*A*C))/(2*A); y1=m2*x1+c; y2=m2*x2+c;
}
}
int[] cp1={p2[0]-p[0], p2[1]-p[1]}, cp2={(int)x1-p[0], (int)y1-p[1]}, xy=new int[2];
int cpp=compareAngles(cp1, cp2);
if(cpp>0) { xy[0]=(int)x1; xy[1]=(int)y1; } else { xy[0]=(int)x2; xy[1]=(int)y2; }
return xy;
}
void poly() {
int[][] p={{100, 400}, {110, 440}, {250, 300}, {350, 400}, {300, 310}};
BufferedImage bim=new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Graphics2D g=(Graphics2D)bim.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setStroke(new BasicStroke(5f));
g.setColor(Color.black);
Line2D.Double l1=new Line2D.Double(), l2=new Line2D.Double();
int[] currentp=new int[2], lastp=new int[2];
for(int i=0; i<p.length-1; i++) {
g.setColor(Color.black);
g.drawLine(p[i][0], p[i][1], p[i+1][0], p[i+1][1]);
int[] p1=approximate(p[i], p[i+1], 0, 10), p2=approximate(p[i], p[i+1], 1, 10);
g.setColor(Color.red);
g.drawLine(p1[0], p1[1], p2[0], p2[1]);
if(i==0) { l1=new Line2D.Double(p1[0], p1[1], p2[0], p2[1]); currentp[0]=p1[0]; currentp[1]=p1[1]; }
else {
l2=new Line2D.Double(p1[0], p1[1], p2[0], p2[1]);
int[] pi=intersectionPoint(l1, l2);
g.setColor(Color.green);
g.fillOval(pi[0], pi[1], 5, 5);
g.setColor(Color.yellow);
g.drawLine(currentp[0], currentp[1], pi[0], pi[1]);
currentp[0]=pi[0]; currentp[1]=pi[1];
l1.setLine(l2);
}
if(i==p.length-2) { lastp[0]=p2[0]; lastp[1]=p2[1]; }
}
g.setColor(Color.yellow);
g.drawLine(currentp[0], currentp[1], lastp[0], lastp[1]);
}
public int[] intersectionPoint(Line2D.Double l1, Line2D.Double l2) {
return intersectionPoint((int)l1.getX1(), (int)l1.getY1(), (int)l1.getX2(), (int)l1.getY2(), (int)l2.getX1(), (int)l2.getY1(), (int)l2.getX2(), (int)l2.getY2());
}
public int[] intersectionPoint(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
int[] xy={(int)(1.0*((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4))),
(int)(1.0*((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)))};
return xy;
}
public int compareAngles(int[] a, int[] b) {
int cp=a[0]*b[1]-a[1]*b[0];
return -cp;
}
我面临一项任务,我必须使用多边形绘制多段线。
作为输入参数,我有一个点数组和厚度值。见下图。
我有形成黑色折线和厚度的点,例如10px。现在我需要计算点并构造一条蓝色折线以形成多边形然后渲染它。
有一些相关的文章:
- Drawing Polylines by Tessellation
- An algorithm for polylines outline construction
- EFFICIENT RENDERING OF THICK POLYLINES
但我觉得它们有点复杂和难以理解。 是否没有任何现有的库或更简单的算法来实现它。不需要圆形接头。我正在使用 java 和 libGDX.
我不太确定你为什么要在框架中实现一些高级图形算法,首要任务是轻松呈现事物:)
Libgdx 具有内置的 ShapeRenderer,可让您绘制简单的形状。您所要做的就是根据 thickness
计算出新的顶点,并将它们传递给 ShapeRenderer 以绘制圆和连接这些圆的线。
为了超级简单,您甚至可以使用
rectLine(float x1, float y1, float x2, float y2, float width)
允许您绘制给定粗细线条的方法。所以你只需要做的就是遍历点并绘制所有的线,就像这个伪代码一样:
for point in points:
if thereIsANextPoint():
next = getNextPoint()
sr.rectLine(point.x, point.y, next.x, next .y, thickness)
关于如何使用 ShapeRenderer 的精彩描述包含在我上面附上的 reference
中我想点之间的连接可能会有一点问题(例如因为不同的角度)我认为这个连接上方的渲染圆圈将是一个很好的解决方法:)
算法如下:
for each line:
find the parallel line upwards:
find the perpendicular: has a slope m2 in approximate
check which side is right (compare angles)
find the two points of the parallel line by solving a small equation problem (A, B, C)
if this line is the first one keep it (l1)
else find the intersection with the previous line (l1, l2): this will give the next articulation point
黄线就是你要的;红线是一般平行线。 关节点是绿色的。您可以在某些组件中打印此缓冲图像。
注意:多边形的宽度不能如您所想的那样固定,因为在关节点处距离会更大。保证的是线段之间的距离是恒定的。
int[] approximate(int[] p, int[] p2, double dr, int l) { // l is the distance, dr is 0 for the beginning of the segment and 1 for the end
double d=Math.sqrt(Math.pow(p[0]-p2[0], 2)+Math.pow(p[1]-p2[1], 2));
double ix=p[0]+dr*(p2[0]-p[0]), iy=p[1]+dr*(p2[1]-p[1]);
double x1=0, x2=0, y1=0, y2=0;
if(p2[0]==p[0]) {
x1=ix+l; x2=ix-l; y1=iy; y2=iy;
}
else {
double m=1.0*(p2[1]-p[1])/(p2[0]-p[0]);
if(Math.abs(m)==0) {
x1=ix; x2=ix; y1=iy+l; y2=iy-l;
}
else {
double m2=-1/m;
double c=iy-m2*ix;
double A=1+m2*m2, B=-2*(ix-m2*c+m2*iy), C=ix*ix+iy*iy+c*c-2*c*iy-l*l;
x1=(-B+Math.sqrt(B*B-4*A*C))/(2*A); x2=(-B-Math.sqrt(B*B-4*A*C))/(2*A); y1=m2*x1+c; y2=m2*x2+c;
}
}
int[] cp1={p2[0]-p[0], p2[1]-p[1]}, cp2={(int)x1-p[0], (int)y1-p[1]}, xy=new int[2];
int cpp=compareAngles(cp1, cp2);
if(cpp>0) { xy[0]=(int)x1; xy[1]=(int)y1; } else { xy[0]=(int)x2; xy[1]=(int)y2; }
return xy;
}
void poly() {
int[][] p={{100, 400}, {110, 440}, {250, 300}, {350, 400}, {300, 310}};
BufferedImage bim=new BufferedImage(500, 500, BufferedImage.TYPE_INT_RGB);
Graphics2D g=(Graphics2D)bim.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, 500, 500);
g.setStroke(new BasicStroke(5f));
g.setColor(Color.black);
Line2D.Double l1=new Line2D.Double(), l2=new Line2D.Double();
int[] currentp=new int[2], lastp=new int[2];
for(int i=0; i<p.length-1; i++) {
g.setColor(Color.black);
g.drawLine(p[i][0], p[i][1], p[i+1][0], p[i+1][1]);
int[] p1=approximate(p[i], p[i+1], 0, 10), p2=approximate(p[i], p[i+1], 1, 10);
g.setColor(Color.red);
g.drawLine(p1[0], p1[1], p2[0], p2[1]);
if(i==0) { l1=new Line2D.Double(p1[0], p1[1], p2[0], p2[1]); currentp[0]=p1[0]; currentp[1]=p1[1]; }
else {
l2=new Line2D.Double(p1[0], p1[1], p2[0], p2[1]);
int[] pi=intersectionPoint(l1, l2);
g.setColor(Color.green);
g.fillOval(pi[0], pi[1], 5, 5);
g.setColor(Color.yellow);
g.drawLine(currentp[0], currentp[1], pi[0], pi[1]);
currentp[0]=pi[0]; currentp[1]=pi[1];
l1.setLine(l2);
}
if(i==p.length-2) { lastp[0]=p2[0]; lastp[1]=p2[1]; }
}
g.setColor(Color.yellow);
g.drawLine(currentp[0], currentp[1], lastp[0], lastp[1]);
}
public int[] intersectionPoint(Line2D.Double l1, Line2D.Double l2) {
return intersectionPoint((int)l1.getX1(), (int)l1.getY1(), (int)l1.getX2(), (int)l1.getY2(), (int)l2.getX1(), (int)l2.getY1(), (int)l2.getX2(), (int)l2.getY2());
}
public int[] intersectionPoint(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) {
int[] xy={(int)(1.0*((x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4))),
(int)(1.0*((x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4))/((x1-x2)*(y3-y4)-(y1-y2)*(x3-x4)))};
return xy;
}
public int compareAngles(int[] a, int[] b) {
int cp=a[0]*b[1]-a[1]*b[0];
return -cp;
}