Vuo  2.3.2
VuoInteraction.c
Go to the documentation of this file.
1 
10 #include <math.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include "type.h"
14 // @todo
15 // #include "VuoMouseUtility.h"
16 
18 #ifdef VUO_COMPILER
20  "title" : "UI Interaction",
21  "description" : "Stores information about a UI device's input.",
22  "keywords" : [ "gui" ],
23  "version" : "1.0.0",
24  "dependencies" : [
25  "VuoBoolean",
26  "VuoList_VuoInteraction",
27  "VuoReal",
28  "VuoText",
29  "VuoUuid",
30  "VuoPoint2d",
31  "VuoInteractionType"
32  // "VuoMouseUtility"
33  ]
34  });
35 #endif
37 
51 {
52  VuoInteraction interaction = VuoInteraction_make();
53 
54  json_object *o = NULL;
55 
56  if (json_object_object_get_ex(js, "uuid", &o))
57  interaction.uuid = VuoUuid_makeFromJson(o);
58 
59  if (json_object_object_get_ex(js, "position", &o))
60  interaction.position = VuoPoint2d_makeFromJson(o);
61 
62  if (json_object_object_get_ex(js, "isPressed", &o))
63  interaction.isPressed = json_object_get_boolean(o);
64 
65  if (json_object_object_get_ex(js, "type", &o))
66  interaction.type = VuoInteractionType_makeFromJson(o);
67 
68  if( json_object_object_get_ex(js, "origin", &o))
69  interaction.origin = VuoPoint2d_makeFromJson(o);
70 
71  if( json_object_object_get_ex(js, "timestamp", &o))
72  interaction.timestamp = json_object_get_double(o);
73 
74  if( json_object_object_get_ex(js, "clickCount", &o))
75  interaction.clickCount = VuoInteger_makeFromJson(o);
76 
77  return interaction;
78 }
79 
87 {
88  json_object *js = json_object_new_object();
89 
90  json_object_object_add(js, "uuid", VuoUuid_getJson(value.uuid));
91  json_object_object_add(js, "position", VuoPoint2d_getJson(value.position));
92  json_object_object_add(js, "isPressed", json_object_new_boolean(value.isPressed));
93  json_object_object_add(js, "type", VuoInteractionType_getJson(value.type));
94  json_object_object_add(js, "origin", VuoPoint2d_getJson(value.origin));
95  json_object_object_add(js, "timestamp", json_object_new_double(value.timestamp));
96  json_object_object_add(js, "clickCount", VuoInteger_getJson(value.clickCount));
97 
98  return js;
99 }
100 
108 {
109  char* typeStr = VuoInteractionType_getSummary(value.type);
110 
111  char* sum = VuoText_format("<div>Type: %s</div><div>Position: %.2f, %.2f</div><div>Is Pressed: %s</div><div>Click Count: %lli</div>",
112  typeStr,
113  value.position.x,
114  value.position.y,
115  (value.isPressed ? "Yes" : "No"),
116  value.clickCount);
117 
118  free(typeStr);
119  return sum;
120 }
121 
127 bool VuoInteraction_areEqual(const VuoInteraction value1, const VuoInteraction value2)
128 {
129  return VuoPoint2d_areEqual(value1.position, value2.position) &&
130  VuoBoolean_areEqual(value1.isPressed, value2.isPressed) &&
131  VuoInteractionType_areEqual(value1.type, value2.type) &&
132  VuoPoint2d_areEqual(value1.origin, value2.origin) &&
133  VuoReal_areEqual(value1.timestamp, value2.timestamp);
134 }
135 
142 {
143  // @todo
144  return (int)&a < (int)&b;
145 }
146 
147 #define MAX_CLICK_DELTA .2
148 
149 #define MIN_DRAG_DISTANCE .08
150 
156 static bool VuoInteraction_isDrag(const VuoInteraction previous, const bool isPressed, const VuoPoint2d position)
157 {
158  // if this frame isn't pressed and neither was the previous one this definitely isn't a drag
159  if(!isPressed || !previous.isPressed)
160  return false;
161 
162  // If the last frame was a drag and the device is still engaged, it's still a drag
163  if(previous.type == VuoInteractionType_Drag || previous.type == VuoInteractionType_DragStart)
164  return true;
165 
166  // if the device has been pressed for a long-ish time and moved slightly it could be a fine-grained drag
167  if( VuoLogGetTime() - previous.timestamp > MAX_CLICK_DELTA && fabs(VuoPoint2d_distance(previous.origin, position)) > .001 )
168  return true;
169  else
170  return fabs(VuoPoint2d_distance(position, previous.origin)) > MIN_DRAG_DISTANCE;
171 }
172 
179 {
180  return .5;
181 }
182 
188 bool VuoInteraction_update(const VuoPoint2d position, const VuoBoolean isPressed, VuoInteraction* interaction)
189 {
190  bool changed = false;
191  VuoInteraction prev = *interaction;
192  interaction->type = VuoInteractionType_None;
193 
194  bool isDrag = VuoInteraction_isDrag(prev, isPressed, position);
195 
196  // type is a little hierarchy; sometimes multiple events can occur in a single interaction (move & release for example)
197  // release/click/press/drag take priority over move in these cases.
198  if(isDrag || !VuoPoint2d_areEqual(position, interaction->position))
199  {
200  changed = true;
201 
202  if( isDrag )
203  interaction->type = (prev.type == VuoInteractionType_Drag || prev.type == VuoInteractionType_DragStart) ? VuoInteractionType_Drag : VuoInteractionType_DragStart;
204  else
205  interaction->type = VuoInteractionType_Move;
206 
207  interaction->position = position;
208  interaction->clickCount = 0;
209  }
210 
211  if( interaction->isPressed != isPressed )
212  {
213  changed = true;
214 
215  if(isPressed)
216  {
217  interaction->type = VuoInteractionType_Press;
218  interaction->origin = position;
219  interaction->timestamp = VuoLogGetTime();
220 
221  if(prev.clickCount < 1 || ((interaction->timestamp - prev.timestamp) < VuoMouseUtility_getDoubleClickInterval()))
222  {
223  interaction->clickCount = prev.clickCount + 1;
224  }
225  else
226  {
227  interaction->clickCount = 1;
228  }
229  }
230  else
231  {
232  // is drag?
233  // is click?
234  // is release.
235  if( prev.type == VuoInteractionType_DragStart || prev.type == VuoInteractionType_Drag )
236  {
237  interaction->clickCount = 0;
238  interaction->type = VuoInteractionType_DragFinish;
239  }
240  else if( VuoLogGetTime() - interaction->timestamp < MAX_CLICK_DELTA )
241  {
242  interaction->clickCount = prev.clickCount;
243  interaction->type = VuoInteractionType_Click;
244  }
245  else
246  {
247  interaction->clickCount = prev.clickCount;
248  interaction->type = VuoInteractionType_Release;
249  }
250  }
251 
252  interaction->isPressed = isPressed;
253  }
254 
255  return changed;
256 }
257