Interactive display of statements

Hi all! I'm posting this question as the related question was marked answered by @GoToLoop, and I still have questions.

I'm a data journalist doing an analysis of speeches from the remaining three candidates. I have three separate file containing key context words from their speeches. The file contains columns for category, statement, nchar, and polarity (i.e., pos, neg, neut). What I'm trying to do is make the sketch so that when someone clicks the mouse INSIDE the blank canvas, the statements belonging to a specific category will appear, and they will be colored according to polarity. They have to click inside the canvas because there will be three of these, one for each candidate. Then, the next time the mouse is clicked, it cycles through another category. I also want the statements to appear on the canvas with a random placement.

I've adapted the code from the previous question about interactivity, and tried to add functions to map the placement of statements as random and color of the statements according to polarity.

Here is the adapted code I've written, which is NOT working.

/**
 * Clickable Random Image Groups (v1.0.1)
 * GoToLoop (2016-Jun-02)
* Adapted by ldlpdx (2016-Jun-03)
 *
 * forum.Processing.org/two/discussion/16944/
 * interactivity-changing-image-upon-click-in-p5js
*/


var clContext;

"use strict";

const STATEMENTS = 118, CATEGORY = 3, QTY = STATEMENTS/CATEGORY | 0,
      POLARITY = 3,
      states = Array(STATEMENTS), inds = Array(CATEGORY), cols = Array(POLARITY);

      //load the table of Clinton's words and frequencies
function preload() {
        clContext = loadTable("cl_context.csv", "header");
      }

function setup() {
  createCanvas(400, 647);
  // iterate over the table rows
  for(var i=0; i<clContext.getRowCount(); i++){
      //- Get data out of the relevant columns from each row -//
      var cat = clContext.get(i, "category");
      var states = clContext.get(i, "statement");
      var polarity = clContext.get(i, "polarity")
    }

  for (let i = 0; i < CATEGORY; randomCategoryStates(i++));
}

function draw() {
  background(51);
  // Calling noStroke once here to avoid unnecessary repeated function calls
  noStroke();

  for(var i=0; i<cat.length; i++) {
      inds[i].display();
  }
}

function mouseClicked() {
  if((mouseIsPressed < width) && (mouseIsPressed < height)) {
  randomCategoryStates(~~random(CATEGORY));
  redraw();
}

// Function to display statements by category in a random fashion
function randomCategoryStates(cat) {
  let idx = inds[cat], rnd;
  while ((rnd = ~~random(QTY)) == idx);
  inds[cat] = rnd;
}

// Function to align statements, categories, and polarity
function Statements() {
  this.x = x;
  this.y = y;
  this.cat = cat;
  this.statement = statement;
  this.polarity = polarity;
  // set a random x,y
  this.vx = random();
  this.vy = random();
}

// Attach pseudo-class methods to prototype;
// Maps polarity to color and x,y to random placement on canvas
Statement.prototype.display = function() {
  this.x += this.vx;
  this.y += this.vy;
  var cols = map(this.polarity == -1, 205, 38, 38);
  var cols = map(this.polarity == 0, 148, 0, 211);
  var cols = map(this.polarity == 1, 0, 145, 205);
  fill(cols);
  textSize(14);
  text(this.statement, this.x, this.y);
};

I'm VERY new to non-R programming, so any advice would be most appreciated. Thanks! :)

Tagged:

Answers

  • In what way is it not working?

  • Hi @blindfish. I'm getting an error in the Dev Console that says Did you just try to use p5.js's exp() function? If so, you may want to move it into your sketch's setup() function.

    For more details, see: https://github.com/processing/p5.js/wiki/Frequently-Asked-Questions#why-cant-i-assign-variables-using-p5-functions-and-variables-before-setup"
    

    Also getting this error:

    sketch.js:81 Uncaught SyntaxError: Unexpected end of input
    

    Both errors I've seen before, but the first one, especially, confuses me, as the "for more details" link says nothing about the exp() function. That, and I don't have any functions before setup, except reading in the data file, which is allowed.

    I think the problem lies somewhere in the code I've adapted from @GoToLoop. I had trouble figuring out which constant to put where, an I fear that I've completely messed it up. And, of course, there is my utter inexperience with JavaScript. :(

  • you don't use the exp() function.

    but what I found in the link you posted is:

    when declaring variables before setup(), you will need to assign them values inside setup() if you wish to use p5 functions.

    this could be referring to Array etc.

    so place them into setup() please

    so before setup()

    const STATEMENTS = 118, CATEGORY = 3, QTY = STATEMENTS/CATEGORY | 0,
          POLARITY = 3,
          states, inds, cols;
    

    in setup()

          states = Array(STATEMENTS);
          inds = Array(CATEGORY);
          cols = Array(POLARITY);
    

    maybe even this needs to move QTY = STATEMENTS/CATEGORY | 0

    the 2nd error

    the 2nd error

    sketch.js:81 Uncaught SyntaxError: Unexpected end of input

    is a riddle for me - is 81 the line number? What input is meant here?

  • edited June 2016

    This could be referring to Array, etc. So place them into setup() please.

    Array is 1 of the many JS standard built-in objects/classes. They're always available everywhere: L-)

    For specific p5js API, go here: http://p5js.org/reference/

  • but what is the error referring to, then?

  • Also, I do actually call those arrays in Setup. I had mistakenly called inds cat, but have changed that in my js code.

    The unexpected end of input refers to the last line of the code. I'm not sure why it's doing that, as a previous sketch used that same syntax at the end and it worked fine.

  • edited June 2016
    • No idea! But the sketch is fulla errors! =;
    • For example, class Statement's constructor is plural @ line #61: function Statements() {.
    • Also there's no parameter declaration there! It simply access the global 1s instead! @-)
    • Actually, class Statement is never used. So it's moot!
  • If it would help to see the data file, you can find it here. It's 'cl_context.csv'.

    https://github.com/ldlathrop/Data-Visualization/tree/master/context-clouds

  • You had missed a closing brace on the mouseClicked function... I also fixed the inconsistency with Statement(s). As GoToLoop says your Statement function didn't accept any named parameters: I've added these based on the properties you're storing...

    The following still throws an error. You presumably intend to populate inds with Statement instances - or is this something you intend to pass to your Statement? At the moment you appear to be feeding it random numbers; which as the console says don't have a display function.

    var clContext;
    
    "use strict";
    
    const STATEMENTS = 118, CATEGORY = 3, QTY = STATEMENTS/CATEGORY | 0,
          POLARITY = 3,
          states = Array(STATEMENTS), inds = Array(CATEGORY), cols = Array(POLARITY);
    
          //load the table of Clinton's words and frequencies
    function preload() {
            clContext = loadTable("cl_context.csv", "header");
          }
    
    function setup() {
      createCanvas(400, 647);
      background(51);
      // Calling noStroke once here to avoid unecessary repeated function calls
      noStroke();
      // iterate over the table rows
      for(var i=0; i<clContext.getRowCount(); i++){
          //- Get data out of the relevant columns for each row -//
          var inds = clContext.get(i, "category");
          var states = clContext.get(i, "statement");
          var cols = clContext.get(i, "polarity")
        }
    
      for (let i = 0; i < STATEMENTS; randomCategoryStates(i++));
    
        console.info(inds);
    }
    
    
    function draw() {
      for(var i=0; i<states.length; i++) {
          inds[i].display();
      }
    }
    
    
    function mouseClicked() {
      if((mouseIsPressed < width) && (mouseIsPressed < height)) {
          randomCategoryStates(~~random(CATEGORY));
          redraw();
      }
    //missed closing brace:
    }
    
    
    // Function to display statements by category in a random fashion
    function randomCategoryStates(cat) {
      let idx = inds[cat], rnd;
      while ((rnd = ~~random(QTY)) == idx);
      inds[cat] = rnd;
    }
    
    // Function to align statements, categories, and polarity
    //Statement or Statements?
    function Statement(x, y, inds, statement, polarity) {
      this.x = x;
      this.y = y;
      this.cat = inds;
      this.statement = statement;
      this.polarity = polarity;
      // set a random x,y
      this.vx = random();
      this.vy = random();
    }
    
    // Attach pseudo-class methods to prototype;
    // Maps polarity to color and x,y to random placement on canvas
    Statement.prototype.display = function() {
      this.x += this.vx;
      this.y += this.vy;
      //var cols = map(this.polarity, -1, 1, #cd2626, #9400d3 #009acd); // Something appears wrong here, trying to map 3 categories to 3 hex colors
      var cols = map(this.polarity == -1, 205, 38, 38);
      var cols = map(this.polarity == 0, 148, 0, 211);
      var cols = map(this.polarity == 1, 0, 145, 205);
      fill(cols);
      textSize(14);
      text(this.statement, this.x, this.y);
    };
    
  • edited June 2016

    Just a little detail: "use strict"; only kicks in when it's the 1st statement from the <script>.
    Or individually for each function when it's also its 1st statement.
    Therefore, place "use strict"; before var clContext;, making it the 1st statement. ;;)

    https://developer.Mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

  • edited June 2016

    I truly appreciate all of your comments and advice, @blindfish and @GoToLoop. I think maybe I'm almost there with the changed sketch below. I've added some more comments about things that confuse me and are throwing errors.

    "use strict";
    
    var clContext;
    var x;
    var y;
    
    const STATEMENTS = 118, CATEGORY = 3, QTY = STATEMENTS/CATEGORY | 0,
          POLARITY = 3,
          states = Array(STATEMENTS), inds = Array(CATEGORY), polarity =     Array(POLARITY);
    
          //load the table of Clinton's statements, categories, and polarity
    function preload() {
                clContext = loadTable("cl_context.csv", "header");
              }
    
    function setup() {
      createCanvas(647, 400);
      background(51);
      // Calling noStroke once here to avoid unecessary repeated function calls
      noStroke();
      // iterate over the table rows
      for(var i=0; i<clContext.getRowCount(); i++){
      //- Get data out of the relevant columns for each row -//
          var inds = clContext.get(i, "category");
          var states = clContext.get(i, "statement");
          var polarity = clContext.get(i, "polarity")
        }
    
      for (let i = 0; i < states; randomCategoryStates(i++));
      // create the inds object and add to
      // the states array for use later
      inds[i] = new Statement();
    //**Not sure what this does.**
        console.info(inds);
    }
    
    function draw() {
      for(var i=0; i<states.length; i++) {
          // **inds[i].display(); was throwing an error that there was no inds function, so added inds[i] = new Statement(): under setup
              inds[i].display();
          }
        }
    
    function mouseClicked() {
      if((mouseIsPressed < width) && (mouseIsPressed < height)) {
          randomCategoryStates(~~random(CATEGORY));
          redraw();
      }
    //missed closing brace:
    }
    
    
    // Function to display statements by a random category with each mouse click
    // **Not sure if this is right. Trying to pull the set of 'states' that match a 
    // given category. So is 'inds[states]' the right syntax?**
        function randomCategoryStates(states) {
          let idx = inds[states], rnd;
          while ((rnd = ~~random(QTY)) == idx);
          inds[states] = rnd;
        }
    
    // Function to align statements, categories, and polarity
    //Statement or Statements?
    function Statement() {
      this.x = x;
      this.y = y;
      this.cat = inds;
      this.statement = states;
      this.polarity = polarity;
      // set a random x,y
    
    // **Found this on a StackOverflow entry. Trying to get the category-based
    // 'states' to appear at random x,y coordinates on the canvas. But am getting 
    //an "Uncaught type error:Cannot read property 'random' of undefined"**
      this.vx = (this.x.random ? WIDTH*Math.random() : this.x.xdef);
      this.vy = (this.y.random ? HEIGHT*Math.random() : this.y.ydef);
    }
    
    // Attach pseudo-class methods to prototype;
    // Maps polarity to color and x,y to random placement on canvas
    Statement.prototype.display = function() {
      this.x += this.vx;
      this.y += this.vy;
    // **Is this the right way to map the various polarities to specific colors
    // or can it be done with one line?**
      var cols = map(this.polarity == -1, 205, 38, 38);
      var cols = map(this.polarity == 0, 148, 0, 211);
      var cols = map(this.polarity == 1, 0, 145, 205);
      fill(cols);
      textSize(14);
      text(this.statement, this.x, this.y);
    };
    

    To make my questions easier to find, I've preceded them with "**". I do think I'm getting close, but still need to work out some kinks. Thanks!

  • edited June 2016 Answer ✓
  • @ldlpdx I've taken pity on you and commented up your code. It's currently far too broken for me to fully understand what you're trying to achieve; let alone fix :/

    "use strict";
    
    var clContext;
    var x;
    var y;
    
    const STATEMENTS = 118, 
          CATEGORY = 3, 
          QTY = STATEMENTS/CATEGORY | 0,
          POLARITY = 3,
          states = Array(STATEMENTS), 
          inds = Array(CATEGORY), 
          polarity = Array(POLARITY);
    
    //load the table of Clinton's statements, categories, and polarity
    function preload() {
        clContext = loadTable("cl_context.csv", "header");
    }
    
    function setup() {
        createCanvas(647, 400);
        background(51);
        // Calling noStroke once here to avoid unecessary repeated function calls
        noStroke();
        // iterate over the table rows
        for(var i=0; i<clContext.getRowCount(); i++){
            //- Get data out of the relevant columns for each row -//
    
            // >> There is no block level scope in (old) JS
            // >> These variables are all hoisted to
            // >> the top of the function.  Also the variable
            // >> name 'inds' has already been assigned
            // >> to an array globally; but you
            // >> overwrite it here!!!
            var inds = clContext.get(i, "category");
            var states = clContext.get(i, "statement");
            var polarity = clContext.get(i, "polarity")
        }
    
        // >> this looks like some of GoToLoop's evil optimisation/obfuscation
        // >> do you understand what it does?
        for (let i = 0; i < states; randomCategoryStates(i++));
    
        // create the inds object and add to
        // the states array for use later
    
        // >> because of what you've done above inds 
        // >> is no longer an array.
        // >> Output the value of inds so you can establish why things are broken:
        console.log(inds);
        // >> So trying to reference an element of the 'array'
        // >> will definitely fail here...
        inds[i] = new Statement();
        //**Not sure what this does.**
        // >> See above. The code fails on the above statement so it does nothing.
        console.info(inds);
    }
    
    function draw() {
        for(var i=0; i<states.length; i++) {
            // **inds[i].display(); was throwing an error that there was no inds function, so added inds[i] = new Statement(): under setup
            inds[i].display();
        }
    }
    
    function mouseClicked() {
        if((mouseIsPressed < width) && (mouseIsPressed < height)) {
            randomCategoryStates(~~random(CATEGORY));
            redraw();
        }
    //>> missed closing brace:
    }
    
    
    // Function to display statements by a random category with each mouse click
    // **Not sure if this is right. Trying to pull the set of 'states' that match a 
    // given category. So is 'inds[states]' the right syntax?**
    
    // >> it might be if inds was actually an array...
    function randomCategoryStates(states) {
        let idx = inds[states], rnd;
        while ((rnd = ~~random(QTY)) == idx);
        inds[states] = rnd;
    }
    
    // Function to align statements, categories, and polarity
    //Statement or Statements?
    function Statement() {
        this.x = x;
        this.y = y;
        this.cat = inds;
        this.statement = states;
        this.polarity = polarity;
        // set a random x,y
    
        // **Found this on a StackOverflow entry. Trying to get the category-based
        // 'states' to appear at random x,y coordinates on the canvas. But am getting 
        //an "Uncaught type error:Cannot read property 'random' of undefined"**
    
        // >> it's a good idea to include a link to your source.
        // >> That would make it easier to confirm if
        // >> it's applicable here.  It's not clear what you
        // >> intend to pass to your x and y properties
        // >> but I see no evidence that you've created an object
        // >> to store the sub-properties referenced here.
        // >> ergo: don't use code from SO if you don't
        // >> understand it at all...
        this.vx = (this.x.random ? WIDTH*Math.random() : this.x.xdef);
        this.vy = (this.y.random ? HEIGHT*Math.random() : this.y.ydef);
    }
    
    // Attach pseudo-class methods to prototype;
    // Maps polarity to color and x,y to random placement on canvas
    Statement.prototype.display = function() {
        this.x += this.vx;
        this.y += this.vy;
        // **Is this the right way to map the various polarities to specific colors
        // or can it be done with one line?**
    
        // >> not sure what you're trying to do here; but
        // >> this is invalid code: you can't have
        // >> 3 variable definitions with the
        // >> same variable name...  And at best the latter would 
        // >> overwrite the previous
        var cols = map(this.polarity == -1, 205, 38, 38);
        var cols = map(this.polarity == 0, 148, 0, 211);
        var cols = map(this.polarity == 1, 0, 145, 205);
        fill(cols);
        textSize(14);
        text(this.statement, this.x, this.y);
    };
    

    TBH I think you need to do some more work on JS basics before attempting something this ambitious. I highly recommend the You don't know JS series

  • edited June 2016

    Thanks for the book tip, @blindfish. I do have a very thick, very heavy JS reference, but I will check that one out, too. My code is actually now significantly different from what I initially posted in this question. Perhaps, in asking a separate question elsewhere in this forum, I should have posted the whole, revised script, but it seemed unnecessary, since I've marked this question as "answered" and I'm only asking about an isolated, very specific issue.

    I appreciate your going through and commenting, but I was able to figure out how to make JS do what I wanted and the complete code is very different from what's posted here, which is why I closed this question. Everything in my current code works without throwing any errors. It displays the statements randomly on the screen, moves them off and back on the canvas, colors them according to their polarity, and the animation starts and stops when a user clicks inside the canvas. It's all good, except...

    There's just one last piece that I need help with as I'm having an awful time trying to get it to display the sentences on multiple lines. So that's why I posted it as a new and separate question here. I just need help on this isolated issue, not the broader script. Thanks, though!

Sign In or Register to comment.