construct2_3d ›

construct2

//
//  construct2.rs
//
//  Copyright (c) 2001 Fredrik Olofsson Musikproduktion. All rights reserved.
//
//  2001 original code in java
//  010502 max patch (rev.011004)
//  040920 ported to processing
//  060227 3d version for processing
//  071201 ported to sc (RedConstruct)
//  200326 ported to p5js
//  210517 ported to nannou

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

use nannou::prelude::*;

const MOV_X: u8 = 95;
const MOV_Y: u8 = 95;

struct Model {
  c: Rgb8,
  step_x: i32,
  step_y: i32,
  x: i32,
  y: i32,
  dx: i32,
  dy: i32,
  v: u8,  //variant
  m: u8,  //mapping
  col: i32,
  row: i32,
  max_x: i32,
  max_y: i32,
  reset: u8,
}

fn main() {
  nannou::app(model).update(update).run();
}

fn model(app: &App) -> Model {
  app.new_window()
  .event(event)
  .size(640, 480)
  .title("Construct2")
  .view(view)
  .build()
  .unwrap();
  let c = rgb(255, 55, 66);
  let step_x = 1;
  let step_y = 1;
  let x = 10;
  let y = 10;
  let dx = 1;
  let dy = 1;
  let v = 1;
  let m = 1;
  let col = 4;
  let row = 3;
  let max_x = app.window_rect().w() as i32;
  let max_y = app.window_rect().h() as i32;
  let reset = 1;
  Model {
    c,
    step_x,
    step_y,
    x,
    y,
    dx,
    dy,
    v,
    m,
    col,
    row,
    max_x,
    max_y,
    reset,
  }
}

fn event(_app: &App, model: &mut Model, event: WindowEvent) {
  match event {
    KeyPressed(key) => {
      if let Key::Space = key {
        model.reset = 1;
      }
    },
    MousePressed(_button) => {
      model.reset = 1;
    },
    _ => {},
  }
}

fn update(_app: &App, model: &mut Model, _update: Update) {
  if random_range(0, 1881) == 0 {
    model.reset = 1;
  }

  if model.reset == 1 {
    nytt(model); // nya värden
    model.reset = 2;
  } else if model.reset == 2 {
    model.reset = 0;
  }

  model.x += model.dx * model.step_x; // flytta position x
  if model.x < 1 { // om vänster kant
    model.dx = random_range(0, 2); // vänd eller stå still i x-led
  } else if model.x > model.max_x / model.col { // om höger kant
    model.dx = 0 - random_range(0, 2); // vänd eller stå still i x-led
  }

  model.y += model.dy * model.step_y; // flytta position y
  if model.y < 1 { // om övre kanten
    model.dy = random_range(0, 2) // vänd eller stå still i y-led
  } else if model.y > model.max_y / model.row { // om nedre kanten
    model.dy = 0 - random_range(0, 2) // vänd eller stå still i y-led
  }

  match model.v {
    1 => variant1(model),
    2 => variant2(model),
    3 => variant3(model),
    4 => variant4(model),
    5 => variant5(model),
    6 => variant6(model),
    _ => (),
  }
}

// ---------------------------------------------------------------------------------------
fn variant1(model: &mut Model) { // 1. slumpa riktningar individuellt
  if random_range(0, 100) >= MOV_X {
    slumpa_riktning_x(model);
  }
  if random_range(0, 100) >= MOV_Y {
    slumpa_riktning_y(model);
  }
}
fn variant2(model: &mut Model) { // 2. slumpa alltid riktningar tillsammans
  slumpa_riktning_xy(model);
}
fn variant3(model: &mut Model) { // 3. slumpa riktningar individuellt
  if random_range(0, 100) >= MOV_X {
    if model.dx == 0 && model.x >= 1 { // dra åt vänster
      model.dx = -1;
    } else {
      model.dx = 0;
    }
  }
  if random_range(0, 100) >= MOV_Y {
    if model.dy == 0 && model.y >= 1 { // dra uppåt
      model.dy = -1;
    } else {
      model.dy = 0;
    }
  }
}
fn variant4(model: &mut Model) { // 4. slumpa riktningar individuellt
  if random_range(0, 100) >= MOV_X {
    if model.dx == 0 && model.x >= 1 { // dra åt vänster
      model.dx = -1;
    } else {
      model.dx = 0;
    }
  }
  if random_range(0, 100) >= MOV_Y {
    if model.dy == 0 && model.y <= model.max_y / model.row { // dra nedåt
      model.dy = 1;
    } else {
      model.dy = 0;
    }
  }
}
fn variant5(model: &mut Model) { // 5. slumpa riktningar individuellt
  if random_range(0, 100) >= MOV_X {
    if model.dx == 0 && model.x <= model.max_x / model.col { // dra åt höger
      model.dx = 1;
    } else {
      model.dx = 0;
    }
  }
  if random_range(0, 100) >= MOV_Y {
    if model.dy == 0 && model.y >= 1 { // dra uppåt
      model.dy = -1;
    } else {
      model.dy = 0;
    }
  }
}
fn variant6(model: &mut Model) { // 6. slumpa riktningar individuellt
  if random_range(0, 100) >= MOV_X {
    if model.dx == 0 && model.x <= model.max_x / model.col { // dra åt höger
      model.dx = 1;
    } else {
      model.dx = 0;
    }
  }
  if random_range(0, 100) >= MOV_Y {
    if model.dy == 0 && model.y <= model.max_y / model.row { // dra nedåt
      model.dy = 1;
    } else {
      model.dy = 0;
    }
  }
}

// ---------------------------------------------------------------------------------------
fn nytt(model: &mut Model) {
  model.v = random_range(0, 6) + 1;
  model.m = random_range(0, 16) + 1;
  model.col = random_range(0, 12) + 1;
  model.row = random_range(0, 7) + 1;
  model.step_x = 1;
  // model.step_x = random_range(0, 2) + 1;
  model.step_y = 1;
  // model.step_y = random_range(0, 2) + 1;
  slumpa_riktning_xy(model);
  model.c.red = random_range(0, 255);
}

fn slumpa_riktning_x(model: &mut Model) {
  model.dx = random_range(0, 2);
}
fn slumpa_riktning_y(model: &mut Model) {
  model.dy = random_range(0, 2);
}
fn slumpa_riktning_xy(model: &mut Model) {
  while model.dx == 0 && model.dy == 0 { // kolla att inte båda riktningar blir 0
    model.dx = random_range(0, 2);
    model.dy = random_range(0, 2);
  }
}

// ---------------------------------------------------------------------------------------
fn draw_fo(model: &Model, draw: &Draw) {
  for i in 0..model.col {
    let i_col = (model.max_x / model.col) * i;
    for j in 0..model.row {
      let j_row = (model.max_y / model.row) * j;
      let point = match model.m { // kolla mapping
        1 => mapping1(model, i, j, i_col, j_row),
        2 => mapping2(model, i, j, i_col, j_row),
        3 => mapping3(model, i, j, i_col, j_row),
        4 => mapping4(model, i, j, i_col, j_row),
        5 => mapping5(model, i, j, i_col, j_row),
        6 => mapping6(model, i, j, i_col, j_row),
        7 => mapping7(model, i, j, i_col, j_row),
        8 => mapping8(model, i, j, i_col, j_row),
        9 => mapping9(model, i, j, i_col, j_row),
        10 => mapping10(model, i, j, i_col, j_row),
        11 => mapping11(model, i, j, i_col, j_row),
        12 => mapping12(model, i, j, i_col, j_row),
        13 => mapping13(model, i, j, i_col, j_row),
        14 => mapping14(model, i, j, i_col, j_row),
        15 => mapping15(model, i, j, i_col, j_row),
        16 => mapping16(model, i, j, i_col, j_row),
        _ => ivec2(0, 0),
      };
      draw.rect().x_y(point.x as f32, point.y as f32).w_h(1.0, 1.0).color(model.c);
    }
  }
}

fn view(app: &App, model: &Model, frame: Frame) {
  let win = app.main_window().rect();
  let draw = app.draw().x_y(win.w() * -0.5, win.h() * -0.5);
  if model.reset > 0 {
    frame.clear(WHITE); // rensa skärmen
  }
  draw_fo(model, &draw);
  draw.to_frame(app, &frame).unwrap();
}

// ---------------------------------------------------------------------------------------
fn mapping1(model: &Model, _i: i32, _j: i32, i_col: i32, j_row: i32) -> IVec2 { // no flip
  ivec2(model.x + i_col, model.y + j_row)
}
fn mapping2(model: &Model, _i: i32, _j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip all x
  ivec2(mirror_x(model) + i_col, model.y + j_row)
}
fn mapping3(model: &Model, _i: i32, _j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip all y
  ivec2(model.x + i_col, mirror_y(model) + j_row)
}
fn mapping4(model: &Model, _i: i32, _j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip all xy
  ivec2(mirror_x(model) + i_col, mirror_y(model) + j_row)
}
// --
fn mapping5(model: &Model, i: i32, _j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd col x
  if i % 2 == 1 {
    ivec2(mirror_x(model) + i_col, model.y + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping6(model: &Model, i: i32, _j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd col y
  if i % 2 == 1 {
    ivec2(model.x + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping7(model: &Model, i: i32, _j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd col xy
  if i % 2 == 1 {
    ivec2(mirror_x(model) + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
// --
fn mapping8(model: &Model, _i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd row x
  if j % 2 == 1 {
    ivec2(mirror_x(model) + i_col, model.y + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping9(model: &Model, _i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd row y
  if j % 2 == 1 {
    ivec2(model.x + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping10(model: &Model, _i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd row xy
  if j % 2 == 1 {
    ivec2(mirror_x(model) + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
// --
fn mapping11(model: &Model, i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd col & even row x, flip even col & odd row x
  if i % 2 == 1 && j % 2 == 0 {
    ivec2(mirror_x(model) + i_col, model.y + j_row)
  } else if i % 2 == 0 && j % 2 == 1 {
    ivec2(mirror_x(model) + i_col, model.y + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping12(model: &Model, i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd col & even row y, flip even col & odd row y
  if i % 2 == 1 && j % 2 == 0 {
    ivec2(model.x + i_col, mirror_y(model) + j_row)
  } else if i % 2 == 0 && j % 2 == 1 {
    ivec2(model.x + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping13(model: &Model, i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip odd col & even row xy, flip even col & odd row xy
  if i % 2 == 1 && j % 2 == 0 {
    ivec2(mirror_x(model) + i_col, mirror_y(model) + j_row)
  } else if i % 2 == 0 && j % 2 == 1 {
    ivec2(mirror_x(model) + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
// --
fn mapping14(model: &Model, i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip even col & even row x, flip odd col & odd row x
  if i % 2 == 0 && j % 2 == 0 {
    ivec2(mirror_x(model) + i_col, model.y + j_row)
  } else if i % 2 == 1 && j % 2 == 1 {
    ivec2(mirror_x(model) + i_col, model.y + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping15(model: &Model, i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip even col & even row y, flip odd col & odd row y
  if i % 2 == 0 && j % 2 == 0 {
    ivec2(model.x + i_col, mirror_y(model) + j_row)
  } else if i % 2 == 1 && j % 2 == 1 {
    ivec2(model.x + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}
fn mapping16(model: &Model, i: i32, j: i32, i_col: i32, j_row: i32) -> IVec2 { // flip even col & even row xy, flip odd col & odd row xy
  if i % 2 == 0 && j % 2 == 0 {
    ivec2(mirror_x(model) + i_col, mirror_y(model) + j_row)
  } else if i % 2 == 1 && j % 2 == 1 {
    ivec2(mirror_x(model) + i_col, mirror_y(model) + j_row)
  } else {
    ivec2(model.x + i_col, model.y + j_row)
  }
}

// ---------------------------------------------------------------------------------------
fn mirror_x(model: &Model) -> i32 {
  model.max_x / model.col - model.x
}
fn mirror_y(model: &Model) -> i32 {
  model.max_y / model.row - model.y
}