Skip to content

Commit ac8f194

Browse files
committed
remove the shape type
1 parent c667054 commit ac8f194

File tree

11 files changed

+137
-135
lines changed

11 files changed

+137
-135
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ _Options are **not** [sanitized](https://en.wikipedia.org/wiki/HTML_sanitization
4242
Name | Type | Description
4343
-------------- | ---------- | ---------------------------------------------
4444
`size` | `number` | Bounding box dimensions (in pixels)
45-
`complexity` | `number` | Shape complexity (number of points)
46-
`contrast` | `number` | Shape contrast (randomness of point position)
45+
`complexity` | `number` | Blob complexity (number of points)
46+
`contrast` | `number` | Blob contrast (randomness of point position)
4747

4848

4949
#### Optional

internal/animate/interpolate.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {Shape} from "../types";
2-
import {split, splitLine, mod, smooth, mapShape} from "../util";
1+
import {Point} from "../types";
2+
import {split, splitLine, mod, smooth, mapPoints} from "../util";
33

44
// Interpolates between angles a and b. Angles are normalized to avoid unnecessary rotation.
55
// Direction is chosen to produce the smallest possible movement.
@@ -17,19 +17,19 @@ const interpolateAngle = (percentage: number, a: number, b: number): number => {
1717
return split(percentage, aNorm, bNorm);
1818
};
1919

20-
// Interpolates linearly between shapes a and b. Can only interpolate between shapes that have the
20+
// Interpolates linearly between a and b. Can only interpolate between point lists that have the
2121
// same number of points. Easing effects can be applied to the percentage given to this function.
2222
// Percentages outside the 0-1 range are supported.
23-
export const interpolateBetween = (percentage: number, a: Shape, b: Shape): Shape => {
24-
if (a.length !== b.length) throw new Error("shapes have different number of points");
23+
export const interpolateBetween = (percentage: number, a: Point[], b: Point[]): Point[] => {
24+
if (a.length !== b.length) throw new Error("must have equal number of points");
2525

2626
// Clamped range for use in values that could look incorrect otherwise.
2727
// ex. Handles that invert if their value goes negative (creates loops at corners).
2828
const clamped = Math.min(1, Math.max(0, percentage));
2929

30-
const shape: Shape = [];
30+
const points: Point[] = [];
3131
for (let i = 0; i < a.length; i++) {
32-
shape.push({
32+
points.push({
3333
...splitLine(percentage, a[i], b[i]),
3434
handleIn: {
3535
angle: interpolateAngle(percentage, a[i].handleIn.angle, b[i].handleIn.angle),
@@ -41,22 +41,22 @@ export const interpolateBetween = (percentage: number, a: Shape, b: Shape): Shap
4141
},
4242
});
4343
}
44-
return shape;
44+
return points;
4545
};
4646

47-
// Interpolates between shapes a and b while applying a smoothing effect. Smoothing effect's
48-
// strength is relative to how far away the percentage is from either 0 or 1. It is strongest in the
49-
// middle of the animation (percentage = 0.5) or when bounds are exceeded (percentage = 1.8).
47+
// Interpolates between a and b while applying a smoothing effect. Smoothing effect's strength is
48+
// relative to how far away the percentage is from either 0 or 1. It is strongest in the middle of
49+
// the animation (percentage = 0.5) or when bounds are exceeded (percentage = 1.8).
5050
export const interpolateBetweenSmooth = (
5151
strength: number,
5252
percentage: number,
53-
a: Shape,
54-
b: Shape,
55-
): Shape => {
53+
a: Point[],
54+
b: Point[],
55+
): Point[] => {
5656
strength *= Math.min(1, Math.min(Math.abs(0 - percentage), Math.abs(1 - percentage)));
5757
const interpolated = interpolateBetween(percentage, a, b);
5858
const smoothed = smooth(interpolated, Math.sqrt(strength + 0.25) / 3);
59-
return mapShape(interpolated, ({index, curr}) => {
59+
return mapPoints(interpolated, ({index, curr}) => {
6060
const sp = smoothed[index];
6161
curr.handleIn.angle = interpolateAngle(strength, curr.handleIn.angle, sp.handleIn.angle);
6262
curr.handleIn.length = split(strength, curr.handleIn.length, sp.handleIn.length);

internal/animate/prepare.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,55 +7,55 @@ import {
77
mod,
88
angleOf,
99
coordEqual,
10-
mapShape,
11-
forShape,
10+
mapPoints,
11+
forPoints,
1212
} from "../util";
13-
import {Point, Shape} from "../types";
13+
import {Point} from "../types";
1414

1515
// OPT extract optimization logic
16-
const optimizeOrder = (a: Shape, b: Shape): Shape => {
16+
const optimizeOrder = (a: Point[], b: Point[]): Point[] => {
1717
const count = a.length;
1818

1919
let minSum = Infinity;
2020
let minOffset = 0;
21-
let minShape: Shape = [];
21+
let minOffsetBase: Point[] = [];
2222

23-
const setMinOffset = (shape: Shape) => {
23+
const setMinOffset = (points: Point[]) => {
2424
for (let i = 0; i < count; i++) {
2525
let sum = 0;
2626
for (let j = 0; j < count; j++) {
27-
sum += (100 * distance(a[j], shape[mod(j + i, count)])) ** 1 / 2;
27+
sum += (100 * distance(a[j], points[mod(j + i, count)])) ** 1 / 2;
2828
if (sum > minSum) break;
2929
}
3030
if (sum <= minSum) {
3131
minSum = sum;
3232
minOffset = i;
33-
minShape = shape;
33+
minOffsetBase = points;
3434
}
3535
}
3636
};
3737
setMinOffset(b);
3838
setMinOffset(reverse(b));
3939

40-
return shift(minOffset, minShape);
40+
return shift(minOffset, minOffsetBase);
4141
};
4242

4343
// OPT allow extra division
44-
export const divideShape = (count: number, shape: Shape): Shape => {
45-
if (shape.length < 3) throw new Error("not enough points");
46-
if (count < shape.length) throw new Error("cannot remove points");
47-
if (count === shape.length) return shape.slice();
44+
export const divide = (count: number, points: Point[]): Point[] => {
45+
if (points.length < 3) throw new Error("not enough points");
46+
if (count < points.length) throw new Error("cannot remove points");
47+
if (count === points.length) return points.slice();
4848

4949
const lengths: number[] = [];
50-
forShape(shape, ({curr, next}) => {
50+
forPoints(points, ({curr, next}) => {
5151
lengths.push(length(curr, next()));
5252
});
5353

54-
const divisors = divideLengths(lengths, count - shape.length);
55-
const out: Shape = [];
56-
for (let i = 0; i < shape.length; i++) {
57-
const curr: Point = out[out.length - 1] || shape[i];
58-
const next = shape[mod(i + 1, shape.length)];
54+
const divisors = divideLengths(lengths, count - points.length);
55+
const out: Point[] = [];
56+
for (let i = 0; i < points.length; i++) {
57+
const curr: Point = out[out.length - 1] || points[i];
58+
const next = points[mod(i + 1, points.length)];
5959
out.pop();
6060
out.push(...insertCount(divisors[i], curr, next));
6161
}
@@ -66,8 +66,8 @@ export const divideShape = (count: number, shape: Shape): Shape => {
6666
};
6767

6868
// OPT disable
69-
const fixAnglesWith = (fixee: Shape, fixer: Shape): Shape => {
70-
return mapShape(fixee, ({index, curr, prev, next}) => {
69+
const fixAnglesWith = (fixee: Point[], fixer: Point[]): Point[] => {
70+
return mapPoints(fixee, ({index, curr, prev, next}) => {
7171
if (curr.handleIn.length === 0 && coordEqual(prev(), curr)) {
7272
curr.handleIn.angle = fixer[index].handleIn.angle;
7373
}
@@ -79,8 +79,8 @@ const fixAnglesWith = (fixee: Shape, fixer: Shape): Shape => {
7979
};
8080

8181
// OPT disable
82-
const fixAnglesSelf = (shape: Shape): Shape => {
83-
return mapShape(shape, ({curr, prev, next}) => {
82+
const fixAnglesSelf = (points: Point[]): Point[] => {
83+
return mapPoints(points, ({curr, prev, next}) => {
8484
const angle = angleOf(prev(), next());
8585
if (curr.handleIn.length === 0) {
8686
curr.handleIn.angle = angle + Math.PI;
@@ -114,10 +114,10 @@ const divideLengths = (lengths: number[], add: number): number[] => {
114114
return divisors;
115115
};
116116

117-
export const prepShapes = (a: Shape, b: Shape): [Shape, Shape] => {
117+
export const prepare = (a: Point[], b: Point[]): [Point[], Point[]] => {
118118
const pointCount = Math.max(a.length, b.length);
119-
const aNorm = divideShape(pointCount, a);
120-
const bNorm = divideShape(pointCount, b);
119+
const aNorm = divide(pointCount, a);
120+
const bNorm = divide(pointCount, b);
121121
const bOpt = optimizeOrder(aNorm, bNorm);
122122
return [fixAnglesWith(fixAnglesSelf(aNorm), bNorm), fixAnglesWith(fixAnglesSelf(bOpt), aNorm)];
123123
};

internal/animate/testing/script.ts

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {interpolateBetweenSmooth} from "../interpolate";
2-
import {divideShape, prepShapes} from "../prepare";
3-
import {Coord, Point, Shape} from "../../types";
4-
import {length, insertAt, insertCount, rad, mod, mapShape, forShape, smooth} from "../../util";
5-
import {clear, drawInfo, drawShape} from "../../render/canvas";
2+
import {divide, prepare} from "../prepare";
3+
import {Coord, Point} from "../../types";
4+
import {length, insertAt, insertCount, rad, mod, mapPoints, forPoints} from "../../util";
5+
import {clear, drawInfo, drawClosed} from "../../render/canvas";
66
import {genBlob} from "../../blobs";
77
import {rand} from "../../rand";
88

@@ -33,35 +33,35 @@ const point = (x: number, y: number, ia: number, il: number, oa: number, ol: num
3333
};
3434

3535
const testSplitAt = (percentage: number) => {
36-
let shape: Shape = [
36+
let points: Point[] = [
3737
point(0.15, 0.15, 135, 0.1, 315, 0.2),
3838
point(0.85, 0.15, 225, 0.1, 45, 0.2),
3939
point(0.85, 0.85, 315, 0.1, 135, 0.2),
4040
point(0.15, 0.85, 45, 0.1, 225, 0.2),
4141
];
4242

43-
const count = shape.length;
43+
const count = points.length;
4444
const stop = 2 * count - 1;
4545
for (let i = 0; i < count; i++) {
4646
const double = i * 2;
4747
const next = mod(double + 1, stop);
48-
shape.splice(double, 2, ...insertAt(percentage, shape[double], shape[next]));
48+
points.splice(double, 2, ...insertAt(percentage, points[double], points[next]));
4949
}
50-
shape.splice(0, 1);
50+
points.splice(0, 1);
5151

5252
let sum = 0;
53-
forShape(shape, ({curr, next}) => {
53+
forPoints(points, ({curr, next}) => {
5454
sum += length(curr, next());
5555
});
5656
drawInfo(ctx, 1, "split at lengths sum", sum);
5757

58-
drawShape(ctx, debug, shape);
58+
drawClosed(ctx, debug, points);
5959
};
6060

6161
const testSplitBy = () => {
6262
const count = 10;
6363
for (let i = 0; i < count; i++) {
64-
drawShape(
64+
drawClosed(
6565
ctx,
6666
debug,
6767
insertCount(
@@ -73,13 +73,13 @@ const testSplitBy = () => {
7373
}
7474
};
7575

76-
const testDivideShape = () => {
76+
const testDividePoints = () => {
7777
const count = 10;
7878
for (let i = 0; i < count; i++) {
79-
drawShape(
79+
drawClosed(
8080
ctx,
8181
debug,
82-
divideShape(i + 3, [
82+
divide(i + 3, [
8383
point(0.3, 0.2 + i * 0.05, -10, 0.04, -45, 0.02),
8484
point(0.35, 0.2 + i * 0.05 - 0.02, 180, 0.02, 0, 0.02),
8585
point(0.4, 0.2 + i * 0.05, -135, 0.02, 170, 0.04),
@@ -101,29 +101,29 @@ const testInterpolateBetween = (percentage: number) => {
101101
point(0.35, 0.82, 360 * 10, 0, 180, 0),
102102
point(0.3, 0.77, 90, 0, -90, 0),
103103
];
104-
drawShape(ctx, debug, loopBetween(percentage, a, b));
104+
drawClosed(ctx, debug, loopBetween(percentage, a, b));
105105
};
106106

107-
const testPrepShapesA = (percentage: number) => {
107+
const testPrepPointsA = (percentage: number) => {
108108
const a = blob("a", 6, 0.15, {x: 0.45, y: 0.1});
109109
const b = blob("b", 10, 0.15, {x: 0.45, y: 0.1});
110-
drawShape(ctx, debug, loopBetween(percentage, ...prepShapes(a, b)));
110+
drawClosed(ctx, debug, loopBetween(percentage, ...prepare(a, b)));
111111
};
112112

113-
const testPrepShapesB = (percentage: number) => {
113+
const testPrepPointsB = (percentage: number) => {
114114
const a = blob("a", 8, 0.15, {x: 0.45, y: 0.25});
115-
const b: Shape = [
115+
const b: Point[] = [
116116
point(0.45, 0.25, 0, 0, 0, 0),
117117
point(0.6, 0.25, 0, 0, 0, 0),
118118
point(0.6, 0.4, 0, 0, 0, 0),
119119
point(0.45, 0.4, 0, 0, 0, 0),
120120
];
121-
drawShape(ctx, debug, loopBetween(percentage, ...prepShapes(a, b)));
121+
drawClosed(ctx, debug, loopBetween(percentage, ...prepare(a, b)));
122122
};
123123

124-
const testPrepShapesC = (percentage: number) => {
124+
const testPrepPointsC = (percentage: number) => {
125125
const a = blob("c", 8, 0.15, {x: 0.45, y: 0.45});
126-
const b: Shape = [
126+
const b: Point[] = [
127127
point(0.5, 0.45, 0, 0, 0, 0),
128128
point(0.55, 0.45, 0, 0, 0, 0),
129129
point(0.55, 0.5, 0, 0, 0, 0),
@@ -137,21 +137,21 @@ const testPrepShapesC = (percentage: number) => {
137137
point(0.45, 0.5, 0, 0, 0, 0),
138138
point(0.5, 0.5, 0, 0, 0, 0),
139139
];
140-
drawShape(ctx, debug, loopBetween(percentage, ...prepShapes(b, a)));
140+
drawClosed(ctx, debug, loopBetween(percentage, ...prepare(b, a)));
141141
};
142142

143-
const testPrepShapesD = (percentage: number) => {
143+
const testPrepPointsD = (percentage: number) => {
144144
const a = blob("d", 8, 0.15, {x: 0.45, y: 0.65});
145-
const b: Shape = [
145+
const b: Point[] = [
146146
point(0.525, 0.725, 0, 0, 0, 0),
147147
point(0.525, 0.725, 0, 0, 0, 0),
148148
point(0.525, 0.725, 0, 0, 0, 0),
149149
];
150-
drawShape(ctx, debug, loopBetween(percentage, ...prepShapes(a, b)));
150+
drawClosed(ctx, debug, loopBetween(percentage, ...prepare(a, b)));
151151
};
152152

153153
const testPrepLetters = (percentage: number) => {
154-
const a: Shape = [
154+
const a: Point[] = [
155155
point(0.65, 0.2, 0, 0, 0, 0),
156156
point(0.85, 0.2, 0, 0, 0, 0),
157157
point(0.85, 0.25, 0, 0, 0, 0),
@@ -165,14 +165,14 @@ const testPrepLetters = (percentage: number) => {
165165
point(0.85, 0.45, 0, 0, 0, 0),
166166
point(0.65, 0.45, 0, 0, 0, 0),
167167
];
168-
const b: Shape = blob("lettersa", 8, 0.25, {x: 0.65, y: 0.2});
169-
drawShape(ctx, debug, loopBetween(percentage, ...prepShapes(a, b)));
168+
const b: Point[] = blob("", 8, 0.25, {x: 0.65, y: 0.2});
169+
drawClosed(ctx, debug, loopBetween(percentage, ...prepare(a, b)));
170170
};
171171

172-
const blob = (seed: string, count: number, scale: number, offset: Coord): Shape => {
172+
const blob = (seed: string, count: number, scale: number, offset: Coord): Point[] => {
173173
const rgen = rand(seed);
174-
const shape = genBlob(count, () => 0.3 + 0.2 * rgen());
175-
return mapShape(shape, ({curr}) => {
174+
const points = genBlob(count, () => 0.3 + 0.2 * rgen());
175+
return mapPoints(points, ({curr}) => {
176176
curr.x *= scale * size;
177177
curr.y *= scale * size;
178178
curr.x += offset.x * size;
@@ -183,7 +183,7 @@ const blob = (seed: string, count: number, scale: number, offset: Coord): Shape
183183
});
184184
};
185185

186-
const loopBetween = (percentage: number, a: Shape, b: Shape): Shape => {
186+
const loopBetween = (percentage: number, a: Point[], b: Point[]): Point[] => {
187187
if (percentage < 0.5) {
188188
return interpolateBetweenSmooth(1, 2 * percentage, a, b);
189189
} else {
@@ -200,12 +200,12 @@ const loopBetween = (percentage: number, a: Shape, b: Shape): Shape => {
200200
drawInfo(ctx, 0, "percentage", percentage);
201201
testSplitAt(percentage);
202202
testSplitBy();
203-
testDivideShape();
203+
testDividePoints();
204204
testInterpolateBetween(percentage);
205-
testPrepShapesA(percentage);
206-
testPrepShapesB(percentage);
207-
testPrepShapesC(percentage);
208-
testPrepShapesD(percentage);
205+
testPrepPointsA(percentage);
206+
testPrepPointsB(percentage);
207+
testPrepPointsC(percentage);
208+
testPrepPointsD(percentage);
209209
testPrepLetters(percentage);
210210

211211
percentage += animationSpeed / 1000;

internal/blobs.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import {Shape} from "./types";
1+
import {Point} from "./types";
22
import {smooth} from "../internal/util";
33

4-
export const genBlob = (pointCount: number, offset: () => number): Shape => {
4+
export const genBlob = (pointCount: number, offset: () => number): Point[] => {
55
const angle = (Math.PI * 2) / pointCount;
66
const boundingSize = 1;
77
const boundingCenter = boundingSize / 2;
88

9-
const shape: Shape = [];
9+
const points: Point[] = [];
1010
for (let i = 0; i < pointCount; i++) {
1111
const randPointOffset = offset();
1212
const pointX = Math.sin(i * angle);
1313
const pointY = Math.cos(i * angle);
14-
shape.push({
14+
points.push({
1515
x: boundingCenter + pointX * randPointOffset,
1616
y: boundingCenter + pointY * randPointOffset,
1717
handleIn: {angle: 0, length: 0},
@@ -22,5 +22,5 @@ export const genBlob = (pointCount: number, offset: () => number): Shape => {
2222
// https://math.stackexchange.com/a/873589/235756
2323
const smoothingStrength = ((4 / 3) * Math.tan(angle / 4)) / Math.sin(angle / 2) / 2;
2424

25-
return smooth(shape, smoothingStrength);
25+
return smooth(points, smoothingStrength);
2626
};

0 commit comments

Comments
 (0)