Skip to content

API reference

NOTE The docstrings were generated by OpenAI ChatGPT then edited by me

pointer_brakes.PointerMotionSim

A simulation of pointer motion, including touch-driven and free rolling motion.

Attributes:

Name Type Description
a_braking float

The magnitude of acceleration caused by pointer brakes.

Example
sim_instance = PointerMotionSim(2.0)
Source code in src/pointer_brakes/_sim.py
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
class PointerMotionSim:
    """A simulation of pointer motion, including touch-driven and free rolling motion.

    Attributes:
        a_braking (float): The magnitude of acceleration caused by pointer brakes.

    Example:
        ```python
        sim_instance = PointerMotionSim(2.0)
        ```
    """

    # magnitude of acceleration due to braking (using during pointer rolling motion)
    a_braking: float

    # simulation state
    _state: State
    _last_state: State

    # initial velocity (using during pointer rolling motion)
    _v0: Vec2D | None

    def __init__(self, a_braking: float) -> None:
        self.a_braking = a_braking
        self._state = State()
        self._last_state = State()
        self._v0 = None

    def tick(self, timestamp: int, touch_pos: tuple[int, int] | None = None) -> None:
        """Update the state of the PointerMotionSim

        This method updates the simulation state of the PointerMotionSim instance. When
        touch position is provided the pointer will be moved.  When the touch position,
        inevitably, goes idle then the simulation will continue moving until the
        pointer comes to rest due to braking.

        Args:
            timestamp (int): The timestamp at which the update occurs.
            touch_pos (tuple[int, int] | None): The current touch position as a tuple
                (x, y), or None if touch is idle.

        Example usage:
        ```python
        a_brakes = 1
        sim_instance = PointerMotionSim(a_brakes)
        current_timestamp = time.monotonic_ns()
        current_touch_pos = (50, 50)
        sim_instance.tick(current_timestamp, current_touch_pos)
        ```
        """
        # if touch is idle and motion is stopped then do nothing
        if not touch_pos and not self.velocity:
            # ensure state is cleared to reflect idleness
            if self._state.timestamp:
                self.stop_motion()
            return

        # if touch is idle then update initial velocity
        if not touch_pos:
            self._v0 = self.velocity

        # reject zero-time ticks
        if self._state.timestamp == timestamp:
            return

        # update simulation state
        self._last_state = self._state.copy()
        self._state = EMPTY_STATE.copy()
        self._state.timestamp = timestamp

        # update touch data if present
        self._state.touch_pos = touch_pos if touch_pos else None

    @property
    def delta_time(self) -> int:
        """The time difference between the current state and the last state.

        This property calculates the delta time, representing the time elapsed between
        the current state and the last state in the simulation. It ensures that both
        timestamps are available; otherwise, it raises a DeltaTimeInvalidError.

        Returns:
            The time difference (delta time) between the current and last state
                timestamps.

        Raises:
            DeltaTimeInvalidError: If either the current state timestamp or the last state
                timestamp is not set.

        Example:
            ```python
            a_brakes = 1
            sim_instance = PointerMotionSim(a_brakes)
            sim_instance.tick(time.monotonic_ns(), (50, 50))
            sim_instance.tick(time.monotonic_ns(), (60, -30))
            time_difference = sim_instance.delta_time
            ```
        """
        if not self._state.timestamp or not self._last_state.timestamp:
            raise DeltaTimeInvalidError(self._last_state.timestamp, self._state.timestamp)

        return self._state.timestamp - self._last_state.timestamp

    @property
    def velocity(self) -> Vec2D | None:
        """The current pointer velocity.

        This property handles various scenarios to determine the current velocity of
        the simulation. If there's touch-driven motion, it calculates velocity based on
        the change in position over time. For pointer rolling motion, it uses standard
        accelerated motion calculations where acceleration direction is opposite to
        velocity with magnitude of acceleration due to braking.

        Returns:
            The calculated velocity as a 2D Vector instance or None if
                motion is stopped.

        Raises:
            DeltaPositionInvalidError: The change in is not valid during touch-driven
                motion.
            VelocityInvalidError: The p method encounters an unexpected condition.

        Example:
            ```python
            a_brakes = 1
            sim_instance = PointerMotionSim(a_brakes)
            sim_instance.tick(time.monotonic_ns(), (30, 12))
            sim_instance.tick(time.monotonic_ns(), (104, 23))
            current_velocity = sim_instance.velocity
            ```
        """
        # if we have blank timestamps then motion is stopped
        if not self._last_state.timestamp and not self._state.timestamp:
            return None

        # handle transition from idle to touch motion
        if self._state.touch_pos and not self._last_state.touch_pos:
            return None

        # handle touch-driven motion
        if self._last_state.touch_pos and self._state.touch_pos:
            if not self.delta_position:
                raise DeltaPositionInvalidError(self._last_state.touch_pos, self._state.touch_pos)

            return self.delta_position / self.delta_time

        # handle pointer rolling motion
        if self._v0 and not self._state.touch_pos:
            # use standard accelerated motion calculation
            v_magnitude = self._v0.len() - self.a_braking * self.delta_time

            # if braking would reduce the velocity to 0 or less then stop motion
            if v_magnitude <= 0:
                self.stop_motion()
                return None

            return self._v0.dir() * v_magnitude

        raise VelocityInvalidError

    def stop_motion(self) -> None:
        """Stop all motion in the simulation

        This method resets the simulation state and initial velocity to indicate that
        all motion has stopped.

        Example usage:
            ```python
            a_brakes = 1
            sim_instance = PointerMotionSim(a_brakes)
            sim_instance.tick(time.monotonic_ns(), (15, -75))
            sim_instance.tick(time.monotonic_ns(), (-83, 11))
            sim_instance.stop_motion()
            ```
        """
        # reset the state to empty to indicate all motion is stopped
        self._last_state = EMPTY_STATE.copy()
        self._state = EMPTY_STATE.copy()
        self._v0 = None

    @property
    def delta_position(self) -> Vec2D | None:
        """The change in position between the last two state ticks.

        For touch-driven motion, it simply calculates the change in position between
        the current and last touch positions. For pointer rolling motion, it uses
        standard accelerated motion calculations where acceleration direction is
        opposite to velocity with magnitude of acceleration due to braking.

        Returns:
            The 2D vector representing the change in position from last
                state tick to the current state tick.

        Raises:
            DeltaPositionInvalidError: If the delta position cannot be determined.

        Example:
            ```python
            a_brakes = 1
            sim_instance = PointerMotionSim(a_brakes)
            sim_instance.tick(time.monotonic_ns(), (-52, -5))
            sim_instance.tick(time.monotonic_ns(), (21, -92))
            change_in_position = sim_instance.delta_position
            ```
        """
        # if theres no touch data and no velocity we're not moving
        if (not self._state.touch_pos or not self._last_state.touch_pos) and not self.velocity:
            return None

        # handle touch-driven motion
        if self._state.touch_pos and self._last_state.touch_pos:
            return Vec2D(
                self._state.touch_pos[0] - self._last_state.touch_pos[0],
                self._state.touch_pos[1] - self._last_state.touch_pos[1],
            )

        # handle pointer rolling motion; use standard accelerated motion calculation
        if not self._v0:
            raise DeltaPositionInvalidError

        delta_pos_mag = self._v0.len() * self.delta_time - self.a_braking / 2 * self.delta_time**2
        return self._v0.dir() * delta_pos_mag

delta_position: Vec2D | None property

The change in position between the last two state ticks.

For touch-driven motion, it simply calculates the change in position between the current and last touch positions. For pointer rolling motion, it uses standard accelerated motion calculations where acceleration direction is opposite to velocity with magnitude of acceleration due to braking.

Returns:

Type Description
Vec2D | None

The 2D vector representing the change in position from last state tick to the current state tick.

Raises:

Type Description
DeltaPositionInvalidError

If the delta position cannot be determined.

Example
a_brakes = 1
sim_instance = PointerMotionSim(a_brakes)
sim_instance.tick(time.monotonic_ns(), (-52, -5))
sim_instance.tick(time.monotonic_ns(), (21, -92))
change_in_position = sim_instance.delta_position

delta_time: int property

The time difference between the current state and the last state.

This property calculates the delta time, representing the time elapsed between the current state and the last state in the simulation. It ensures that both timestamps are available; otherwise, it raises a DeltaTimeInvalidError.

Returns:

Type Description
int

The time difference (delta time) between the current and last state timestamps.

Raises:

Type Description
DeltaTimeInvalidError

If either the current state timestamp or the last state timestamp is not set.

Example
a_brakes = 1
sim_instance = PointerMotionSim(a_brakes)
sim_instance.tick(time.monotonic_ns(), (50, 50))
sim_instance.tick(time.monotonic_ns(), (60, -30))
time_difference = sim_instance.delta_time

velocity: Vec2D | None property

The current pointer velocity.

This property handles various scenarios to determine the current velocity of the simulation. If there's touch-driven motion, it calculates velocity based on the change in position over time. For pointer rolling motion, it uses standard accelerated motion calculations where acceleration direction is opposite to velocity with magnitude of acceleration due to braking.

Returns:

Type Description
Vec2D | None

The calculated velocity as a 2D Vector instance or None if motion is stopped.

Raises:

Type Description
DeltaPositionInvalidError

The change in is not valid during touch-driven motion.

VelocityInvalidError

The p method encounters an unexpected condition.

Example
a_brakes = 1
sim_instance = PointerMotionSim(a_brakes)
sim_instance.tick(time.monotonic_ns(), (30, 12))
sim_instance.tick(time.monotonic_ns(), (104, 23))
current_velocity = sim_instance.velocity

stop_motion()

Stop all motion in the simulation

This method resets the simulation state and initial velocity to indicate that all motion has stopped.

Example usage
a_brakes = 1
sim_instance = PointerMotionSim(a_brakes)
sim_instance.tick(time.monotonic_ns(), (15, -75))
sim_instance.tick(time.monotonic_ns(), (-83, 11))
sim_instance.stop_motion()
Source code in src/pointer_brakes/_sim.py
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def stop_motion(self) -> None:
    """Stop all motion in the simulation

    This method resets the simulation state and initial velocity to indicate that
    all motion has stopped.

    Example usage:
        ```python
        a_brakes = 1
        sim_instance = PointerMotionSim(a_brakes)
        sim_instance.tick(time.monotonic_ns(), (15, -75))
        sim_instance.tick(time.monotonic_ns(), (-83, 11))
        sim_instance.stop_motion()
        ```
    """
    # reset the state to empty to indicate all motion is stopped
    self._last_state = EMPTY_STATE.copy()
    self._state = EMPTY_STATE.copy()
    self._v0 = None

tick(timestamp, touch_pos=None)

Update the state of the PointerMotionSim

This method updates the simulation state of the PointerMotionSim instance. When touch position is provided the pointer will be moved. When the touch position, inevitably, goes idle then the simulation will continue moving until the pointer comes to rest due to braking.

Parameters:

Name Type Description Default
timestamp int

The timestamp at which the update occurs.

required
touch_pos tuple[int, int] | None

The current touch position as a tuple (x, y), or None if touch is idle.

None

Example usage:

a_brakes = 1
sim_instance = PointerMotionSim(a_brakes)
current_timestamp = time.monotonic_ns()
current_touch_pos = (50, 50)
sim_instance.tick(current_timestamp, current_touch_pos)
Source code in src/pointer_brakes/_sim.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def tick(self, timestamp: int, touch_pos: tuple[int, int] | None = None) -> None:
    """Update the state of the PointerMotionSim

    This method updates the simulation state of the PointerMotionSim instance. When
    touch position is provided the pointer will be moved.  When the touch position,
    inevitably, goes idle then the simulation will continue moving until the
    pointer comes to rest due to braking.

    Args:
        timestamp (int): The timestamp at which the update occurs.
        touch_pos (tuple[int, int] | None): The current touch position as a tuple
            (x, y), or None if touch is idle.

    Example usage:
    ```python
    a_brakes = 1
    sim_instance = PointerMotionSim(a_brakes)
    current_timestamp = time.monotonic_ns()
    current_touch_pos = (50, 50)
    sim_instance.tick(current_timestamp, current_touch_pos)
    ```
    """
    # if touch is idle and motion is stopped then do nothing
    if not touch_pos and not self.velocity:
        # ensure state is cleared to reflect idleness
        if self._state.timestamp:
            self.stop_motion()
        return

    # if touch is idle then update initial velocity
    if not touch_pos:
        self._v0 = self.velocity

    # reject zero-time ticks
    if self._state.timestamp == timestamp:
        return

    # update simulation state
    self._last_state = self._state.copy()
    self._state = EMPTY_STATE.copy()
    self._state.timestamp = timestamp

    # update touch data if present
    self._state.touch_pos = touch_pos if touch_pos else None