Physics

Differences between revisions 25 and 31 (spanning 6 versions)
Revision 25 as of 2011-08-18 09:22:10
Size: 7301
Editor: xdsl-83-145-207-155
Comment: Descriptive code samples written
Revision 31 as of 2012-06-14 19:28:34
Size: 5759
Editor: c-67-170-185-42
Comment:
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
Library: uTouch-Physics<<BR>> Library: Physics<<BR>>
Line 5: Line 5:
'''''Note''''': This page is in the process of being rewritten. It will be inconsistent until this task is finished. <<TableOfContents(3)>>
Line 7: Line 7:
<<TableOfContents(3)>> == Quick overview ==

This provides a scrollable view. The content is a rectangle, and the viewport is a rectangle whose area is a subset of the content. The content and viewport can be thought of as boxes. The viewport may be dragged within the content.

While the user is interacting with the viewport by scrolling on an input device, the viewport follows the scroll motion. When the scrolling ceases, the viewport will continue moving. As it moves, friction will slow it down until it finally stops.

When the viewport hits the edge of the area, something happens. By default all motion of the viewport is stopped. The library may provide alternative behaviour, such as "bouncing", sliding along the edge and so on.
Line 19: Line 25:
 * the source of motion can be a touch gesture, mouse flick, accelerometer, or any other device capable of providing velocity information
Line 22: Line 29:
 * it does not convert coordinates, it will only work in pixel units
 * it does not support on-the-fly area resizing because that behaviour is application dependent
 * it does not convert coordinate types (e.g. from touchpad device coordinates to pixels), it will only work in pixel units
 * it does not support on-the-fly area or viewport resizing while preserving motion, resizes reset all existing state
Line 25: Line 32:

== API objects ==

=== PhysicsScroller ===

An opaque type for the scroller area.

== API Functions ==


=== physics_scroller_new ===

{{{
PhysicsScroller physics_scroller_new(
        int area_width,
        int area_height,
        int viewport_width,
        int viewport_height)
}}}

Creates a new scroller with the given dimensions. Returns {{{NULL}}} on failure, such as having a viewport that is larger than the viewable area.


=== physics_scroller_delete ===

{{{
void physics_scroller_delete(PhysicsScroller scroller);
}}}

Deletes a scroller.


=== physics_scroller_reset ===

{{{
int PHYSICS_API physics_scroller_reset(PhysicsScroller scroller,
        int area_width,
        int area_height,
        int viewport_width,
        int viewport_height);
}}}

Resets the scroller with the new given dimensions. If dimensions are invalid, the state of the object is not changed. If they are valid, all old state is permanently destroyed. Using this is equivalent to deleting the old scroller and creating a new one with the given arguments.

=== scroller_get_viewport_location ===

{{{
void physics_scroller_get_viewport_location(PhysicsScroller scroller, int *x, int *y);
}}}

Get the location of the top left corner of the viewport.

=== set_scroller_set_viewport_location ===

{{{
void physics_scroller_set_viewport_location(PhysicsScroller scroller, int x, int y, int clamp_to_area);
}}}

Move viewport's top left corner to the desired location. If {{{clamp_to_area}}} is nonzero ({{{true}}}), the viewport is moved so it is wholly inside the area. If it is zero ({{{false}}}), the behaviour depends on edge behaviour type in a as-yet-unspecified manner. For example, the scroller may move the viewport inside the area with a smooth transition.

=== scroller_set_velocity ===

{{{
void physics_scroller_set_velocity(PhysicsScroller scroller,
        int dx_pixels_per_second,
        int dy_pixels_per_second);
}}}

Sets the scroller's instantaneous velocity. Causes the area to start moving to the specified direction once time is advanced.

=== physics_scroller_advance ===

{{{
int physics_scroller_advance(PhysicsScroller scroller, unsigned long time_in_millis);
}}}

Causes the scroller to simulate motion forward in time for the specified time. Returns 0 if the scroller has come to a stop (and thus further calls to this function would not do anything) and a nonzero value if the viewport is still in motion.

The advance time can be any positive value. It can be 1 millisecond, it can be 10 000 years. It can be different on every call. The scroller takes care of numerical stability issues.
Line 28: Line 114:
'''Note''': the code samples in this section are descriptive. They may not line up exactly with the API described further in this document.

=== Creating a scroller ===

{{{
scroller = new_scroller(area_width, area_height, viewport_width, viewport_height);
}}}
'''Note''': the code samples in this section are descriptive, not normative.
Line 38: Line 118:
A user flicks on the view. This generates a flick event which gets sent to the application. It sets up the scroller with a call such as this.
Line 39: Line 121:
set_viewport_location(scroller, new_x, new_y);
set_velocity(scroller, vx, vy);
physics_scroller_set_velocity(scroller, to_pixels(flick_event->x_speed), to_pixels(flick_event->y_speed));
Line 45: Line 126:
The update is modeled after {{{glib}}}'s {{{g_timeout_add}}} functionality. The scroller is told to advance forward some amount of time. It calculates its new location and returns false if motion has finished and true if motion is still ongoing. The update is modeled after {{{glib}}}'s {{{g_timeout_add}}} functionality. The scroller is told to advance forward some amount of time. It calculates its new location and returns false if the viewport has stopped and true if motion is still ongoing.

A periodically called update function would usually do something like this.
Line 52: Line 135:
motion_remaining = scroller_step_forward_in_time(scroller, delta_t); motion_remaining = physics_scroller_advance(scroller, delta_t);
Line 60: Line 143:

=== Shutting down the scroller ===

{{{
scroller_delete(scroller);
}}}

----

''Old content starts here.''

== Units ==
Screen coordinates make more sense than real-world coordinates. If you have a 30" monitor next to a 24" monitor, both with equal resolutions, scrolling should move the content the same amount in screen coordinates. To facilitate this, units of pixels are used for distance in internal physics calculations. When real-world units are used, the library will convert them to pixels internally before performing computation.

A 24" 1080p monitor is a typical size and resolution for a desktop computer. This works out to 3614 px/m in the diagonal direction. We will assume square pixels. Thus, a reasonable conversion from meters to pixels is about 3.5 MP/m (where MP is a mega-pixel, or 1000 pixels).

== Models ==
=== Scrollview ===
This model may be used to facilitate calculations involving a scrollable view. The content is a rectangle, and the viewport is a rectangle whose area is a subset of the content. The content and viewport can be thought of as boxes. The viewport may be dragged within the content. The viewport cannot be dragged outside of the content.

While the user is interacting with the viewport by scrolling on an input device, the viewport follows the scroll motion. When the scrolling ceases, the viewport will continue moving with the velocity it has until friction or the content boundary force the viewport to a stop.

TODO: How can we add "bouncing" when the viewport hits the edge of the content area?

== API Types ==
=== uphys ===
Handle for uTouch-Physics context.

=== uphys_model ===
Enum specifying the model of the uphys context.

Values:
 * UPHYS_SCROLLVIEW

=== uphys_units ===
Enum specifying a unit type.

Values:
 * UPHYS_MILLIMETER
 * UPHYS_PIXEL

=== uphys_point ===
Holds a point in two axes.

=== uphys_size ===
Holds dimensions in two axes of an object.

== uphys Properties ==
=== error: int ===
Error code from last function call.

Set to 0 on success.

=== error_string: const char * ===
Error string from last function call.

Set to "no error" on success.

=== gravity: float ===
Defaults to 34.3 MP/s^2^. This is calculated by multiplying the standard earth gravity of 9.8 m/s^2^ by the standard conversion from meters to pixels of 3.5 MP/m.

=== mm_multiplier: float ===
Scalar multiplier applied to parameters given in UPHYS_MILLIMETER units. Defaults to value read from dconf settings repository if available, and then to ''TBD''.

This is roughly equivalent to the sensitivity of a trackpad.

=== mu: float ===
Friction constant. Defaults to value read from dconf settings repository if available, and then to ''TBD''.

=== simulation_step_size: float ===
Simulation step size in ms. Defaults to ''TBD'' (maybe 30 Hz).

See [[http://gafferongames.com/game-physics/fix-your-timestep/|here]] for more info.

=== (scrollview) viewport_origin: uphys_point ===
The origin point of the viewport of the scrollview.

The origin is defined as the point within the rectangle with the minimum values for both axes.

=== (scrollview) viewport_size: uphys_size ===
The size of the viewport of the scrollview.

=== (scrollview) content_origin: uphys_point ===
The origin point of the content of the scrollview.

The origin is defined as the point within the rectangle with the minimum values for both axes.

=== (scrollview) content_size: uphys_size ===
The size of the content of the scrollview.

== Functions ==
=== int uphys_set_property(uphys *uphys, const char *name, void *value) ===
Set a property value.

Returns 0 if successful, non-zero on error.

=== int uphys_get_property(const uphys *uphys, const char *name, void *value) ===
Get a property value.

Returns 0 if successful, non-zero on error.

=== void uphys_inject(uphys *uphys, GeisEvent event) ===
Inject an input event.

An error will occur if the time of the event is earlier than the time of a previous uphys_update() call.

=== void uphys_update(uphys *uphys, time_t *time) ===
Perform simulation of model up to the time given.

=== uphys *uphys_new(uphys_model model) ===
Create a new uphys context for a the model.

Returns a new context or NULL on error.

== API Use Case Example ==
Implementing smooth scrolling in a document viewer.

Setup steps:
 1. Create a uphys context using uphys_new(UPHYS_SCROLLVIEW)
 2. Set properties on the context for the origin and size of the content and viewport
 3. Create a Geis context to receive drag events
 4. Add a callback for uphys on the display refresh signal
  - Use OpenGL sync signal if available<<BR>>
  - Otherwise use a static timer at a reasonable interval (maybe 30 Hz)

In Geis drag callbacks:
 1. Call uphys_inject().
  - For touchpads and independent devices use UPHYS_MILLIMETER units<<BR>>
  - For touchscreens use UPHYS_PIXEL units

In the uphys callback:
 1. Call uphys_update() with the current time
 2. Get the new viewport origin using uphys_get_property()
 3. Redraw the scrollview

Library: Physics
Language: C (Maybe built on top of GLib? Qt already depends on GLib by default if available.)
Dependencies: possibly a physics engine such as Chipmunk

Quick overview

This provides a scrollable view. The content is a rectangle, and the viewport is a rectangle whose area is a subset of the content. The content and viewport can be thought of as boxes. The viewport may be dragged within the content.

While the user is interacting with the viewport by scrolling on an input device, the viewport follows the scroll motion. When the scrolling ceases, the viewport will continue moving. As it moves, friction will slow it down until it finally stops.

When the viewport hits the edge of the area, something happens. By default all motion of the viewport is stopped. The library may provide alternative behaviour, such as "bouncing", sliding along the edge and so on.

User stories

The physics library should do following:

  • provide a viewport that can be scrolled and which slows down according to physical laws
  • provide several different behaviours when the area hits the edge: stopping, bouncing like Android's list widget etc
  • support setting friction and other such variables
  • provide (possibly system global) default values for the above
  • allow changing the scrolling speed and direction even if there is a scroll event ongoing
  • the physical simulation is not tied to the system clock
  • the source of motion can be a touch gesture, mouse flick, accelerometer, or any other device capable of providing velocity information

Functionality it will not have:

  • it does not convert coordinate types (e.g. from touchpad device coordinates to pixels), it will only work in pixel units
  • it does not support on-the-fly area or viewport resizing while preserving motion, resizes reset all existing state
  • it is not thread-safe

API objects

PhysicsScroller

An opaque type for the scroller area.

API Functions

physics_scroller_new

PhysicsScroller physics_scroller_new(
        int area_width,
        int area_height,
        int viewport_width,
        int viewport_height)

Creates a new scroller with the given dimensions. Returns NULL on failure, such as having a viewport that is larger than the viewable area.

physics_scroller_delete

void physics_scroller_delete(PhysicsScroller scroller);

Deletes a scroller.

physics_scroller_reset

int PHYSICS_API physics_scroller_reset(PhysicsScroller scroller,
        int area_width,
        int area_height,
        int viewport_width,
        int viewport_height);

Resets the scroller with the new given dimensions. If dimensions are invalid, the state of the object is not changed. If they are valid, all old state is permanently destroyed. Using this is equivalent to deleting the old scroller and creating a new one with the given arguments.

scroller_get_viewport_location

void physics_scroller_get_viewport_location(PhysicsScroller scroller, int *x, int *y);

Get the location of the top left corner of the viewport.

set_scroller_set_viewport_location

void physics_scroller_set_viewport_location(PhysicsScroller scroller, int x, int y, int clamp_to_area);

Move viewport's top left corner to the desired location. If clamp_to_area is nonzero (true), the viewport is moved so it is wholly inside the area. If it is zero (false), the behaviour depends on edge behaviour type in a as-yet-unspecified manner. For example, the scroller may move the viewport inside the area with a smooth transition.

scroller_set_velocity

void physics_scroller_set_velocity(PhysicsScroller scroller,
        int dx_pixels_per_second,
        int dy_pixels_per_second);

Sets the scroller's instantaneous velocity. Causes the area to start moving to the specified direction once time is advanced.

physics_scroller_advance

int physics_scroller_advance(PhysicsScroller scroller, unsigned long time_in_millis);

Causes the scroller to simulate motion forward in time for the specified time. Returns 0 if the scroller has come to a stop (and thus further calls to this function would not do anything) and a nonzero value if the viewport is still in motion.

The advance time can be any positive value. It can be 1 millisecond, it can be 10 000 years. It can be different on every call. The scroller takes care of numerical stability issues.

Using the library

Note: the code samples in this section are descriptive, not normative.

Starting scroll motion

A user flicks on the view. This generates a flick event which gets sent to the application. It sets up the scroller with a call such as this.

physics_scroller_set_velocity(scroller, to_pixels(flick_event->x_speed), to_pixels(flick_event->y_speed));

Updating the scroll

The update is modeled after glib's g_timeout_add functionality. The scroller is told to advance forward some amount of time. It calculates its new location and returns false if the viewport has stopped and true if motion is still ongoing.

A periodically called update function would usually do something like this.

long now_time = get_time();
long delta_t = now_time - previous_time;
bool motion_remaining;

motion_remaining = physics_scroller_advance(scroller, delta_t);
previous_time = now_time;
redraw_canvas_and_other_such_things();
if(motion_remaining)
  more_to_come();
else
  motion_has_finished();

Multitouch/Physics (last edited 2012-06-14 19:28:34 by c-67-170-185-42)