12k
All articles

Echte Geometrie im Browser mit Geometric.js

Geometric.js bringt Polygon-Schnittmengen, Bounds, Point-in-Polygon-Tests und Pfadinterpolation in Browser-Charts, Karten, SVG und Canvas.

OpenReplay Team
OpenReplay Team
Echte Geometrie im Browser mit Geometric.js

Geometric.js ist eine 2D-Geometriebibliothek, bei der jedes Primitiv ein einfaches JavaScript-Array ist: Ein Punkt ist [x, y], eine Linie ist [[x, y], [x, y]], und ein Polygon ist ein Array von Punkten – der Wert, mit dem Sie rechnen, ist also derselbe, den Sie in JSON serialisieren und in SVG oder Canvas zeichnen.

Wer interaktive Diagramme oder Karten entwickelt, kennt die Reibungsverluste, die entstehen, wenn man eine einzelne Geometrieoperation benötigt und die Mathematik jedes Mal neu erfinden muss. Wenn Sie bisher Ray-Casting für Point-in-Polygon-Tests von Hand implementiert oder Bounding-Box-Berechnungen in D3-Diagramme kopiert haben, ersetzt Geometric.js diese Code-Fragmente durch benannte, getestete Funktionen, die direkt auf den Arrays arbeiten, die Sie bereits verwenden. Diese Einführung behandelt drei zentrale Funktionen (Polygon-Schnittmengen, Bounding Boxes und Interpolation) sowie zwei Anwendungsfälle, bei denen der Einsatz der Bibliothek dem eigenen Schreiben der Trigonometrie deutlich überlegen ist.

Wesentliche Erkenntnisse

  • Geometric.js repräsentiert einen Punkt als [x, y], eine Linie als zwei Punkte und ein Polygon als Array von Punkten – keine Klassen müssen instanziiert und keine Anpassungen vorgenommen werden, bevor in SVG oder Canvas gezeichnet wird.
  • pointInPolygon(point, polygon) gibt einen booleschen Wert mittels Ray-Casting zurück und ermöglicht so einen Canvas-Hit-Test mit einem einzigen Aufruf und ohne externe Geometrieberechnungen.
  • polygonBounds(polygon) gibt [topLeft, bottomRight] als zwei [x, y]-Punkte zurück, oder null bei weniger als drei Vertices – ausreichend, um einen Tooltip-Rahmen um eine beliebige Form zu positionieren.
  • Die geometrierückgebenden booleschen Operationen – polygonIntersection, polygonUnion, polygonDifference und polygonXor – erfordern geometric v3 oder höher; das Prädikat polygonIntersectsPolygon ist seit v2 verfügbar.
  • Ab Version 3.x enthält geometric eigene TypeScript-Deklarationen, sodass Sie nur geometric installieren und @types/geometric weglassen können.

Primitive sind einfache Arrays – das ist das Kernkonzept

Der Grund, warum Geometric.js angenehm zu verwenden ist, liegt darin, dass es keine eigenen Datenstrukturen einführt. Ein Punkt ist ein [x, y]-Array, eine Linie ist ein Array aus zwei Punkten, und ein Polygon ist ein Array von Vertices. Es gibt keine benutzerdefinierten Klassen, die instanziiert werden müssen, und keine speziellen Datenstrukturen, die erlernt werden müssen. Das bedeutet, dass die übergebenen Werte dieselben sind, die Sie serialisieren, inspizieren und zeichnen können. Da die Ausgabe ein Array ist, lässt sie sich direkt in D3, ein SVG-points-Attribut oder eine Canvas-ctx.lineTo-Schleife einfügen, ohne Adapter-Code:

import { polygonRegular } from "geometric";

const pentagon = polygonRegular(5, 10000, [150, 150]);
// pentagon ist [[x, y], [x, y], ...] — direkt zeichenbar:

// SVG
polygon.setAttribute("points", pentagon.map(p => p.join(",")).join(" "));

// Canvas
ctx.beginPath();
pentagon.forEach(([x, y], i) => (i ? ctx.lineTo(x, y) : ctx.moveTo(x, y)));
ctx.closePath();

Dieser Durchlauf – berechnen, serialisieren und rendern, alles auf demselben Array – ist das, was die README mit „Geometrie einfach halten” meint.

Installation und Import

Installieren Sie geometric über npm, pnpm oder yarn; das Paket wird als ESM-first-Paket mit CommonJS-Unterstützung und gebündelten TypeScript-Deklarationen ausgeliefert. Die aktuelle Version auf npm ist 3.0.9 (Juli 2026).

npm i geometric
# oder: pnpm add geometric
# oder: yarn add geometric
import { pointInPolygon, polygonBounds } from "geometric"; // ESM
const geometric = require("geometric");                     // CommonJS

Zwei Hinweise zur Einrichtung: Erstens werden TypeScript-Deklarationen aus dem Quellcode generiert und mit dem Paket veröffentlicht, daher sollten Sie nicht @types/geometric installieren. Dieses DefinitelyTyped-Paket ist bei Version 2.5.3 eingefroren und ist in v3 überflüssig. Zweitens erfordern die unten beschriebenen geometrierückgebenden booleschen Operationen geometric@^3; in v2 geben sie undefined zurück.

Polygon-Schnittmengen und boolesche Operationen

Für Überlappungsprüfungen gibt polygonIntersection(a, b) den gemeinsamen Bereich als neues Polygon zurück, während polygonIntersectsPolygon(a, b) einen booleschen Wert zurückgibt. Das Prädikat ist die kostengünstige Prüfung; die geometrierückgebende Operation liefert eine tatsächliche Form zum Rendern.

import { polygonIntersection, polygonIntersectsPolygon } from "geometric";

const a = [[0, 0], [4, 0], [4, 4], [0, 4]];
const b = [[2, 2], [6, 2], [6, 6], [2, 6]];

polygonIntersectsPolygon(a, b); // true — boolesches Prädikat (ab v2)
polygonIntersection(a, b);      // Überlappungsbereich als neues Polygon (ab v3)

Der vollständige boolesche Satz umfasst polygonIntersection, polygonUnion, polygonDifference und polygonXor, die jeweils ein Punkt-Array zurückgeben. Alle vier sind Ergänzungen von v3. Für Enthaltensein statt Überlappung gibt polygonInPolygon(polygonA, polygonB) einen booleschen Wert zurück, der angibt, ob das erste Polygon vollständig im zweiten liegt, wobei Randpunkte als enthalten behandelt werden.

Wie ermittelt man die Bounding Box eines Polygons?

polygonBounds(polygon) gibt [topLeft, bottomRight] als zwei [x, y]-Punkte zurück, oder null bei weniger als drei Vertices – ausreichend, um in einem einzigen Aufruf einen Tooltip- oder Beschriftungsrahmen um eine beliebige Form zu positionieren. Punkte mit ungültigen Werten (null, undefined, NaN, Infinity) werden ignoriert, sodass eine fehlerhafte Lücke in den Daten die Box nicht korrumpiert.

import { polygonBounds } from "geometric";

const region = [[12, 8], [40, 20], [30, 44], [6, 30]];
const [topLeft, bottomRight] = polygonBounds(region);
// topLeft = [6, 8], bottomRight = [40, 44]

const width = bottomRight[0] - topLeft[0];   // 34
const height = bottomRight[1] - topLeft[1];  // 36
// <rect> oder Tooltip bei topLeft mit dieser Breite/Höhe positionieren

Dies ist die typische Aufgabe bei der Datenvisualisierung: Für einen beliebigen Bereich auf einer Karte oder in einem Diagramm einen Beschriftungsrahmen platzieren, ohne Min/Max über alle Vertices manuell zu berechnen.

Interpolation: Animation entlang eines Pfads oder Umfangs

lineInterpolate(line) und polygonInterpolate(polygon) geben eine Interpolatorfunktion zurück, die mit t im Bereich [0, 1] aufgerufen wird – genau das, was Sie benötigen, um einen Marker entlang eines Pfads oder um einen Umfang zu animieren. Im aktuellen 3.x-Build ist clamp standardmäßig true, was die Ausgabe auf das Segment beschränkt; eine explizite Übergabe macht die Absicht deutlicher.

import { lineInterpolate } from "geometric";

const path = [[0, 0], [100, 50]];
const at = lineInterpolate(path, true); // clamp = true

at(0);   // [0, 0]
at(0.5); // [50, 25]
at(1);   // [100, 50]

Steuern Sie t über requestAnimationFrame oder einen D3-Übergang, und Sie erhalten einen Marker, der gleichmäßig entlang der Linie gleitet. polygonInterpolate funktioniert auf dieselbe Weise entlang eines geschlossenen Umfangs – nützlich, um einen Umriss nachzuzeichnen oder einen Punkt entlang des Rands einer Form zu bewegen.

Wie prüft man, ob ein Punkt innerhalb eines Polygons liegt?

Um zu testen, ob ein Klick in einen Bereich fällt, gibt pointInPolygon(point, polygon) einen booleschen Wert mittels Ray-Casting zurück – ohne externe Geometrieberechnungen. Dies ist der klassische Canvas-Hit-Test und reduziert ein Dutzend Zeilen handgeschriebener Kantenschnitt-Logik auf einen einzigen, getesteten Aufruf.

import { pointInPolygon, pointOnPolygon } from "geometric";

const region = [[0, 0], [100, 0], [100, 100], [0, 100]];

pointInPolygon([50, 50], region);  // true
pointInPolygon([150, 50], region); // false

Innenbereichstests haben eine vorhersehbare Schwäche: Klicks, die genau auf einer Kante landen. Session-Replays von Drag-to-Select- und Kartenbereich-Auswahlen zeigen diesen Fehler häufig: Ein „Warum wurde meine Auswahl nicht registriert?”-Bug, den Unit-Tests übersehen, der aber bei der Beobachtung der Interaktion offensichtlich ist. Die saubere Lösung ist pointOnPolygon(point, polygon, epsilon), das den Rand mit einer optionalen Epsilon-Toleranz wie 1e-6 prüft – für Randambiguitäten, die eine reine Innenbereichsprüfung ignoriert.

FunktionMethodeRückgabewertMindestversion
Punkt-in-Bereich-TestpointInPolygonboolean (Ray-Casting)v2
RandprüfungpointOnPolygonboolean (Epsilon)v2
Bounding BoxpolygonBounds[topLeft, bottomRight] oder nullv2
ÜberlappungsformpolygonIntersectionPolygonv3
ÜberlappungsprädikatpolygonIntersectsPolygonbooleanv2
Pfad-InterpolationlineInterpolateFunktion (t) => [x, y]v2

Greifen Sie auf Geometric.js zurück, sobald ein Diagramm, eine Karte oder eine Canvas-Benutzeroberfläche mehr als eine Geometrieoperation benötigt: Das Array-rein, Array-raus-Modell bedeutet, dass jedes Ergebnis direkt in den nächsten Aufruf oder den Renderer einfließt. Installieren Sie geometric, importieren Sie die zwei oder drei Funktionen, die Ihr Feature benötigt, und löschen Sie die Stack-Overflow-Trigonometrie. Die API-Referenz listet den vollständigen Funktionsumfang auf, wenn eine vierte Operation benötigt wird.

Häufig gestellte Fragen

Was ist der Unterschied zwischen polygonIntersection und polygonIntersectsPolygon in Geometric.js?

polygonIntersectsPolygon(a, b) gibt einen booleschen Wert zurück, der angibt, ob sich zwei Polygone überlappen, während polygonIntersection(a, b) den gemeinsamen Bereich selbst als neues Polygon-Array zurückgibt, das gerendert werden kann. Verwenden Sie das Prädikat für eine kostengünstige Ja/Nein-Prüfung und die geometrierückgebende Operation, wenn Sie die tatsächliche Überlappungsform benötigen. Das Prädikat ist seit v2 verfügbar, aber polygonIntersection erfordert geometric v3 oder höher.

Muss ich @types/geometric installieren, um Geometric.js mit TypeScript zu verwenden?

Nein. Ab Version 3.x enthält geometric eigene TypeScript-Deklarationen, die aus dem Quellcode generiert und mit dem Paket veröffentlicht werden. Die Installation von geometric allein bietet daher Editor-Autovervollständigung und Typprüfung. Das separate DefinitelyTyped-Paket @types/geometric ist seit November 2023 bei Version 2.5.3 eingefroren und ist in v3 überflüssig; seine Installation kann die genauen gebündelten Typen durch veraltete überschatten.

Was gibt Geometric.js zurück, wenn ein Punkt genau auf einer Polygon-Kante liegt?

pointInPolygon verwendet Ray-Casting, um den Innenbereich zu prüfen, und Punkte, die genau auf einer Kante liegen, werden als mehrdeutig behandelt und können false zurückgeben. Dies äußert sich als Klicks, die in Drag-to-Select- oder Kartenbereich-Benutzeroberflächen nicht registriert werden. Verwenden Sie pointOnPolygon(point, polygon, epsilon) für Randfälle; die optionale Epsilon-Toleranz, beispielsweise 1e-6, bestimmt, wie nah an der Linie als auf ihr liegend gilt.

Kann die Ausgabe von Geometric.js direkt in SVG oder Canvas gezeichnet werden, ohne Konvertierung?

Ja. Da jedes Primitiv ein einfaches JavaScript-Array und keine Klasseninstanz ist, lassen sich die von Funktionen wie polygonRegular oder polygonIntersection zurückgegebenen Arrays direkt in ein SVG-points-Attribut oder eine Canvas-ctx.lineTo-Schleife einfügen, ohne Adapter-Code. Dasselbe Array, mit dem Sie rechnen, ist der Wert, den Sie in JSON serialisieren, an D3 übergeben oder rendern – das ist das grundlegende Designkonzept der Bibliothek.

DevTools for the frontend

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.