lass-clock

   

For this project, I wanted to make a tidal clock using 3js and shaders. The water rises and cycles through high and low tide twice every ~25 hours. Originally, my design involved a floating island with some water pooled in the middle, but as I worked on the project it I realized that floating islands didn't really make sense and instead used the shape of a tidal pool.

One feature of the clock is that the sky changes during day, night, and sunset. I chose to have this feature because since my shapes were low poly, I wanted to be able to have a wider variety of color palettes.    

       

One of the things that I would have liked to include in this project is using location services and to find the actual tide for the user's location. Right now, I'm basing the tide off of the most recent high tide on Virginia Beach, which will work temporarily but probably require calibration in the future since the length of tidal days is approximate. The same thing goes for sunrise and sunset times, since right now they are shown at a fixed time every day.

<script id="waterVertex" type="x-shader/x-vertex">
    uniform float u_time; 
    uniform float u_height; 
    varying vec3 v_position; 
    varying vec3 v_normal; 
 
    float random(vec2 co){
        return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
    }
 
    void main() {
        vec3 newPosition = position;    
        newPosition.z = cos(u_time / 3.0) / 10.0; 
        newPosition.z += u_height + 0.1; 
        newPosition.z += random(position.xy) / 15.0; 
        v_position = newPosition; 
        gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
    }
</script>
<script id="waterFragment" type="x-shader/x-fragment">
    varying vec3 v_position; 
    uniform float u_radius; 
    uniform vec3 u_camera; 
    uniform float u_hour; 
 
    void main() {
        float fog = distance(u_camera, v_position) / 20.0; 
        float day = clamp(6.0 - abs(12.0 - u_hour), 0.0, 1.0); 
        vec3 darkBlue = vec3(fog / 4.0, 0.25, 0.5); 
        vec3 lightBlue = vec3(fog, 0.85, 0.8); 
        gl_FragColor =  vec4(day * lightBlue + (1.0 - day) * darkBlue, 0.8 - fog ); 
    }
</script>
<script id="sandVertex" type="x-shader/x-vertex">
    varying vec2 vUV;
    varying vec3 v_normal; 
    varying vec3 v_position; 
 
    void main() {  
        vUV = uv;
        vec4 pos = vec4(position, 1.0);
        gl_Position = projectionMatrix * modelViewMatrix * pos;
        v_normal = normalize(normal);
        v_position = position; 
    }
</script>
 
<script id="sandFragment" type="x-shader/x-fragment">
    varying vec3 v_normal; 
    varying vec3 v_position; 
    uniform vec3 u_camera; 
 
    void main() {
        vec3 lightSource = normalize(vec3(0.0, 1.0, 3.0)); 
        float dprod = max(dot(v_normal, lightSource), 0.0); 
        vec3 highlightColor = vec3( 0.7, 0.7, 0.2);
        vec3 shadowColor = vec3(0.3, 0.3, 0.6); 
        float fog = pow(distance(vec3(0.0), v_position) , 2.0) / 30.0; 
        gl_FragColor = vec4( shadowColor + highlightColor  * dprod * 0.4, 0.9 - fog);
    }
</script>
 
<script id="skyVertex" type="x-shader/x-fragment">  
    varying vec2 vUV;
    varying float v_z; 
 
    void main() {  
        vUV = uv;
        vec4 pos = vec4(position, 1.0);
        gl_Position = projectionMatrix * modelViewMatrix * pos;
        v_z = normalize(position).z; 
    }
</script>
 
<script id="skyFragment" type="x-shader/x-fragment">  
    varying float v_z; 
    uniform float u_hour; 
 
    void main() {  
        vec3 dayColor = vec3(0.4 - v_z, 0.7 - v_z, 0.80);
        vec3 sunsetColor = vec3(0.5 + v_z , 0.2 + v_z, 0.3);
        vec3 nightColor = vec3(0.05 - v_z / 10.0, 0.05 - v_z / 5.0, 0.20);
 
        vec3 dtn = vec3(0.0);
        dtn.x = clamp(6.0 - abs(12.0 - u_hour), 0.0, 1.0); 
        dtn.z = 1.0 - dtn.x; 
        dtn.y = (clamp(sin(u_hour * 3.14 / 12.0), 0.5, 1.0) - 0.5) * 2.0; 
        dtn = normalize(dtn); 
 
        gl_FragColor = vec4(dtn.x * dayColor + dtn.y * sunsetColor + dtn.z * nightColor, 1.0); 
    }
</script>  
 
 
<script>
 
    //https://thebookofshaders.com/04/
    var container;
    var camera, scene, renderer, controls;
    var waterUniforms, skyUniforms; 
    var water, tidehand, sand, clockface; 
    var angle, date, height; 
    var pastHighTide = new Date("September 20, 2018 5:12:00")
 
    init();
    animate();
 
    function init() {
        container = document.getElementById( "container" );
 
        camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
        camera.position.z = 6;
        controls = new THREE.OrbitControls( camera );
        controls.maxDistance = 7; 
        controls.minDistance = 1; 
        controls.enableDamping = true; 
        controls.dampingFactor = 0.2;
        controls.maxAzimuthAngle = Math.PI / 4; 
        controls.minAzimuthAngle = Math.PI / -4; 
        controls.minPolarAngle = Math.PI / 4; 
        controls.maxPolarAngle = Math.PI * 3 / 4; 
 
        scene = new THREE.Scene();
 
        date = new Date(); 
        height = Math.cos(-1 * angle) / 7.0; 
 
        var geometry = new THREE.PlaneGeometry(50, 50, 100, 100); 
 
 
        waterUniforms = {
            u_time: { type: "f", value: 1.0 },
            u_height: { type: "f", value: height},
            u_camera: {type:"v3", value: camera.position}, 
            u_hour: {type: "f", value: date.getHours() + date.getMinutes() / 60}
        };
 
        var material = new THREE.ShaderMaterial( {
            uniforms: waterUniforms,
            vertexShader: document.getElementById("waterVertex").textContent,
            fragmentShader: document.getElementById("waterFragment").textContent, 
            transparent: true, 
            depthWrite: false, 
            side: THREE.DoubleSide
 
        } );
        water = new THREE.Mesh( geometry, material );
        scene.add( water );
 
        //sand geometry made in blender
        var loader = new THREE.JSONLoader();
        loader.load("tallsand.json", 
            function(geometry, materials){
                var sandUniforms = ({
                    u_camera: {type:"v3", value: camera.position}, 
                    u_hour: {type: "f", value: date.getHours() + date.getMinutes() / 60}
                }); 
                material = new THREE.ShaderMaterial( {
                    uniforms: sandUniforms, 
                    vertexShader: document.getElementById("sandVertex").textContent,
                    fragmentShader: document.getElementById("sandFragment").textContent, 
                    transparent: true, 
                } );
                var sandy = new THREE.Mesh(geometry, material); 
                sandy.rotation.x += Math.PI / 2; 
                sandy.scale.set(2, 2, 2); 
                sandy.position.z = -0.001; 
                scene.add(sandy); 
            }, 
            function(xhr){console.log("loaded json")},
            function(err){console.log("error loading json")}
        );
 
        geometry = new THREE.PlaneGeometry( .1, 2);
        material = new THREE.MeshBasicMaterial( { color: 0xffffff, side: THREE.DoubleSide, transparent:true} );
        stick = new THREE.Mesh( geometry, material );
        stick.position.y += .9; 
        tidehand = new THREE.Group(); 
        tidehand.add(stick); 
        tidehand.rotation.z = angle; 
        tidehand.position.z += 1.01; 
        scene.add( tidehand );
 
        var texture = new THREE.TextureLoader().load("clockface.png"); 
        geometry = new THREE.PlaneGeometry(6, 6); 
        material = new THREE.MeshBasicMaterial({map:texture, transparent: true, depthWrite: false}); 
        clockface = new THREE.Mesh(geometry, material); 
        clockface.position.z += 1;
        scene.add(clockface); 
 
        //skydome code from Ian Webster http://www.ianww.com/blog/2014/02/17/making-a-skydome-in-three-dot-js/
        //we are inside of a sphere! 
        geometry = new THREE.SphereGeometry(300, 60, 40);  
        skyUniforms = {
            u_hour: {type: "f", value: date.getHours() + date.getMinutes() / 60}
        }
        material = new THREE.ShaderMaterial( {  
            uniforms: skyUniforms,
            vertexShader:   document.getElementById("skyVertex").textContent,
            fragmentShader: document.getElementById("skyFragment").textContent,
            side: THREE.DoubleSide
        });
        skyBox = new THREE.Mesh(geometry, material);  
        skyBox.eulerOrder = "XZY";  
        skyBox.renderDepth = 1000.0;  
        scene.add(skyBox);  
 
        renderer = new THREE.WebGLRenderer();
        renderer.setPixelRatio( window.devicePixelRatio );
        container.appendChild( renderer.domElement );
        onWindowResize();
        window.addEventListener( "resize", onWindowResize, false );
    }
 
    function onWindowResize(event) {
        renderer.setSize( window.innerWidth, window.innerHeight );
    }
 
    function animate() {
        updateTide(); 
        waterUniforms.u_time.value += 0.05;
 
        controls.update(); 
        requestAnimationFrame( animate );
        render();
    }
 
    function updateTide() {
        date = new Date(); 
        //date.setMinutes(date.getMinutes() + 1); //artificial fast forward
        document.getElementById("info").innerHTML = date.toLocaleString(); 
        var diff = (date - pastHighTide) / 60000; 
        diff = diff % 745; 
        angle = -1 * Math.PI * 2 * diff / 745;
        height = Math.cos(-1 * angle) / 7.0; 
        waterUniforms.u_height.value = height; 
        skyUniforms.u_hour.value = date.getHours() + date.getMinutes() / 60; 
        waterUniforms.u_hour.value = date.getHours() + date.getMinutes() / 60; 
 
        tidehand.rotation.z = angle; 
    }
 
    function render() {
        renderer.render( scene, camera );
    }
 
    document.addEventListener("keydown", function(e){
        switch(e.keyCode){
            case 32: 
                controls.reset(); 
                break; 
        } 
    });
</script>

 

lass-LookingOutwards02

For this Looking Outwards, I am focusing on the work of Glen Marshall. When browsing his gallery, I was immediately drawn to his music video for Clouds in Cloudless Skies (2014). Honestly, I think this is just because I really like cubes and squares. The music video shows travel through an infinite world, where cubes are constantly being generated. The generation of these cubes is different in each scene of the music video. I appreciate this because each scene has a different quality to it--in some scenes, cubes are disappearing and reappearing, and in others they are constantly being distorted. I think that the choice to have a theme between these scenes, yet make them distinct from each other, makes it more believable as a "world". Each scene follows a theme, yet they show different aspects of life with different generative methods. It is clear that Marshall used a large variety of algorithms to create this work.

As for effective complexity, it is clear that the cube shapes have a high amount of order. They are all perfect cubes of the same size that tessellate perfectly. However, there is a randomness in their patterns of creation and movement that makes it so believable as a living world. 

lass-Reading03

I think that good first word art requires a special kind of person to create. It's difficult to move away from the norm and create something completely groundbreaking. While I would be interested in creating this kind of art, I simply don't think that I have the ability. Maybe this is an overly defeatist way of thinking, but considering just the number of people on Earth, the probability of a good original thought is really small, and I don't think I'm creative enough.

Instead, I find it easier to focus on concepts that already exist. I think that expanding on and combining existing ideas create really successful works of art that last through time. I often hear the Picasso quote "Good artists copy, great artists steal". To me, this means that you should take inspiration from other people's work, but make it your own in some way. I appreciate this quote because it reminds me that ownership and personality are more important than 100% originality. I think that this places me in between first and old word art, since I don't necessarily pursue complete originality, but I'm also not trying to follow any well established model (at least consciously). 

lass-AnimatedLoop

For my looping gif, I was really interested in doing a pinching/pulling motion, similar to cell division. At first I was very unsure of how to do this, since I had seen it before but didn't know a technical term. Connie Ye told me to look into metaballs, and I learned the process for making them from this coding challenge by Dan Shiffman. My initial plan involved several components, but after I had gotten the metaballs to work initially, many people urged me to keep it simple instead. I ended up only creating the first main component from the sketches below.   

In the end, I'm glad I went with the simple route. It allowed me to have more flexibility with colors, and it resulted in something much cleaner and not so chaotic. In the future I might try to create something more complex with the code that I wrote, since I planned it out to allow for several "groupings" of metaballs. I think it might be very laggy.

Thank you Golan, Connie, and Lauren for helping me out with this project!

debug view: 

BallGroup metaballs;
int numFrames = 30;
int currentFrame = 0;
int colorChoice = 0;
color[] ballColors, backgroundColors;
 
void setup() {
 size(640, 640);
 noStroke();
 
 ballColors = new color[] {
  color(250, 192, 133), color(169, 67, 117), color(50, 70, 171)
 };
 backgroundColors = new color[] {
  color(200, 94, 141), color(250, 200, 113), color(169, 200, 117)
 };
 
 metaballs = new BallGroup();
 metaballs.addBall(new Metaball(width / 2, height - 40, width / 2, -100, 80, 80));
 metaballs.addBall(new Metaball(width / 2, -100, width / 2, -100, 80, 0));
 metaballs.addBall(new Metaball(width / 2 - 50, height - 50, width / 2 - 50, height - 50, 130, 130));
 metaballs.addBall(new Metaball(width / 2 + 50, height - 50, width / 2 + 50, height - 50, 130, 130));
 metaballs.addBall(new Metaball(width / 2, height * 2, width / 2, height - 40, 0, 80));
 
 metaballs.setColors(ballColors[0], ballColors[1]);
}
 
void draw() {
 currentFrame = (currentFrame + 1) % 30;
 if (currentFrame == 0) {
  colorChoice = (colorChoice + 1) % 3;
  metaballs.setColors(ballColors[colorChoice], ballColors[(colorChoice + 1) % 3]);
 }
 float movement = easing(1.0 * currentFrame / numFrames);
 metaballs.fadeCol = (lerpColor(ballColors[(colorChoice + 1) % 3], ballColors[(colorChoice + 2) % 3], movement));
 background(lerpColor(ballColors[(colorChoice + 0) % 3], ballColors[(colorChoice + 1) % 3], movement));
 //background(255, 200, 200); 
 metaballs.update(movement);
 metaballs.show();
}
 
float easing(float x) {
 //this is where i would put an easing function if i had one 
 return x;
}
 
class Metaball {
 PVector position;
 PVector beginning;
 PVector destination;
 float radius;
 float radius1;
 float radius2;
 Metaball(float x1, float y1, float x2, float y2, float r1, float r2) {
  position = new PVector(x1, y1);
  beginning = new PVector(x1, y1);
  destination = new PVector(x2, y2);
  radius = 100;
  radius1 = r1;
  radius2 = r2;
 }
 void show() { //shows the centers (for debugging) 
  noFill();
  stroke(0);
  strokeWeight(1);
  //ellipse(position.x, position.y, radius, radius);
 }
 void update(float movement) {
  position = position.lerp(beginning, destination, movement);
  radius = lerp(radius1, radius2, movement);
 }
}
 
class BallGroup {
 ArrayList < Metaball > arr;
 color startCol;
 color endCol;
 color col;
 color fadeCol;
 
 BallGroup() {
  arr = new ArrayList < Metaball > ();
  col = color(0, 0, 0);
  startCol = color(0, 0, 0);
  endCol = color(0, 0, 0);
 }
 void setColor(color c) {
  col = c;
 }
 void setColors(color c1, color c2) {
  startCol = c1;
  endCol = c2;
 }
 void addBall(Metaball mb) {
  arr.add(mb);
 }
 void show() {//metaball code from Dan Shiffman: https://www.youtube.com/watch?v=ccYLb7cLB1I
  loadPixels();
  for (int x = 0; x < width; x++) {
   for (int y = 0; y < height; y++) {
    float sum = 0;
    for (Metaball mb: arr) {
     float dst = dist(x, y, mb.position.x, mb.position.y);
     sum += 100 * mb.radius / dst;
    }
    if (sum > 200) {
     //adds a border
     //if (sum < 220)
     //  pixels[x + y * width] = col; 
     //else
     pixels[x + y * width] = lerpColor(col, fadeCol, y * 1.0 / height);
    }
   }
  }
  updatePixels();
  for (Metaball mb: arr)
   mb.show();
 }
 void update(float movement) {
  for (Metaball mb: arr)
   mb.update(movement);
  col = lerpColor(startCol, endCol, movement);
 }
}

lass-Scope

download png 

For my praxinoscope, I decided to create an animation of a russian matryoshka doll opening. I liked this idea because it loops easily.
I drew the doll using p5's beginShape() and curveVertex(), but in hindsight I probably could have done it a lot more easily if I just uploaded png images for the top and bottom halves. Still, I got to experiment with drawing curves and using the p5 transformations, which was fun.

Initially, I couldn't decide between doing matryoshkas or the same concept with eggs. I think eggs would have been cool too.

function drawArtFrame ( whichFrame ) { 
  push(); 
  rotate(Math.PI); //because i made it upside down on accident. haha
  fill(255); 
  //vertices form the upper and lower matryoshka halves
  var upperHalf = [ [1.3, 2], [1.3, 0], [1.2, 1.3], [.8, 2], [0, 2.3]];
  var lowerHalf = [[1.3, 2], [1.3, 0], [1.5, -1.8], [0, -2.3]];
 
  //drawing the outer matryoshka
  sWidth = map((whichFrame ) % 10, 0, 9, 6, 12); 
  sHeight = map((whichFrame ) % 10, 0, 9, 7, 14); 
  var heightChange = 0
  fill(200);
 
  strokeWeight(1); 
  stroke(0); 
  fill(0);
  drawMatryoshka(lowerHalf, sWidth, sHeight, -1 * heightChange); 
  fill(255);
  drawMatryoshka(upperHalf, sWidth, sHeight, heightChange); 
  drawDetails(heightChange, sHeight, sWidth, 255); 
 
  //drawing the inner matryoshka
  whichFrame = (whichFrame + 0) % 10; 
  sWidth = map(whichFrame, 0, 9, 12, 15); 
  sHeight = map(whichFrame , 0, 9, 14, 20); 
  var heightChange = map(whichFrame, 0, 9, 6, 80); 
 
  var opacity =   map(whichFrame, 0, 10, 255, 0);
 
  fill(0, opacity);
  fill(0, opacity);
  stroke(0, opacity); 
  drawMatryoshka(lowerHalf, sWidth, sHeight, -1 * heightChange); 
  fill(255, opacity);
  drawMatryoshka(upperHalf, sWidth, sHeight, heightChange); 
  drawDetails(heightChange, sHeight, sWidth, opacity); 
  pop(); 
}
 
//draws shape based on the vertices w/ vertical symmetry 
function drawMatryoshka(verts, sWidth, sHeight, heightChange){
  beginShape();
  for(var i = 0; i  < verts.length; i++ ){
    curveVertex(verts[i][0] * sWidth, verts[i][1] * sHeight + heightChange); 
  }
  for(var i = verts.length - 2; i  >=0; i-- ){
    curveVertex(verts[i][0] * sWidth * -1, verts[i][1] * sHeight + heightChange); 
  }
  endShape(); 
  line(-1.3 * sWidth + .5,  heightChange, sWidth * 1.3 - .5,  heightChange);
}
 
function drawDetails(heightChange, sHeight, sWidth, opacity){
  //face
  strokeWeight(1); 
  fill(255, opacity); 
  ellipse(0, heightChange + sHeight * 1.3, sWidth * 1.7, sWidth * 1.7); 
 
  //hair
  fill(0, opacity);
  arc(0, heightChange + sHeight * 1.3, sWidth * 1.7, sWidth * 1.7, PI * 2, HALF_PI, CHORD); 
  arc(0, heightChange + sHeight * 1.3, sWidth * 1.7, sWidth * 1.7, HALF_PI, PI , CHORD); 
  strokeWeight(0); 
 
  //blush
  fill(255, 150, 150, opacity); 
  ellipse(.4 * sWidth, heightChange + sHeight * 1.2,sWidth * .4, sWidth * .4); 
  ellipse(- .4 * sWidth, heightChange + sHeight * 1.2, sWidth * .4, sWidth * .4); 
 
  //eyes and mouth
  fill(0,  opacity); 
  ellipse(.25 * sWidth, heightChange + sHeight * 1.4, sWidth * .2, sWidth * .2); 
  ellipse(- .25 * sWidth, heightChange + sHeight * 1.4, sWidth * .2, sWidth * .2); 
  ellipse(0, heightChange + sHeight , sWidth * .5, sHeight * .05);
 
  //bow
  fill(255, opacity);
  push(); 
  translate(0, -1 * heightChange);
  rotate(10); 
  ellipse(.2 * sWidth,  -.1 * sHeight, sWidth * .6, sWidth * .3); 
  rotate(-20); 
  ellipse(-.2 * sWidth,  -.1 * sHeight, sWidth * .6, sWidth * .3); 
  pop(); 
 
  //flower
  fill(255, opacity); 
  push();
  translate(0, -1.1 * sHeight -1 * heightChange);
  rotate(sWidth * .2); 
  for(var i = 0; i < 3; i++){
    ellipse(0,  0, sWidth * .3, sWidth * 1.2); 
    rotate(PI / 1.5 ); 
  }
  pop(); 
  fill(0, opacity);
  ellipse(0, -1.1 * sHeight -1 * heightChange, sWidth * .4, sWidth * .4); 
  fill(255); 
  strokeWeight(1); 
}

lass-Reading02

1A) Something I like that exhibits effective complexity is the circle patterns made in sand by pufferfish. These patterns are highly ordered as they always follow a specific geometric procedure that seems to create nearly identical circles each time. I find this interesting because the pattern is created so instinctively, but cannot be perfect as it was created by a living being under varying underwater conditions.

(photo from National Geographic)

1B) The Problem of Dynamics

This problem stood out to me because I thought it was strange that people would try to put a restriction on generative art, saying that it MUST exhibit change over time in order to be truly generative, otherwise it is just an artifact. While I think that being dynamic is an interesting quality to have, I don't think that it should be what qualifies art as generative or not. Even when results are "frozen", the process through which they were created was generative.

 

lass-Interruptions

Observations:

1. The artwork is square
2. The artwork is made of many short black lines of the same length
3. There are areas of the artwork where the lines are missing
4. The lines are angled randomly
5. The lines are distributed evenly
6. There are clusters of missing lines
7. Although the lines are angled randomly, they seem to tend towards vertical(at least in the first 3 images)
8. The lines can be sorted into a square grid, where the height of each row is shorter than the length of a black line
9. The lines overlap in places
10. Some columns seem to have more vertical-tending lines than others

One of the things I was stumped on with this project was how to create the clusters of missing lines (#6). Golan and Char helped me by telling me to look into perlin noise. At first, using perlin noise seemed no different from using randomness to determine if a line should be missing or not, but by looking at pictures of perlin noise I realized what the issue was. In order to get clusters that are large enough to be noticeable, the perlin noise needs to be scaled up. I achieved this effect by calling dividing my i and j by 80 in the call to noise(). This is also noted in the p5 documentation for noise(), where they state that "As a general rule the smaller the difference between coordinates, the smoother the resulting noise sequence will be."

Something interesting that I saw in this project was that using noise() instead of Math.random() to determine the angle of the lines resulted in the "tending-towards-vertical" property (#7). My guess is that that this is because Math.random() has a completely even distribution from 0 to 1, while noise() has less deviation from 0.5.

Also, I think that my observation #10 that "some columns have more vertical-tending lines than others" was completely wrong. Looking closer, it does not seem like column number has any effect on the number of vertical-tending lines. I think that maybe I tricked myself into believing this was true by seeking out some sort of pattern, but in the end it was replicated much better using just noise.

I think that my re-code was pretty successful, and I am glad that the result was simple to achieve. I toggled with values such as line count and length to try and match the original as best as I could.

lass-Intersections


var refresh;
var numLines = 12; 
var lineLength = 300; 
 
function setup() {
  createCanvas(720, 480);
  fill(255, 255, 255, 150); 
  stroke(255, 255, 255); 
  refresh = true;
}
 
function draw() {
  if (refresh) {
    background(200, 240, 240); 
    var lines = []; 
    for(var i = 0; i < numLines; i++) {
      var x = Math.random() * width; 
      var y = Math.random() * height; 
      var theta = Math.random() * Math.PI; 
      lines.push([x, y, theta]); 
      strokeWeight(2); 
      line(x, y, x + lineLength * Math.cos(theta), y + lineLength * Math.sin(theta)); 
      for(var j = 0; j < lines.length; j++) {
        var tan1 = Math.tan(theta)
        var sin2 = Math.sin(lines[j][2]); 
        var cos2 = Math.cos(lines[j][2]); 
        var dx = lines[j][0] - x; 
        var dy = lines[j][1] - y; 
        var s2 = (dx - dy / tan1) / (sin2 / tan1 - cos2); 
        var s1 =  (dx + s2 * cos2) / Math.cos(theta); 
        if(s1 < lineLength && s2 < lineLength && s1 > 0 && s2 > 0){
          strokeWeight(0); 
          ellipse(lines[j][0] + s2 * cos2, lines[j][1] + s2 * sin2, 15, 15); 
        }
      }      
    }
    refresh = false;
  }
}
 
function mousePressed() {
  refresh = true;
}

lass-IterationExercise

var refresh;
var rows = 9; 
var cols = 9; 
var spacing = 55;
var xColor; 
var dudeColor; 
 
function setup() {
  createCanvas(550, 550);
  refresh = true;
  colorMode(HSB, 100); 
}
 
function draw() {
  if (refresh) {
    background(0, 0, 100); 
    var myHue = Math.random() * 100; 
    dudeColor = color(myHue, 30, 95); 
    xColor = color((myHue + 50) % 100, 40, 95); 
    fill(dudeColor); 
    stroke(100, 100, 30); 
    for(var i = 1; i <=rows; i++)
	for(var j = 1; j <= cols; j++){ 
            if(Math.random() > .1) 
              drawDude(i, j);
            else 
              drawX(i, j); 
	}
    refresh = false;
  }
}
 
function drawDude(x, y){
  stroke(0, 0, 100); 
  strokeWeight(0); 
  ellipse(x * spacing, y * spacing, 40, 40);
  ellipse(x * spacing - 4, y * spacing - 20, 10, 20); 
  ellipse(x * spacing + 9, y * spacing - 18, 10, 20); 
  strokeWeight(3); 
  ellipse(x * spacing - 10, y * spacing - 4, 2, 2); 
  ellipse(x * spacing + 10, y * spacing - 2, 2, 2); 
  strokeWeight(2); 
  ellipse(x * spacing, y * spacing + 1, 1, 6); 
  ellipse(x * spacing, y * spacing + 8, 15, 1); 
}
 
function drawX(x, y){
  stroke(xColor); 
  strokeWeight(4); 
  line(x * spacing + 5, y * spacing + 5, x * spacing - 5, y * spacing - 5); 
  line(x * spacing - 5, y * spacing + 5, x * spacing + 5, y * spacing - 5); 
}
 
function mousePressed() {
  refresh = true;
}

lass-reading01

  1. The Critical Engineer recognises that each work of engineering engineers its user, proportional to that user's dependency upon it. 

This tenet of The Critical Engineering Manifesto states that a critical engineer must note how engineering has the ability to "train" its user once the user begins to rely on it. I found this tenet to be interesting because it phrases our dependency on technology in a way that is new and almost frightening. It is true that we are engineered by works of engineering, even though we usually think of ourselves as the engineers. As engineers, it is important to have foresight about how the things we make will affect the user, and to pay attention to how we ourselves are affected by technology. This almost suggests through transitivity that we can engineer others with our work, which is a dangerous thought to have.

Examples of people being engineered by works of engineering are prevalent throughout our lives. Personally, I feel as though I am heavily engineered by my computer. I use it for work, communication, and entertainment among many other things. All of the things I can do on my computer affect the daily routine of my life. Checking emails, submitting assignments, and even making art depend so much on this work of engineering that I rely on.