如何在Processing中从一个点到另一个点画线

4
我正在尝试生成一张图表,绘制运输路径从起点到终点的连线。我已经将纬度和经度数据转换为适合美国地图上像素坐标(1620, 1080)的形式。
目前的代码按照csv文件中的顺序依次绘制直线。然而,我想要的是让直线从起点开始向外辐射至终点。目前我只能找到已绘制直线的下拉方式。
我认为代码中涉及到的部分是“// Draw Lines”。
long current;

int x;
int y;
ArrayList loads;

void setup() {
  size(1620, 1080);
  background(55);
  smooth();
  frameRate(15);


// Draw US Map



String[] lines = loadStrings("Map2.csv");    // File containing coordinates to plot US Map
  stroke(55);
  strokeWeight(1);
  smooth();

  String[] pieces = split(lines[0], ',');

  for ( int i = 0; i < lines.length; i++) {

    fill(0);

    beginShape();
    current = int(pieces[0]);

    while ( current == int(pieces[0]) & i < lines.length) {

      x = int(pieces[2]);
      y = int(pieces[1]);
      vertex(x, y);
      i++;

      if ( i < lines.length) {
        pieces = split(lines[i], ',');
      }
    }
    endShape();
  }



// Add Lakes to Map




 String[] lines2 = loadStrings("Water.csv");    // File containing coordinates to plot great lakes
  smooth();

  fill(22, 25, 180);

  String[] pieces2 = split(lines2[0], ',');
  for (int i = 0; i < lines2.length; i++)
  {

    fill(110);
    beginShape();
    current =  int(pieces2[0]);

    while (current == int(pieces2[0]) & i < lines2.length) {

      x = int(pieces2[2]);
      y = int(pieces2[1]);
      vertex(x, y);
      i++;
      if (i < lines2.length) {
        pieces2 = split(lines2[i], ',');
      }
    }
    endShape();
  }


// Draw Lines



 loads = new ArrayList();

  String[] loadset = loadStrings("data1.csv");    
  for ( int i3 = 0; i3 < loadset.length; i3++) {
    String[] loads2 = split(loadset[i3], ',');
    loads.add( new Lane(int(loads2[0]), int(loads2[1]), int(loads2[2]), int(loads2[3])) );
  }
  }
     int i=1;
       int imax = 1;
       int incmult = 1;

    void draw() {
     if (i < loads.size()-imax){

      for(int iadd = 0; iadd < imax; iadd++)
      {
            Lane Lane = (Lane) loads.get(iadd);
            Lane.display();
            Lane = (Lane) loads.get(i+iadd);
            Lane.display();      
      }
          i +=imax;  
     }
      imax = imax + incmult;
    }

    class Lane {
        int x;
        int y;
        int x2;
        int y2;

        Lane( int tempX, int tempY, int tempX2, int tempY2) {
          x  = tempX;
          y  = tempY;
          x2 = tempX2;
          y2 = tempY2;
        }

        void display() {
          int r = 65;
          int g = 255;
          int b = 35;
          strokeWeight(1);
          stroke(r, g, b, 55);
          line(x, y, x2, y2);

          stroke(255, 255, 255);      // Origin
          fill(255, 255, 255, 55);
          ellipse(x, y, 3, 3);

          stroke(171, 62, 193);       // Destination
          fill(171, 62, 193);
          ellipse(x2, y2, 3, 3);
      }
      }

我的data1.csv文件包含四列x,y,x2,y2,其中(x,y)表示起点坐标,(x2,y2)表示终点坐标。

//  data.csv 

data[0] data[1] data[2] data[3]
929     327      602      507
1335    458      1327     782
1422    325      848      744
1302    280      1118     458
1041    583      1193     666
1267    616      1058     394
1215    671      1351     857
1334    851      1410     946
1334    851      1409     916
828     761      861      653
1386    323      1203     594
1037    293      1013     522
908     869      958      532
1029    331      1053     409
906     357      828      761
.        .       .        .
.        .       .        .

更新 由于我仍然在绘制我所设想的图像时遇到一些困难,因此我已经添加了我的数据链接。

地图2

数据1 <"https://docs.google.com/spreadsheets/d/1QzbCGW8H6PZgLkmWN8OyplVNTJhp3tlPGxR_Zv6lttM/pub?output=csv">


你的csv文件中原点始终是x,y吗?你能分享一些更大的csv文件示例吗? - George Profenza
csv文件'Map2'和'Water'仅包含在我的设置中绘制地图的'(x, y)'像素坐标。我想要绘制的线的原始坐标存储在'data.csv'中。现在,从起点到终点的线按顺序绘制在我的地图上。我希望能够绘制线条,使其从起点城市坐标'(data[0],data[1]) = (x,y)'辐射到目的地城市坐标'(data[2],data[3]) = '(x2,y2)'。如果您需要任何澄清,请告诉我。感谢您的帮助。 - Rick Arko
我认为我理解了问题的一部分,但并不完全明白。CSV行上的每个x、y对都是一个位置。如果您有一个起始城市,则其坐标应在CSV文件中重复出现。在上面的示例中,1334 851 就是这样。如果数据集有更多从此位置出发的目的地,它将会有汇聚到该位置的线路,这正是您想要的吗?我也想绘制这些数据。如果可以的话,您能否发布一个包含完整数据的pastebin(或类似)链接?谢谢。 - George Profenza
抱歉耽搁了。会有起点和终点重叠的情况,这些线条将被表示为较粗的线。我的 .csv 文件太大了,无法使用 pastebin。我可以通过电子邮件将它们发送给您吗? - Rick Arko
1个回答

4

目前问题不是很清楚,因此当前的答案并不完整。

以下是一些建议,可能会简化代码,并希望能帮助达到最终目标:

  1. 尝试使用 Processing 的内置 CSV 解析器 loadTable()。它支持制表符和标题,就像你的示例数据一样。解析标题的好处是可以使用列标签使用现成的函数(如 getInt())来解析每个值。
  2. 尝试使用 PShape:您可以在 setup 中设置绘图命令,然后在一个命令中呈现最终形状。优点是如果需要,您可以稍后访问使用的形状和顶点。

这是一个基本示例,使用基于上面数据的 TSV 保存为 data.tsv

Table data;
PShape dataPlot;

size(1620, 1080,P2D);
//create a group to store the lines from each row
dataPlot = createShape();
//load the data, specifying it has a header and it's tab separated
data = loadTable("data.tsv", "header, tsv");
//traverse each row
dataPlot.beginShape(LINES);
for(TableRow row : data.rows()){
  //extract each value
  int x1 = row.getInt("x1");
  int y1 = row.getInt("y1");
  int x2 = row.getInt("x2");
  int y2 = row.getInt("y2");
  //add the coordinates as lines to the group
  dataPlot.stroke(160);
  dataPlot.vertex(x1,y1);
  dataPlot.stroke(0);
  dataPlot.vertex(x2,y2);
}
dataPlot.endShape();
//render the plot
shape(dataPlot); 

使用深灰色到浅灰色来显示路径的起点和终点,以下是使用部分示例数据的结果:

Data Plot 1

如果您需要在创建PShape实例后访问每个顶点,则可以使用getVertex()。它需要您可能想要检索的顶点的索引。 例如,dataPlot.getVertex(0);dataPlot.getVertex(1);将返回第一条线的坐标,dataPlot.getVertex(2);dataPlot.getVertex(2);将返回第二条线的坐标,依此类推。
如果管理树形层次结构比管理顶点索引更容易,则可以创建PShape组。唯一的注意事项是,接下来创建的子PShape实例将使用以set为前缀的函数绘制,而不是您已经熟悉的stroke()/fill()/等: setStroke()/setFill()/等。
以下是一个使用PShape组将距离映射到线条颜色和粗细的代码示例:
Table data;
PShape dataPlot;

size(1620, 1080, P2D);
//create a group to store the lines from each row
dataPlot = createShape(GROUP);
//load the data, specifying it has a header and it's tab separated
data = loadTable("data.tsv", "header, tsv");
//traverse each row
for (TableRow row : data.rows ()) {
  //extract each value
  int x1 = row.getInt("x1");
  int y1 = row.getInt("y1");
  int x2 = row.getInt("x2");
  int y2 = row.getInt("y2");
  //add the coordinates as lines to the group
  PShape line = createShape(LINE, x1, y1, x2, y2);
  float dist = dist(x1, y1, x2, y2);
  line.setStroke(color(map(dist, 0, height, 160, 0)));
  line.setStrokeWeight(map(dist, 0, height, 10.0, 1.0));
  dataPlot.addChild(line);
}
//render the plot
shape(dataPlot); 

plot 2

在这种情况下,检索第一行将如下所示:
PShape line0 = dataPlot.getChild(0);

它允许您访问其两个顶点:

println(line0.getVertex(0));
println(line0.getVertex(1));

如果你想要对这些位置进行动画,你可以轻松地在单个值上使用lerp(),或者在PVectors上使用PVector's lerp()方法。此函数将期望一对起始/结束值和一个标准化的值(介于0.0和1.0之间),并返回中间值。将其视为沿着路径的百分比:
  • 传递0.0将是线的起点
  • 传递0.5将是线的50%
  • 传递1.0将是线的终点
以下是一个基本示例,在遍历每条线时绘制椭圆,并且颜色指示起始位置(黑色)或结束位置(白色)(拖动应允许手动控制将X轴映射到线遍历):
Table data;
PShape plot;

void setup(){
  size(1620, 1080, P2D);
  //create a group to store the lines from each row
  plot = createShape(GROUP);
  //load the data, specifying it has a header and it's tab separated
  data = loadTable("data.tsv", "header, tsv");
  //traverse each row
  for (TableRow row : data.rows ()) {
    //extract each value
    int x1 = row.getInt("x1");
    int y1 = row.getInt("y1");
    int x2 = row.getInt("x2");
    int y2 = row.getInt("y2");
    //add the coordinates as lines to the group
    PShape line = createShape(LINE, x1, y1, x2, y2);
    float dist = dist(x1, y1, x2, y2);
    line.setStroke(color(map(dist, 0, height, 160, 0)));
    line.setStrokeWeight(map(dist, 0, height, 10.0, 1.0));
    plot.addChild(line);
  }
}
void draw(){
  background(255);
  //render the plot
  shape(plot);
  //animate the trajectories
  //use normalized (between 0.0 and 1.0) value to traverse the paths (think of it as 0 and 100%, 0 is at the start 100% is at the end)
  //if can be interactive
  float traversal;
  if(mousePressed) {
    traversal = map(mouseX,0,width,0.0,1.0);
  }else{//or time based, up to you :)
    traversal = map(sin(frameCount * 0.1),-1.0,1.0,0.0,1.0);
  } 
  //for each trajectory
  for(int i = 0 ; i < plot.getChildCount(); i++){
    PShape line = plot.getChild(i);
    //access each line's start and end points
    PVector start = line.getVertex(0);
    PVector end   = line.getVertex(1);
    //calculate the linearly interpolated point in between start end using the traversal value and lerp()
    PVector inbetween = PVector.lerp(start,end,traversal);
    //use the interpolated value to draw
    fill(traversal * 255);
    ellipse(inbetween.x,inbetween.y,15,15);
  }
}

这是一个非常相似的例子,只是淡化了点:
Table data;
PShape plot;

void setup(){
  size(1620, 1080, P2D);
  //create a group to store the lines from each row
  plot = createShape(GROUP);
  //load the data, specifying it has a header and it's tab separated
  data = loadTable("data.tsv", "header, tsv");
  //traverse each row
  for (TableRow row : data.rows ()) {
    //extract each value
    int x1 = row.getInt("x1");
    int y1 = row.getInt("y1");
    int x2 = row.getInt("x2");
    int y2 = row.getInt("y2");
    //add the coordinates as lines to the group
    PShape line = createShape(LINE, x1, y1, x2, y2);
    float dist = dist(x1, y1, x2, y2);
    line.setStroke(color(map(dist, 0, height, 160, 0)));
    line.setStrokeWeight(map(dist, 0, height, 10.0, 1.0));
    plot.addChild(line);
  }
  //clear the background
  noStroke();
  shape(plot);//this needs to be drawn at least once it seems
  background(255);
}
void draw(){
  //hacky fade effect, change the alpha (16) transparency value to experiment with fade amount 
  fill(255,16);
  rect(0,0,width,height);
  //animate the trajectories
  //use normalized (between 0.0 and 1.0) value to traverse the paths (think of it as 0 and 100%, 0 is at the start 100% is at the end)
  //if can be interactive
  float traversal;
  if(mousePressed) {
    traversal = map(mouseX,0,width,0.0,1.0);
  }else{//or time based, up to you :)
    traversal = map(sin(frameCount * 0.01),-1.0,1.0,0.0,1.0);
  } 
  //for each trajectory
  for(int i = 0 ; i < plot.getChildCount(); i++){
    PShape line = plot.getChild(i);
    //access each line's start and end points
    PVector start = line.getVertex(0);
    PVector end   = line.getVertex(1);
    //calculate the linearly interpolated point in between start end using the traversal value and lerp()
    PVector inbetween = PVector.lerp(start,end,traversal);
    //use the interpolated value to draw
    fill(lerpColor(color(255,0,0),color(0,255,0),traversal));
    ellipse(inbetween.x,inbetween.y,15,15);
  }
}

fading trails

这是另一种有趣的小变化:

Table data;
PShape plot;

void setup(){
  size(1620, 1080, P2D);
  smooth(8);
  //create a group to store the lines from each row
  plot = createShape(GROUP);
  //load the data, specifying it has a header and it's tab separated
  data = loadTable("data.tsv", "header, tsv");
  //traverse each row
  for (TableRow row : data.rows ()) {
    //extract each value
    int x1 = row.getInt("x1");
    int y1 = row.getInt("y1");
    int x2 = row.getInt("x2");
    int y2 = row.getInt("y2");
    //add the coordinates as lines to the group
    PShape line = createShape(LINE, x1, y1, x2, y2);
    plot.addChild(line);
  }
  shape(plot);
  strokeWeight(5.0);
}
void draw(){
  //hacky fade effect, change the alpha/transparency value to experiment with fade amount 
  background(255);
  //animate the trajectories
  //use normalized (between 0.0 and 1.0) value to traverse the paths (think of it as 0 and 100%, 0 is at the start 100% is at the end)
  //if can be interactive
  float traversal;
  if(mousePressed) {
    traversal = map(mouseX,0,width,0.0,1.0);
  }else{//or time based, up to you :)
    traversal = map(sin(frameCount * 0.01),-1.0,1.0,0.0,1.0);
  } 
  beginShape(LINES);
  //for each trajectory
  for(int i = 0 ; i < plot.getChildCount(); i++){
    PShape line = plot.getChild(i);
    //access each line's start and end points
    PVector start = line.getVertex(0);
    PVector end   = line.getVertex(1);
    //calculate the linearly interpolated point in between start end using the traversal value and lerp()
    PVector inbetween = PVector.lerp(start,end,traversal);
    //use the interpolated value to draw
    stroke(64);
    vertex(start.x,start.y);
    stroke(160);
    vertex(inbetween.x,inbetween.y);
  }
  endShape();
}

视频演示 这里

如果线性插值不够用,需要更多对时间或插值类型的控制,请查看Benedikt Groß的Ani库


感谢回复,很抱歉直到现在才有机会澄清。我的主要动机是能够从起点到终点画出每条线段。因此,当我运行文件时,我可以看到线段从一个点到另一个点,而不是只是瞬间绘制完整的线段。如果我能弄清楚如何将我的.csv文件发送给您,您将看到当前所有线段都是按顺序绘制的,但是如果可能,我希望能够看到绘制线段的过程。到目前为止,我还没有找到任何相关示例。 - Rick Arko
我现在明白了:您想要将每条线从起点动画到终点,而不是在一个帧中绘制整个路径。就像逐渐随着时间的推移绘制/跟踪这些线条一样。如果是这种情况,请查看我上面的更新。如果有帮助,请随意投票和/或选中答案 ;) - George Profenza
为什么 size(1620, 1080, P2D) 会出现问题?你是否收到任何错误信息?你使用的 Processing 版本是什么? - George Profenza
您介意将此问题作为单独的问题发布,并展示出现问题的简化代码吗? - George Profenza
http://stackoverflow.com/questions/34456143/motion-drawing-a-line-from-an-origin-to-a-destination-in-processing - Rick Arko
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接