-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathslpf.js
72 lines (64 loc) · 1.71 KB
/
slpf.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
const {
pointsToEdges, getYMin, getXofYMin, getXofYMax, setPixel, lerp, getYMax
} = require('./util');
// Scnaline Polygon Fill
module.exports = function slpf(points, img) {
if(points.length < 3) return;
const setPixelAlias = setPixel.bind(null, img);
// initialize ET and AET
const ET = pointsToEdges(points)
.sort((e1, e2) => getYMin(e2) - getYMin(e1));
const AET = [];
let yScan = getYMin(ET[ET.length - 1]);
// repeat until both ET and AET are empty
while(ET.length > 0 || AET.length > 0) {
// manage AET
moveEdges(yScan, ET, AET);
removeEdges(yScan, AET);
AET.sort((e1, e2) => {
const cmp = getXofYMin(e1) - getXofYMin(e2);
return cmp == 0 ? getXofYMax(e1) - getXofYMax(e2) : cmp;
});
// fill spans on scanline
const spans = getSpans(yScan, AET);
drawSpans(spans, yScan, setPixelAlias);
yScan++;
}
};
// move active edges from ET to AET
function moveEdges(yScan, ET, AET) {
while(ET.length > 0 && yScan == getYMin(ET[ET.length - 1])) {
AET.push(ET.pop());
}
}
// remove inactive edges from AET
function removeEdges(yScan, AET) {
for(let i = 0; i < AET.length; i++) {
if(yScan >= getYMax(AET[i])) {
const last = AET.pop();
if(i < AET.length) {
AET[i] = last;
i--;
}
}
}
}
// find spans along scanline
function getSpans(yScan, AET) {
const spans = [];
for(const edge of AET) {
spans.push(lerp(yScan, edge));
}
return spans;
}
function drawSpans(spans, yScan, setPixel) {
for(let i = 0; i < spans.length; i += 2) {
fillSpan(spans[i], spans[i + 1], yScan, setPixel);
}
}
// fill pixels within a span
function fillSpan(x1, x2, y, setPixel) {
for(let x = x1; x < x2; x++) {
setPixel(x, y);
}
}