amzon-ex
amzon-ex

Reputation: 1744

Draw a line of varying width in p5.js

I wish to draw lines on my canvas with width that gradually vary from start to end. That is, say the line starts at (0, 0) with width = 1 (equivalently, strokeWeight) and ends at (50, 50) with width = 3, and the width must (linearly) increase from 1 to 3 from start to end.

Any ideas on how to achieve this? Can't scrape it out of the web.

Upvotes: 6

Views: 3652

Answers (2)

Malte
Malte

Reputation: 556

You could also just draw the line "as a trapezoid" using beginShape() / endShape() and transforming each end of the line into two separate points. See the code snippet below:

function setup() {
  createCanvas(400, 400);
  noStroke();
  fill(220);
}

function draw() {
  background(20);
  drawVaryingWidthLine(0, 0, 50, 50, 1, 3);
  drawVaryingWidthLine(80, 20, 200, 140, 1, 5);
  drawVaryingWidthLine(30, 60, 230, 260, 10, 3);
  drawVaryingWidthLine(210, 180, 360, 330, 40, 20);
}


function drawVaryingWidthLine(x1, y1, x2, y2,  startWidth, endWidth) { 
  const halfStartWidth = startWidth / 2
  const halfEndwidth = endWidth / 2
  beginShape();
    vertex(x1 + halfStartWidth, y1 - halfStartWidth);
    vertex(x2 + halfEndwidth, y2 - halfEndwidth);
    vertex(x2 - halfEndwidth, y2 + halfEndwidth);
    vertex(x1 - halfStartWidth, y1 + halfStartWidth);
    vertex(x1 + halfStartWidth, y1 - halfStartWidth);
  endShape();
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/p5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.10.2/addons/p5.sound.min.js"></script>
    <link rel="stylesheet" type="text/css" href="style.css">
    <meta charset="utf-8" />

  </head>
  <body>
    <script src="sketch.js"></script>
  </body>
</html>

Compared to @ffmaer's solution, this one doesn't have any smoothness / transparency artifacts. I assume that mine is somewhat faster because you only draw one shape per line (though I haven't tested it). However, this version here is currently limited to lines of the angle asked for in the original post (45 degrees downward right). It could be quite easily adapted to arbitrary angles by adjusting the corner positions of the trapezoid.

Edit: now using trapezoids instead of triangles, based on @ffmaer's comment.

Upvotes: 1

ffmaer
ffmaer

Reputation: 781

enter image description here

The key is to separate the line into for example 30 segments. You draw each segment with increasing strokeWeight(). The more segments you have, the smoother the line will look.

You can use lerp() to find x,y coordinates for points between two ends.

You can use lerp() to find strokeWeight() for lines between two ends.

function setup() {
  createCanvas(200, 200);
  background("black");
  stroke("white");
  gradientLine(0, 0, 50, 50, 1, 3, 30);
  noLoop();
}

function gradientLine(
  start_x,
  start_y,
  end_x,
  end_y,
  start_weight,
  end_weight,
  segments
) {
  let prev_loc_x = start_x;
  let prev_loc_y = start_y;
  for (let i = 1; i <= segments; i++) {
    let cur_loc_x = lerp(start_x, end_x, i / segments);
    let cur_loc_y = lerp(start_y, end_y, i / segments);
    push();
    strokeWeight(lerp(start_weight, end_weight, i / segments));
    line(prev_loc_x, prev_loc_y, cur_loc_x, cur_loc_y);
    pop();
    prev_loc_x = cur_loc_x;
    prev_loc_y = cur_loc_y;
  }
}
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/p5.min.js"></script>

EDIT: Also, in case one is working with colours with alpha < 255, such artifacts can appear: enter image description here

This happens because the default capping for strokes is set to ROUND. Setting strokeCap(SQUARE) will fix this problem:

enter image description here

This needs to be set in the push()...pop() block in gradientLine(...) function. (Note: This will make the ends of the line look flat, and that needs more refined work.)

Upvotes: 8

Related Questions