Parallel Coords program written with Processing can't show anything in Mac

edited October 2013 in Questions about Code

So I write a parallel coordinates program with Processing in my mac pro. However, I can't see any lines on the screen when I try to run this program. I am very confused because the program runs pretty well in my friend's windows based computer. But when I add "noLoop()" in the end of my "draw()" function, it works. Anyone knows the specific reason? Thanks in advance!

FloatTable data;

String dataPath = "cars.csv";
int numRows;
int numCols;
int[] colMin;
int[] colMax;
String[] colNames;

float plotX1, plotY1;
float plotX2, plotY2;
float diffBetweenXCoords;
PFont titleFont;
PFont labelFont;
PFont axisLimitsFont;

color[] axisColor       = { #333333, #000000 };
color[] fontAxisColor   = { #333333, #FF2222 };
color[] fontLimitsColor = { #555555, #FF2222 };
color triangleColor     = #888888;
color[] linesColor      = { #ED1317, #1397ED };

int[] axisOrder;
boolean[] axisFlipped;

// Setup
void setup()
{
  size(1000, 500);

  // Read data
  data = new FloatTable(dataPath);
  numRows = data.getRowCount();
  numCols = data.getColumnCount();
  colNames = data.getColumnNames();

  colMin  = new int[ numCols ];
  colMax  = new int[ numCols ];
  axisOrder = new int[ numCols ];
  axisFlipped = new boolean[ numCols ];

  for(int col = 0; col < numCols; col++)
  {
    float maxNumber = data.getColumnMax(col);
    float minNumber = data.getColumnMin(col);

    colMin[col] = int(floor(minNumber));
    colMax[col] = int(ceil(maxNumber));

    axisOrder[col] = col;
    axisFlipped[col] = false;
  }

  // Fonts
  titleFont = createFont("Verdana", 16);
  labelFont = createFont("Verdana Bold", 11);
  axisLimitsFont = createFont("Georgia", 11);

  // Plot area limits
  plotX1 = 30;
  plotX2 = width - plotX1;
  plotY1 = 60;
  plotY2 = height - plotY1;

  diffBetweenXCoords = (plotX2 - plotX1) / (numCols - 1);

  if(frame != null)
  {
    frame.setTitle(dataPath);
  }

  smooth();
}
     // Draw
void draw()
{
    // Background
    background(240);

    // Draw the plot area
    fill(240);
    noStroke();
    rect(plotX1, plotY1, plotX2 - plotX1, plotY2 - plotY1);

    //drawTitle();
    drawAxis();
    drawLines();
}

void drawAxis()
{
  float xCoordsForAxis = plotX1;
  float yAxisLbl = plotY2 + 40;
  float yMinLbl  = plotY2 + 15;
  float yMaxLbl  = plotY1 - 7;
  float yTriMin  = plotY1 - 25;
  float yTriMax  = plotY1 - 35;

  strokeCap(PROJECT);
  strokeWeight(1);
  stroke(0);

  for( int col = 0; col < numCols; col++, xCoordsForAxis += diffBetweenXCoords )
  {
    int colToDraw = axisOrder[col];

    // Draw Axis
    stroke(axisColor[0]);
    line(xCoordsForAxis, plotY1, xCoordsForAxis, plotY2);

    // Label min/max
    textAlign(CENTER);
    textFont(axisLimitsFont);
    fill(fontLimitsColor[0]);
    if( axisFlipped[colToDraw])
    {
      text( colMin[colToDraw], xCoordsForAxis, yMaxLbl);
      text( colMax[colToDraw], xCoordsForAxis, yMinLbl);
    }
    else
    {
      text( colMin[colToDraw], xCoordsForAxis, yMinLbl);
      text( colMax[colToDraw], xCoordsForAxis, yMaxLbl);
    }

    // Axis label
    textFont( labelFont );
    fill(fontAxisColor[0]);
    text( colNames[colToDraw], xCoordsForAxis, yAxisLbl );

    // Triangle
    fill(triangleColor);
    noStroke();
    if( axisFlipped[colToDraw] )
    {
      triangle(xCoordsForAxis - 3, yTriMax, xCoordsForAxis, yTriMin, xCoordsForAxis + 3, yTriMax);
    }
    else
    {
      triangle(xCoordsForAxis - 3, yTriMin, xCoordsForAxis, yTriMax, xCoordsForAxis + 3, yTriMin);
    }
  }
}

void drawLines()
{
  noFill();
  strokeWeight(1);

  for(int row = 0; row < numRows; row++)
  {
    beginShape();
    for(int column = 0; column < numCols; column++)
    {
      int colToDraw = axisOrder[column];
      if(data.isValid(row, column))
      {
        float cMax = ( axisFlipped[colToDraw] ? colMin[colToDraw] : colMax[colToDraw] );
        float cMin = ( axisFlipped[colToDraw] ? colMax[colToDraw] : colMin[colToDraw] );
        float value = data.getFloat(row, colToDraw);

        float x = plotX1 + diffBetweenXCoords * colToDraw;
        float y = map(value, cMin, cMax, plotY2, plotY1);

        //stroke(#5679C1);
        if(colToDraw == 0)
        {
          stroke( lerpColor(linesColor[0], linesColor[1],  map(value, cMin, cMax, 0., 1.) ), 150 );
        }
        vertex(x, y);
      }
    }
    endShape();
  }
}

Here are a few lines of my .csv file:

make    mpg cylinders   displacement (cu in)    horsepower  weight (lb) acceleration (sec)  year    origin
chevrolet   18  8   307 130 3504    12  70  1
buick   15  8   350 165 3693    11.5    70  1
plymouth    18  8   318 150 3436    11  70  1
amc 16  8   304 150 3433    12  70  1
ford    17  8   302 140 3449    10.5    70  1
ford    15  8   429 198 4341    10  70  1
chevrolet   14  8   454 220 4354    9   70  1
plymouth    14  8   440 215 4312    8.5 70  1

http://i.stack.imgur.com/rrEMX.jpg http://i.stack.imgur.com/Fis8e.png

Tagged:

Answers

  • So I add "noLoop()" in the end of my "draw()" function and it works. But I still can't believe that and obviously it is a not a good solution.

    why isn't it a good solution? the code posted doesn't seem to have an interactive component, nothing moves from one frame to the next, so why bother to redraw it 60 times a second?

    (drawAxis() and drawLines() aren't defined, i'm guessing they don't do anything odd)

  • @koogs I don't think it a good solution because I will add a lot of interactive features later. You know, in that situation, things become quite complicated with noLoop() added in the end of the draw() function.

  • edited October 2013

    Actually, noLoop() placed inside setup() would work nonetheless, since draw() is invoked at least once! ;)
    And as stated above, if it's a static drawing, there's no need to keep redrawing the same thing over & over! <):)

  • @GoToLoop But if I want to add lots of interactive features later, what should I do? Should I figure out my algorithms and methods with the noLoop() added in the end of the draw() function? I don't think that is a wonderful solution... :(

  • edited October 2013

    I don't think it a good solution because I will add a lot of interactive features later.

    If that is 100% input event driven, you can use redraw() to control exactly when draw() is invoked within those triggers!

  • @GoToLoop Unfortunately, it is not 100% input event driven. In fact, it is necessary to keep running the draw() function to detect the position of the mouse. When the mouse is close to some of my lines, some other function will be called.

  • edited October 2013

    ... it is necessary to keep running the draw() function to detect the position of the mouse.

    Mouse coordinates are updated automatically by mouseX & mouseY variables (also pmouseX & pmouseY).
    And they can be accessed anywhere. In no way draw() exclusive! [-(

    Moreover, checking anything can be done anywhere as well. That includes checking positions of drawings & shapes! :ar!

    Like I've stated above, simply use redraw() when something actually changes in canvas and needs to be re-rendered. :P

  • njuhobby, ok, i understand. was just going on what was posted (which is all i can do!)

    would be easier for us to debug with those two methods added and a sample .csv file

  • @GoToLoop Yeah, I understand that checking anything can be done anywhere as well, but in the end, I still have to keep running one function, maybe not draw(), to keep tracking the position of the mouse.

  • @koogs have already added the two methods as you required, but I don't know how to upload my files (kind of embarrassing:()). Could you please tell me how? Thank you very much.

  • edited October 2013 Answer ✓

    ... I still have to keep running one function..., to keep tracking the position of the mouse.

    Processing got 2 event functions for whenever mouse moves: mouseMoved() & mouseDragged().
    We can read mouseX & mouseY from there. And if needed to render canvas again, call redraw() afterwards. :)>-

    What I'm trying to tell you all along is that in the case your program does nothing until its user does some input,
    you can disable draw() w/ noLoop(), and invoke it on demand w/ redraw() to render canvas again within the various input functions.

    That's what I mean by 100% input event driven programs! :P

  • @GoToLoop That seems to be a good solution, thanks very much. However, I still don't know why the result is totally different between a windows-based computer and my mac pro. That's very weird and there has to be an answer for this..... I really want to figure it out.

  • edited October 2013

    Sorry I can't help ya! For I don't own a Mac nor I know what is a FloatTable! ~X(

  • @GoToLoop Really appreciate your help, thanks. :)

  • I don't know how to upload my files

    csv files will just be text, one record per line. just copy and paste a few lines of that into text box here. we don't need all of it, just enough to give us an idea.

  • (and FloatTable is a thing from Visualising Data book. it just wraps a csv / tsv file.)

  • @koogs I have already added some lines of my .csv file here. As to the FloatTable, just like you mentioned, it is a thing from Visualising Data book. :)

  • edited October 2013

    For anyone trying to replicate the problem, a related class is Ben Fry's FloatTable class.

    And you can load in a csv/tsv formatted file; here the OP is trying to use the cars dataset.

    I have gotten the code working in updated versions of Processing on both Windows & Mac OS 10.7, so it is likely some issue with your specific machine and version of Processing. That is my best guess.

    Are you able to draw anything on the screen on the Mac? The text does not even look like it is getting drawn, so I would wonder if you could even put a rectangle on the screen. This just seems rather odd that you got nothing displayed. Could it possibly be a driver issue? Does a regular sketch have an issue? If not, some part of your added code is causing the issue...

    If the issue is that the draw loop is getting called too quickly, you could try playing with the frame rate command, to see if you slow it down if anything shows up. Not sure if that is your issue though...

    As the other posters have mentioned, you can always use noLoop & redraw to keep track of the dynamic drawing. For adding in animation, you would want to stay in the draw loop till the animation is complete (and then go back to noLoop).

  • is nothing being displayed or is it, as suggested, being drawn and then erased? seeing things when using noLoop would suggest things are being drawn. using saveFrame(); at the bottom of draw might also show what's going on.

    (haven't had a chance to run the code myself yet. and it's doubtful that'll really help as i'm on neither windows nor mac)

    The text does not even look like it is getting drawn

    is that a font thing? what happens if a font doesn't exist? does it degrade gracefully? (again, it working with noLoop points to this not being a problem)

  • also, this:

    background(240);
    
    // Draw the plot area
    fill(240);
    noStroke();
    rect(plotX1, plotY1, plotX2 - plotX1, plotY2 - plotY1);
    

    looks like it's doing the same thing twice - clearing the entire screen and then drawing a background-coloured rectangle with no border over (part of) the screen.

  • edited October 2013

    shows up fine in linux.

    i did have an initial problem that looked like you describe when i had a space delimited data file, before replacing the spaces with tabs. after that it worked fine (but maxed out one processor redrawing data that didn't change... a personal bugbear)

    (and, yes, the rect() was unnecessary)

  • I can verify that this issue occurs on my 13" Macbook Retina, Spring 2013, using Mac OS 10.8 and the most recent version of Processing. It does NOT occur if I run Processing in a VM on Ubuntu or Windows 8.

    This issue has something to do with the lines, draw cycle, & render mode.

    Drawing less lines, or drawing rectangles instead, and everything shows up again.

    Using noLoop() or limiting the draw cycle (lowering the frame rate) also causes it to show up for me.

    Also, changing my renderer to P2D in the size() command ALSO fixes the issue...

    I am guessing this is SOME form of bug with the default renderer, for a limited set of hardware/software (OS). I am going to try and create a simpler Processing sketch that results in this same issue...

  • Here is a slimmed down single-sketch version of the OP's code. It simply draws ~3,200 lines on the screen per each draw cycle, random locations & colors:

    int numRows = 400;
    int numCols = 8;
    
    float plotX1, plotY1, plotX2, plotY2, diff;
    
    void setup(){
      size(1000, 500);
      plotX1 = 30;
      plotX2 = width - plotX1;
      plotY1 = 60;
      plotY2 = height - plotY1;
      diff = (plotX2 - plotX1) / (numCols - 1);
    }
    
    void draw(){
      background(255);
    
      fill(77);
      rect(plotX1, plotY1, plotX2 - plotX1, plotY2 - plotY1);
    
      drawManyLines();
    }
    
    void drawManyLines(){
      noFill();
      for(int row = 0; row < numRows; row++){
        for(int col = 1; col < numCols; col++){
          float x1 = plotX1 + diff * (col - 1);
          float x2 = x1 + diff;
          float y1 = random(plotY1, plotY2);
          float y2 = random(plotY1, plotY2);
          if(col == 1)
            stroke(random(255), random(255), random(255));
          line(x1, y1, x2, y2);
        }
      }
    }
    

    When this works, it has many randomized, flashing lines. When it doesn't work, you get nothing (not even a background color).

    All the same "solutions" mentioned above work on this code, too.

  • I have asked a few people to test this out.

    From what I can tell, this is a bug that affects Macs with a Retina display.

  • This bug has been reported previously.

    It is an issue with Java & Retina displays on Mac OS X. It will be fixed in the next version of Processing.

Sign In or Register to comment.