Basic sitemode
This commit is contained in:
141
lib/algorithms/shadowcasting.dart
Normal file
141
lib/algorithms/shadowcasting.dart
Normal file
@ -0,0 +1,141 @@
|
||||
// Port of https://www.albertford.com/shadowcasting/
|
||||
import 'package:dartterm/algorithms/geometry.dart' as geo;
|
||||
|
||||
void shadowcast(geo.Offset origin, bool Function(geo.Offset) isBlocking,
|
||||
Function(geo.Offset) markVisible) {
|
||||
markVisible(origin);
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var quadrant = Quadrant(i, origin.x, origin.y);
|
||||
|
||||
void reveal(geo.Offset tile) {
|
||||
markVisible(quadrant.transform(tile));
|
||||
}
|
||||
|
||||
bool isWall(geo.Offset? tile) {
|
||||
if (tile == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isBlocking(quadrant.transform(tile));
|
||||
}
|
||||
|
||||
bool isFloor(geo.Offset? tile) {
|
||||
if (tile == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !isBlocking(quadrant.transform(tile));
|
||||
}
|
||||
|
||||
void scan(Row row) {
|
||||
geo.Offset? prevTile;
|
||||
for (var tile in row.tiles()) {
|
||||
if (isWall(tile) || isSymmetric(row, tile)) {
|
||||
reveal(tile);
|
||||
}
|
||||
if (isWall(prevTile) && isFloor(tile)) {
|
||||
row.startSlope = slope(tile);
|
||||
}
|
||||
if (isFloor(prevTile) && isWall(tile)) {
|
||||
Row nextRow = row.next();
|
||||
nextRow.endSlope = slope(tile);
|
||||
scan(nextRow);
|
||||
}
|
||||
prevTile = tile;
|
||||
}
|
||||
|
||||
if (isFloor(prevTile)) {
|
||||
scan(row.next());
|
||||
}
|
||||
}
|
||||
|
||||
Row firstRow = Row(1, Fraction(-1, 1), Fraction(1, 1));
|
||||
scan(firstRow);
|
||||
}
|
||||
}
|
||||
|
||||
class Quadrant {
|
||||
static const int north = 0;
|
||||
static const int east = 1;
|
||||
static const int south = 2;
|
||||
static const int west = 3;
|
||||
|
||||
final int cardinal;
|
||||
final int ox, oy;
|
||||
|
||||
Quadrant(this.cardinal, this.ox, this.oy);
|
||||
|
||||
geo.Offset transform(geo.Offset tile) {
|
||||
var geo.Offset(x: row, y: col) = tile;
|
||||
|
||||
switch (cardinal) {
|
||||
case north:
|
||||
return geo.Offset(ox + col, oy - row);
|
||||
case south:
|
||||
return geo.Offset(ox + col, oy + row);
|
||||
case east:
|
||||
return geo.Offset(ox + row, oy + col);
|
||||
case west:
|
||||
return geo.Offset(ox - row, oy + col);
|
||||
default:
|
||||
throw Exception("Quadrant must be initialized with a real cardinal");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Row {
|
||||
int depth;
|
||||
Fraction startSlope;
|
||||
Fraction endSlope;
|
||||
|
||||
Row(this.depth, this.startSlope, this.endSlope);
|
||||
|
||||
Iterable<geo.Offset> tiles() sync* {
|
||||
var minCol = roundTiesUp(startSlope.scale(depth));
|
||||
var maxCol = roundTiesDown(endSlope.scale(depth));
|
||||
for (int col = minCol; col <= maxCol; col++) {
|
||||
yield geo.Offset(depth, col);
|
||||
}
|
||||
}
|
||||
|
||||
Row next() {
|
||||
return Row(depth + 1, startSlope, endSlope);
|
||||
}
|
||||
}
|
||||
|
||||
class Fraction {
|
||||
final int numerator;
|
||||
final int denominator;
|
||||
|
||||
Fraction(this.numerator, this.denominator);
|
||||
|
||||
Fraction scale(int n) {
|
||||
return Fraction(numerator * n, denominator);
|
||||
}
|
||||
|
||||
// We're often comparing this to an int or a double, so it's OK
|
||||
// to have precision loss _so long as we do all divides after all multiplies_
|
||||
double toDouble() {
|
||||
return numerator.toDouble() / denominator.toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
Fraction slope(geo.Offset tile) {
|
||||
var geo.Offset(x: rowDepth, y: col) = tile;
|
||||
return Fraction(2 * col - 1, 2 * rowDepth);
|
||||
}
|
||||
|
||||
bool isSymmetric(Row row, geo.Offset tile) {
|
||||
var geo.Offset(x: rowDepth, y: col) = tile;
|
||||
return (col >= row.startSlope.scale(rowDepth).toDouble() &&
|
||||
col <= (row.endSlope.scale(row.depth)).toDouble());
|
||||
}
|
||||
|
||||
int roundTiesUp(Fraction n) {
|
||||
return (n.toDouble() + 0.5).floor();
|
||||
}
|
||||
|
||||
int roundTiesDown(Fraction n) {
|
||||
return (n.toDouble() - 0.5).ceil();
|
||||
}
|
Reference in New Issue
Block a user