During precomputation, we store the minimal cellrays by target and sort them according to niceness.
find_ray now simply picks the first non-blocked ray to a target, which means looping through the 10 or so minimal cellrays with that target – this should be a lot more efficient. (An extended findray test went from 150s to 40s (debug) and 40s to 26s (profile)).
The interface to find_ray has changed: cycle_dir=-1,0,1 was changed to cyle=false/true since we never cycle in the other direction anyway. find_shortest was removed: all rays to a target had the same length all along, but now we also return the straightest one automatically.
The change also eliminates the duplicate corner-cutting code between ray_def::advance and find_ray as imbalance calculation now relies on ray_def::advance.
DTLSPUE47XI4YC3QWKTBWJOWBU52GXPGXFEEBG374I4JAYVM7KZAC
BLJ6YULKT57MKKU3KUKMS7HITS2CDUFPZ4U6EQHBGLGQ7O7GUSLAC
3MCIHHM7HEXQPZHF2MZBUV3STJHRMVSUTWENFM3KUYIQXI5FJ2GAC
6KANK4BOEJYMBUWOZURH57BZJ52YJRGBXN4TMKO27B3JFIP6MHSQC
IFYZSFWJRTV6JM46H6U4CMTQX46VT562EAM64Y6UYU3F6RDWRRYQC
TZNURHCLTLE3A55EPNU66BU6NX7LOBA7EFG4NAJJBUKZLOAEFU4AC
KFULGQQOHWUTXOM3BXCCYPGGVGGY4Z6265XUFRCBPNLTZAEHJZSQC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
QNDI5MFPHZZXZOJFF2IELF2LIHWVY2HGFRCJWYEFN4WL4ICYZUYQC
5X6QOOUFVHPEEHOSLZ5RUTNRD5LH4BSGIIFWNEDJJBVJJNM7MBZQC
4FBWCGO5NTT2KBBTYTYVR2AOBI5LHSP7L3TB3ESYF6OFLMEJQESQC
ACZYEIX7WMPIIODKCATBCUE626AJ4ZGGBOMVC6BGXM27EQU2RECAC
B5ED3LZM7H6NMC7HBVUWKE4AODTQSKF4EOKZ7KZBZVOCGBZ3XMGQC
YGPAMROCZY2O2N4PJONW6NMFUNOFHAGT5ZA4APCXKFXQUHLEIN2AC
WY3Q6JZ3HTBF2CYFJOUVG4MMFINBK4PI2DJ625CVKMJ5BPMXAW2AC
UFMQQPYCBI6Z576P7PH4ZAPC7L7P3D4H66NJMFQKP6WRAPIK2NOQC
W2JKSN3BG7JHORONWK6J53OOEJ3EBVKY5QTBG4ITNVORLWRZNU5QC
ZJBFGI5FGCP7WGFKEUTLWZK4ATGB35Q4I5IB4BKJTHVKS2HDR5LQC
5KZF63OICCGFAZ5E3TRS5GKZJLKQYWLGKOA7IYV4X3C6AOXB4MMAC
ICVDXXH2Z5MV7BLYVWQNLQSV63THIB4E6NVORIDBB7D6TBEHBXOAC
MRKWHW7QPSB7BBOLKP5OZTYKAARQGLSQCOEFKGS64JK5BAODT5OQC
KMF52RF3NIY2ABMHEZABT3GOG3KCHEJD55NBJ3IPAOHBKF2WPKQAC
QY27OCPL2PEW4TRTMKEOK2IJRQFUQX2A2YF2FHEFI52EE7WU43CQC
STVGTZVV4C2DWFZTJS5MSXCG7CALIDQXUXQITH2IP3U75LBVLKRQC
3ORZZ66JXYWJUO4W5YP2JRKKZ6ZNMHU7QWAF2QMKH4LFWNNMPM7QC
PEWNWU7TD3LETUMWCCI365KH7PVV2XCUOIFTPBH2LK2ZPGS4URZAC
PHJ2TT2CQ2IRXOB5KAV2664KKTPYFPFUIBEGAOQBGB4SAZ7PKNHAC
DUUH7Q4YH3XJUQDOCWBCGRPVJAOJBENZ2B3LR5N6ALZRPRELAI4AC
2MVY3CA2SGUEQDDHIFOLAJ7N532M4ZYZT7LSCPWZR7VKPMRX2CQAC
T7CCGLOZ25B7BQKKGR6IA6LWBRKUWTXLTIRXUQ4YKQRVAA7AHZKQC
JM7UAK777RAVDAVLQLEOBRTGNW2B47S5G55XITJXO243IUNZHVYQC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
XCL4GC6RUWUK57HONM5CRUEOCCECR4NYAI5KLPJKCYM5OURU3AYQC
VD4KDTGHVKCN35AWREYB4TEOUMCTW7SAUPAMTMF5ABC7VBHVKP4AC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
DDYDJKL5CGSTC3NGTOBCNKHDTG5LX5F4U7VNZN2YAK5ANLT7UO5AC
6DNNPEMZGBQDMA7YG4LCTQUVZ7LYPC3R4A2XBYT5SDQ65GYOLJVAC
cellray(const los_ray& r, int l) : ray(r), length(l) {}
cellray(const los_ray& r, unsigned int e)
: ray(r), end(e), imbalance(-1), slope_diff(-1)
{
}
// The end-point's index inside ray_coord.
int index() const { return (ray.start + end); }
// The end-point.
coord_def target() const { return ray_coords[index()]; }
int index() const { return ray.start + length; }
coord_def end() const { return ray_coords[index()]; }
// XXX: Currently ray/cellray[0] is the first point outside the origin.
coord_def operator[](unsigned int i)
{
ASSERT(0 <= i && i <= end);
return ray_coords[ray.start+i];
}
// Parameters used in find_ray. These need to be calculated
// only for the minimal cellrays.
int imbalance;
double slope_diff;
void calc_params();
// Compare two cellrays to the same target.
// This determines which ray is considered better by find_ray,
// used with list::sort.
// Returns true if a is strictly better than b, false else.
bool _is_better(const cellray& a, const cellray& b)
{
// Only compare cellrays with equal target.
ASSERT(a.target() == b.target());
// calc_params() has been called.
ASSERT(a.imbalance >= 0 && b.imbalance >= 0);
if (a.imbalance < b.imbalance)
return (true);
else if (a.imbalance > b.imbalance)
return (false);
else
return (a.slope_diff < b.slope_diff);
}
for (min_it = minima(*qi).begin(); min_it != minima(*qi).end(); min_it++)
{
std::list<cellray>& min = minima(*qi);
for (min_it = min.begin(); min_it != min.end(); min_it++)
{
// Calculate imbalance and slope difference for sorting.
min_it->calc_params();
// Determine nonduplicated rays and store their end points.
std::vector<int> min_cellrays = _find_minimal_cellrays();
const int n_min_rays = min_cellrays.size();
// Determine minimal cellrays and store their indices in ray_coords.
std::vector<int> min_indices = _find_minimal_cellrays();
const int n_min_rays = min_indices.size();
}
static int _cyclic_offset(int i, int cycle_dir, int startpoint,
int maxvalue)
{
if (startpoint < 0)
return i;
switch (cycle_dir)
{
case 1:
return (i + startpoint + 1) % maxvalue;
case -1:
return (i - 1 - startpoint + maxvalue) % maxvalue;
case 0:
default:
return i;
}
}
static bool _superior_ray(int imbalance, int rayimbalance,
double slope_diff, double ray_slope_diff)
{
if (imbalance != rayimbalance)
return (imbalance > rayimbalance);
return (slope_diff > ray_slope_diff);
// Compute the imbalance, defined as the
// number of consecutive diagonal or orthogonal moves
// in the ray. This is a reasonable measure of deviation from
// the Bresenham line between our selected source and
// destination.
static int _imbalance(const std::vector<coord_def>& ray)
static int _imbalance(ray_def ray, const coord_def& target)
bool found = false;
int imbalance = INFINITE_DISTANCE;
const double want_slope = _calc_slope(target.x, target.y);
double slope_diff = VERTICAL_SLOPE * 10.0;
double ray_slope_diff = slope_diff;
std::vector<coord_def> unaliased_ray;
const std::vector<cellray> &min = min_cellrays(target);
ASSERT(min.size() > 0);
cellray c = min[0]; // XXX: const cellray &c ?
unsigned int index = 0;
for (unsigned int fray = 0; fray < fullrays.size(); ++fray)
{
const int fullray = _cyclic_offset(fray, cycle_dir, ray.fullray_idx,
fullrays.size());
los_ray lray = fullrays[fullray];
#ifdef DEBUG_DIAGNOSTICS
if (cycle)
mprf(MSGCH_DIAGNOSTICS, "cycling from %d (total %d), ignores_solid=%d",
ray.cycle_idx, min.size(), ignore_solid);
#endif
for (unsigned int cellray = 0; cellray < lray.length; ++cellray)
{
if (lray[cellray] != target)
continue;
unsigned int start = cycle ? ray.cycle_idx + 1 : 0;
ASSERT(0 <= start && start <= min.size());
if (find_best)
if (ignore_solid)
{
index = start % min.size();
c = min[index];
}
else
{
bool blocked = true;
for (unsigned int i = start; blocked && (i < start + min.size()); i++)
{
index = i % min.size();
c = min[index];
blocked = false;
// Check all inner points.
for (unsigned int j = 0; j < c.end && !blocked; j++)
// Check if we're blocked so far.
bool blocked = false;
coord_def c1, c3;
for (unsigned int inray = 0; inray <= cellray; ++inray)
{
c3 = ray_coords[inray + fullrays[fray].start];
if (inray < cellray && !ignore_solid
&& grid_is_solid(grd(t.transform(c3))))
{
blocked = true;
break;
}
if (find_best)
{
// We've moved at least two steps if inray > 0.
if (inray)
{
// Check for a perpendicular corner on the ray and
// pretend that it's a diagonal.
if ((c3 - c1).abs() == 2)
{
// c2 was a dud move, pop it off
unaliased_ray.pop_back();
}
}
unaliased_ray.push_back(c3);
c1 = unaliased_ray[unaliased_ray.size() - 2];
}
}
// If this ray is a candidate for shortest, calculate
// the imbalance.
int cimbalance = 0;
if (!blocked && find_best)
{
cimbalance = _imbalance(unaliased_ray);
ray_slope_diff = fabs(_slope_factor(lray) - want_slope);
}
if (blocked || (find_best &&
!_superior_ray(imbalance, cimbalance,
slope_diff, ray_slope_diff)))
{
continue;
}
// Success!
found = true;
ray = lray;
ray.fullray_idx = fullray;
imbalance = cimbalance;
slope_diff = ray_slope_diff;
if (!find_best)
return (true);