4. Friksjon

Skolekoding.no > Tekst-koding > p5.js > NOC trinn for trinn


Eksempler fra The Nature of Code av Daniel Shiffman
2.4: Including friction
2.5: Fluid resistance



2.4 Friksjon

Nytt

Friksjon som virker i motsatt retning av hastigheten.

  • let movers = []; 
  • let numMovers = 10; 
  • ​let gravity;
  • let wind;
  • let friction;
  • ​let c = 0.01;
  • function setup() {
    • createCanvas(400, 400);
    • for (let i = 0; i < numMovers; i++) {
      • movers[i] = new Mover(random(0.5, 3), 0, 0); 
    • }
    • wind = createVector(0.01, 0);
  • }
  • function draw() {
    • background(255);
    • for (let i = 0; i < movers.length; i++) {
      • let m = movers[i].mass;
      • gravity = createVector(0, 0.1 * m);
      • friction = movers[i].velocity.copy(); 
      • friction.mult(-1);
      • friction.normalize();
      • friction.mult(c);
      • movers[i].applyForce(friction);
      • movers[i].applyForce(wind);
      • movers[i].applyForce(gravity);
      • movers[i].update();
      • movers[i].display();
      • movers[i].checkEdges();
    • }
  • }​
  • class Mover {
    • constructor(temp_m, temp_x, temp_y) {
      • this.mass = temp_m;
      • this.pos = createVector(temp_x, temp_y);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0);
    • }
    • applyForce(force) { 
      • let a = p5.Vector.div(force, this.mass); 
      • this.acceleration.add(a);
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -1;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -1;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -1;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -1;
      • }
    • }
    • display() {
      • fill(175, 100);
      • circle(this.pos.x, this.pos.y, this.mass * 16);
    • }
  • }

Før setup()

  1. Ny vektorvariabel: friction
  2. ​Variabel «c» som angir hvor stor friksjonen er (friksjonskonstanten).

draw() 

  1. Vektorvariabel «friction» som får samme verdi og retning som hastighetsvektoren til sirkelen movers[i] (movers nr i).
  2. «friction» reverseres ved å multiplisere med -1.
  3. «friction» normaliseres. Dvs den gis verdien 1. Friksjonen er uavhengig av hvor fort sirklene faller. Det som er viktig er at den virker i motsatt retning av hastigheten.
  4. «friction» får sin verdi ved å multipliseres med friksjonskonstanten c.
  5. «friction» legges til den samlede akselerasjonen som påvirker sirkelen movers[i].


Ekstra forklaring

  1. Friksjon introduseres i The Nature of Code kapittel 2.7 side 80.
  2. Formelen for bevegelesfriksjon er: Friksjon = -1 * c * N * ṽ
  3. Hvis vi rokerer litt på symboene kan vi skrive formelen slik:
    Friksjon = -1 * ṽ * c * N
  4. Symbolet for hastighetsvektoren v skal egentlig ha en ^ (cirkumfleks eller»hatt») over for å vise at det er enhetsvektoren vi skal ha.
  5. For enhetsvektoren til en vektor er lengden satt til 1, mens retningen er beholdt. Det er retningen vi er interessert i.
  6. – 1 betyr at friksjonen får motsatt retning av hastigheten v.
  7. c er friksjonskonstanten (egentlig brukes den greske bokstaven µ «my» i formelen) c varierer etter hva slags materiale det er snakk om. Ting glir lettere på is enn på asfalt. Is har derfor lavere friksjonskonstant enn asfalt. Her i vår virtuelle verden kan vi velge c som vi vil. For eksempel c= 0.01.
  8. N er kraften som virker tilbake fra underlaget normalt på bevegelsesretningen. Denne kraften er like stor som tyngden av gjenstanden, men har motsatt retning. En tung koffert har større friksjon mot et golv enn en lett koffert.
  9. Her gjør vi det enkelt og setter N = 1 for alle sirklene.
  10. Formelen blir dermed slik:
    Friksjon = -1 * ṽ * c
  11. NB! Her er ṽ altså enhetsvektoren til hastighetsvektoren v, ikke hastighetsvektoren selv.
  12. Når vi legger til friksjon på sirklene vil de hele tiden bremses opp, enten de beveger seg ned eller opp.
  13. Til slutt ligger de i ro på bunnen.

2.5 a Væske

Nytt

  1. Vi fjerner vinden
  2. Vi fjerner friksjonen
  3. Vi legger til et vannbasseng i form av en egen klasse: liquid
  4. Vi lar alle sirklene starte på linje
  • let movers = []; 
  • let numMovers = 10; 
  • let liquid;
  • function setup() {
    • createCanvas(400, 400);
    • for (let i = 0; i < numMovers; i++) {
      • movers[i] = new Mover(random(0.5, 3), width / numMovers * i + 12, 0);
    • }
    • liquid = new Liquid(0, height / 2, width, height / 2);
  • }
  • function draw() {
    • background(220);
    • liquid.display(); 
    • for (let i = 0; i < numMovers; i++) {
      • // Friksjon fra forrige eksempel er fjernet
      • let m =  movers[i].mass
      • let gravity = createVector(0, 0.1 * m);
      • movers[i].applyForce(gravity);
      • movers[i].update();
      • movers[i].display();
      • movers[i].checkEdges();
    • }
  • }
  • class Mover {
    • constructor(temp_m, temp_x, temp_y) {
      • this.mass = temp_m;
      • this.pos = createVector(temp_x, temp_y);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0);
    • }
    • applyForce(force) { 
      • let a = p5.Vector.div(force, this.mass);
      • this.acceleration.add(a); 
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -1;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -1;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -1;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -1;
      • }
    • }
    • display() {
      • fill(125);
      • ellipse(this.pos.x, this.pos.y, this.mass * 7);
    • }
  • }
  • // Ny klasse
  • class Liquid {
    • constructor(temp_x, temp_y, temp_w, temp_h) {
      • this.x = temp_x;
      • this.y = temp_y;
      • this.w = temp_w;
      • this.h = temp_h;
    • }
    • display() {
      • fill(175);
      • noStroke();
      • rect(this.x, this.y, this.w, this.h);
      • stroke(0); 
    • }
  • }

før setup()

  1. Variabel for et nytt objekt: liquid

function setup()

  1. Alle sirklene starter på linje.
  2. Vi fordeler sirklene utover ved å dividere bredden på antall sirkler og multiplisere med sirkelens plassering i tabellen (0, 1, 2, 3, 4, 5, 6, 7, 8, 9).
  3. Vi forskyver sirklene 12 punkter mot høyre slik første sirkel kommer litt ut fra venstre kant (pixel nr null)
  4. y-koordinaten er 0 som før.
  5. Vi oppretter et liquid-objekt med øvre venstre hjørne i punktet (0, halve høyden)
  6. Bredden er lik bredden på lerretet
  7. Høyden er lik halve lerretetshøyden

draw()

  1. Kaller objektet liquid sin display-metode.
  2. Det vil si: tegner vannbassenget.
  3. Friksjonen fra forrige eksempel er fjernet

class Liquid() 
constructor(temp_x, temp_y, temp_w, temp_h)

  1. Fire parametre tas inn i klassen
  2. Koordinater (temp_x, temp_y)
  3. Bredde (temp_w)
  4. Høyde (temp_h)
  5. Kooordinatene er øvre venstre hjørne av vannbassenget

display()

  1. Setter en mørk grå farge (175)
  2. Fjerner omrisset
  3. Tegner vannbassenget
  4. Gjennoppretter omriss for at ikke også omrisset av sirklene skal bli fjernet.

2.5 b Sjekk væske

Nytt

  • Vi sjekker om sirklene er i vannbassenget
  • Dette gjøres med en ny metode i klassen Mover
  • Metoden heter «isInside»
  • let movers = []; 
  • let numMovers = 10; 
  • let liquid;
  • function setup() {
    • createCanvas(400, 400);
    • for (let i = 0; i < numMovers; i++) {
      • movers[i] = new Mover(random(0.5, 3), width / numMovers * i + 12, 0);
    • }
    • liquid = new Liquid(0, height / 2, width, height / 2);
  • }
  • function draw() {
    • background(220);
    • liquid.display(); 
    • for (let i = 0; i < numMovers; i++) {
      • if (movers[0].isInside(liquid)) { 
        • text(«I vannet», 10, 20);   
      • }
    • let m =  movers[i].mass
    • let gravity = createVector(0, 0.1 * m);
    • movers[i].applyForce(gravity);
    • movers[i].update();
    • movers[i].display();
    • movers[i].checkEdges();
    • }
  • }
  • class Mover {
    • constructor(temp_m, temp_x, temp_y) {
      • this.mass = temp_m;
      • this.pos = createVector(temp_x, temp_y);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0);
    • }
    • applyForce(force) { 
      • let a = p5.Vector.div(force, this.mass);
      • this.acceleration.add(a); 
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0);
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -1;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -1;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -1;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -1;
      • }
    • }
    • display() {
      • fill(125);
      • ellipse(this.pos.x, this.pos.y, this.mass * 7);
    • }
    • isInside(liquid) { // Ny metode
      • if (this.pos.y > liquid.y) {
        • return true;
      • } else {
        • return false;
      • }
    • }
  • }
  • class Liquid {
    • constructor(temp_x, temp_y, temp_w, temp_h, temp_c) {
      • this.x = temp_x;
      • this.y = temp_y;
      • this.w = temp_w;
      • this.h = temp_h;
      • this.c = temp_c;
    • }
    • display() {
      • fill(175);
      • noStroke();
      • rect(this.x, this.y, this.w, this.h);
      • stroke(0); 
    • }
  • }

draw() 

  1. Sjekker om sirkel nr [i] er i vannet
  2. Kaller movers[i] sin isInside-metode
  3. Metoden tar objektet liquid som parameter
  4. Skriver «Vann» i punktet (10, 20) hvis første sirkel [0] er i vannet
  5. print(«I vann»); // Skriver til konsoll nedenfor kodevinduet

class Mover
isInside(liquid)

  1. Metoden tar objektet liquid som parameter
  2. Sjekker om sirkelens y-koordinat er større enn liquid sin y-koordinat (overflaten av vannet)
  3. Hvis sirkelen er i vannet returnerer «true» tilbake til der metoden ble kallet , dvs i draw()
  4. Returnerer «false» hvis sirkelen er over vannet.

2.5 c Friksjon i væske

Nytt

  • ​Ny form for friksjon: friksjon i væske og gass
  • Ny formel
  • Legger friksjonskraften til sirklene idet de treffer vannflaten.
  • let movers = []; 
  • let numMovers = 10; 
  • let liquid;
  • let gravity;
  • let m;
  • function setup() {
    • createCanvas(400, 400);
    • for (let i = 0; i < numMovers; i++) {
      • movers[i] = new Mover(random(0.5, 3), width / numMovers * i + 12, 0);
    • }
    • liquid = new Liquid(0, height / 2, width, height / 2, 0.1);
  • }
  • function draw() {
    • background(220);
    • liquid.display(); 
    • for (let i = 0; i < numMovers; i++) {
      • if (movers[i].isInside(liquid)) { 
        • dragForce =  movers[i].drag(liquid);
        • movers[i].applyForce(dragForce);
      • }
      • m =  movers[i].mass
      • gravity = createVector(0, 0.1 * m);
      • movers[i].applyForce(gravity);
      • movers[i].update();
      • movers[i].display();
      • movers[i].checkEdges();
    • }
  • }
  • class Mover {
    • constructor(temp_m, temp_x, temp_y) {
      • this.mass = temp_m;
      • this.pos = createVector(temp_x, temp_y);
      • this.velocity = createVector(0, 0);
      • this.acceleration = createVector(0, 0);
    • }
    • applyForce(force) { 
      • let a = p5.Vector.div(force, this.mass);
      • this.acceleration.add(a); 
    • }
    • update() {
      • this.velocity.add(this.acceleration);
      • this.pos.add(this.velocity);
      • this.acceleration.mult(0); 
    • }
    • checkEdges() {
      • if (this.pos.x > width) {
        • this.pos.x = width;
        • this.velocity.x *= -1;
      • } else if (this.pos.x < 0) {
        • this.pos.x = 0;
        • this.pos.x *= -1;
      • }
      • if (this.pos.y > height) {
        • this.pos.y = height;
        • this.velocity.y *= -1;
      • } else if (this.pos.y < 0) {
        • this.pos.y = 0;
        • this.velocity.y *= -1;
      • }
    • }
    • display() {
      • fill(125);
      • circle(this.pos.x, this.pos.y, this.mass * 7);
    • }
    • isInside(liquid) {
      • if (this.pos.y > liquid.y) {
        • return true;
      • } else {
        • return false;
      • }
    • }
    • drag(liquid) { 
      • let speed = this.velocity.mag();
      • let dragMagnitude = liquid.c * speed * speed;
      • let dragForce = this.velocity.copy();
      • dragForce.mult(-1); 
      • dragForce.normalize();
      • dragForce.mult(dragMagnitude);
      • return dragForce;
    • }
  • }
  • class Liquid {
    • constructor(temp_x, temp_y, temp_w, temp_h, temp_c) {
      • this.x = temp_x;
      • this.y = temp_y;
      • this.w = temp_w;
      • this.h = temp_h;
      • this.c = temp_c;
    • }
    • display() {
      • fill(175);
      • noStroke();
      • rect(this.x, this.y, this.w, this.h);
      • stroke(0); 
    • }
  • }

function setup()

  1. En ekstra parameter når objektet liquid opprettes
  2. Parameteren er vannets friksjonskonstant
  3. Her setter vi den til 0.1

function draw() 

  1. Kaller objektet movers[i] sin drag-metode
  2. Metoden tar objektet liquid som parameter
  3. Legger returverdien i variabelen «dragForce»
  4. Legger til dragForce på sirkelen movers[i]

class Mover
drag(liquid)

  1. Metoden tar objektet liquid som parameter
  2. Lagrer lengden av hastighetsvektoren i en ny variabel: speed
  3. Beregner trekkkraften (dragMagnitude) som vannet utøver på sirklene ved å multiplisere friksjonskonstanten c med hastigheten i andre potens. (se nærmere forklaring lenger ned)
  4. Kopierer sirkelens hastighetsvektor til en ny vektorvariabel: dragForce
  5. Reverserer dragForce ved å multiplisere med -1
  6. Normaliserer dragForce (setter lenden lik 1)
  7. Skalerer opp dragForce ved å multiplisere med trekkkraften dragMagnitude.
  8. Legger til dragForce til den samlede kraften sirkelene utsettes for


​Ekstra forklaring

  1. Å bruke formelen for kontaktfriksjon i eksempel 2.4 er egentlig litt feil ettersom ballene skal forstille å bevege seg gjennom et luftrom, altså gjennom en gass.
  2. Ved bevegelse i væske eller gass gjelder andre prinsipper enn når en koffert slepes langs bakken. 
  3. I væske og gass har hastigheten en betydning.
  4. Jo raskere ballene beveger seg, jo større blir friksjonen.
  5. Formel: F = -1/2 * ρ *  * A * C * ṽ
  6. – (minus): er viktig. Retningen til ṽ (enhetsvektoren) reverseres. Kraften virker mot bevegelsesrtningen som tidligere
  7. 1/2: er uviktig. Vi finner uansett på verdier til konstanter. Vi bruker istedet 1.
  8. ρ: (rho) Tettheten til væsken. Vi bruker 1.
  9. v²: Verdien (lengden) av hastighetsvektoren i andre potens. Den er viktig.
  10. A: Arealet av flaten som peker framover. Vi ignorerer denne også.
  11. C: Trekkonstanten (tilsvarende friksjonskontanten i tidligere eksempel)
  12. ṽ: Gode gamle enhetsvektoren til hastigheten
  13. Altså: F = v² * C * ṽ * -1​

Veien videre

Kjøp boka på www.adlibris.com eller www.natureofcode.com og les mer:

Spillelister på Youtube

Noen eksempler oversatt til p5.js


skolekoding.no
Stein Olav Kivle