Spaces:
Running
Running
File size: 3,997 Bytes
87b3b3a |
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 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
/**
* @class Precise shadowcasting algorithm
* @augments ROT.FOV
*/
ROT.FOV.PreciseShadowcasting = function(lightPassesCallback, options) {
ROT.FOV.call(this, lightPassesCallback, options);
}
ROT.FOV.PreciseShadowcasting.extend(ROT.FOV);
/**
* @see ROT.FOV#compute
*/
ROT.FOV.PreciseShadowcasting.prototype.compute = function(x, y, R, callback) {
/* this place is always visible */
callback(x, y, 0, 1);
/* standing in a dark place. FIXME is this a good idea? */
if (!this._lightPasses(x, y)) { return; }
/* list of all shadows */
var SHADOWS = [];
var cx, cy, blocks, A1, A2, visibility;
/* analyze surrounding cells in concentric rings, starting from the center */
for (var r=1; r<=R; r++) {
var neighbors = this._getCircle(x, y, r);
var neighborCount = neighbors.length;
for (var i=0;i<neighborCount;i++) {
cx = neighbors[i][0];
cy = neighbors[i][1];
/* shift half-an-angle backwards to maintain consistency of 0-th cells */
A1 = [i ? 2*i-1 : 2*neighborCount-1, 2*neighborCount];
A2 = [2*i+1, 2*neighborCount];
blocks = !this._lightPasses(cx, cy);
visibility = this._checkVisibility(A1, A2, blocks, SHADOWS);
if (visibility) { callback(cx, cy, r, visibility); }
if (SHADOWS.length == 2 && SHADOWS[0][0] == 0 && SHADOWS[1][0] == SHADOWS[1][1]) { return; } /* cutoff? */
} /* for all cells in this ring */
} /* for all rings */
}
/**
* @param {int[2]} A1 arc start
* @param {int[2]} A2 arc end
* @param {bool} blocks Does current arc block visibility?
* @param {int[][]} SHADOWS list of active shadows
*/
ROT.FOV.PreciseShadowcasting.prototype._checkVisibility = function(A1, A2, blocks, SHADOWS) {
if (A1[0] > A2[0]) { /* split into two sub-arcs */
var v1 = this._checkVisibility(A1, [A1[1], A1[1]], blocks, SHADOWS);
var v2 = this._checkVisibility([0, 1], A2, blocks, SHADOWS);
return (v1+v2)/2;
}
/* index1: first shadow >= A1 */
var index1 = 0, edge1 = false;
while (index1 < SHADOWS.length) {
var old = SHADOWS[index1];
var diff = old[0]*A1[1] - A1[0]*old[1];
if (diff >= 0) { /* old >= A1 */
if (diff == 0 && !(index1 % 2)) { edge1 = true; }
break;
}
index1++;
}
/* index2: last shadow <= A2 */
var index2 = SHADOWS.length, edge2 = false;
while (index2--) {
var old = SHADOWS[index2];
var diff = A2[0]*old[1] - old[0]*A2[1];
if (diff >= 0) { /* old <= A2 */
if (diff == 0 && (index2 % 2)) { edge2 = true; }
break;
}
}
var visible = true;
if (index1 == index2 && (edge1 || edge2)) { /* subset of existing shadow, one of the edges match */
visible = false;
} else if (edge1 && edge2 && index1+1==index2 && (index2 % 2)) { /* completely equivalent with existing shadow */
visible = false;
} else if (index1 > index2 && (index1 % 2)) { /* subset of existing shadow, not touching */
visible = false;
}
if (!visible) { return 0; } /* fast case: not visible */
var visibleLength, P;
/* compute the length of visible arc, adjust list of shadows (if blocking) */
var remove = index2-index1+1;
if (remove % 2) {
if (index1 % 2) { /* first edge within existing shadow, second outside */
var P = SHADOWS[index1];
visibleLength = (A2[0]*P[1] - P[0]*A2[1]) / (P[1] * A2[1]);
if (blocks) { SHADOWS.splice(index1, remove, A2); }
} else { /* second edge within existing shadow, first outside */
var P = SHADOWS[index2];
visibleLength = (P[0]*A1[1] - A1[0]*P[1]) / (A1[1] * P[1]);
if (blocks) { SHADOWS.splice(index1, remove, A1); }
}
} else {
if (index1 % 2) { /* both edges within existing shadows */
var P1 = SHADOWS[index1];
var P2 = SHADOWS[index2];
visibleLength = (P2[0]*P1[1] - P1[0]*P2[1]) / (P1[1] * P2[1]);
if (blocks) { SHADOWS.splice(index1, remove); }
} else { /* both edges outside existing shadows */
if (blocks) { SHADOWS.splice(index1, remove, A1, A2); }
return 1; /* whole arc visible! */
}
}
var arcLength = (A2[0]*A1[1] - A1[0]*A2[1]) / (A1[1] * A2[1]);
return visibleLength/arcLength;
}
|