view src/x_hover.cc @ 22:f1fb248bf997

Get rid of the swapping and legacy far etc. memory handling code.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 24 Sep 2011 13:03:13 +0300
parents 241c93442be0
children 8eaf72e2041b
line wrap: on
line source

/*
 *	x_hover.cc
 *	AYM 2000-11-08
 */


/*
This file is part of Yadex.

Yadex incorporates code from DEU 5.21 that was put in the public domain in
1994 by Raphaël Quinet and Brendon Wyber.

The rest of Yadex is Copyright © 1997-2003 André Majorel and others.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 2 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307, USA.
*/


#include "yadex.h"
#include <math.h>
#include <vector>
#include <algorithm>
#include "drawmap.h"
#include "levels.h"
#include "objid.h"
#include "x_hover.h"


class Close_obj
{
  public :
    Close_obj () { nil (); }
    void nil ()
    {
      obj.nil ();
      distance = ULONG_MAX;
      radius   = INT_MAX;
      inside   = false;
    }
    bool operator== (const Close_obj& other) const
    {
      if (inside == other.inside
	 && radius == other.radius
	 && distance == other.distance)
       return true;
      return false;
    }
    bool operator< (const Close_obj& other) const
    {
      if (inside && ! other.inside)
	return true;
      if (! inside && other.inside)
	return false;

      // Small objects should "mask" large objects
      if (radius < other.radius)
	return true;
      if (radius > other.radius)
	return false;

      if (distance < other.distance)
	return true;
      if (distance > other.distance)
	return false;

      return radius < other.radius;
    }
    bool operator<= (const Close_obj& other) const
    {
      return *this == other || *this < other;
    }
    Objid  obj;
    double distance;
    bool   inside;
    int    radius;
};


static const Close_obj& get_cur_linedef (int x, int y);
static const Close_obj& get_cur_sector  (int x, int y);
static const Close_obj& get_cur_thing   (int x, int y);
static const Close_obj& get_cur_vertex  (int x, int y);


/*
 *	GetCurObject - determine which object is under the pointer
 * 
 *	Set <o> to point to the object under the pointer (map
 *	coordinates (<x>, <y>). If several objects are close
 *	enough to the pointer, the smallest object is chosen.
 */
void GetCurObject (Objid& o, int objtype, int x, int y)
{
switch (objtype)
   {
   case OBJ_THINGS:
     {
       o = get_cur_thing (x, y).obj;
       return;
     }

   case OBJ_VERTICES:
     {
       o = get_cur_vertex (x, y).obj;
       return;
     }

   case OBJ_LINEDEFS:
     {
       o = get_cur_linedef (x, y).obj;
       return;
     }

   case OBJ_SECTORS:
     {
       o = get_cur_sector (x, y).obj;
       return;
     }

   case OBJ_ANY:
     {
       std::vector<Close_obj> v;
       v.push_back (get_cur_linedef (x, y));
       v.push_back (get_cur_thing   (x, y));
       v.push_back (get_cur_vertex  (x, y));
       sort (v.begin (), v.end ());
       if (v[0].obj ())
       {
	 o = v[0].obj;
	 return;
       }
       o = get_cur_sector (x, y).obj;
       return;
     }

   default:
     nf_bug ("GetCurObject: objtype %d", (int) objtype);
     o.nil ();
     return;
   }
}


/*
 *	get_cur_linedef - determine which linedef is under the pointer
 */
static const Close_obj& get_cur_linedef (int x, int y)
{
  static Close_obj object;
  const int screenslack = 15;			// Slack in pixels
  double mapslack = fabs (screenslack / Scale);	// Slack in map units
  int xmin = (int) (x - mapslack + 0.5);
  int xmax = (int) (x + mapslack + 0.5);
  int ymin = (int) (y - mapslack + 0.5);
  int ymax = (int) (y + mapslack + 0.5);
  object.nil ();
  for (int n = 0; n < NumLineDefs; n++)
  {
    int x0 = Vertices[LineDefs[n].start].x;
    int y0 = Vertices[LineDefs[n].start].y;
    int x1 = Vertices[LineDefs[n].end].x;
    int y1 = Vertices[LineDefs[n].end].y;
    int dx;
    int dy;

    /* Skip all lines of which all points are more than <mapslack>
       units away from (x,y). In a typical level, this test will
       filter out all the linedefs but a handful. */
    if (x0 < xmin && x1 < xmin
     || x0 > xmax && x1 > xmax
     || y0 < ymin && y1 < ymin
     || y0 > ymax && y1 > ymax)
      continue;

    /* This is where it gets ugly. We're trying to calculate the
       distance between the pointer (x,y) and the linedef. Doing that
       rigorously involves doing an orthogonal projection. I use
       something simpler (for me).

       If the pointer is not within the ends of the linedef, I calculate
       the distance between the pointer and the closest end.

       If the pointer is within the ends of the linedef, I calculate
       what the ordinate would be for a point of the linedef that had
       the same abscissa as the pointer. Then I calculate the difference
       between that and the actual ordinate of the pointer and I call
       that the distance. It's a gross approximation but, in practice,
       it gives quite satisfactory results. Of course, for lines where
       dy > dx, the abscissa is y and the ordinate is x.
       -- AYM 1998-06-29 */
    dx = x1 - x0;
    dy = y1 - y0;
    double dist = ULONG_MAX;
    // The linedef is rather horizontal
    if (abs (dx) > abs (dy))
    {
      /* Are we to the left of the leftmost end or to the right of the
	 rightmost end or between the two ? */
      if (x < (dx > 0 ? x0 : x1))
	dist = hypot (x - (dx > 0 ? x0 : x1),
		      y - (dx > 0 ? y0 : y1));
      else if (x > (dx > 0 ? x1 : x0))
	dist = hypot (x - (dx > 0 ? x1 : x0),
		      y - (dx > 0 ? y1 : y0));
      else
	dist = fabs (y0 + ((double) dy)/dx * (x - x0) - y);
    }
    // The linedef is rather vertical
    else
    {
      /* Are we above the high end or below the low end or in between ? */
      if (y < (dy > 0 ? y0 : y1))
	dist = hypot (x - (dy > 0 ? x0 : x1),
		      y - (dy > 0 ? y0 : y1));
      else if (y > (dy > 0 ? y1 : y0))
	dist = hypot (x - (dy > 0 ? x1 : x0),
		      y - (dy > 0 ? y1 : y0));
      else
	dist = fabs (x0 + ((double) dx)/dy * (y - y0) - x);
    }
    if (dist <= object.distance  /* "<=" because if there are superimposed
				    linedefs, we want to return the
				    highest-numbered one. */
       && dist <= mapslack)
    {
      object.obj.type = OBJ_LINEDEFS;
      object.obj.num  = n;
      object.distance = dist;
      object.radius   = (int) (hypot (dx, dy) / 2);
      object.inside   = dist < 2 * Scale;
    }
  }
  return object;
}


/*
 *	get_cur_sector - determine which sector is under the pointer
 */
static const Close_obj& get_cur_sector (int x, int y)
{
  /* hack, hack...  I look for the first LineDef crossing
     an horizontal half-line drawn from the cursor */

  /* RQ & BW have been very smart here. Their method works remarkably
     well for normal sectors. However, self-referencing sectors are
     frequently unclosed. If your SRS has only horizontal linedefs, this
     method fails miserably. I suppose that the solution would be to look
     for intersections in BOTH directions and pick the closest. Remind me
     to look into it one of these days :-). -- AYM 1998-06-29 */

  /* Initializing curx to 32767 instead of MapMaxX + 1. Fixes the old
     DEU bug where sometimes you couldn't select a newly created sector
     to the west of the level until you saved. (MapMaxX got out of date
     and SaveLevelData() refreshed it.) -- AYM 1999-03-18 */
  static Close_obj object;
  int best_match = -1;
  int curx = 32767;  // Oh yes, one more hard-coded constant!
  for (int n = 0; n < NumLineDefs; n++)
    if ((Vertices[LineDefs[n].start].y > y)
     != (Vertices[LineDefs[n].end].y > y))
    {
      int lx0 = Vertices[LineDefs[n].start].x;
      int ly0 = Vertices[LineDefs[n].start].y;
      int lx1 = Vertices[LineDefs[n].end].x;
      int ly1 = Vertices[LineDefs[n].end].y;
      int m = lx0 + (int) ((long) (y - ly0) * (long) (lx1 - lx0)
					    / (long) (ly1 - ly0));
      if (m >= x && m < curx)
      {
	curx = m;
	best_match = n;
      }
    }

  // Now look if this linedef has a sidedef bound to one sector
  object.nil ();
  if (best_match >= 0)
  {
    if (Vertices[LineDefs[best_match].start].y
      > Vertices[LineDefs[best_match].end].y)
      best_match = LineDefs[best_match].sidedef1;
    else
      best_match = LineDefs[best_match].sidedef2;
    if (best_match >= 0)
    {
      object.obj.type = OBJ_SECTORS;
      object.obj.num  = SideDefs[best_match].sector;
      object.distance = 0;	// Not meaningful for sectors
      object.radius   = 1;	// Not meaningful for sectors
      object.inside   = true;	// Not meaningful for sectors
    }
    else
      ;
  }
  else
    ;
  return object;
}


/*
 *	get_cur_thing - determine which thing is under the pointer
 */
static const Close_obj& get_cur_thing (int x, int y)
{
  static Close_obj closest;
  const int screenslack = 15;			// Slack in pixels
  double mapslack = fabs (screenslack / Scale);	// Slack in map units
  int max_radius = (int) (get_max_thing_radius () + mapslack);
  int xmin = x - max_radius;
  int xmax = x + max_radius;
  int ymin = y - max_radius;
  int ymax = y + max_radius;

  closest.nil ();
  for (int n = 0; n < NumThings; n++)
  {
    // Filter out things that are farther than <max_radius> units away.
    if (Things[n].xpos < xmin
     || Things[n].xpos > xmax
     || Things[n].ypos < ymin
     || Things[n].ypos > ymax)
       continue;

    // So how far is that thing exactly ?
#ifdef ROUND_THINGS
    double dist = hypot (x - Things[n].xpos, y - Things[n].ypos);
    if (dist < closest.distance
     && dist <= get_thing_radius (Things[n].type) + mapslack)
    {
      closest.obj.type = OBJ_THINGS;
      closest.obj.num  = n;
      closest.distance = dist;
      closest.radius   = get_thing_radius (Things[n].type);
      closest.inside   = dist < closest.radius;
    }
#else
    {
      int thing_radius = (int) (get_thing_radius (Things[n].type) + mapslack);
      if (x > Things[n].xpos - thing_radius
       && x < Things[n].xpos + thing_radius
       && y > Things[n].ypos - thing_radius
       && y < Things[n].ypos + thing_radius)
      {
	Close_obj current;
	current.obj.type = OBJ_THINGS;
	current.obj.num  = n;
	current.distance = hypot (x - Things[n].xpos, y - Things[n].ypos);
	current.radius   = get_thing_radius (Things[n].type);
	current.inside   = x > Things[n].xpos - current.radius
			&& x < Things[n].xpos + current.radius
			&& y > Things[n].ypos - current.radius
			&& y < Things[n].ypos + current.radius;
       if (current <= closest)  /* "<=" because if there are superimposed
				   things, we want to return the
				   highest-numbered one. */
	  closest = current;
      }
    }
#endif
  }
  return closest;
}


/*
 *	get_cur_vertex - determine which vertex is under the pointer
 */
static const Close_obj& get_cur_vertex (int x, int y)
{
  static Close_obj object;
  const int screenradius = vertex_radius (Scale);	// Radius in pixels
  const int screenslack  = screenradius + 15;		// Slack in pixels
  double    mapslack     = fabs (screenslack / Scale);	// Slack in map units
  const int mapradius    = (int) (screenradius / Scale);// Radius in map units
  int xmin = (int) (x - mapslack + 0.5);
  int xmax = (int) (x + mapslack + 0.5);
  int ymin = (int) (y - mapslack + 0.5);
  int ymax = (int) (y + mapslack + 0.5);

  object.nil ();
  for (int n = 0; n < NumVertices; n++)
  {
    /* Filter out objects that are farther than <radius> units away. */
    if (Vertices[n].x < xmin
     || Vertices[n].x > xmax
     || Vertices[n].y < ymin
     || Vertices[n].y > ymax)
       continue;
    double dist = hypot (x - Vertices[n].x, y - Vertices[n].y);
    if (dist <= object.distance  /* "<=" because if there are superimposed
				   vertices, we want to return the
				   highest-numbered one. */
       && dist <= mapslack)
    {
      object.obj.type = OBJ_VERTICES;
      object.obj.num  = n;
      object.distance = dist;
      object.radius   = mapradius;
      object.inside   = x > Vertices[n].x - mapradius
		     && x < Vertices[n].x + mapradius
		     && y > Vertices[n].y - mapradius
		     && y < Vertices[n].y + mapradius;
    }
  }
  return object;
}