9

I'm trying to make this one https://massmoca.org/event/walldrawing340/

enter image description here

in Javascript code, using p5.js, but I have no clue how to fill these shapes with lines. Is there any other possibility, like making canvas that is circle or something like that, or I just have to make each shape seperately?

For now I was doing shape by shape, but making triangle and trapezoid is rough...

        var sketch = function (p) {
          with(p) {

            let h,
                w,
                space;

            p.setup = function() {
              createCanvas(900, 400);
              h = height / 2;
              w = width / 3;
              space = 10;
              noLoop();
            };
        
            p.draw = function() {
              drawBackground('red', 'blue', 0, 0);
              shape('Circle', 'red', 'blue', 0, 0);
              drawBackground('yellow', 'red', w, 0);
              shape('Square', 'yellow', 'red', w, 0);
              drawBackground('blue', 'yellow', 2 * w, 0);
              shape('Triangle', 'blue', 'red', 2 * w, 0)
              drawBackground('red', 'yellow', 0, h);
              shape('Rectangle', 'red', 'blue', 0, h)
              drawBackground('yellow', 'blue', w, h);
              shape('Trapezoid', 'yellow', 'red', w, h);
              drawBackground('blue', 'red', 2 * w, h);            
            };

            function drawBackground(bColor, lColor, x, y) {
                fill(bColor)
                noStroke();
                rect(x, y, w, h)
                stroke(lColor);
                strokeWeight(1);
                for (let i = 0; i < h / space; i++) {
                    line(0 + x, i * space + y + 10, w + x, i * space + y + 10);
                }

            }
            function shape(shape, bColor, lColor, x, y) {
                fill(bColor)
                noStroke();
                let w1;
                switch (shape) {
                    case 'Circle':
                        circle(x + w / 2, y + h / 2, h - space * 6);
                        stroke(lColor);
                        strokeWeight(1);
                        for (let i = 0; i < w / space; i++) {

                            for (let j = 0; j < h; j++) {
                                pX = i * space + x;
                                pY = 0 + y + j;
                                if (pow(x + w / 2 - pX, 2)
                                    + pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
                                    point(pX, pY);
                                }

                            }
                        }
                        break;

                    case 'Square':
                        w1 = w - (h - space * 6);
                        rect(x + w1 / 2, y + space * 3, h - space * 6, h - space * 6);
                        stroke(lColor);
                        strokeWeight(1);
                        for (let i = 0; i < 15; i++) {
                            for (let j = 0; j < h - space * 6; j++) {
                                point(x + w1 / 2 + i * space, y + space * 3 + j)
                            }
                        }
                        break;

                    case 'Triangle':
                        w1 = w - (h - space * 6);
                        triangle(x + w1 / 2, h - space * 3 + y, x + w / 2, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
                        for (let i = 0; i < w / space; i++) {

                            for (let j = 0; j < h; j++) {
                                pX = i * space + x;
                                pY = 0 + y + j;
                                if (pow(x + w / 2 - pX, 2)
                                    + pow(pY - (y + h / 2), 2) <= pow(h - space * 6 * 2 - 10, 2)) {
                                    point(pX, pY);
                                }

                            }
                        }
                        break;

                    case 'Rectangle':
                        w1 = w - (h - space * 6) / 2;
                        rect(x + w1 / 2, y + space * 3, (h - space * 6) / 2, h - space * 6)
                        break;

                    case 'Trapezoid':
                        w1 = w - (h - space * 6);
                        quad(x + w1 / 2, h - space * 3 + y, x + w1 / 2 + (h - space * 6) / 4, y + space * 3, x + w1 / 4 + h - space * 6, y + space * 3, x + w1 / 2 + h - space * 6, h - space * 3 + y)
                        break;

                    case 'Parallelogram':
                            w1 = w - (h - space * 6);
                            quad(x + w1 / 4, h - space * 3 + y, x + w1 / 2, y + space * 3, x + w1 / 2 + h - space * 6, y + space * 3, x + w1 / 4 + h - space * 6, h - space * 3 + y)
                            break;
                        break;
                }

            }

          }
        };
        
        let node = document.createElement('div');
        window.document.getElementById('p5-container').appendChild(node);
        new p5(sketch, node);
    body {
      background-color:#efefef;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.js"></script>
    <div id="p5-container"></div>

No messages, everything is working, I just want to know if I have to do so much arduous job...

Dominique Fortin
  • 2,212
  • 15
  • 20
Drunken Janna
  • 146
  • 1
  • 10
  • The problem is, I have to draw upper lines without leaving the actual shape. – Drunken Janna Aug 18 '19 at 16:40
  • in the code you showed, many of your variables are not defined. What were they defined as? – Darrow Hartman Aug 18 '19 at 16:49
  • 1
    to simplify the circle: you can use acos on the x to get an angle and then sin / -sin for the y limits. – Isitar Aug 18 '19 at 16:54
  • @DarrowHartman just uploaded whole code now – Drunken Janna Aug 18 '19 at 16:55
  • @Isitar thx do you have any ideas about triangle and trapezoid? – Drunken Janna Aug 18 '19 at 16:56
  • 2
    i suggest using linear algebra, get the equation (ax+b) for the skew lines, if the bottom is horizontal, you get the lower bound, the upper one is f(x). in a trapezoid you have the upper line as upper bound --> use max(f(x), upperY). detect when the second line (the right one) starts and then use g(x) – Isitar Aug 18 '19 at 16:57
  • @DrunkenJanna Do you need the line coordinates or can you get away with rendering pixels without having to compute each vertex position ? – George Profenza Sep 03 '20 at 16:46

3 Answers3

7

If you don't need actual line coordinates (for plotting for example), I'd just make most out of createGraphics() to easily render shapes and lines into (taking advantage of the fact that get() returns a p5.Image) and p5.Image's mask() function.

Here's a basic example:

function setup() {
  createCanvas(600, 300);
  
  let w = 300;
  let h = 150;
  let spacing = 12;
  let strokeWidth = 1;
  
  const BLUE   = color('#005398');
  const YELLOW = color('#f9db44');
  const RED    = color('#dc1215');
  
  bg = getLinesRect(w, h, RED, BLUE, spacing, strokeWidth, true);
  fg = getLinesRect(w, h, RED, YELLOW, spacing, strokeWidth, false);
  mask = getCircleMask(w, h, w * 0.5, h * 0.5, 100, 0);
  
  image(bg, 0, 0);
  image(fg, w, 0);
  // render opaque mask (for visualisation only), mask() requires alpha channel
  image(getCircleMask(w, h, w * 0.5, h * 0.5, 100, 255),0, h);
  
  // apply mask
  fg.mask(mask);
  // render bg + masked fg
  image(bg, w, h);
  image(fg, w, h);
  
  // text labels
  noStroke();
  fill(255);
  text("bg layer", 9, 12);
  text("fg layer", w + 9, 12);
  text("mask", 9, h + 12);
  text("bg + masked fg", w + 9, h + 12);
}

function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
  let rect = createGraphics(w, h);
  rect.background(bg);
  rect.stroke(fg);
  rect.strokeWeight(strokeWidth);
  
  if(isHorizontal){
    for(let y = 0 ; y < h; y += spacing){
      rect.line(0, y + strokeWidth, w, y + strokeWidth);
    } 
  }else{
    for(let x = 0 ; x < w; x += spacing){
      rect.line(x + strokeWidth, 0, x + strokeWidth, h);
    }
  }
  // convert from p5.Graphics to p5.Image
  return rect.get();
}

function getCircleMask(w, h, cx, cy, cs, opacity){
  let mask = createGraphics(w, h);
  // make background transparent (alpha is used for masking)
  mask.background(0, opacity);
  mask.noStroke();
  mask.fill(255);
  mask.circle(cx, cy, cs);
  // convert p5.Graphics to p5.Image
  return mask.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

3 coloured composition with horizontal lined background and vertical lined foreground, applying a circular mask to the foreground

You can apply the same logic for the rest of the shapes:

function setup() {
  createCanvas(1620, 590);
  
  let compWidth  = 500;
  let compHeight = 250;
  let compSpacing= 30;
  
  let lineWeight = 1.5;
  let lineSpacing = 12;
  
  const BLUE   = color('#005398');
  const YELLOW = color('#f9db44');
  const RED    = color('#dc1215');
  
  // yellow square
  circleMask   = getCircleMask(compWidth, compHeight, compWidth * 0.5, compHeight * 0.5, 210);
  redCircle    = getComposition(compWidth, compHeight, RED, 
                                                    BLUE,
                                                    YELLOW,
                                                    lineSpacing, lineWeight, circleMask);
  
  
  // red box
  boxMask      = getRectMask(compWidth, compHeight, (compWidth - 100) * 0.5, 20, 100, 210);
  redBox       = getComposition(compWidth, compHeight, RED, 
                                                    YELLOW,
                                                    BLUE,
                                                    lineSpacing, lineWeight, boxMask);
  
  
  // yellow square
  squareMask   = getRectMask(compWidth, compHeight, 144, 20, 210, 210);
  yellowSquare = getComposition(compWidth, compHeight, YELLOW, 
                                                    RED,
                                                    BLUE,
                                                    lineSpacing, lineWeight, squareMask);
                                                    
  // yellow trapeze
  trapezeMask   = getQuadMask(compWidth, compHeight, 200, 25, 200 + 115, 25,
                                                     150 + 220, 220, 150, 220);
  yellowTrapeze = getComposition(compWidth, compHeight, YELLOW, 
                                                    BLUE,
                                                    RED,
                                                    lineSpacing, lineWeight, trapezeMask);
                                                    
  // blue triangle
  triangleMask   = getTriangleMask(compWidth, compHeight, compWidth * 0.5, 25,
                                                     150 + 220, 220, 150, 220);
  blueTriangle   = getComposition(compWidth, compHeight, BLUE, 
                                                    YELLOW,
                                                    RED,
                                                    lineSpacing, lineWeight, triangleMask);
                                                    
  // blue parallelogram
  parallelogramMask = getQuadMask(compWidth, compHeight, 200, 25, 200 + 145, 25,
                                                         150 + 145, 220, 150, 220);
  blueParallelogram = getComposition(compWidth, compHeight, BLUE, 
                                                    RED,
                                                    YELLOW,
                                                    lineSpacing, lineWeight, parallelogramMask);
  
  // render compositions
  image(redCircle, compSpacing, compSpacing);
  image(redBox, compSpacing, compSpacing + (compHeight + compSpacing));
  
  
  image(yellowSquare, compSpacing + (compWidth + compSpacing), compSpacing);
  image(yellowTrapeze, compSpacing + (compWidth + compSpacing), compSpacing + (compHeight + compSpacing));
  
  image(blueTriangle, compSpacing + (compWidth + compSpacing) * 2, compSpacing);
  image(blueParallelogram, compSpacing + (compWidth + compSpacing) * 2, compSpacing + (compHeight + compSpacing));
  
}

function getComposition(w, h, bgFill, bgStroke, fgStroke, spacing, strokeWidth, mask){
  let comp = createGraphics(w, h);
  
  bg = getLinesRect(w, h, bgFill, bgStroke, spacing, strokeWidth, true);
  fg = getLinesRect(w, h, bgFill, fgStroke, spacing, strokeWidth, false);
  // apply mask
  fg.mask(mask);
  // render to final output
  comp.image(bg, 0, 0);
  comp.image(fg, 0, 0);
  
  return comp;
}

function getRectMask(w, h, rx, ry, rw, rh){
  let mask = createGraphics(w, h);
  // make background transparent (alpha is used for masking)
  mask.background(0,0);
  mask.noStroke();
  mask.fill(255);
  mask.rect(rx, ry, rw, rh);
  // convert p5.Graphics to p5.Image
  return mask.get();
}

function getCircleMask(w, h, cx, cy, cs){
  let mask = createGraphics(w, h);
  // make background transparent (alpha is used for masking)
  mask.background(0,0);
  mask.noStroke();
  mask.fill(255);
  mask.circle(cx, cy, cs);
  // convert p5.Graphics to p5.Image
  return mask.get();
}

function getQuadMask(w, h, x1, y1, x2, y2, x3, y3, x4, y4){
  let mask = createGraphics(w, h);
  // make background transparent (alpha is used for masking)
  mask.background(0,0);
  mask.noStroke();
  mask.fill(255);
  mask.quad(x1, y1, x2, y2, x3, y3, x4, y4);
  // convert p5.Graphics to p5.Image
  return mask.get();
}

function getTriangleMask(w, h, x1, y1, x2, y2, x3, y3){
  let mask = createGraphics(w, h);
  // make background transparent (alpha is used for masking)
  mask.background(0,0);
  mask.noStroke();
  mask.fill(255);
  mask.triangle(x1, y1, x2, y2, x3, y3);
  // convert p5.Graphics to p5.Image
  return mask.get();
}

function getLinesRect(w, h, bg, fg, spacing, strokeWidth, isHorizontal){
  let rect = createGraphics(w, h);
  rect.background(bg);
  rect.stroke(fg);
  rect.strokeWeight(strokeWidth);
  
  if(isHorizontal){
    for(let y = 0 ; y < h; y += spacing){
      rect.line(0, y + strokeWidth, w, y + strokeWidth);
    } 
  }else{
    for(let x = 0 ; x < w; x += spacing){
      rect.line(x + strokeWidth, 0, x + strokeWidth, h);
    }
  }
  // convert from p5.Graphics to p5.Image
  return rect.get();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.1.9/p5.min.js"></script>

rough reproduction of Soll Lewitt's Wall Drawing 340

Probably both rectangles and the triangle could've been drawn using getQuadMask() making good use of coordinates.

Note that I've just eye balled the shapes a bit so they're not going to be perfect, but it should be easy to tweak. Bare in mind the placement of the mask will have an effect of on how the vertical lines will align.

There are probably other ways to get the same visual effect. For example, using texture() and textureWrap(REPEAT) with beginShape()/endShape(), using pixels for each line and checking intersections before changing direction and colours, etc.

In terms of generating lines for plotting I would start with horizontal lines, doing line to convex polygon intersection to determine where to stop the horizontal lines and start vertical lines. @AgniusVasiliauskas's answer(+1) is good for that approach.

Freya Holmér has a pretty nice visual explanation for the test.

George Profenza
  • 50,687
  • 19
  • 144
  • 218
3

You need linear algebra stuff, basically noticing how vertical line starting/ending Y coordinate changes in relation to line's X coordinate. And of course a lot of experimenting until you get something usable. Something like :

var w = 600
    h = 600
    sp = 15

var slides = [fcircle, fsquare, ftriangle, ftrapezoid, fparallelogram];

var active = 0;

var ms;

function blines(){
  stroke(0);
  for (var i=0; i < h; i+=sp) {
    line(0,i,w,i);
  }
}

function vertlines(calcline) {
   for (var x=w/2-w/4+sp; x < w/2+w/4; x+=sp) {
       var pnts = calcline(x);
       line(pnts[0],pnts[1],pnts[2],pnts[3]);
   }
}

function fcircle() {
   // cut background
   noStroke();
   circle(w/2, h/2, w/2);
   stroke('red');
   // draw figure lines
   let calc = function (x){
      var sx = x-w/2;
      var sy = h/2;
      var ey = h/2;
      sy += 137*sin(2.5+x/135);
      ey -= 137*sin(2.5+x/135);
      return [x,sy,x,ey];
   }
   vertlines(calc);
}

function fsquare() {
   // cut background
   noStroke();
   quad(w/2-w/4, h/2-h/4, w/2+w/4, h/2-h/4,
        w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);  
   stroke('red');
   // draw figure lines
   let calc = function (x){
      return [x,h/2-h/4,x,h/2+h/4];
   }
   vertlines(calc);
}

function ftriangle() {
   // cut background
   noStroke();
   quad(w/2, h/2-h/4, w/2+w/4, h/2+h/4,
        w/2-w/4, h/2+h/4, w/2, h/2-h/4);  
  stroke('red');
   // draw figure lines
   let calc = function (x){
      var inpx = x > w/2 ? w-x : x;
      var ys = h/2+h/4;
      ys += -(0.3*inpx*log(inpx)-220);
      return [x,ys,x,h/2+h/4];
   }
   vertlines(calc);
}

function ftrapezoid() {
   // cut background
   noStroke();
   quad(w/2-w/10, h/2-h/4, w/2+w/10, h/2-h/4,
        w/2+w/4, h/2+h/4, w/2-w/4, h/2+h/4);  
   stroke('red');
   // draw figure lines
   let calc = function (x){
      var inpx = x > w/2 ? w-x : x;
      var ys = h/2+h/4;
      ys += -(0.55*inpx*log(inpx)-420);
      if (x >= w/2-w/10 && x <= w/2+w/10) {
         ys=h/2-h/4;
      }
      return [x,ys,x,h/2+h/4];
   }
   vertlines(calc);
}

function fparallelogram() {
   // cut background
   noStroke();
   quad(w/2-w/10, h/2-h/4, w/2+w/7, h/2-h/4,
        w/2, h/2+h/4, w/2-w/4, h/2+h/4);  
   stroke('red');
   // draw figure lines
   let calc = function (x){
      // guard condition
      if (x > w/2+w/7)
         return [0,0,0,0];
      var inpx = x > w/2 ? w-x : x;
      var ys = h/2+h/4;
      ys += -(0.55*inpx*log(inpx)-420);
      var ye=h/2+h/4
      if (x >= w/2-w/10) {
         ys=h/2-h/4;
      }
      if (x > w/2) {
         ye = h/2+h/4;
         ye += 0.50*inpx*log(inpx)-870;
      }
      return [x,ys,x,ye];
   }
   vertlines(calc);
}

function setup() {
  ms = millis();
  createCanvas(w, h);
}

function draw() {
   if (millis() - ms > 2000) {
      ms = millis();
      active++;
      if (active > slides.length-1)
         active = 0;
   }
   background('#D6EAF8');
   fill('#D6EAF8');
   blines();
   slides[active]();
}

Slideshow DEMO

Agnius Vasiliauskas
  • 10,935
  • 5
  • 50
  • 70
0

I have a way to do some of the shapes, but I am not sure about others. One way you could do it is if you know where every point on the outline of the shape is, you could just use a for loop and connect every other point from the top and bottom using the line or rect function. This would be relatively easy with shapes like squares and parallelograms, but I am not sure what functions could be used to get this for the points of a circle or trapezoid.

See more here: https://www.openprocessing.org/sketch/745383

Darrow Hartman
  • 4,142
  • 2
  • 17
  • 36