finger id => position, 0..1 of the screen */ private array $fingers = []; private float $factor = 1.0; private ?float $last = null; public function __construct(private ?TouchControls $inner = null) { } public function beforeEvents(): void { $this->inner?->beforeEvents(); } public function handle($event, Renderer $renderer): bool { $f = $event->tfinger; $id = (string)$f->fingerId; if ($event->type === \SDL_FINGERUP) { unset($this->fingers[$id]); $this->last = null; } else { $this->fingers[$id] = [(float)$f->x, (float)$f->y]; } if (count($this->fingers) > 2) { // a third finger: start again with the newest two $this->fingers = array_slice($this->fingers, -2, null, true); $this->last = null; } if (count($this->fingers) === 2) { [$a, $b] = array_values($this->fingers); $d = hypot(($a[0] - $b[0]) * 16, ($a[1] - $b[1]) * 9); if ($this->last !== null && $this->last > 0.01 && $d > 0.01) { $this->factor *= $d / $this->last; } $this->last = $d; } return $this->inner?->handle($event, $renderer) ?? false; } public function draw(Renderer $r): void { $this->inner?->draw($r); } /** Forgets every finger (a lost finger-up must not leave a pinch on forever). */ public function reset(): void { $this->fingers = []; $this->factor = 1.0; $this->last = null; } /** Two fingers are down. */ public function active(): bool { return count($this->fingers) >= 2; } /** How much the fingers spread since the last call (1: nothing), and where their middle is (0..1). @return array{0: float, 1: float, 2: float} */ public function take(): array { $factor = $this->factor; $this->factor = 1.0; $pts = array_values($this->fingers); $n = max(1, count($pts)); return [$factor, array_sum(array_column($pts, 0)) / $n, array_sum(array_column($pts, 1)) / $n]; } }