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 low-level functions that intend to go into the back-end, 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.