# Giving variable thickness to a line (calculating normals) ``````ArrayList<PVector> points;
ArrayList<PVector> normals;

void setup() {
size(400, 400);
background(255);

// 1. Define polyline points
points = new ArrayList<PVector>();

// 2. calculations

// Ensure normals has the same size as points
normals = new ArrayList<PVector>();
while (normals.size() < points.size()) {
}

// Calculate normals
for (int i=0; i<points.size()-1; i++) {
PVector sub = PVector.sub(points.get(i), points.get(i+1));
normals.get(i).set(-sub.y, sub.x);
}
for (int i=1; i<points.size(); i++) {
PVector sub = PVector.sub(points.get(i), points.get(i-1));
}

// Resize normals
for(PVector n : normals) {
n.normalize().mult(random(10, 30));
}

// 3. Use the calculated normals
// to draw in different ways

// draw calculated thick line
noStroke();
fill(#FFCC00);
for (int i=0; i<points.size(); i++) {
PVector p = points.get(i);
PVector n = normals.get(i);
vertex(p.x + n.x, p.y + n.y);
vertex(p.x - n.x, p.y - n.y);
}
endShape();

// draw the original line
stroke(#552200);
noFill();
beginShape();
for (PVector p : points) {
vertex(p.x, p.y);
}
endShape();

// draw line vertices
stroke(#552200);
strokeWeight(2);
fill(255);
for (PVector p : points) {
ellipse(p.x, p.y, 6, 6);
}

// draw contour points
noStroke();
fill(#883300);
for (int i=0; i<points.size(); i++) {
PVector p = points.get(i);
PVector n = normals.get(i);
ellipse(p.x + n.x, p.y + n.y, 3, 3);
ellipse(p.x - n.x, p.y - n.y, 3, 3);
}
}
void draw() {
}
``````

Here an animated version on p5.js:

5 Likes

This is actually quite useful for something I was trying to do recently. Thanks for sharing hamoid.

1 Like

Thank you so much @hamoid!

I have ported it to Python mode ``````"""
Giving variable thickness to a line (calculating normals)
Python port of code by Hamoid, found at:
https://discourse.processing.org/t/giving-variable-thickness-to-a-line-calculating-normals/5890
"""

def draw():
pass

def setup():
global points, normals
size(400, 400)
background(255)

# 1. Define polyline points
points = []
points.append(PVector(61, 183))
points.append(PVector(108, 113))
points.append(PVector(193, 118))
points.append(PVector(256, 158))
points.append(PVector(248, 239))
points.append(PVector(258, 310))
points.append(PVector(328, 353))
points.append(PVector(377, 341))

# 2. calculations

# Ensure normals has the same size as points
normals = [PVector()] * len(points)

# Calculate normals
for i in range(len(points) - 1):
sub = PVector.sub(points[i], points[i + 1])
normals[i] = PVector(-sub.y, sub.x)

for i in range(1, len(points)):
sub = PVector.sub(points[i], points[i - 1])

# Resize normals
for n in normals:
n.normalize().mult(random(10, 30))

# 3. Use the calculated normals to draw in different ways

# draw calculated thick line
noStroke()
fill("#FFCC00")
for (p, n) in zip(points, normals):
vertex(p.x + n.x, p.y + n.y)
vertex(p.x - n.x, p.y - n.y)

# draw the original line
stroke("552200")
noFill()
with beginShape():
for p in points:
vertex(p.x, p.y)

# draw line vertices
stroke("#552200")
strokeWeight(2)
fill(255)
for p in points:
ellipse(p.x, p.y, 6, 6)

# draw contour points
noStroke()
fill("#883300")
for (p, n) in zip(points, normals):
ellipse(p.x + n.x, p.y + n.y, 3, 3)
ellipse(p.x - n.x, p.y - n.y, 3, 3)

``````
2 Likes

Oh, I forgot to add this operator overloaded Python style PVector use:

``````    # Calculate normals
for i in range(len(points) - 1):
sub = points[i] - points[i + 1]
normals[i] = PVector(-sub.y, sub.x)

for i in range(1, len(points)):
sub = points[i] - points[i - 1]
normals[i] += PVector(sub.y, -sub.x)
``````
1 Like

Thanks! Using `-` to subtract PVectors is a really nice use of Python operator overloading.

I am glad you provided both forms, however. The previous form is less Pythonic but easier for new users to translate into / out of Java – for those who use both, or who are trying to learn from sketches in one mode while working in another.

2 Likes