Math and processing


#1

I am working as a teacher and I would like to use Processing for solving simple mathematical equations. Eg. If one have the two equations:

  1. Y = 2X + 1
  2. Y = - X + 4
    I would like the students to make a program that finds the correct value for X and Y. graf

Any suggestions on how to best do this? Any libraries etc I can used that will move the coordinate system so that 0.0 is in the middle of the screen?


#2

let’s say for just one function and a static grid
could start with basics

// grid of rectangles
int x = 0, y = x, w = 25, h = w, offset = 0;
int grid = 20, many = grid*grid;
boolean diagp = true;

void setup() {
  size(500, 500);
}

void draw() {
  background(200, 200, 0);
  draw_grid();
  draw_f1();
}


void draw_grid() {
  noFill();
  stroke(0, 0, 200);
  for (int i = 0; i < many; i++)  rect(x+(i%grid)*(w+offset), y+(floor(i/grid))*(h+offset), w, h);   // or any other shape/text on that position
  translate(width/2, height/2);
  fill(200, 200, 0);
  circle(0, 0, 4);                           // show center point
}

void draw_f1() {
  push();
  int steps = 100;
  float X, Y, Xo=0, Yo=0, f_start_x = -10, f_stop_x = 10;
  float dx = (f_stop_x - f_start_x )/float(steps);

  for ( int i = 0; i< steps; i++) {
    X = f_start_x + dx * i;
    Y = 2.0 * X*X -8;   // 2.0*X +1.0                              // The function to plot
    if ( i == 0 ) {                         // need a dummy init for first line
      Xo=X; 
      Yo=Y;
    }
    if ( diagp ) println("i "+i+" X "+nf(X, 1, 1)+" Y "+nf(Y, 1, 1));
    stroke(200, 0, 0);
    fill(200, 0, 0);
    line(Xo*w, -Yo*w, X*w, -Y*w);            // in processing draw y negativ!
    circle(X*w, -Y*w, 2);
    Xo = X;                                  // remember for next line
    Yo = Y;
  }
  diagp= false;                              // print list only one time
  pop();
}

a more interesting version would be here

but that is just a show of one function?? far from

rev for 2 functions and rearranged so the USER == students area is more visible


// USER FUNCTION
float y1(float x) {
  return 2.0 * x * x -8.0;   // 2.0*x +1.0        // The function to plot
}

float y2(float x) {
  return -x + 4.0;                                // The function to plot
}

//______________________________________________________________________
void setup() {
  size(500, 500);
  noLoop();
}

void draw() {
  background(200, 200, 0);
  draw_grid();
  draw_function(1);
  draw_function(2);
}

// can be other TAB..

void draw_function(int k) {
  push();
  int steps = 100;
  float X, Y=0, Xo=0, Yo=0, f_start_x = -10, f_stop_x = 10;
  float dx = (f_stop_x - f_start_x )/float(steps);
  if ( k == 1 ) {
    stroke(200, 0, 0);
    fill(200, 0, 0);
  }
  if ( k == 2 ) {
    stroke(0, 200, 0);
    fill(0, 200, 0);
  }

  for ( int i = 0; i< steps; i++) {
    X = f_start_x + dx * i;
    if ( k == 1 ) Y = y1(X);
    if ( k == 2 ) Y = y2(X);
    if ( i == 0 ) {                          // need a dummy init for first line
      Xo=X; 
      Yo=Y;
    }
    line(Xo*w, -Yo*w, X*w, -Y*w);            // in processing draw y negativ!
    circle(X*w, -Y*w, 2);
    Xo = X;                                  // remember for next line
    Yo = Y;
  }
  pop();
}

// grid of rectangles
int x = 0, y = x, w = 25, h = w, offset = 0;
int grid = 20, many = grid*grid;

void draw_grid() {
  noFill();
  stroke(0, 0, 200);
  for (int i = 0; i < many; i++)  rect(x+(i%grid)*(w+offset), y+(floor(i/grid))*(h+offset), w, h);   // or any other shape/text on that position
  translate(width/2, height/2);
  fill(200, 200, 0);
  circle(0, 0, 4);                           // show center point
}


#3

what do you mean by this? Just that the students are able to read the values in the graph / graphic?

Then your goal is to draw a graph in a coordinate system graphically?


#4

What do you expect the students to do?

  • write program code to solve simultaneous equations?
  • write a program to display the two equations and the students find the intersection
  • be able to enter the equations at runtime and the program displays the two functions.

To get a good answer your question must be clear and unambiguous.


#5

Sorry for beeing unclear but I would like the students to write a program that both calculates the values for x and y and simultaniously show the graph for the two functions to visually display the intersection. I basically need a standard coordinate system as shown in the image I posted.


#6

The problem is not just about moving the origin, you can use translate(x,y) for that. You also need to reverse the Y axis to convert the display coordinate system to a traditional mathematics coordinate system, you could use scale(1,-1) to do that. You could provide a bootstrap program where the student add methods to draw the equations, thus removing some of the programming complexity.

I am not aware of a library to do this for you. Sorry.


#7

using kll version

and adding some stuff, e.g. getXtoDraw and getYtoDraw to convert from real calculated values to screen / pixels






//consts
color RED  = color(255, 0, 0); 
color BLUE = color(0, 0, 255); 

// grid of rectangles
int x = 0, y = 0, 
  w = 25, h = w, 
  offset = 0;

int grid = 20, 
  many = grid*grid;

float f_start_x = -10, 
  f_stop_x = 10;
int steps = 100;
float dx = (f_stop_x - f_start_x )/float(steps);

boolean diagPrintlnFlag = true;

// -----------------------------------------------------------------------

void setup() {
  size(500, 500);
}

void draw() {
  background(255);
  draw_grid();
  draw_f1(RED);
  draw_f2(BLUE);
}

// -----------------------------------------------------------------------
// TWO FUNCTIONS

void draw_f1(color col1) {
  // function 
  pushMatrix();
  translate(width/2, height/2);

  float X, Y, 
    f_start_x = -10, 
    f_stop_x = 10;
  float dx = (f_stop_x - f_start_x )/float(steps);

  float xToDraw, yToDraw;
  float xToDrawPrevious=0, yToDrawPrevious=0; 

  for ( int i = 0; i < steps; i++) {
    X = f_start_x + dx * i;


    // ***********************************************************************
    // The function to plot
    Y = 2*X + 1;  // change this line
    //Y = 2.0 * X*X -8;   // 2.0*X +1.0
    // ***********************************************************************

    xToDraw = getXtoDraw(X); 
    yToDraw = getYtoDraw(Y); 

    if ( i == 0 ) {                         // need a dummy init for first line
      xToDrawPrevious=xToDraw; 
      yToDrawPrevious=yToDraw;
    }
    if ( diagPrintlnFlag ) {
      println("i "+i+" X "+nf(X, 1, 1)+" Y "+nf(Y, 1, 1));
    }
    stroke(col1);
    fill(col1);
    line(xToDrawPrevious, yToDrawPrevious, xToDraw, yToDraw) ;    
    //ellipse(X*w, -Y*w, 2, 2);

    // remember for next line
    xToDrawPrevious=xToDraw; 
    yToDrawPrevious=yToDraw;
  }//for 

  // print list only one time
  diagPrintlnFlag= false;                            
  popMatrix();
}

void draw_f2(color col1) {
  // function 
  pushMatrix();
  translate(width/2, height/2);

  int steps = 100;
  float X, Y;  


  float xToDraw, yToDraw;
  float xToDrawPrevious=0, yToDrawPrevious=0; 

  for ( int i = 0; i < steps; i++) {
    X = f_start_x + dx * i;

    // ***********************************************************************
    // The function to plot
    Y =  - X + 4;    // change this line
    //Y = 2.0 * X*X -8;   // 2.0*X +1.0  
    // ***********************************************************************

    xToDraw = getXtoDraw(X); 
    yToDraw = getYtoDraw(Y); 

    if ( i == 0 ) {                         // need a dummy init for first line
      xToDrawPrevious=xToDraw; 
      yToDrawPrevious=yToDraw;
    }
    if ( diagPrintlnFlag ) {
      println("i "+i+" X "+nf(X, 1, 1)+" Y "+nf(Y, 1, 1));
    }
    stroke(col1);
    fill(col1);
    line(xToDrawPrevious, yToDrawPrevious, xToDraw, yToDraw) ;    
    //ellipse(X*w, -Y*w, 2, 2);

    // remember for next line
    xToDrawPrevious=xToDraw; 
    yToDrawPrevious=yToDraw;
  }//for 

  // print list only one time
  diagPrintlnFlag= false;                            
  popMatrix();
}

// ------------------------------------------------------------
// coordinate function

void draw_grid() {

  stroke(0);
  for (int i = 0; i < many; i++) {
    noFill();
    rect(x+(i%grid)*(w+offset), y+(floor(i/grid))*(h+offset), 
      w, h);   // or any other shape/text on that position
  }

  // small lines with numbers ||||||||||| : for x axis 
  pushMatrix();
  translate(width/2, height/2);
  strokeWeight(3);
  textSize(15); 
  textAlign(CENTER);
  fill(0); // BLACK 
  stroke(0); // BLACK
  for (int i = -grid/2; i < grid/2; i++) {
    float dx = 1;
    float X =  dx * i;
    if (X==0 || X==-10) 
      continue;  // skip those numbers 
    float xToDraw = getXtoDraw(X); 
    float yToDraw = getYtoDraw(0); 
    line(xToDraw, yToDraw, xToDraw, yToDraw+11);
    text(i, 
      xToDraw-1, yToDraw+29-1);
  }//for 
  textAlign(LEFT);
  strokeWeight(1);
  popMatrix(); 

  // small lines with numbers : for y axis 
  pushMatrix();
  translate(width/2, height/2);
  stroke(0); // BLACK
  fill(0);  // BLACK
  textSize(15); 
  textAlign(CENTER);
  strokeWeight(3);
  for (int i = -grid/2; i < grid/2; i++) {
    float dx = 1;
    float Y =  dx * i;
    if (Y==0 || Y==9 || Y==-1) 
      continue;  // skip these numbers 
    float xToDraw = getXtoDraw(0); 
    float yToDraw = getYtoDraw(Y); 
    line(xToDraw, yToDraw, xToDraw+11, yToDraw);
    text(i, 
      xToDraw+24, yToDraw);
  }//for 
  textAlign(LEFT);
  strokeWeight(1);
  popMatrix(); 

  // show arrows and texts X and Y 
  decoration(); 

  // show center point
  pushMatrix();
  translate(width/2, height/2);
  // fill(200, 200, 0);
  fill(255, 0, 0);
  ellipse(0, 0, 5, 5);       // show center point
  popMatrix();
}

void decoration () {
  stroke(0); 
  strokeWeight(3); 
  // line |
  line ( width/2, 0, width/2, height); 

  // line -
  line ( 0, height/2, width, height/2);


  // texts X and its arrow ---
  fill(0); 
  textSize(24);
  text ( "X", 
    width-17, height/2+31); 

  pushMatrix();
  noFill();
  strokeWeight(3.0);
  strokeJoin(MITER);
  beginShape();

  float f = 3.0; 
  translate(width-f*7, height/2);  

  vertex(3.5*f, -3.0*f);
  vertex(6.5*f, 0.0*f);
  vertex(3.5*f, 3.0*f);
  endShape();
  strokeWeight(1); 
  popMatrix();

  // texts Y and its arrow --- 
  fill(0); 
  textSize(24);
  text ( "Y", 
    width/2+10, 22); 

  pushMatrix();
  noFill();
  strokeWeight(3.0);
  strokeJoin(MITER);
  beginShape();
  translate(width/2, 22);  
  f = 3.0; 
  vertex( -3.0*f, -3.5*f);
  vertex( 0.0*f, - 6.5 * f);
  vertex( 3.0*f, - 3.5 * f);
  endShape();
  strokeWeight(1); 
  popMatrix();

  strokeWeight(1);
}

// ------------------------------------------------------------
// HELPER FUNCTIONS 

float getXtoDraw(float xin_) {
  return xin_*w;
} 

float getYtoDraw(float yin_) {
  // in processing draw y negativ!
  return -yin_*w;
}
//

#8

Here’s an animation I made using p5.js without inverting the y-axis.

let x;
let epsilon;

function setup() {
  createCanvas(400, 400);
  x = 0;
  epsilon = 3;
}

function draw() {
  if (abs(f(x) - g(x)) < epsilon) {
    print(`Solution: (${x}, ${f(x)})`);
    stroke(255, 0, 0);
    strokeWeight(2);
  } else {
    stroke(0);
    strokeWeight(1);
  }

  point(x, f(x));
  point(x, g(x));
  x++;
}

function f(x) {
  return 5*x + 2;
}

function g(x) {
  return -2*x + height;
}

@kll l I’m also a teacher and am working with @ashneel.das and @bhaskar on a GSoC proposal for an addon math library. We’ve been discussing ideas for modules (common math classes) and implementation (TensorFlow.js for linear algebra and ml5.js for machine learning). We’d love your input!

Personally, I’d like to teach Algebra 2 students to solve linear systems using matrices with p5.js. One thought for a lesson is an Etch A Sketch game wherein each player has control over the orientation of their knob. Each player’s orientation would be stored as elements in a matrix that is then solved to find the solution, if any. A preview of their solution would be visible on the canvas at all times, and they could press space to draw the solution (etch). I’ll write up an example later.


#9

put the ideas together, thanks
@Chrisir @mcintyre


// USER FUNCTION
float y1(float x) {
  return 2.0 * x * x -8.0;   // 2.0*x +1.0        // The function to plot
}

float y2(float x) {
  return -x + 4.0;                                // The function to plot
}

//______________________________________________________________________
void setup() {
  size(500, 500);
  noLoop();
}

void draw() {
  background(255);
  draw_grid();
  translate(width/2, height/2);
  draw_functions();
}

// can be other TAB..
static int steps = 100;
float X, Y1, Y2, Xo=0, Y1o, Y2o;
static float f_start_x = -10, f_stop_x = 10;
static float dx = (f_stop_x - f_start_x )/float(steps);
static float epsilon = 1;                         // @mcintyre

//consts
color RED  = color(255, 0, 0); 
color BLUE = color(0, 0, 255); 

void draw_functions() {
  strokeWeight(1);
  for ( int i = 0; i< steps; i++) {
    X = f_start_x + dx * i;
    if ( i == 0 ) Xo=X;                      // need a dummy init for first line
    Y1 = y1(X);
    if ( i == 0 ) Y1o=Y1;
    Y2 = y2(X);
    if ( i == 0 ) Y2o=Y2;
    if ( abs(Y2 - Y1) <= epsilon )  println("solution near i "+i+" X "+nf(X, 1, 2)+" Y1 "+nf(Y1, 1, 2)+" Y2 "+nf(Y2, 1, 2)); 
    // draw it: // in processing draw y negativ! // scale to GRID by w
    stroke(RED);
    line(Xo*w, -Y1o*w, X*w, -Y1*w);            
    circle(X*w, -Y1*w, 2);
    stroke(BLUE);
    line(Xo*w, -Y2o*w, X*w, -Y2*w);
    circle(X*w, -Y2*w, 2);
    // remember for next loop draw line
    Y1o = Y1;
    Y2o = Y2;
    Xo = X;
  }
}

// grid of rectangles
int x = 0, y = x, w = 25, h = w, offset = 0;
int grid = 20, many = grid*grid;

void draw_grid() {
  noFill();
  stroke(0);
  for (int i = 0; i < many; i++)  rect(x+(i%grid)*(w+offset), y+(floor(i/grid))*(h+offset), w, h);   // or any other shape/text on that position

  // add 
  // https://discourse.processing.org/t/math-and-processing/9292/7 @CHRISIR
  // small lines with numbers ||||||||||| : for x axis 
  pushMatrix();
  translate(width/2, height/2);
  strokeWeight(3);
  textSize(15); 
  textAlign(CENTER);
  fill(0); // BLACK 
  stroke(0); // BLACK
  for (int i = -grid/2; i < grid/2; i++) {
    float dx = 1;
    float X =  dx * i;
    if (X==0 || X==-10) 
      continue;  // skip those numbers 
    float xToDraw = getXtoDraw(X); 
    float yToDraw = getYtoDraw(0); 
    line(xToDraw, yToDraw, xToDraw, yToDraw+11);
    text(i, 
      xToDraw-1, yToDraw+29-1);
  }//for 
  textAlign(LEFT);
  strokeWeight(1);
  popMatrix(); 

  // small lines with numbers : for y axis 
  pushMatrix();
  translate(width/2, height/2);
  stroke(0); // BLACK
  fill(0);  // BLACK
  textSize(15); 
  textAlign(CENTER);
  strokeWeight(3);
  for (int i = -grid/2; i < grid/2; i++) {
    float dx = 1;
    float Y =  dx * i;
    if (Y==0 || Y==9 || Y==-1) 
      continue;  // skip these numbers 
    float xToDraw = getXtoDraw(0); 
    float yToDraw = getYtoDraw(Y); 
    line(xToDraw, yToDraw, xToDraw+11, yToDraw);
    text(i, 
      xToDraw+24, yToDraw);
  }//for 
  textAlign(LEFT);
  strokeWeight(1);
  popMatrix(); 
  // show arrows and texts X and Y 
  decoration(); 
  fill(255);
  ellipse(width/2, height/2, 5, 5);       // show center point
}

void decoration () {
  stroke(0); 
  strokeWeight(3); 
  // line |
  line ( width/2, 0, width/2, height); 
  // line -
  line ( 0, height/2, width, height/2);
  // texts X and its arrow ---
  fill(0); 
  textSize(24);
  text ( "X", 
    width-17, height/2+31); 

  pushMatrix();
  noFill();
  strokeWeight(3.0);
  strokeJoin(MITER);
  beginShape();

  float f = 3.0; 
  translate(width-f*7, height/2);  
  vertex(3.5*f, -3.0*f);
  vertex(6.5*f, 0.0*f);
  vertex(3.5*f, 3.0*f);
  endShape();
  strokeWeight(1); 
  popMatrix();

  // texts Y and its arrow --- 
  fill(0); 
  textSize(24);
  text ( "Y",  width/2+10, 22); 
  pushMatrix();
  noFill();
  strokeWeight(3.0);
  strokeJoin(MITER);
  beginShape();
  translate(width/2, 22);  
  vertex( -3.0*f, -3.5*f);
  vertex( 0.0*f, - 6.5 * f);
  vertex( 3.0*f, - 3.5 * f);
  endShape();
  popMatrix();
}

// ------------------------------------------------------------ HELPER FUNCTIONS 

float getXtoDraw(float xin_) {
  return xin_*w;
} 

float getYtoDraw(float yin_) {  // in processing draw y negativ!
  return -yin_*w;
}
//


#10

I am not sure if the numbers on the axis are okay

is one line of the grid 1 away from the next or is the distance 0.5 ?


#11

now in the zoomable version i have a even bigger problem with half grid
but i see that your numbers and the function values always FIT.

could not stop without testing
SCROLLITIS functions for this show.

// SCROLLITIS ( combination of a key and mouseWheel to tune a parameter )
// use Y1 [q] [w] [e] [r] and mouseWheel,
// use Y2 [a] [s] [d] [f] and mouseWheel
// and [z] and mouseWheel for grid zoom and [p] and mouseWheel for play epsilon
// key [h] to en/dis-able formula, numbers...

// USER FUNCTION  
float Q = 1.0, W = 2.0, E = 0.0, R = 0.0;
float y1(float x) {
  return Q + W * x + E * x*x + R * x*x*x;        // The Y1 function to plot
}

float A = 4.0, S = -1.0, D = 0.0, F = 0.0;
float y2(float x) {
  return A + S * x + D * x*x + F * x*x*x;        // The Y2 function to plot
}

void print_Y1() {
  String formula = "Y1 = ";
  if ( notnearZero(Q) ) formula += nf(Q, 1, 2)+" ";
  if ( notnearZero(W) ) formula += plus(W)+nf(W, 1, 2)+"*x ";
  if ( notnearZero(E) ) formula += plus(E)+nf(E, 1, 2)+"*xx ";
  if ( notnearZero(R) ) formula += plus(R)+nf(R, 1, 2)+"*xxx";
  formula += "   use [q] [w] [e] [r] and mouseWheel ";
  textSize(12);
  fill(RED);
  text(formula, 10, 15 );
}
void print_Y2() {
  String formula = "Y2 = ";
  if ( notnearZero(A) ) formula += nf(A, 1, 2)+" ";
  if ( notnearZero(S) ) formula += plus(S)+nf(S, 1, 2)+"*x ";
  if ( notnearZero(D) ) formula += plus(D)+nf(D, 1, 2)+"*xx ";
  if ( notnearZero(F) ) formula += plus(F)+nf(F, 1, 2)+"*xxx";
  formula += "   use [a] [s] [d] [f] and mouseWheel ";
  textSize(12);
  fill(BLUE);
  text(formula, 10, 45 );
  formula = "and [z] for zoom and play epsilon [p] and mouseWheel";
  formula += "\nuse key [h] toggle all that info";
  fill(0);
  text(formula, 10, 75 );
}

String plus(float val ) {
  if ( val < 0 ) return "";
  return "+";
}

boolean notnearZero(float val ) {
  float check = 0.05;
  if ( val > -check && val < check ) return false;
  return true;
}
//______________________________________________________________________
void setup() {
  size(500, 500);
  draw_it();
}

void draw() {
}

void draw_it() {
  background(255);
  draw_grid();
  push();
  translate(gridcenter, gridcenter);
  draw_functions();
  pop();
  if ( showhelp) print_Y1();
  if ( showhelp) print_Y2();
}

// can be other TAB..
// rev 0.4 try even zoom key [z] and check grid / numbers and function plot correct
int steps = 100;
float X, Y1, Y2, Xo=0, Y1o, Y2o;
float f_start_x = -10, f_stop_x = 10;  // changed in draw and mousewheel
float dx = (f_stop_x - f_start_x )/float(steps);
float epsilon = 1;                         // @mcintyre
boolean showhelp = true;

//consts
color RED  = color(255, 0, 0); 
color BLUE = color(0, 0, 255); 

void draw_functions() {
  strokeWeight(1);
  f_start_x = -width/(2*w); 
  f_stop_x = width/(2*w);
  dx = (f_stop_x - f_start_x )/float(steps);
  for ( int i = 0; i< steps; i++) {
    X = f_start_x + dx * i;
    if ( i == 0 ) Xo=X;                      // need a dummy init for first line
    Y1 = y1(X);
    if ( i == 0 ) Y1o=Y1;
    Y2 = y2(X);
    if ( i == 0 ) Y2o=Y2;
    if ( abs(Y2 - Y1) <= epsilon )  println("solution near i "+i+" X "+nf(X, 1, 2)+" Y1 "+nf(Y1, 1, 2)+" Y2 "+nf(Y2, 1, 2)+ " for epsilon "+nf(epsilon, 1, 2)); 
    // draw it: // in processing draw y negativ! // scale to GRID by w
    stroke(RED);
    line(Xo*w, -Y1o*w, X*w, -Y1*w);            
    circle(X*w, -Y1*w, 2);
    stroke(BLUE);
    line(Xo*w, -Y2o*w, X*w, -Y2*w);
    circle(X*w, -Y2*w, 2);
    // remember for next loop draw line
    Y1o = Y1;
    Y2o = Y2;
    Xo = X;
  }
}

// grid of rectangles
int x = 0, y = x, w = 25, h = w, offset = 0;
int grid = 20, many = grid*grid;
int gridcenter = x + ( w + offset ) * grid/2;

void draw_grid() {
  println("x "+x+" y "+y+" w "+w+" h "+h+" offset "+offset+" grid "+grid+" many "+many);
  noFill();
  stroke(0);
  strokeWeight(0.5);
  for (int i = 0; i < many; i++)  rect(x+(i%grid)*(w+offset), y+(floor(i/grid))*(h+offset), w, h);   // or any other shape/text on that position
  if ( showhelp) {
    // https://discourse.processing.org/t/math-and-processing/9292/7 @CHRISIR
    // small lines with numbers ||||||||||| : for x axis 
    pushMatrix();
    translate(gridcenter, gridcenter);
    strokeWeight(3);
    textSize(12); 
    textAlign(CENTER);
    fill(0); // BLACK 
    stroke(0); // BLACK
    for (int i = -grid/2; i < grid/2; i++) {
      float dx = 1;
      float X =  dx * i;
      if ( X==0 ) continue;  // || X==-10 // skip those numbers 
      float xToDraw = getXtoDraw(X); 
      float yToDraw = getYtoDraw(0); 
      line(xToDraw, yToDraw, xToDraw, yToDraw+11);
      text(i, xToDraw+3, yToDraw+24);
    }//for 
    textAlign(LEFT);
    strokeWeight(1);
    popMatrix(); 

    // small lines with numbers : for y axis 
    pushMatrix();
    translate(gridcenter, gridcenter);
    stroke(0); // BLACK
    fill(0);  // BLACK
    textSize(12); 
    textAlign(CENTER);
    strokeWeight(3);
    for (int i = -grid/2; i < grid/2; i++) {
      float dx = 1;
      float Y =  dx * i;
      if ( Y==0 ) continue;  // skip these numbers   // || Y==9 || Y==-1) 
      float xToDraw = getXtoDraw(0); 
      float yToDraw = getYtoDraw(Y); 
      line(xToDraw, yToDraw, xToDraw+11, yToDraw);
      text(i, xToDraw+15, yToDraw-3);  // +24
    }//for 
    textAlign(LEFT);
    strokeWeight(1);
    popMatrix(); 
    // show arrows and texts X and Y 
    decoration(); 
    fill(255);
    ellipse(gridcenter, gridcenter, 5, 5);       // show center point
  }
}

void decoration () {
  stroke(0); 
  strokeWeight(3); 
  // line |
  line ( gridcenter, 0, gridcenter, gridcenter*2); 
  // line -
  line ( 0, gridcenter, gridcenter*2, gridcenter);
  // texts X and its arrow ---
  fill(0); 
  textSize(24);
  text ( "X", gridcenter*2-17, gridcenter+31); 

  pushMatrix();
  noFill();
  strokeWeight(3.0);
  strokeJoin(MITER);
  beginShape();

  float f = 3.0; 
  translate(gridcenter*2-f*7, gridcenter);  
  vertex(3.5*f, -3.0*f);
  vertex(6.5*f, 0.0*f);
  vertex(3.5*f, 3.0*f);
  endShape();
  strokeWeight(1); 
  popMatrix();

  // texts Y and its arrow --- 
  fill(0); 
  textSize(24);
  text ( "Y", gridcenter+10, 22); 
  pushMatrix();
  noFill();
  strokeWeight(3.0);
  strokeJoin(MITER);
  beginShape();
  translate(gridcenter, 22);  
  vertex( -3.0*f, -3.5*f);
  vertex( 0.0*f, - 6.5 * f);
  vertex( 3.0*f, - 3.5 * f);
  endShape();
  popMatrix();
}

// ------------------------------------------------------------ HELPER FUNCTIONS 

float getXtoDraw(float xin_) {
  return xin_*w;
} 

float getYtoDraw(float yin_) {  // in processing draw y negativ!
  return -yin_*w;
}

void keyPressed() {
  if ( key == 'h' ) {
    showhelp = !showhelp;                // [h]
    draw_it();
  }
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount();
  float k=0.05;                                             // step change parameter
  if ( keyPressed && key == 'z' ) w += e;                  // [z]    grid size
  // Y1 = Q + W * x + E * x*x + R * x*x*x; 
  if ( keyPressed && key == 'q' ) Q += e*k;                // [q]
  if ( keyPressed && key == 'w' ) W += e*k;                // [w]
  if ( keyPressed && key == 'e' ) E += e*k;                // [e]
  if ( keyPressed && key == 'r' ) R += e*k;                // [r]
  // Y2 =  A + S * x + D * x*x + F * x*x*x;
  if ( keyPressed && key == 'a' ) A += e*k;                // [a]
  if ( keyPressed && key == 's' ) S += e*k;                // [s]
  if ( keyPressed && key == 'd' ) D += e*k;                // [d]
  if ( keyPressed && key == 'f' ) F += e*k;                // [f]

  if ( keyPressed && key == 'p' ) epsilon += e*k;          // [p]

  auto_scale();
  draw_it();
}

void auto_scale() {    // autoscale grid on changing w
  grid = floor((width-2*x)/(w + offset ));
  many = grid*grid;
  gridcenter = x + ( w + offset ) * grid/2;
  h = w;
}


#12

@kmll – it would be nice if we had a simple graph-mode library that inverted the y-axis, scaled the display and corrected the strokeWeight accordingly, drew a graph paper with labeled axis, and then got out of the way and let the student writing simple vanilla sketches on top of it without requiring that they use a library API. If I was going to write something like that I would probably use grafica to do it:

https://jagracar.com/sketches/multiplePanels.php
https://jagracar.com/sketches/multiplePlots.php

Putting a graphing-calculator-display-style for drawing sketches aside for a moment:

I think the thing confusing me here is that the lesson plan for teaching a math student to solve the intersection of two linear equations (pen & paper) looks very different from the way that I would teach a computer science student to program the rendering of intersecting line segments, or to solve programmatically for a line-line intersection. I’m trying to understand: is this an algebra class in solving equations or a programming class in how to render lines or find points with algorithms? or both? What level are the students, and what are the learning goals?

Here’s an “intersection of two lines” algebra lesson of the kind that I’m familiar with:

http://zonalandeducation.com/mmts/intersections/intersectionOfTwoLines1/intersectionOfTwoLines1.html

Algebra process:

  1. find y = 2x + 1 and y = -x + 4
  2. set ys equal to each other: 2x + 1 = -x + 4
  3. reduce one side to x: 3x = 3x = 1
  4. plug x value into either equation to solve y: y = 2+1y = 3
  5. give solution for intersection point: (1, 3)

The process produces an answer point. If we want the students to get pictures of the lines then they can learn (on paper) to find two points on each line (one on either side of the intersection, if that is the point of interest) and then mark each line with a straight edge. We can also teach them how to use a graphing calculator app – like https://www.desmos.com/calculator. They type in the equations and get pictures. These exist for the web, laptops, iOS and Android etc.

But I’m assuming the goal isn’t to create a graphing calculator app for students in Processing and have them use it – it is to have them program their equations. However, algebraic equations aren’t typically how you write a program to draw a line or solve for an intersection. If your goal is to teach how to draw a line with a computer, the normal way is to define a line segment with two points – like in Processing:

line(x1, y1, x2, y2).

Intro to line programming process:

  1. determine the current display range, e.g. -200 to 200. This is a size of 400, and requires translating the center by half the width and half the height.
size(400,400);
translate(width/2.0, height/2.0);
  1. determine x points just outside the current display range, e.g. -201 to 201. This will create a spanning line segment.
  2. for y = 2x + 1 plug in x=-201 to get (-201, -401) and x=201 to get (201, 403).
  3. for y = -x + 4 plug in x=-201 to get (-201, 205) and x=201 to get (201, -197)
  4. write the line rendering calls:
size(400,400);
translate(width/2.0, height/2.0);
line(-201, -401, 201, 403);
line(-201, 205, 201, -197);
  1. Next, you teach how to abstract the point finding processing into point functions:
void setup(){
  size(400,400);
}
void draw(){
  background(255);
  translate(width/2.0, height/2.0);
  line(-201, f1(-201), 201, f1(201));
  line(-201, f2(-201), 201, f2(201));
}
float f1(float x) {  
  return 2*x + 1;
}
float f2(float x) {
  return -x + 4;
}
  1. Then you show how you can (although you usually shouldn’t) plot every point in a line with a for loop yourself, rather than drawing line segments:
void setup(){
  size(400,400);
}
void draw(){
  background(255);
  translate(width/2.0, height/2.0);
  myline1(-width/2.0, width/2.0);
}
void myline1(float x1, float x2){
   for(int x=(int)x1; x<x2; x++){
     point(x, f1(x));
   }
}
float f1(float x) {  
  return 2*x + 1;
}
  1. optionally, then use classes to further abstract line equation objects that can draw themselves.
LineEquation eq1, eq2;

void setup(){
  size(400,400);
  eq1 = new LineEquation(2, 1);
  eq2 = new LineEquation(-1, 4);
}
void draw(){
  background(255);
  translate(width/2.0, height/2.0);
  eq1.render(-width/2.0, width/2.0);
  eq2.render(-width/2.0, width/2.0);
}

// linear equation in the form y=mx+b
class LineEquation {
  float m;
  float b;
  LineEquation(float m, float b){
    this.m = m;
    this.b = b;
  }
  float f(float x){
    return m*x + b;
  }
  void render(float x1, float x2){
    float y1 = this.f(x1);
    float y2 = this.f(x2);
    line(x1, y1, x2, y2);
  }
}

These sketches could be made much prettier and easier to read with a grafica-based background of grid lines and labeled axes, but they focus on the minimal code needed to convey the concepts.

The next concept is solving for the intersection. Here, the students are going to learn something different than setting ys equal and solving for x. Instead, they will use the line-line collision detection of two segments defined by end points:

PVector lineLine(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4) {
  // calculate the distance to intersection point
  float uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
  float uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
  // if uA and uB are between 0-1, lines are colliding
  if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
    float intersectionX = x1 + (uA * (x2-x1));
    float intersectionY = y1 + (uA * (y2-y1));
    return new PVector(intersectionX,intersectionY);
  }
  return null;
}

We can teach them mathematically WHY this function returns correct results, and what the distance to intersection is – although often for new programmers we initially focus on HOW to use it – adding it as a top level function, or adding it as a class method, then passing it parameters, then using the returned PVector object’s x and y values to draw an ellipse marking the intersection point if it is found (and handling null correctly if it is not found).

LineEquation eq1, eq2;
PVector cross;
void setup(){
  size(400,400);
  eq1 = new LineEquation(2, 1);
  eq2 = new LineEquation(-1, 4);
}
void draw(){
  background(255);
  translate(width/2.0, height/2.0);
  float rng = width/2.0;
  eq1.render(-rng, rng);
  eq2.render(-rng, rng);
  cross = lineLine(
    -rng, eq1.f(-rng), rng, eq1.f(rng),
    -rng, eq2.f(-rng), rng, eq2.f(rng)
  );
  if(cross!=null){
    fill(0);
    ellipse(cross.x, cross.y, 10, 10);
    text(cross.x + "," + cross.y, cross.x + 10, cross.y);
  }
}

Building up from this to curves could involve curve functions (or classes/objects) and then finding curve-curve intersections by stepping over segments of two functions and checking for collisions.

A graphing calculator app hides how different computer drawing is from algebra. If the goal for the students to work out that logic while writing their own graphing software then even teaching them how to create their own graph paper or labeled axes from scratch might be valuable.