能否制作带有线条背景或非矩形形状的画布?

9
我正在尝试制作这个https://massmoca.org/event/walldrawing340/

enter image description here

在使用p5.js编写的JavaScript代码中,我不知道如何用线条填充这些形状。是否有其他可能性,比如制作圆形之类的画布,或者我必须分别制作每个形状?目前我正在逐个制作形状,但制作三角形和梯形很困难...

        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>

没有消息,一切都正常,我只是想知道是否需要做这么辛苦的工作...


在你展示的代码中,许多变量都没有被定义。它们被定义为什么? - Darrow Hartman
1
为简化圆形:您可以在x上使用acos来获取角度,然后使用sin / -sin作为y限制。 - Isitar
@DarrowHartman 刚刚上传了整个代码。 - Drunken Janna
2
我建议使用线性代数,获取斜线的方程(ax+b),如果底部是水平的,则得到下限,上限为f(x)。在梯形中,您将上线作为上限-->使用max(f(x), upperY)。检测第二条线(右侧)何时开始,然后使用g(x)。 - Isitar
@DrunkenJanna 你需要线条坐标吗?还是只需渲染像素而无需计算每个顶点的位置? - George Profenza
显示剩余2条评论
3个回答

7
如果您不需要实际的线条坐标(例如绘图),我建议利用createGraphics()函数,将形状和线条轻松渲染到其中(利用get()返回一个p5.Image的特性),并使用p5.Image的mask()函数。
以下是一个基本示例:

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>

将圆形的遮罩应用于前景,形成具有水平线背景和垂直线前景的三色组合

对于其余形状,您可以采用相同的逻辑:

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>

索尔·勒维特(Soll Lewitt)的《墙绘340》粗略复制品

可能使用getQuadMask()函数可以画出矩形和三角形,能够很好地利用坐标。

请注意,我只是凭眼睛看一下形状,所以它们不会完美,但调整应该很容易。要注意蒙版的位置会影响垂直线的对齐方式。

可能有其他方法可以达到相同的视觉效果。例如,使用texture()textureWrap(REPEAT)beginShape()/endShape(),为每条线使用像素并在更改方向和颜色之前检查交点等。

在生成绘图线方面,我会从水平线开始,通过线到凸多边形的交点确定何时停止水平线并开始垂直线。@AgniusVasiliauskas的回答(+1)对于这种方法是很好的。

Freya Holmér有一个相当不错的视觉解释。


3

您需要线性代数的内容,主要是注意垂直线的起始/终止Y坐标如何与线的X坐标相关。当然,还需要进行大量实验,直到得到可用的内容。就像这样:

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]();
}

幻灯片演示DEMO


0

我有一种方法可以做出一些形状,但其他的我不确定。一种方法是,如果你知道形状轮廓上每个点的位置,你可以使用for循环,并使用线条或矩形函数连接从顶部和底部的每个其他点。这对于像正方形和平行四边形这样的形状相对容易,但我不确定用什么函数可以得到圆形或梯形的点。

在此处查看更多信息:https://www.openprocessing.org/sketch/745383


2
我可以手动绘制那些形状,所以我也这样做了,但是我实际上在梯形方面遇到了问题;/ - Drunken Janna

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接