implements EventGesturePan,Pinch,Twist - OS agnostic

This commit is contained in:
Jérémy Zurcher 2021-11-10 20:40:16 +01:00
parent e8870ddefc
commit 89cf90534f
10 changed files with 381 additions and 20 deletions

View file

@ -183,7 +183,7 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S
}
}
void Input::SpeedTrack::update(const Vector2 &p_delta_p) {
void Input::TouchTrack::update(const Vector2 &p_delta_p) {
uint64_t tick = OS::get_singleton()->get_ticks_usec();
uint32_t tdiff = tick - last_tick;
float delta_t = tdiff / 1000000.0;
@ -191,6 +191,8 @@ void Input::SpeedTrack::update(const Vector2 &p_delta_p) {
accum += p_delta_p;
accum_t += delta_t;
last_delta = p_delta_p;
position += p_delta_p;
if (accum_t > max_ref_frame * 10) {
accum_t = max_ref_frame * 10;
@ -206,16 +208,25 @@ void Input::SpeedTrack::update(const Vector2 &p_delta_p) {
}
}
void Input::SpeedTrack::reset() {
void Input::TouchTrack::reset(const Vector2 &p_position_p) {
last_tick = OS::get_singleton()->get_ticks_usec();
speed = Vector2();
position = p_position_p;
last_delta = Vector2();
accum_t = 0;
}
Input::SpeedTrack::SpeedTrack() {
int Input::TouchTrack::sector(const Vector2 &p_center_p) {
Point2 adjusted_position = p_center_p - position;
float raw_angle = fmod(adjusted_position.angle_to(last_delta) + (Math_PI / 4), Math_TAU);
float adjusted_angle = raw_angle >= 0 ? raw_angle : raw_angle + Math_TAU;
return floor(adjusted_angle / (Math_PI / 2));
}
Input::TouchTrack::TouchTrack() {
min_ref_frame = 0.1;
max_ref_frame = 0.3;
reset();
reset(Vector2());
}
bool Input::is_key_pressed(Key p_keycode) const {
@ -454,6 +465,7 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
// require additional handling by this class.
Ref<InputEventKey> k = p_event;
if (k.is_valid() && !k->is_echo() && k->get_keycode() != 0) {
if (k->is_pressed()) {
keys_pressed.insert(k->get_keycode());
@ -509,12 +521,11 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (st.is_valid()) {
if (st->is_pressed()) {
SpeedTrack &track = touch_speed_track[st->get_index()];
track.reset();
touch_track[st->get_index()].reset(st->get_position());
} else {
// Since a pointer index may not occur again (OSs may or may not reuse them),
// imperatively remove it from the map to keep no fossil entries in it
touch_speed_track.erase(st->get_index());
touch_track.erase(st->get_index());
}
if (emulate_mouse_from_touch) {
@ -554,9 +565,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
Ref<InputEventScreenDrag> sd = p_event;
if (sd.is_valid()) {
SpeedTrack &track = touch_speed_track[sd->get_index()];
track.update(sd->get_relative());
sd->set_speed(track.speed);
TouchTrack &touch = touch_track[sd->get_index()];
touch.update(sd->get_relative());
sd->set_speed(touch.speed);
if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) {
Ref<InputEventMouseMotion> motion_event;
@ -571,6 +582,79 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
_parse_input_event_impl(motion_event, true);
}
int sz = touch_track.size();
if (sz > 1) {
Point2 center = Point2();
for (Map<int, TouchTrack>::Element *e = touch_track.front(); e; e = e->next()) {
center += e->get().position;
}
center /= sz;
int sector = -1;
for (Map<int, TouchTrack>::Element *e = touch_track.front(); e; e = e->next()) {
int e_sector = e->get().sector(center);
if (sector == -1) {
sector = e_sector;
} else if (sector != e_sector) {
sector = -1;
break;
}
}
if (sector == -1) {
Point2 speed = Point2();
Point2 relative = Point2();
for (Map<int, TouchTrack>::Element *e = touch_track.front(); e; e = e->next()) {
TouchTrack &track = e->get();
speed += track.speed;
relative += track.last_delta;
}
speed /= sz;
relative /= sz;
Ref<InputEventGesturePan> ev;
ev.instantiate();
ev->set_touches(sz);
ev->set_position(center);
ev->set_speed(speed);
ev->set_relative(relative);
_parse_input_event_impl(ev, false);
} else if (sector == 0 || sector == 2) {
float distance = 0;
float distance_p = 0;
for (Map<int, TouchTrack>::Element *e = touch_track.front(); e; e = e->next()) {
TouchTrack &track = e->get();
Vector2 d = track.position - center;
distance += d.length();
distance_p += (d + (track.last_delta / sz)).length();
}
distance /= sz;
distance_p /= sz;
Ref<InputEventGesturePinch> ev;
ev.instantiate();
ev->set_touches(sz);
ev->set_position(center);
ev->set_distance(distance);
ev->set_factor(distance / distance_p);
_parse_input_event_impl(ev, false);
} else if (sector == 1 || sector == 3) {
float rotation = 0;
for (Map<int, TouchTrack>::Element *e = touch_track.front(); e; e = e->next()) {
TouchTrack &track = e->get();
rotation += (track.position - center).angle_to(track.position + (track.last_delta / sz) - center);
}
rotation /= sz;
Ref<InputEventGestureTwist> ev;
ev.instantiate();
ev->set_touches(sz);
ev->set_position(center);
ev->set_rotation(rotation);
_parse_input_event_impl(ev, false);
}
}
}
Ref<InputEventJoypadButton> jb = p_event;

View file

@ -116,8 +116,10 @@ private:
int mouse_from_touch_index = -1;
struct SpeedTrack {
struct TouchTrack {
uint64_t last_tick;
Vector2 position;
Vector2 last_delta;
Vector2 speed;
Vector2 accum;
float accum_t;
@ -125,8 +127,9 @@ private:
float max_ref_frame;
void update(const Vector2 &p_delta_p);
void reset();
SpeedTrack();
void reset(const Vector2 &p_position_p);
int sector(const Vector2 &p_center_p);
TouchTrack();
};
struct Joypad {
@ -140,8 +143,8 @@ private:
int hat_current = 0;
};
SpeedTrack mouse_speed_track;
Map<int, SpeedTrack> touch_speed_track;
TouchTrack mouse_speed_track;
Map<int, TouchTrack> touch_track;
Map<int, Joypad> joy_names;
int fallback_mapping = -1;

View file

@ -1348,15 +1348,26 @@ void InputEventGesture::set_position(const Vector2 &p_pos) {
pos = p_pos;
}
Vector2 InputEventGesture::get_position() const {
return pos;
}
void InputEventGesture::set_touches(const int p_touches) {
touches = p_touches;
}
int InputEventGesture::get_touches() const {
return touches;
}
void InputEventGesture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_position", "position"), &InputEventGesture::set_position);
ClassDB::bind_method(D_METHOD("get_position"), &InputEventGesture::get_position);
ClassDB::bind_method(D_METHOD("set_touches", "touches"), &InputEventGesture::set_touches);
ClassDB::bind_method(D_METHOD("get_touches"), &InputEventGesture::get_touches);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position"), "set_position", "get_position");
}
Vector2 InputEventGesture::get_position() const {
return pos;
ADD_PROPERTY(PropertyInfo(Variant::INT, "touches"), "set_touches", "get_touches");
}
///////////////////////////////////
@ -1420,6 +1431,7 @@ Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, con
ev->set_position(p_xform.xform(get_position() + p_local_ofs));
ev->set_delta(get_delta());
ev->set_touches(get_touches());
return ev;
}
@ -1441,6 +1453,142 @@ void InputEventPanGesture::_bind_methods() {
///////////////////////////////////
void InputEventGesturePan::set_relative(const Vector2 &p_relative) {
relative = p_relative;
}
Vector2 InputEventGesturePan::get_relative() const {
return relative;
}
Vector2 InputEventGesturePan::get_delta() const {
return relative / get_touches();
}
void InputEventGesturePan::set_speed(const Vector2 &p_speed) {
speed = p_speed;
}
Vector2 InputEventGesturePan::get_speed() const {
return speed;
}
Ref<InputEvent> InputEventGesturePan::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventGesturePan> ev;
ev.instantiate();
ev->set_device(get_device());
ev->set_modifiers_from_event(this);
ev->set_position(p_xform.xform(get_position() + p_local_ofs));
ev->set_relative(p_xform.basis_xform(relative));
ev->set_speed(p_xform.basis_xform(speed));
ev->set_touches(get_touches());
return ev;
}
String InputEventGesturePan::as_text() const {
return "InputEventGesturePan : position=(" + String(get_position()) + "), relative=(" + String(get_relative()) + "), speed=(" + String(get_speed()) + ")";
}
void InputEventGesturePan::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_relative", "relative"), &InputEventGesturePan::set_relative);
ClassDB::bind_method(D_METHOD("get_relative"), &InputEventGesturePan::get_relative);
ClassDB::bind_method(D_METHOD("get_delta"), &InputEventGesturePan::get_delta);
ClassDB::bind_method(D_METHOD("set_speed", "speed"), &InputEventGesturePan::set_speed);
ClassDB::bind_method(D_METHOD("get_speed"), &InputEventGesturePan::get_speed);
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative"), "set_relative", "get_relative");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "speed"), "set_speed", "get_speed");
}
/////////////////////////////
void InputEventGesturePinch::set_distance(float p_distance) {
distance = p_distance;
}
float InputEventGesturePinch::get_distance() const {
return distance;
}
void InputEventGesturePinch::set_factor(float p_factor) {
factor = p_factor;
}
float InputEventGesturePinch::get_factor() const {
return factor;
}
Ref<InputEvent> InputEventGesturePinch::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventGesturePinch> ev;
ev.instantiate();
ev->set_device(get_device());
ev->set_modifiers_from_event(this);
ev->set_position(p_xform.xform(get_position() + p_local_ofs));
ev->set_distance(distance);
ev->set_factor(factor);
ev->set_touches(get_touches());
return ev;
}
String InputEventGesturePinch::as_text() const {
return "InputEventGesturePinch : position=(" + String(get_position()) + "), distance=(" + rtos(get_distance()) + "), factor=(" + rtos(get_factor()) + ")";
}
void InputEventGesturePinch::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_distance", "distance"), &InputEventGesturePinch::set_distance);
ClassDB::bind_method(D_METHOD("get_distance"), &InputEventGesturePinch::get_distance);
ClassDB::bind_method(D_METHOD("set_factor", "factor"), &InputEventGesturePinch::set_factor);
ClassDB::bind_method(D_METHOD("get_factor"), &InputEventGesturePinch::get_factor);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "distance"), "set_distance", "get_distance");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "factor"), "set_factor", "get_factor");
}
/////////////////////////////
void InputEventGestureTwist::set_rotation(float p_rotation) {
rotation = p_rotation;
}
float InputEventGestureTwist::get_rotation() const {
return rotation;
}
Ref<InputEvent> InputEventGestureTwist::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventGestureTwist> ev;
ev.instantiate();
ev->set_device(get_device());
ev->set_modifiers_from_event(this);
ev->set_position(p_xform.xform(get_position() + p_local_ofs));
ev->set_rotation(rotation);
return ev;
}
String InputEventGestureTwist::as_text() const {
return "InputEventGestureTwist : position=(" + String(get_position()) + "), rotation=(" + rtos(get_rotation()) + ")";
}
void InputEventGestureTwist::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rotation", "rotation"), &InputEventGestureTwist::set_rotation);
ClassDB::bind_method(D_METHOD("get_rotation"), &InputEventGestureTwist::get_rotation);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation"), "set_rotation", "get_rotation");
}
/////////////////////////////
void InputEventMIDI::set_channel(const int p_channel) {
channel = p_channel;
}

View file

@ -452,6 +452,7 @@ class InputEventGesture : public InputEventWithModifiers {
GDCLASS(InputEventGesture, InputEventWithModifiers);
Vector2 pos;
int touches = 0;
protected:
static void _bind_methods();
@ -459,6 +460,8 @@ protected:
public:
void set_position(const Vector2 &p_pos);
Vector2 get_position() const;
void set_touches(const int p_touches);
int get_touches() const;
};
class InputEventMagnifyGesture : public InputEventGesture {
@ -497,6 +500,61 @@ public:
InputEventPanGesture() {}
};
class InputEventGesturePan : public InputEventGesture {
GDCLASS(InputEventGesturePan, InputEventGesture);
Vector2 relative;
Vector2 speed;
protected:
static void _bind_methods();
public:
void set_relative(const Vector2 &p_relative);
Vector2 get_relative() const;
Vector2 get_delta() const;
void set_speed(const Vector2 &p_speed);
Vector2 get_speed() const;
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
};
class InputEventGesturePinch : public InputEventGesture {
GDCLASS(InputEventGesturePinch, InputEventGesture);
float distance = 0;
float factor = 0;
protected:
static void _bind_methods();
public:
void set_distance(float p_distance);
float get_distance() const;
void set_factor(float p_factor);
float get_factor() const;
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
};
class InputEventGestureTwist : public InputEventGesture {
GDCLASS(InputEventGestureTwist, InputEventGesture);
float rotation = 0;
protected:
static void _bind_methods();
public:
void set_rotation(float p_rotation);
float get_rotation() const;
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
};
class InputEventMIDI : public InputEvent {
GDCLASS(InputEventMIDI, InputEvent);

View file

@ -163,6 +163,9 @@ void register_core_types() {
GDREGISTER_VIRTUAL_CLASS(InputEventGesture);
GDREGISTER_CLASS(InputEventMagnifyGesture);
GDREGISTER_CLASS(InputEventPanGesture);
GDREGISTER_CLASS(InputEventGesturePan);
GDREGISTER_CLASS(InputEventGesturePinch);
GDREGISTER_CLASS(InputEventGestureTwist);
GDREGISTER_CLASS(InputEventMIDI);
// Network

View file

@ -96,7 +96,7 @@
<argument index="0" name="xform" type="Transform2D" />
<argument index="1" name="local_ofs" type="Vector2" default="Vector2(0, 0)" />
<description>
Returns a copy of the given input event which has been offset by [code]local_ofs[/code] and transformed by [code]xform[/code]. Relevant for events of type [InputEventMouseButton], [InputEventMouseMotion], [InputEventScreenTouch], [InputEventScreenDrag], [InputEventMagnifyGesture] and [InputEventPanGesture].
Returns a copy of the given input event which has been offset by [code]local_ofs[/code] and transformed by [code]xform[/code]. Relevant for events of type [InputEventMouseButton], [InputEventMouseMotion], [InputEventScreenTouch], [InputEventScreenDrag], [InputEventMagnifyGesture], [InputEventPanGesture], [InputEventGesturePan], [InputEventGesturePinch] and [InputEventGestureTwist].
</description>
</method>
</methods>

View file

@ -11,5 +11,8 @@
<member name="position" type="Vector2" setter="set_position" getter="get_position" default="Vector2(0, 0)">
The local gesture position relative to the [Viewport]. If used in [method Control._gui_input], the position is relative to the current [Control] that received this gesture.
</member>
<member name="touches" type="int" setter="set_touches" getter="get_touches" default="0">
Number of touches constituting this gesture event.
</member>
</members>
</class>

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="InputEventGesturePan" inherits="InputEventGesture" version="4.0">
<brief_description>
Pan gesture event.
</brief_description>
<description>
A pan gesture event is generated by a translational movement of at least 2 touch points.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_delta" qualifiers="const">
<return type="Vector2" />
<description>
Returns relative divided by touches.
</description>
</method>
</methods>
<members>
<member name="relative" type="Vector2" setter="set_relative" getter="get_relative" default="Vector2(0, 0)">
The pan position relative to its start position.
</member>
<member name="speed" type="Vector2" setter="set_speed" getter="get_speed" default="Vector2(0, 0)">
The pan speed.
</member>
</members>
</class>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="InputEventGesturePinch" inherits="InputEventGesture" version="4.0">
<brief_description>
Pinch gesture event.
</brief_description>
<description>
A pinch gesture event is generated by at least 2 touch points moving towards or away of each other.
</description>
<tutorials>
</tutorials>
<members>
<member name="distance" type="float" setter="set_distance" getter="get_distance" default="0.0">
The average distance to the center ([member InputEventGesture.position]) of the gesture.
</member>
<member name="factor" type="float" setter="set_factor" getter="get_factor" default="0.0">
The ratio between the current distance and the previous one.
</member>
</members>
</class>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="InputEventGestureTwist" inherits="InputEventGesture" version="4.0">
<brief_description>
Twist gesture event.
</brief_description>
<description>
A twist gesture event is generated by a twist movement of at least 2 touch points.
</description>
<tutorials>
</tutorials>
<members>
<member name="rotation" type="float" setter="set_rotation" getter="get_rotation" default="0.0">
Rotation in radians, relative to the preceding angle.
</member>
</members>
</class>