Last active
November 21, 2023 01:34
-
-
Save arcollector/a7a1492689dee2e947ec to your computer and use it in GitHub Desktop.
Polygon scanline filling in JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var polygonFilling = function( vertices ) { | |
// "global" variables | |
var verticesCount = vertices.length, | |
edgesEntered, | |
// table cols | |
yMax, yMin, xInt, invNegSlope; | |
// ************************* | |
if( verticesCount <= 4 ) { | |
console.error( 'polygon has insuffient number of vertices' ); | |
return; | |
} | |
if( verticesCount % 2 !== 0 ) { | |
console.error( 'polygon has an incorrect number of vertices' ); | |
return; | |
} | |
// find out table length | |
var y1 = Math.round( vertices[verticesCount-1] ), y2, | |
i; | |
edgesEntered = 0; | |
for( i = 1; i < verticesCount; i += 2 ) { | |
y2 = Math.round( vertices[i] ); | |
if( y1 !== y2 ) { | |
y1 = y2; | |
edgesEntered++; | |
} | |
} | |
if( edgesEntered < 2 ) { | |
console.error( 'malformed polygon' ); | |
return; | |
} | |
// setup table length | |
xInt = new Float64Array( edgesEntered ); | |
yMin = new Uint32Array( edgesEntered ); | |
yMax = new Uint32Array( edgesEntered ); | |
invNegSlope = new Float64Array( edgesEntered ); | |
// ************************* | |
var insertTableEntry = function( x1,y1, x2,y2, edgesEntered ) { | |
var biggerY = Math.max( y1, y2 ), | |
i = edgesEntered; | |
for( ; i !== 0 && yMax[i-1] < biggerY; i-- ) { // insertion sort mechanism | |
// shift elements to right | |
yMax[i] = yMax[i-1]; | |
yMin[i] = yMin[i-1]; | |
xInt[i] = xInt[i-1]; | |
invNegSlope[i] = invNegSlope[i-1]; | |
} | |
yMax[i] = biggerY; | |
invNegSlope[i] = -(x2-x1)/(y2-y1); | |
if( biggerY === y1 ) { | |
yMin[i] = y2; | |
xInt[i] = x1; | |
} else { | |
yMin[i] = y1; | |
xInt[i] = x2; | |
} | |
}; | |
var x1 = Math.round( vertices[verticesCount-2] ), | |
y1 = Math.round( vertices[verticesCount-1] ), | |
x2, y2, | |
i; | |
edgesEntered = 0; | |
for( i = 0; i < verticesCount; i += 2 ) { | |
x2 = Math.round( vertices[i] ); | |
y2 = Math.round( vertices[i+1] ); | |
if( y1 !== y2 ) { // avoid horizontal edges | |
insertTableEntry( x1,y1, x2,y2, edgesEntered ); | |
edgesEntered++; | |
} | |
x1 = x2; | |
y1 = y2; | |
} | |
// ************************* | |
// helpers functions | |
var exclude = function( leftMostEdge, rightMostEdge, scan ) { | |
var i, j; | |
for( i = leftMostEdge; i <= rightMostEdge; i++ ) { | |
if( yMin[i] > scan ) { | |
leftMostEdge++; | |
for( j = i; j >= leftMostEdge; j-- ) { | |
// shift elements to right | |
yMin[j] = yMin[j-1]; | |
xInt[j] = xInt[j-1]; | |
invNegSlope[j] = invNegSlope[j-1]; | |
} | |
} | |
} | |
return leftMostEdge; | |
}; | |
var updateXInt = function( leftMostEdge, rightMostEdge ) { | |
for( var i = leftMostEdge; i <= rightMostEdge; i++ ) { | |
xInt[i] += invNegSlope[i]; | |
} | |
}; | |
var include = function( rightMostEdge, scan, edgesEntered ) { | |
for( ; rightMostEdge + 1 < edgesEntered && yMax[rightMostEdge + 1] > scan; rightMostEdge++ ); | |
return rightMostEdge; | |
}; | |
var sortXInt = function( leftMostEdge, rightMostEdge ) { // bubble sort mechanism | |
var i, j, tmp; | |
for( i = leftMostEdge; i <= rightMostEdge - 1; i++ ) { | |
for( j = i + 1; j <= rightMostEdge; j++ ) { | |
if( xInt[i] > xInt[j] ) { | |
tmp = yMin[i]; | |
yMin[i] = yMin[j]; | |
yMin[j] = tmp; | |
tmp = xInt[i]; | |
xInt[i] = xInt[j]; | |
xInt[j] = tmp; | |
tmp = invNegSlope[i]; | |
invNegSlope[i] = invNegSlope[j]; | |
invNegSlope[j] = tmp; | |
} | |
} | |
} | |
}; | |
var fillScan = function( leftMostEdge, rightMostEdge, scan ) { | |
var x1, x2, i; | |
for( i = leftMostEdge; i <= rightMostEdge; i += 2 ) { | |
x1 = Math.round( xInt[i] ); | |
x2 = Math.round( xInt[i+1] ); | |
console.log( 'drawing line from', x1, 'to', x2, 'at y =', scan ); | |
for( ; x1 <= x2; x1++ ) { | |
// plot pixel (x1,scan) | |
} | |
} | |
}; | |
// ************************* | |
// start filling | |
var scan = yMax[0], | |
leftMostEdge = 0, | |
rightMostEdge = -1; | |
while( leftMostEdge < edgesEntered ) { | |
scan--; | |
leftMostEdge = exclude( leftMostEdge, rightMostEdge, scan ); | |
updateXInt( leftMostEdge, rightMostEdge ); | |
rightMostEdge = include( rightMostEdge, scan, edgesEntered ); | |
sortXInt( leftMostEdge, rightMostEdge ); | |
fillScan( leftMostEdge, rightMostEdge, scan ); | |
} | |
}; | |
/*var vertices = new Float64Array( [ | |
3,8, | |
6,3, | |
1,1 | |
] );*/ | |
var vertices = new Float64Array( [ | |
1,1, | |
1,6, | |
3.5,4, | |
5,6, | |
6,4, | |
7,6, | |
8.5,4, | |
7,1, | |
5,3, | |
] ); | |
polygonFilling( vertices ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment