如何正确绘制路径?
How to draw a path correctly?
我目前正在重写路径绘图算法。
我正在使用 apache-commons-math 的 Spline Interpolator 来获得通过 2D 中所有给定点的平滑路径 Space...
目前我有:
/**
* Draws a route on a map.
*/
public class MapRouteDrawer {
private static final SplineInterpolator splineInterpolator = new SplineInterpolator();
/**
* Draws the route to the screen, does nothing if null.
*/
public static void drawRoute(final Graphics2D graphics, final RouteDescription routeDescription, final MapPanel view, final MapData mapData, final String movementLeftForCurrentUnits) {
if (routeDescription == null) {
return;
}
final Route route = routeDescription.getRoute();
if (route == null) {
return;
}
final Point[] points = getRoutePoints(routeDescription, mapData);
final int xOffset = view.getXOffset();
final int yOffset = view.getYOffset();
final int jointsize = 10;
final int numTerritories = route.getAllTerritories().size();
//set thickness and color of the future drawings
graphics.setStroke(new BasicStroke(3.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
graphics.setPaint(Color.red);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if(Arrays.asList(points).contains(null)){//If the Array is null at some point
return;
}
if(numTerritories <= 1 || points.length <= 2){
drawLineWithTranslate(graphics, new Line2D.Float(routeDescription.getStart(), routeDescription.getEnd()), xOffset, yOffset);
graphics.fillOval((routeDescription.getEnd().x - xOffset) - jointsize / 2, (routeDescription.getEnd().y - yOffset) - jointsize / 2, jointsize, jointsize);
}
else{
drawCurvedPath(graphics, points, view);
}
}
}
private static double[] getIndex(Point[] points) {
final double[] index = new double[points.length];
for(int i = 0; i < points.length; i++){
index[i] = i;
}
return index;
}
private static void drawLineWithTranslate(Graphics2D graphics, Line2D line2D, double translateX, double translateY) {
final Line2D line = (Line2D) line2D;
final Point2D point1 = new Point2D.Double(line.getP1().getX() - translateX, line.getP1().getY() - translateY);
final Point2D point2 = new Point2D.Double(line.getP2().getX() - translateX, line.getP2().getY() - translateY);
graphics.draw(new Line2D.Double(point1, point2));
}
private static Point[] getRoutePoints(RouteDescription routeDescription, MapData mapData){
final List<Territory> territories = routeDescription.getRoute().getAllTerritories();
final int numTerritories = territories.size();
final Point[] points = new Point[numTerritories];
for (int i = 0; i < numTerritories; i++) {
points[i] = mapData.getCenter(territories.get(i));
}
if (routeDescription.getStart() != null) {
points[0] = routeDescription.getStart();
}
if (routeDescription.getEnd() != null && numTerritories > 1) {
points[numTerritories - 1] = new Point(routeDescription.getEnd());
}
return points;
}
private static double[] pointsXToDoubleArray(Point[] points){
double[] result = new double[points.length];
for(int i = 0; i < points.length; i++){
result[i] = points[i].getX();
}
return result;
}
private static double[] pointsYToDoubleArray(Point[] points){
double[] result = new double[points.length];
for(int i = 0; i < points.length; i++){
result[i] = points[i].getY();
}
return result;
}
private static double[] getCoords(PolynomialSplineFunction curve, float stepSize){
final double[] coords = new double[(int) (curve.getN() / stepSize)];
for(int i = 0; i < curve.getN() / stepSize; i++){
coords[i] = curve.value(i * stepSize);
}
return coords;
}
private static void drawCurvedPath(Graphics2D graphics, Point[] points, MapPanel view){
final double[] index = getIndex(points);
final float stepSize = 0.01f;//TODO calculating a step size that makes sense
final PolynomialSplineFunction xcurve = splineInterpolator.interpolate(index, pointsXToDoubleArray(points));
final PolynomialSplineFunction ycurve = splineInterpolator.interpolate(index, pointsYToDoubleArray(points));
final double[] xcoords = getCoords(xcurve, stepSize);
final double[] ycoords = getCoords(ycurve, stepSize);
for(int i = 1; i < xcoords.length; i++){
//TODO maybe a line is not the best way to draw this...
drawLineWithTranslate(graphics, new Line2D.Double(xcoords[i-1], ycoords[i-1], xcoords[i], ycoords[i]), view.getXOffset(), view.getYOffset());
}
}
}
这背后的想法是,由于 SplineInterpolator 只接受函数(例如 f(x) = y)并且 x 必须增加,所以我们将点数组拆分为 2 个双精度数组并对其进行插值 2 次。 .
首先是 X 值,然后是 Y 值...
作为 X 值,一个名为 "index" 的 "dummy array" 被采用,第一个值为 0 第二个 1 第三个 2 依此类推...
为了绘制这条路径,我从点 0 到 1、1 到 2、2 到 3 等等画了一条线...
有两件事需要考虑...
- 在索引中选择 1 作为步长有意义吗?这可能会导致不准确,因为 java 双精度数只有 64 位,而我们正在使用静态 1 步长拉伸和压缩值。如果是这样,我可以改变什么来优化这个...
- 其次,如何更好地绘制这两个双数组?我尝试绘制多条线看起来不太好 - 这是因为我以太大的步长读取内插值吗?
非常感谢任何帮助
编辑:
选择1作为'index'数组的步长就是所谓的均匀参数化,除非你的数据点也分布相对均匀,否则通常不会产生好的结果。我建议使用弦长参数化或向心参数化,如下所示:
t0 = 0.0
t1 = d1/L
t2 = t1 + d2/L
t3 = t2 + d3/L
............
t(n-1)= 1.0.
其中
d1=|P1-P0|^e, d2=|P2-P1|^e, d3=|P3-P2|^e and L = d1+d2+d3+.....d(n-1).
对于弦长参数化,在上面的公式中使用e=1.0。对于向心参数化,使用 e=0.5。请注意,使用 e=0.0 只会导致统一参数化。如果您的数据点具有非常不均匀的分布(即点之间的一些距离很大而一些很小),向心参数化通常会比弦长参数化产生更好的结果。
我目前正在重写路径绘图算法。 我正在使用 apache-commons-math 的 Spline Interpolator 来获得通过 2D 中所有给定点的平滑路径 Space...
目前我有:
/**
* Draws a route on a map.
*/
public class MapRouteDrawer {
private static final SplineInterpolator splineInterpolator = new SplineInterpolator();
/**
* Draws the route to the screen, does nothing if null.
*/
public static void drawRoute(final Graphics2D graphics, final RouteDescription routeDescription, final MapPanel view, final MapData mapData, final String movementLeftForCurrentUnits) {
if (routeDescription == null) {
return;
}
final Route route = routeDescription.getRoute();
if (route == null) {
return;
}
final Point[] points = getRoutePoints(routeDescription, mapData);
final int xOffset = view.getXOffset();
final int yOffset = view.getYOffset();
final int jointsize = 10;
final int numTerritories = route.getAllTerritories().size();
//set thickness and color of the future drawings
graphics.setStroke(new BasicStroke(3.5f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
graphics.setPaint(Color.red);
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
if(Arrays.asList(points).contains(null)){//If the Array is null at some point
return;
}
if(numTerritories <= 1 || points.length <= 2){
drawLineWithTranslate(graphics, new Line2D.Float(routeDescription.getStart(), routeDescription.getEnd()), xOffset, yOffset);
graphics.fillOval((routeDescription.getEnd().x - xOffset) - jointsize / 2, (routeDescription.getEnd().y - yOffset) - jointsize / 2, jointsize, jointsize);
}
else{
drawCurvedPath(graphics, points, view);
}
}
}
private static double[] getIndex(Point[] points) {
final double[] index = new double[points.length];
for(int i = 0; i < points.length; i++){
index[i] = i;
}
return index;
}
private static void drawLineWithTranslate(Graphics2D graphics, Line2D line2D, double translateX, double translateY) {
final Line2D line = (Line2D) line2D;
final Point2D point1 = new Point2D.Double(line.getP1().getX() - translateX, line.getP1().getY() - translateY);
final Point2D point2 = new Point2D.Double(line.getP2().getX() - translateX, line.getP2().getY() - translateY);
graphics.draw(new Line2D.Double(point1, point2));
}
private static Point[] getRoutePoints(RouteDescription routeDescription, MapData mapData){
final List<Territory> territories = routeDescription.getRoute().getAllTerritories();
final int numTerritories = territories.size();
final Point[] points = new Point[numTerritories];
for (int i = 0; i < numTerritories; i++) {
points[i] = mapData.getCenter(territories.get(i));
}
if (routeDescription.getStart() != null) {
points[0] = routeDescription.getStart();
}
if (routeDescription.getEnd() != null && numTerritories > 1) {
points[numTerritories - 1] = new Point(routeDescription.getEnd());
}
return points;
}
private static double[] pointsXToDoubleArray(Point[] points){
double[] result = new double[points.length];
for(int i = 0; i < points.length; i++){
result[i] = points[i].getX();
}
return result;
}
private static double[] pointsYToDoubleArray(Point[] points){
double[] result = new double[points.length];
for(int i = 0; i < points.length; i++){
result[i] = points[i].getY();
}
return result;
}
private static double[] getCoords(PolynomialSplineFunction curve, float stepSize){
final double[] coords = new double[(int) (curve.getN() / stepSize)];
for(int i = 0; i < curve.getN() / stepSize; i++){
coords[i] = curve.value(i * stepSize);
}
return coords;
}
private static void drawCurvedPath(Graphics2D graphics, Point[] points, MapPanel view){
final double[] index = getIndex(points);
final float stepSize = 0.01f;//TODO calculating a step size that makes sense
final PolynomialSplineFunction xcurve = splineInterpolator.interpolate(index, pointsXToDoubleArray(points));
final PolynomialSplineFunction ycurve = splineInterpolator.interpolate(index, pointsYToDoubleArray(points));
final double[] xcoords = getCoords(xcurve, stepSize);
final double[] ycoords = getCoords(ycurve, stepSize);
for(int i = 1; i < xcoords.length; i++){
//TODO maybe a line is not the best way to draw this...
drawLineWithTranslate(graphics, new Line2D.Double(xcoords[i-1], ycoords[i-1], xcoords[i], ycoords[i]), view.getXOffset(), view.getYOffset());
}
}
}
这背后的想法是,由于 SplineInterpolator 只接受函数(例如 f(x) = y)并且 x 必须增加,所以我们将点数组拆分为 2 个双精度数组并对其进行插值 2 次。 .
首先是 X 值,然后是 Y 值...
作为 X 值,一个名为 "index" 的 "dummy array" 被采用,第一个值为 0 第二个 1 第三个 2 依此类推...
为了绘制这条路径,我从点 0 到 1、1 到 2、2 到 3 等等画了一条线...
有两件事需要考虑...
- 在索引中选择 1 作为步长有意义吗?这可能会导致不准确,因为 java 双精度数只有 64 位,而我们正在使用静态 1 步长拉伸和压缩值。如果是这样,我可以改变什么来优化这个...
- 其次,如何更好地绘制这两个双数组?我尝试绘制多条线看起来不太好 - 这是因为我以太大的步长读取内插值吗?
非常感谢任何帮助
编辑:
选择1作为'index'数组的步长就是所谓的均匀参数化,除非你的数据点也分布相对均匀,否则通常不会产生好的结果。我建议使用弦长参数化或向心参数化,如下所示:
t0 = 0.0
t1 = d1/L
t2 = t1 + d2/L
t3 = t2 + d3/L
............
t(n-1)= 1.0.
其中
d1=|P1-P0|^e, d2=|P2-P1|^e, d3=|P3-P2|^e and L = d1+d2+d3+.....d(n-1).
对于弦长参数化,在上面的公式中使用e=1.0。对于向心参数化,使用 e=0.5。请注意,使用 e=0.0 只会导致统一参数化。如果您的数据点具有非常不均匀的分布(即点之间的一些距离很大而一些很小),向心参数化通常会比弦长参数化产生更好的结果。