ENIGMA Forums
Contributing to ENIGMA => Function Peer Review => Topic started by: RetroX on December 20, 2010, 09:02:53 PM

#include <limits>
double distance_to_object_point(int object)
{
enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator>inst);
double distance = std::numeric_limits<double>::infinity();
double tempdist;
for (enigma::inst_iter *it = enigma::fetch_inst_iter_by_int(object); it != NULL; it = it>next)
{
const enigma::object_collisions* inst2 = (enigma::object_collisions*)it>inst;
if (inst1 == inst2) continue;
tempdist = point_distance(inst1>x, inst1>y, inst2>x, inst2>y);
if (tempdist < distance)
{
distance = tempdist;
}
}
return (distance == std::numeric_limits<double>::infinity() ? 0 : distance);
}
inline double dist_ranges(double left1, double right1, double left2, double right2)
{
double right = min(right1, right2), left = max(left1, left2);
return (left > right ? left  right : 0);
}
double distance_to_object_bbox(int object)
{
enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator>inst);
const double left1 = inst1>x + inst1>bbox_left, top1 = inst1>y + inst1>bbox_top,
right1 = inst1>x + inst1>bbox_right, bottom1 = inst1>y + inst1>bbox_bottom;
double distance = std::numeric_limits<double>::infinity();
double tempdist;
for (enigma::inst_iter *it = enigma::fetch_inst_iter_by_int(object); it != NULL; it = it>next)
{
const enigma::object_collisions* inst2 = (enigma::object_collisions*)it>inst;
if (inst1 == inst2) continue;
const double left2 = inst2>x + inst1>bbox_left, top2 = inst2>y + inst1>bbox_top,
right2 = inst2>x + inst1>bbox_right, bottom2 = inst2>y + inst1>bbox_bottom;
tempdist = hypot(dist_ranges(left1, right1, left2, right2),
dist_ranges(top1, bottom1, top2, bottom2));
if (tempdist < distance)
{
distance = tempdist;
}
}
return (distance == std::numeric_limits<double>::infinity() ? 0 : distance);
}
Tested and both work. I don't know which method that GM uses, but here's two different algorithms.

I also did distance_to_object_bbox and Josh didn't seem to notice.

Was it in C++ or GML?

Issue is, I don't know what GM does either. It does compensate for something like a bbox, but I'm not sure if it calculates the distance between the closest two points in the entirety of either mask, or if it calculates the distance between those along a straight line, or if it just defaults to bbox (which would be the only reasonable method), and if in that case it uses a rotated bbox or just a standard one... I'm in the dark on it. But I might accept Retro's bottom function. Provided, of course, that dist_ranges be made inline or into a macro.

@retro: It was in GML, although I used int and other premature optimizations where I could like in e.g. C#. I guess you could say I did it in EDL. :P

But I might accept Retro's bottom function. Provided, of course, that dist_ranges be made inline or into a macro.
I honestly don't know why I didn't make it inline. I was probably rushing. If you're going to use the bottom method, I'd still keep both labeled.
#define distance_to_object(x) distance_to_object_point(x)
or
inline double distance_to_object(int object) { return distance_to_object_point(object); }

I'm pretty sure with() just gets converted into the iterator, so there's no speed benefit one way or the other.

Thought that was probably the case, was just wondering why Retro looped through instead of using the with statement. So thought it might be due to speed.

sigh

For lowlevel functions that intend to go into the backend, it's not uncommon for people to use straight C++ rather than EDL.

OK I'll keep that in mind.

Not to mention, enigma::fetch_inst_iter_by_int(object) is literally as efficient as can be. It is, in fact, the function that composes with(). It is set up such that there is no way to make it any faster; the iterator it returns accounts for all aspects of instance iteration, including ID, object index with heredity, and keywords such as all, other, or noone.

Updated code:
/* ********************* */
// Place this in some math header or something.
inline double range_difference(double left1, double right1, double left2, double right2)
{
double right = min(right1, right2), left = max(left1, left2);
return (left > right ? left  right : 0);
}
/* ********************* */
#include <limits>
double distance_to_object_point(int object)
{
enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator>inst);
double distance = std::numeric_limits<double>::infinity();
double tempdist;
for (enigma::inst_iter *it = enigma::fetch_inst_iter_by_int(object); it != NULL; it = it>next)
{
const enigma::object_collisions* inst2 = (enigma::object_collisions*)it>inst;
if (inst1 == inst2) continue;
tempdist = point_distance(inst1>x, inst1>y, inst2>x, inst2>y);
if (tempdist < distance)
{
distance = tempdist;
}
}
return (distance == std::numeric_limits<double>::infinity() ? 0 : distance);
}
double distance_to_object_bbox(int object)
{
enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator>inst);
const double left1 = inst1>x + inst1>bbox_left, top1 = inst1>y + inst1>bbox_top,
right1 = inst1>x + inst1>bbox_right, bottom1 = inst1>y + inst1>bbox_bottom;
double distance = std::numeric_limits<double>::infinity();
double tempdist;
for (enigma::inst_iter *it = enigma::fetch_inst_iter_by_int(object); it != NULL; it = it>next)
{
const enigma::object_collisions* inst2 = (enigma::object_collisions*)it>inst;
if (inst1 == inst2) continue;
const double left2 = inst2>x + inst1>bbox_left, top2 = inst2>y + inst1>bbox_top,
right2 = inst2>x + inst1>bbox_right, bottom2 = inst2>y + inst1>bbox_bottom;
tempdist = hypot(range_difference(left1, right1, left2, right2),
range_difference(top1, bottom1, top2, bottom2));
if (tempdist < distance)
{
distance = tempdist;
}
}
return (distance == std::numeric_limits<double>::infinity() ? 0 : distance);
}
inline double distance_to_point_point(double x, double y)
{
enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator>inst);
return point_distance(inst1>x, inst1>y, x, y);
}
inline double distance_to_point_bbox(double x, double y)
{
enigma::object_collisions* const inst1 = ((enigma::object_collisions*)enigma::instance_event_iterator>inst);
const double left1 = inst1>x + inst1>bbox_left, top1 = inst1>y + inst1>bbox_top,
right1 = inst1>x + inst1>bbox_right, bottom1 = inst1>y + inst1>bbox_bottom;
return hypot(abs(min(left1  x, right1 x),
min(top1  x, bottom1 x)));
}
// Possible option in configuration to choose which method is used?
inline double distance_to_object(int object) { return distance_to_object_bbox(object); }
inline double distance_to_point(double x, double y) { return distance_to_point_bbox(x, y); }
I'm almost certain that someone will modify this before it's put into ENIGMA, but this is what I have.

If you make it complex enough and it works, nobody will bother touching it.

I still prefer this:
return (distance == std::numeric_limits<double>::infinity() ? 1 : distance);

But of course.