Rework stage line animations

This commit is contained in:
Ruben van de Ven 2025-10-28 16:03:37 +01:00
parent bcb2369046
commit c4cdda64e1
5 changed files with 2020 additions and 1069 deletions

View file

@ -37,8 +37,15 @@ class CounterFpsSender():
self.is_finished = threading.Event() self.is_finished = threading.Event()
def tick(self): def tick(self):
"""
returns dt since previous tock
"""
self.iterations += 1 self.iterations += 1
self.snapshot() self.snapshot()
if len(self.tocs) > 1:
return float(self.tocs[-1][0] - self.tocs[-2][0])
else:
return 0.
def snapshot(self): def snapshot(self):
self.tocs.append((time.perf_counter(), self.iterations)) self.tocs.append((time.perf_counter(), self.iterations))

View file

@ -1,20 +1,33 @@
from __future__ import annotations from __future__ import annotations
from abc import ABC, abstractmethod
import copy
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum, IntEnum from enum import Enum, IntEnum
import math import math
from pathlib import Path from pathlib import Path
from typing import Dict, List, Tuple import time
from typing import Dict, List, Optional, Tuple
import numpy as np import numpy as np
import shapely
import shapely.ops
from simplification.cutil import simplify_coords_idx, simplify_coords_vw_idx from simplification.cutil import simplify_coords_idx, simplify_coords_vw_idx
import svgpathtools import svgpathtools
from noise import snoise2
from trap.utils import exponentialDecayRounded, inv_lerp
""" """
See [notebook](../test_path_transforms.ipynb) for examples See [notebook](../test_path_transforms.ipynb) for examples
""" """
RenderablePosition = Tuple[float,float] RenderablePosition = Tuple[float,float]
Coordinate = Tuple[float, float]
DeltaT = float # delta_t in seconds
class CoordinateSpace(IntEnum): class CoordinateSpace(IntEnum):
CAMERA = 1 CAMERA = 1
@ -35,6 +48,10 @@ class SrgbaColor():
def as_faded(self, alpha: float) -> SrgbaColor: def as_faded(self, alpha: float) -> SrgbaColor:
return SrgbaColor(self.red, self.green, self.blue, self.alpha * alpha) return SrgbaColor(self.red, self.green, self.blue, self.alpha * alpha)
def __eq__(self, other):
return math.isclose(self.red, other.red) and math.isclose(self.green, other.green) and math.isclose(self.blue, other.blue) and math.isclose(self.alpha, other.alpha)
@dataclass @dataclass
class RenderablePoint(): class RenderablePoint():
position: RenderablePosition position: RenderablePosition
@ -63,6 +80,9 @@ class SimplifyMethod(Enum):
class RenderableLine(): class RenderableLine():
points: List[RenderablePoint] points: List[RenderablePoint]
def __len__(self):
return len(self.points)
def as_simplified(self, method: SimplifyMethod = SimplifyMethod.RDP, factor = SIMPLIFY_FACTOR_RDP): def as_simplified(self, method: SimplifyMethod = SimplifyMethod.RDP, factor = SIMPLIFY_FACTOR_RDP):
linestring = [p.position for p in self.points] linestring = [p.position for p in self.points]
if method == SimplifyMethod.RDP: if method == SimplifyMethod.RDP:
@ -72,6 +92,32 @@ class RenderableLine():
points = [self.points[i] for i in indexes] points = [self.points[i] for i in indexes]
return RenderableLine(points) return RenderableLine(points)
def as_linestring(self):
return shapely.geometry.LineString([p.position for p in self.points])
@classmethod
def from_multilinestring(cls, mls: shapely.geometry.MultiLineString, color: SrgbaColor) -> RenderableLine:
points: List[RenderablePoint] = []
for i, line in enumerate(mls.geoms):
if len(line.coords) < 2:
continue
is_first = i == 0
is_last = i == (len(mls.geoms) - 1)
# blank point
if not is_first:
points.append(RenderablePoint(line.coords[0], color.as_faded(0)))
points.extend(
[RenderablePoint(pos, color) for pos in line.coords]
)
# blank point
if not is_last:
points.append(RenderablePoint(line.coords[-1], color.as_faded(0)))
return RenderableLine(points)
@dataclass @dataclass
class RenderableLines(): class RenderableLines():
@ -137,7 +183,7 @@ def cross_points(cx, cy, r, c: SrgbaColor):
return [path, path2] return [path, path2]
def load_lines_from_svg(svg_path: Path, scale: float, c: SrgbaColor) -> List[RenderableLine]: def load_lines_from_svg(svg_path: Path, scale: float, c: SrgbaColor, max_len = 0.3) -> List[RenderableLine]:
lines = [] lines = []
paths, attributes = svgpathtools.svg2paths(svg_path) paths, attributes = svgpathtools.svg2paths(svg_path)
@ -181,12 +227,641 @@ def load_lines_from_svg(svg_path: Path, scale: float, c: SrgbaColor) -> List[Ren
# Create LineString from coordinates # Create LineString from coordinates
if len(coordinates) > 1: if len(coordinates) > 1:
coordinates = (np.array(coordinates) / scale).tolist() coordinates = (np.array(coordinates) / scale).tolist()
points = [RenderablePoint(pos, c) for pos in coordinates]
# cut into smaller segments, so the laser corrections apply nicely
linestring = shapely.geometry.LineString(coordinates)
linestring = shapely.segmentize(linestring, max_len)
points = [RenderablePoint(pos, c) for pos in linestring.coords]
line = RenderableLine(points) line = RenderableLine(points)
lines.append(line) lines.append(line)
# linestring = shapely.geometry.LineString(coordinates)
# linestrings.append(linestring) # linestrings.append(linestring)
except Exception as e: except Exception as e:
print(f"Error processing path: {e}") print(f"Error processing path: {e}")
return lines return lines
class LineGenerator(ABC):
@abstractmethod
def update_drawn_positions(self, dt: DeltaT):
pass
def extend(self, *args):
self.points.extend(args)
def as_renderable(self, dt: DeltaT, color: SrgbaColor) -> RenderableLines:
points = [RenderablePoint(p, color) for p in self.get_drawn_points(dt)]
lines = [RenderableLine(points)]
return RenderableLines(lines)
class StaticLine(LineGenerator):
def __init__(self, points: Optional[List[Coordinate]] = None):
self.target_points: List[Coordinate] = points if points is not None else []
self._drawn_points = points
def get_drawn_points(self, dt):
return self._drawn_points
def update_drawn_positions(self, dt):
# nothing to update
pass
class AppendableLine(LineGenerator):
"""
A line generator that allows for points to be added over time.
Simply use `line.points.extend([p1, p2])` and it will animate in
"""
def __init__(self, points: Optional[List[Coordinate]] = None, draw_decay_speed = 25.):
self.target_points: List[Coordinate] = points if points is not None else [] # when providing [] as default, it messes up instancing by reusing the same list
self.drawn_points = []
self.ready = len(self.target_points) == 0
self.draw_decay_speed = draw_decay_speed
def nr_of_passed_points(self):
"""The number of points passed in the animation"""
return len(self.drawn_points) - 1
def update_drawn_positions(self, dt: DeltaT):
if len(self.target_points) == 0:
# nothing to draw yet
return
# self._drawn_points = self.points
if len(self.drawn_points) == 0:
# create origin
self.drawn_points.append(self.target_points[0])
# and drawing head
self.drawn_points.append(self.target_points[0])
idx = len(self.drawn_points) - 1
target = self.target_points[idx]
if np.isclose(self.drawn_points[-1], target, atol=.05).all():
# TODO: might want to migrate to np.isclose()
if len(self.drawn_points) == len(self.target_points):
self.ready = True
return # done until a new point is added
# add new point as drawing head
self.drawn_points.append(self.drawn_points[-1])
self.ready = False
x = exponentialDecayRounded(self.drawn_points[-1][0], target[0], self.draw_decay_speed, dt, .05)
y = exponentialDecayRounded(self.drawn_points[-1][1], target[1], self.draw_decay_speed, dt, .05)
self.drawn_points[-1] = (float(x), float(y))
class ProceduralChain(LineGenerator):
"""A line that can be 'dragged' to a target. In which
it disappears."""
MOVE_DECAY_SPEED = 80 # speed at which the drawing head should approach the next point
VELOCITY_DAMPING = 10
VELOCITY_FACTOR = 2
link_size = .1 # 10cm
# angle_constraint = 5
def __init__(self, joints: List[Coordinate], use_velocity = False):
self.joints: List[Coordinate] = joints
self.target: Coordinate = joints[-1]
self.ready = False
self.move_decay_speed = self.MOVE_DECAY_SPEED
self.use_velocity = use_velocity
if self.use_velocity:
if len(self.joints) > 1:
self.v = np.array(self.joints[-2]) - np.array(self.joints[-1])
self.v /= np.linalg.norm(self.v) / 10
else:
self.v = np.array([0,0])
@classmethod
def from_appendable_line(cls, al: AppendableLine) -> ProceduralChain:
# TODO: create more segments:
# last added points becomes the head of the chain
points = list(reversed(al.target_points))
linestring = shapely.LineString(points)
linestring = linestring.segmentize(cls.link_size)
joints = list(linestring.coords)
return cls(joints)
def update_drawn_positions(self, dt: DeltaT):
if self.ready:
return
# direction = np.array(self.joints[-1] - self.target)
# TODO: check self.joints empty, and stop then
if self.use_velocity:
vx = exponentialDecayRounded(self.v[0], self.target[0] - self.joints[0][0], self.VELOCITY_DAMPING, dt, .05)
vy = exponentialDecayRounded(self.v[1], self.target[1] - self.joints[0][1], self.VELOCITY_DAMPING, dt, .05)
self.v = np.array([vx, vy])
self.joints[0] = (float(self.joints[0][0] + self.v[0] * dt * self.VELOCITY_FACTOR), float(self.joints[0][1] + self.v[1] * dt * self.VELOCITY_FACTOR))
else:
x = exponentialDecayRounded(self.joints[0][0], self.target[0], self.move_decay_speed, dt, .05)
y = exponentialDecayRounded(self.joints[0][1], self.target[1], self.move_decay_speed, dt, .05)
self.joints[0] = (float(x), float(y))
# Loop inspired by: https://github.com/argonautcode/animal-proc-anim/blob/main/Chain.pde
# see that code for angle constrains.
for i, (joint, prev_joint) in enumerate(zip(self.joints[1:], self.joints), start=1):
diff = np.array(prev_joint) - np.array(joint)
direction = diff / np.linalg.norm(diff)
self.joints[i] = prev_joint - direction * self.link_size
if np.isclose(self.joints[0], self.target, atol=.05).all():
# self.ready = True
# TODO: smooth transition instead of cutting off
self.joints.pop(0)
if len(self.joints) == 0:
self.ready = True
self._drawn_points = self.joints
# class AnimatedLineMask():
# """
# given a line, create an animation by masking/skewing it
# """
# def __init__(self, line: LineGenerator):
# self.line = line
# self.start_at = time.time()
# @abstractmethod
# def apply(self, dt: DeltaT, color: SrgbaColor) -> RenderableLines:
# pass
# class FadeoutMask(AnimatedLineMask):
# def __init__(self, line: LineGenerator, max_len = 200, fade_len = 20):
# self.line = LineGenerator
# def apply(self, lines: RenderableLines, factor):
# for line in lines.lines:
# for point in line.points:
# point.color = point.color.as_faded(factor)
# class FadeoutTailMask(AnimatedLineMask):
# def __init__(self, line: LineGenerator, max_len = 200, fade_len = 20):
# self.line = LineGenerator
class StaticLine():
"""
Always a continuous line of monotonous color
"""
def __init__(self, points = None, color: SrgbaColor = None):
self.points: List[Coordinate] = points if points is not None else [] # when providing [] as default, it messes up instancing by reusing the same list
self.color = color if color else SrgbaColor(0,0,0,1)
def get_positions(self):
return self.points
def __len__(self):
return len(self.points)
def extend(self, coords: List[Coordinate]):
self.points.extend(coords)
def as_renderable_line(self, dt: DeltaT) -> RenderableLine:
points = [RenderablePoint(p, self.color) for p in self.points]
line = RenderableLine(points)
return line
def is_ready(self):
return True
def start(self):
return True
class LineAnimator(StaticLine):
"""
Animate a line, can/should be nested. Always derives from a StaticLine
"""
def __init__(self, target_line: Optional[StaticLine] = None):
# print(self, target_line, bool(target_line), target_line is not None)
self.target = target_line if target_line is not None else StaticLine()
self.ready = len(self.target) == 0
self.start_t = time.time()
self.skip = False
def extend(self, coords):
return self.target.extend(coords)
def __len__(self):
return len(self.target)
def as_renderable_line(self, dt: DeltaT) -> RenderableLine:
target = self.target.as_renderable_line(dt)
if self.skip:
return target
return self.apply(target, dt)
def apply(self, target_line: RenderableLine, dt: DeltaT) -> RenderableLine:
raise RuntimeError("Not Implemented")
def is_ready(self):
return self.ready and self.target.is_ready()
def start(self):
self.target.start()
self.start_t = time.time()
return True
def running_for(self):
if self.start:
return time.time() - self.start_t
return 0.
class AppendableLineAnimator(LineAnimator):
"""
Animate a line, can/should be nested. Always derives from a StaticLine
"""
def __init__(self, target_line = None, draw_decay_speed: float = 25., draw_decay_speed_init = 1000, transition_in_on_init = True):
super().__init__(target_line)
self.drawn_points: List[RenderablePoint] = []
self.draw_decay_speed = draw_decay_speed
self.draw_decay_speed_init = draw_decay_speed_init # faster drawing until catching up
self.transition_in_on_init = transition_in_on_init # when False, immediately draw the full line
self.is_init = False
def apply(self, target_line, dt: DeltaT) -> RenderableLine:
if len(target_line) == 0:
# nothing to draw yet
return RenderableLine()
if len(self.drawn_points) == 0:
if self.transition_in_on_init:
# create origin
self.drawn_points.append(target_line.points[0])
else:
self.drawn_points.extend(target_line.points[:-1])
# and a copy as drawing head
self.drawn_points.append(copy.deepcopy(self.drawn_points[-1]))
idx = len(self.drawn_points) - 1
target = target_line.points[idx]
if np.isclose(self.drawn_points[-1].position, target.position, atol=.05).all():
# TODO: might want to migrate to np.isclose()
if len(self.drawn_points) == len(target_line):
self.ready = True
self.is_init = True
return RenderableLine(self.drawn_points) # done until a new point is added
# add new point as drawing head, by duplicating the current point
self.drawn_points.append(copy.deepcopy(self.drawn_points[-1]) )
self.drawn_points[-1].color = target.color # set to color of the next point
self.ready = False
decay_speed = self.draw_decay_speed if self.is_init else self.draw_decay_speed_init
x = exponentialDecayRounded(self.drawn_points[-1].position[0], target.position[0], decay_speed, dt, .05)
y = exponentialDecayRounded(self.drawn_points[-1].position[1], target.position[1], decay_speed, dt, .05)
# handle color gradient:
# if self.drawn_points[-1].color != target.color:
# # t
# tx = inv_lerp(self.drawn_points[-2].position[0], target.position[0], x)
# ty = inv_lerp(self.drawn_points[-2].position[1], target.position[1], y)
# t = (tx+ty) / 2
# TODO: this should set color to intermediate color, but this is hardly used, so skip it for sake of speed
self.drawn_points[-1].position = (float(x), float(y))
return RenderableLine(self.drawn_points)
class FadeOutLine(LineAnimator):
"""
Fade the line providing an alpha, 1 by default
"""
def __init__(self, target_line = None):
super().__init__(target_line)
self.alpha = 1
self.ready = True # filter holds no state, so always ready
def set_alpha(self, alpha: float):
self.alpha = min(1, max(0, alpha))
def apply(self, target_line: RenderableLine, dt: DeltaT) -> RenderableLine:
for point in target_line.points:
point.color = point.color.as_faded(self.alpha)
return target_line
class CropLine(LineAnimator):
"""
Crop the line at a max nr of points (thus not actual lenght!)
Keeps the tail, removes the start
"""
def __init__(self, target_line = None, max_points = 200):
super().__init__(target_line)
self.max_points = max_points
self.ready = True # static filter, always ready
def apply(self, target_line: RenderableLine, dt: DeltaT) -> RenderableLine:
target_line.points = target_line.points[-1 * self.max_points:]
return target_line
class FadedTailLine(LineAnimator):
"""
Fade the tail of the line, proving a max length
"""
def __init__(self, target_line = None, fade_after: int = 170, fade_frames: int = 30):
super().__init__(target_line)
self.fade_after = fade_after
self.fade_frames = fade_frames
self.frame_offset = 0
def set_frame_offset(self, frame_offset: int):
"""
This can be used to provide an additional offset between fade_frames and the lenght of
the actual track. Can e.g. be used to process the diff between last tracking time and
actual wall time (in case a track is lost, it keeps getting 'eaten')
"""
self.frame_offset = frame_offset
def apply(self, target_line: RenderableLine, dt: DeltaT) -> RenderableLine:
l = len(target_line.points)
points = []
for i, point in enumerate(target_line.points):
reverse_i = l - i
fade_i = reverse_i - self.fade_after + self.frame_offset # -90
fade_i /= self.fade_frames # / 10
# fade_i = np.clip(fade_i, 0, 1)
alpha = 1 - fade_i
if alpha >= 0:
alpha = min(1, alpha)
point.color = point.color.as_faded(alpha)
points.append(point)
self.ready = not bool(len(points))
return RenderableLine(points)
class NoiseLine(LineAnimator):
"""
Apply animated noise to line normals
"""
def __init__(self, target_line = None, amplitude=.3, frequency=.02, t_factor = 1.0):
super().__init__(target_line)
self.amplitude = amplitude
self.frequency = frequency
self.t_factor = t_factor
self.t = 0
self.ready = True # static filter, always ready
# Gemma3:27b prompt: "python. Given a list of coordinates, that describes a line: `drawable_points: List[Tuple[float,float]]` apply perlin noise over the normal of the line, that changes over time `dt`."
@classmethod
def apply_perlin_noise_to_line_normal(cls, drawable_points: np.ndarray, t: float, amplitude: float = 1.0, frequency: float = 1.0, fade_over_n_points = 8) -> List[RenderablePosition]:
"""
Applies Perlin noise to the normals of a line described by a list of coordinates, changing over time.
Args:
drawable_points: A list of (x, y) tuples representing the points of the line.
dt: The time delta, used to animate the Perlin noise.
amplitude: The strength of the Perlin noise effect.
frequency: The frequency of the Perlin noise (how many waves per unit).
Returns:
A new list of (x, y) tuples representing the line with Perlin noise applied to the normals. If drawable_points
has fewer than 2 points, it returns the original list unchanged.
Raises:
TypeError: If drawable_points is not a list or dt is not a float.
ValueError: If the input points are not tuples of length 2.
"""
if len(drawable_points) < 2:
return drawable_points # Nothing to do with fewer than 2 points
# for point in drawable_points:
# if not isinstance(point, tuple) or len(point) != 2:
# raise ValueError("Each point in drawable_points must be a tuple of length 2.")
# noise = PerlinNoise(octaves=4) # You can adjust octaves for different noise patterns
new_points = []
for i in range(len(drawable_points)):
x, y = copy.deepcopy(drawable_points[i])
# Calculate the normal vector. We'll approximate it using the previous and next points.
if i == 0:
# For the first point, use the next point to estimate the normal
next_x, next_y = drawable_points[i + 1]
normal_x = next_y - y
normal_y = -(next_x - x)
elif i == len(drawable_points) - 1:
# For the last point, use the previous point
prev_x, prev_y = drawable_points[i - 1]
normal_x = y - prev_y
normal_y = -(x - prev_x)
else:
prev_x, prev_y = drawable_points[i - 1]
next_x, next_y = drawable_points[i + 1]
normal_x = next_y - prev_y
normal_y = -(next_x - prev_x)
# Normalize the normal vector
norm = np.sqrt(normal_x**2 + normal_y**2)
if norm > 0:
normal_x /= norm
normal_y /= norm
# Apply Perlin noise to the normal
# noise_x = noise([x * frequency, (y + dt) * frequency]) * amplitude * normal_x
# noise_y = noise([x * frequency, (y + dt) * frequency]) * amplitude * normal_y
noise = snoise2(i * frequency, t % 1000, octaves=4)
use_amp = amplitude
if fade_over_n_points > 0:
rev_step = len(drawable_points) - i
amp_factor = rev_step / fade_over_n_points
if amp_factor < 1:
use_amp *= amp_factor
noise_x = noise * use_amp * normal_x
noise_y = noise * use_amp * normal_y
# print(noise_x, noise_y, dt, frequency, i, dt, snoise2(i * frequency, dt % 1000, octaves=4))
# Add the noise to the point's coordinates
new_x = x + noise_x
new_y = y + noise_y
new_points.append((new_x, new_y))
return new_points
def apply(self, target_line: RenderableLine, dt: DeltaT) -> RenderableLine:
self.t += dt
if self.amplitude < 0.01:
return target_line
positions = np.array([p.position for p in target_line.points])
new_positions = self.apply_perlin_noise_to_line_normal(
positions,
self.t * self.t_factor,
self.amplitude,
self.frequency
)
points = []
for point, pos in zip(target_line.points, new_positions):
p = copy.deepcopy(point)
p.position = pos
points.append(p)
return RenderableLine(points)
class SegmentLine(LineAnimator):
def __init__(self, target_line = None):
super().__init__(target_line)
def apply(self, target_line: RenderableLine, dt: DeltaT):
if len(target_line) < 2:
return target_line
i = self.running_for()
ls = target_line.as_linestring()
return super().apply(target_line, dt)
class DashedLine(LineAnimator):
"""
Dashed line
"""
def __init__(self, target_line = None, dash_len: float = 1., gap_len: float = .5, t_factor: float = 1., loop_offset: bool = False):
super().__init__(target_line)
self.dash_len = dash_len
self.gap_len = gap_len
self.loop_offset = loop_offset
self.t_factor = t_factor
# def set_offset_t(self, dt: DeltaT):
# self.offset_t = dt
@classmethod
def dashed_line(cls, line: shapely.geometry.LineString, dash_len: float, gap_len: float, offset: float = 0, loop_offset = True) -> shapely.geometry.MultiLineString:
total_length = line.length
# index_helper = LineStringIncrementingDistanceOffset(line)
segments = []
if loop_offset:
# by default, prepend skipped gap
pos = offset % (dash_len + gap_len)
if pos > gap_len:
# TODO: use index_helper.get_index_and_offset to lerp the colors of all points on substring
segments.append(shapely.ops.substring(line, 0, pos - gap_len))
else:
pos = offset
while pos < total_length:
end = min(pos + dash_len, total_length)
if pos < end:
# TODO: use index_helper.get_index_and_offset to lerp the colors of all points on substring
dash = shapely.ops.substring(line, pos, end)
segments.append(dash)
pos += dash_len + gap_len
# TODO: return all color together with the points
return shapely.geometry.MultiLineString(segments)
def apply(self, target_line: RenderableLine, dt: DeltaT) -> RenderableLine:
"""
warning, dashing (for now) removes all color
"""
if len(target_line) < 2:
self.ready = True
return target_line
ls = target_line.as_linestring()
multilinestring = self.dashed_line(ls, self.dash_len, self.gap_len, self.t_factor * self.running_for(), self.loop_offset)
self.ready = not bool(len(multilinestring.geoms))
color = target_line.points[0].color
return RenderableLine.from_multilinestring(multilinestring, color)
IndexAndOffset = Tuple[int, float]
class LineStringIncrementingDistanceOffset():
"""
Helper for linestrings: find the index + lerp-offset, ASAP provided that
distance to look for is always increasing
"""
def __init__(self, ls: shapely.geometry.LineString):
self.ls = ls
self.gen = self.index_distances(self.ls)
self.current_range = next(self.gen)
self.pos = 0
def get_index_and_offset(self, length: float) -> IndexAndOffset:
if length > self.pos:
raise RuntimeError("Cannot reverse")
self.pos = length
while True:
index_range = self.current_range[0]
start_distance = self.current_range[1][0]
end_distance = self.current_range[1][1]
if length < end_distance:
# fill here
segment_distance = length - start_distance
segment_length = end_distance - start_distance
lerp = segment_distance / segment_length
return index_range[0], lerp
else:
try:
self.current_range = next(self.gen)
except StopIteration as e:
# when exhausting the list return the last index
return len(self.ls.coords) - 1, 0.
@classmethod
def index_distances(cls, line: shapely.geometry.LineString):
"""
Build a list of segments with the distance range they cover
"""
cumulative_length = 0
for i in range(len(line.coords) - 1):
segment_length = line.coords[i+1].distance(line.coords[i])
start_at = cumulative_length
cumulative_length += float(segment_length)
yield (i, i+1), (start_at, cumulative_length)

View file

@ -22,6 +22,8 @@ class Node():
self._prev_loop_time = 0 self._prev_loop_time = 0
self.dt_since_last_tick = 0
self.setup() self.setup()
@classmethod @classmethod
@ -29,7 +31,7 @@ class Node():
return logging.getLogger(f"trap.{cls.__name__}") return logging.getLogger(f"trap.{cls.__name__}")
def tick(self): def tick(self):
self.fps_counter.tick() self.dt_since_last_tick = self.fps_counter.tick()
# with self.fps_counter.get_lock(): # with self.fps_counter.get_lock():
# self.fps_counter.value+=1 # self.fps_counter.value+=1

File diff suppressed because it is too large Load diff

1009
trap/stage_old.py Normal file

File diff suppressed because it is too large Load diff