Panda3D
pointerEventList.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pointerEventList.cxx
10  * @author jyelon
11  * @date 2007-09-20
12  */
13 
14 #include "pointerEventList.h"
15 #include "indent.h"
16 #include "config_event.h"
17 #include "clockObject.h"
18 #include "deg_2_rad.h"
19 #include "cmath.h"
20 
21 TypeHandle PointerEventList::_type_handle;
22 
23 /**
24  * Compute the difference between two angles. Returns a value in the range
25  * -180 to 180.
26  */
27 INLINE double delta_angle(double angle1, double angle2) {
28  double deltang = angle2 - angle1;
29  while (deltang < -180.0) deltang += 360.0;
30  while (deltang > 180.0) deltang -= 360.0;
31  return deltang;
32 }
33 
34 
35 /**
36  * Compute the difference between two angles. Returns a value in the range
37  * -180 to 180.
38  */
39 INLINE double normalize_angle(double angle) {
40  while (angle < 0.0) angle += 360.0;
41  while (angle > 360.0) angle -= 360.0;
42  return angle;
43 }
44 
45 
46 
47 /**
48  *
49  */
50 void PointerEventList::
51 output(std::ostream &out) const {
52  if (_events.empty()) {
53  out << "(no pointers)";
54  } else {
55  Events::const_iterator ei;
56  ei = _events.begin();
57  out << "(" << (*ei);
58  ++ei;
59  while (ei != _events.end()) {
60  out << " " << (*ei);
61  ++ei;
62  }
63  out << ")";
64  }
65 }
66 
67 /**
68  *
69  */
70 void PointerEventList::
71 write(std::ostream &out, int indent_level) const {
72  indent(out, indent_level) << _events.size() << " events:\n";
73  Events::const_iterator ei;
74  for (ei = _events.begin(); ei != _events.end(); ++ei) {
75  indent(out, indent_level + 2) << (*ei) << "\n";
76  }
77 }
78 
79 /**
80  * Adds a new event from the given PointerData object.
81  */
83 add_event(const PointerData &data, int seq, double time) {
84  PointerEvent pe;
85  pe._in_window = data._in_window;
86  pe._type = data._type;
87  pe._id = data._id;
88  pe._xpos = data._xpos;
89  pe._ypos = data._ypos;
90  pe._pressure = data._pressure;
91  pe._sequence = seq;
92  pe._time = time;
93  if (_events.size() > 0) {
94  pe._dx = data._xpos - _events.back()._xpos;
95  pe._dy = data._ypos - _events.back()._ypos;
96  double ddx = pe._dx;
97  double ddy = pe._dy;
98  pe._length = csqrt(ddx*ddx + ddy*ddy);
99  if (pe._length > 0.0) {
100  pe._direction = normalize_angle(rad_2_deg(catan2(-ddy,ddx)));
101  } else {
102  pe._direction = _events.back()._direction;
103  }
104  pe._rotation = delta_angle(_events.back()._direction, pe._direction);
105  } else {
106  pe._dx = 0;
107  pe._dy = 0;
108  pe._length = 0.0;
109  pe._direction = 0.0;
110  pe._rotation = 0.0;
111  }
112  _events.push_back(pe);
113 }
114 
115 /**
116  * Adds a new event to the end of the list. Automatically calculates the dx,
117  * dy, length, direction, and rotation for all but the first event.
118  */
120 add_event(bool in_win, int xpos, int ypos, int seq, double time) {
121  PointerEvent pe;
122  pe._in_window = in_win;
123  pe._xpos = xpos;
124  pe._ypos = ypos;
125  pe._sequence = seq;
126  pe._time = time;
127  if (_events.size() > 0) {
128  pe._dx = xpos - _events.back()._xpos;
129  pe._dy = ypos - _events.back()._ypos;
130  double ddx = pe._dx;
131  double ddy = pe._dy;
132  pe._length = csqrt(ddx*ddx + ddy*ddy);
133  if (pe._length > 0.0) {
134  pe._direction = normalize_angle(rad_2_deg(catan2(-ddy,ddx)));
135  } else {
136  pe._direction = _events.back()._direction;
137  }
138  pe._rotation = delta_angle(_events.back()._direction, pe._direction);
139  } else {
140  pe._dx = 0;
141  pe._dy = 0;
142  pe._length = 0.0;
143  pe._direction = 0.0;
144  pe._rotation = 0.0;
145  }
146  _events.push_back(pe);
147 }
148 
149 /**
150  * Adds a new event to the end of the list based on the given mouse movement.
151  */
153 add_event(bool in_win, int xpos, int ypos, double xdelta, double ydelta, int seq, double time) {
154  PointerEvent pe;
155  pe._in_window = in_win;
156  pe._xpos = xpos;
157  pe._ypos = ypos;
158  pe._dx = xdelta;
159  pe._dy = ydelta;
160  pe._sequence = seq;
161  pe._time = time;
162  pe._length = csqrt(xdelta*xdelta + ydelta*ydelta);
163  if (pe._length > 0.0) {
164  pe._direction = normalize_angle(rad_2_deg(catan2(-ydelta,xdelta)));
165  } else if (!_events.empty()) {
166  pe._direction = _events.back()._direction;
167  } else {
168  pe._direction = 0.0;
169  }
170  if (!_events.empty()) {
171  pe._rotation = delta_angle(_events.back()._direction, pe._direction);
172  } else {
173  pe._rotation = 0.0;
174  }
175  _events.push_back(pe);
176 }
177 
178 /**
179  * Returns true if the trail loops around the specified point.
180  */
182 encircles(int x, int y) const {
183  int tot_events = _events.size();
184  if (tot_events < 3) {
185  return false;
186  }
187  int last = tot_events-1;
188  double dx = _events[last]._xpos - x;
189  double dy = _events[last]._ypos - y;
190  double lastang = rad_2_deg(catan2(dy, dx));
191  double total = 0.0;
192  for (int i=last; (i>=0) && (total < 360.0) && (total > -360.0); i--) {
193  dx = _events[i]._xpos - x;
194  dy = _events[i]._ypos - y;
195  if ((dx==0.0)&&(dy==0.0)) {
196  continue;
197  }
198  double angle = rad_2_deg(catan2(dy,dx));
199  double deltang = delta_angle(lastang, angle);
200  if (deltang * total < 0.0) {
201  total = 0.0;
202  }
203  total += deltang;
204  lastang = angle;
205  }
206  return (total > 360.0) || (total < -360.0);
207 }
208 
209 /**
210  * returns the total angular deviation that the trail has made in the
211  * specified time period. A small number means that the trail is moving in a
212  * relatively straight line, a large number means that the trail is zig-
213  * zagging or spinning. The result is in degrees.
214  */
215 double PointerEventList::
216 total_turns(double sec) const {
217  double old = ClockObject::get_global_clock()->get_frame_time() - sec;
218  int pos = _events.size()-1;
219  double tot = 0.0;
220  while ((pos >= 0)&&(_events[pos]._time >= old)) {
221  double rot = _events[pos]._rotation;
222  if (rot < 0.0) rot = -rot;
223  tot += rot;
224  }
225  return tot;
226 }
227 
228 /**
229  * This function is not implemented yet. It is a work in progress. The
230  * intent is as follows:
231  *
232  * Returns a nonzero value if the mouse movements match the specified pattern.
233  * The higher the value, the better the match. The pattern is a sequence of
234  * compass directions (ie, "E", "NE", etc) separated by spaces. If rot is
235  * nonzero, then the pattern is rotated counterclockwise by the specified
236  * amount before testing. Seglen is the minimum length a mouse movement needs
237  * to be in order to be considered significant.
238  */
239 double PointerEventList::
240 match_pattern(const std::string &ascpat, double rot, double seglen) {
241  // Convert the pattern from ascii to a more usable form.
242  vector_double pattern;
243  parse_pattern(ascpat, pattern);
244 
245  // Apply the rotation to the pattern.
246  for (size_t i=0; i<pattern.size(); i++) {
247  pattern[i] = normalize_angle(pattern[i] + rot);
248  }
249 
250  return 0.0;
251 }
252 
253 /**
254  * Parses a pattern as used by match_pattern.
255  */
256 void PointerEventList::
257 parse_pattern(const std::string &ascpat, vector_double &pattern) {
258  int chars = 0;
259  double dir = 180.0;
260  for (size_t i=0; i<ascpat.size(); i++) {
261  char c = ascpat[i];
262  double ang = -1.0;
263  if ((c=='E')||(c=='e')) ang=0.0;
264  else if ((c=='N')||(c=='n')) ang=90.0;
265  else if ((c=='W')||(c=='w')) ang=180.0;
266  else if ((c=='S')||(c=='s')) ang=270.0;
267  if (ang >= 0.0) {
268  double offset = delta_angle(dir, ang);
269  double newang = dir + offset;
270  dir = normalize_angle((dir * chars + newang) / (chars + 1));
271  chars += 1;
272  } else {
273  if ((c != ' ')&&(c != '\t')) {
274  event_cat.warning() <<
275  "Invalid pattern in PointerEventList::match_pattern\n";
276  pattern.clear();
277  return;
278  }
279  if (chars > 0) {
280  pattern.push_back(dir);
281  }
282  chars = 0;
283  dir = 180.0;
284  }
285  }
286  if (chars > 0) {
287  pattern.push_back(dir);
288  }
289 
290  std::cerr << "Pattern: ";
291  for (int i=0; i<(int)pattern.size(); i++) {
292  std::cerr << pattern[i] << " ";
293  }
294  std::cerr << "\n";
295 }
static ClockObject * get_global_clock()
Returns a pointer to the global ClockObject.
Definition: clockObject.I:215
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double delta_angle(double angle1, double angle2)
Compute the difference between two angles.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double total_turns(double sec) const
returns the total angular deviation that the trail has made in the specified time period.
void add_event(const PointerData &data, int seq, double time)
Adds a new event from the given PointerData object.
get_frame_time
Returns the time in seconds as of the last time tick() was called (typically, this will be as of the ...
Definition: clockObject.h:91
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
Holds the data that might be generated by a 2-d pointer input device, such as the mouse in the Graphi...
Definition: pointerData.h:38
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
double normalize_angle(double angle)
Compute the difference between two angles.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
double match_pattern(const std::string &pattern, double rot, double seglen)
This function is not implemented yet.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool encircles(int x, int y) const
Returns true if the trail loops around the specified point.
Records a pointer movement event.
Definition: pointerEvent.h:26