2

I am writing a program in Processing to make a 3-D scatterplot that one can rotate around in space using PeasyCam. Data is read in from a text file to an ArrayList of PVectors. The entire code is shown below. What I don't understand is that importTextfile() needs to be called (repeatedly) within draw() and this significantly slows things down. Why can't I get away with calling it once within setup()? With "println(pointsList)" within draw() I can see that pointList changes if it's not preceded by importTextFile(). Can anyone explain why?

(Update: From trying to construct a minimum working example I see now that the problem is when I make a PVector V and then write over it to map it to the display window. I would still appreciate feedback on a good work around that involves calling importTextFile() just during setup(). What do I use instead of .get() so I get a copy of what's in the Arraylist and not a pointer to the actually value in the ArrayList?)

Here is the code:

import peasy.*;
PeasyCam cam;
ArrayList <PVector>pointList;   

int maxX = 0;
int maxY = 0;
int maxZ = 0;

int minX = 10000;
int minY = 10000;
int minZ = 10000;

int range = 0;

void setup() {
   size(500, 500, P3D);
   cam = new PeasyCam(this, width/2, width/2, width/2, 800);
   cam.setMinimumDistance(100);
   cam.setMaximumDistance(2000);
   pointList = new ArrayList();
   importTextFile();
   println(pointList);

 //Determine min and max along each axis
   for (int i=0; i < pointList.size(); i++) {
      PVector R = pointList.get(i);
      if (R.x > maxX) {
        maxX = (int)R.x;
      }
      if (R.x < minX) {
        minX = (int)R.x;
      }
      if (R.y > maxY) {
        maxY = (int)R.y;
      }
      if (R.y < minY) {
        minY = (int)R.y;
      }
      if (R.z > maxZ) {
        maxZ = (int)R.z;
      }
      if (R.z < minZ) {
        minZ = (int)R.z;
      }
   }

   if (maxX - minX > range) {
      range = maxX - minX;
   }
   if (maxY - minY > range) {
      range = maxY - minY;
   }
   if (maxZ - minZ > range) {
      range = maxZ - minZ;
   }
   println(pointList);
}

void draw() {
    //importTextFile();  Uncomment to make run properly
    println(pointList);
    background(255);
    stroke(0);
    strokeWeight(2);
    line(0, 0, 0, width, 0, 0);
    line(0, 0, 0, 0, width, 0);
    line(0, 0, 0, 0, 0, width);
    stroke(150);
    strokeWeight(1);
    for (int i=1; i<6; i++) {
      line(i*width/5, 0, 0, i*width/5, width, 0);
      line(0, i*width/5, 0, width, i*width/5, 0);
    }
    lights();
    noStroke();
    fill(255, 0, 0);
    sphereDetail(10);

    //**The problem is here**
    for (int i=0; i < pointList.size(); i++) {
      PVector V = pointList.get(i);
      V.x = map(V.x, minX-50, minX+range+50, 0, width);
      V.y = map(V.y, minY-50, minY+range+50, 0, width);
      V.z = map(V.z, minZ-50, minZ+range+50, 0, width);
      pushMatrix();
      translate(V.x, V.y, V.z);
      sphere(4);
      popMatrix();
    }
}

void importTextFile() {   
       String[] strLines = loadStrings("positions.txt");
       for (int i = 0; i < strLines.length; ++i) {
         String[] arrTokens = split(strLines[i], ',');      
         float xx = float(arrTokens[2]);                     
         float yy = float(arrTokens[1]);                    
         float zz = float(arrTokens[0]);                     
         pointList.add( new PVector(xx,zz,yy) );             
       }
    }
4

3 回答 3

2

你可以继续重构,我不知道为什么他们在处理文档中展示了这种遍历 ArrayLists 的方式,但这里是重构它的一个尝试:

float calcV (float n) {
  return map (n, minX-50, minX+range+50, 0, width);
}

for (PVector V: pointList) {
  pushMatrix();
    translate(calcV(V.x), calcV(V.y), calcV(V.z));
    sphere(4);
  popMatrix();
}

您可以考虑创建一个新的类,将 PVector 作为字段或扩展 PVector。Daniel Shiffman 经常使用这种模式。创建一个类,其中一个 PVector 用于位置,另一个用于速度,一个计算每帧新位置的方法和另一个将其绘制到屏幕上的方法。这为您提供了很大的灵活性,而无需编写大量循环,除了计算和显示 ArrayList 中的对象。

于 2013-07-25T23:32:07.947 回答
0

由于 PVector 是一个对象,因此当您将 pointList 中PVector V = pointList.get(i)该特定元素的引用传递给 V 时。它是内存中的地址。所以现在 V 和 pointList.get(number) 共享相同的内存地址。任何一个的任何变化都会改变两者,因为它们是指向同一个地方的两个不同的指针。但是,如果你这样做:

PVector V = new PVector(pointList.get(i).x, pointList.get(i).y, pointList.get(i).z);

V 将是一个新对象,事情会按照你的意愿工作,因为现在 V 有它自己的内存地址。并且 poinList 将保持不变。其他对象也是如此,例如数组,试试这个看看:

int[] one = new int[3];
int[] two = new int[3];

void setup(){
  one[0] = 0;
  one[1] = 1;
  one[2] = 2;

  print("one firstPrint -> \n");
  println(one);

  two = one;

  print("two firstPrint -> \n");
  println(two);

  two[2] = 4;

  // we didn't mean to change one, but...
  print("one secondPrint -> \n");
  println(one); 
}
于 2013-07-26T04:53:41.093 回答
0

我用下面的代码修复了一些问题,但仍然对其他方式感兴趣。

for (int i=0; i < pointList.size(); i++) {
  PVector V = pointList.get(i);
  PVector W = new PVector(0.0, 0.0, 0.0);
  W.x = map(V.x, minX-50, minX+range+50, 0, width);
  W.y = map(V.y, minY-50, minY+range+50, 0, width);
  W.z = map(V.z, minZ-50, minZ+range+50, 0, width);
  pushMatrix();
  translate(W.x, W.y, W.z);
  sphere(4);
  popMatrix();
}
于 2013-07-25T21:29:10.123 回答