Setzen wir uns ein rein praktisches Ziel - eine endlose Leinwand mit der FĂ€higkeit zu realisieren, sie mit der Maus zu bewegen und zu skalieren. Eine solche ZeichenflĂ€che kann beispielsweise als bewegliches Koordinatensystem in einem grafischen Editor dienen. Die Umsetzung unserer Idee ist nicht so kompliziert, aber der Prozess des Verstehens ist mit grundlegenden mathematischen und physikalischen Objekten verbunden, die wir bei unserer Entwicklung berĂŒcksichtigen werden.
ErgebnisErklÀrung des Problems
Auf unserer Leinwand möchten wir nur zwei Funktionen ausfĂŒhren: Bewegen der Maus und Bewegen der Maus sowie Zoomen beim Scrollen. Die Arena unserer Transformationen wird einen Browser auswĂ€hlen. Waffen mĂŒssen in diesem Fall nicht wĂ€hlen.
Zustandsmaschine
Das Verhalten solcher Systeme wird zweckmĂ€Ăigerweise durch ĂbergĂ€nge zwischen ihren ZustĂ€nden beschrieben, d.h. Zustandsmaschine wo - die Ăbergangsfunktion zwischen ZustĂ€nden, die viele ZustĂ€nde an sich anzeigt.
In unserem Fall sieht das Zustandsdiagramm folgendermaĂen aus:

Bei der Implementierung der Ăbergangsfunktion ist es zweckmĂ€Ăig, das Ereignis abhĂ€ngig zu machen. Dies wird sich in Zukunft zeigen. Ebenso ist es bequem, eine Ănderung des Zustands der Maschine abonnieren zu können.
type State = string | number; type Transition<States = State> = { to: States, where: (event: Event) => Array<boolean>, }; type Scheme<States = State> = { [key: States]: Array<Transition<States>> }; interface FSM<States = State> { constructor(state: States, scheme: Scheme<States>): void; // Returns true if the scheme had a transition, false otherwise get isActive(): boolean; // Dispatch event and try to do transition dispatch(event: Event): void; // subscribe on state change on(state: States, cb: (event: Event) => any): FSM<States>; // remove subscriber removeListener(state: States, cb: (event: Event) => any): void; };
Wir werden die Implementierung fĂŒr eine Weile verschieben und die geometrischen Transformationen aufgreifen, die unserer Aufgabe zugrunde liegen.
Geometrien
Wenn das Problem beim Verschieben der Leinwand so offensichtlich ist, dass wir uns nicht damit befassen, sollte das Dehnen genauer betrachtet werden. ZunĂ€chst benötigen wir, dass die Dehnung einen einzelnen Punkt stationĂ€r lĂ€sst - den Mauszeiger. Die ReversibilitĂ€tsbedingung muss ebenfalls erfĂŒllt sein, d.h. Die umgekehrte Reihenfolge der Benutzeraktionen sollte die Leinwand an ihre ursprĂŒngliche Position bringen. Welche Geometrie ist dafĂŒr geeignet? Wir werden eine Gruppe von Punkttransformationen der Ebene in sich selbst betrachten , was im Allgemeinen durch die EinfĂŒhrung neuer Variablen ausgedrĂŒckt wird definiert als Funktionen der alten:
In Ăbereinstimmung mit dem Prinzip der DualitĂ€t in der Mathematik können solche Transformationen als Ănderung des Koordinatensystems sowie als Transformation des Raums selbst interpretiert werden, wobei letzteres festgelegt ist. Die zweite Interpretation ist fĂŒr unsere Zwecke zweckmĂ€Ăig.
Ein modernes VerstĂ€ndnis der Geometrie unterscheidet sich von einem VerstĂ€ndnis der Alten. Nach F. Klein untersucht die Geometrie Invarianten in Bezug auf bestimmte Transformationsgruppen. Also in einer Gruppe von Bewegungen Die Invariante ist der Abstand zwischen zwei Punkten . Es enthĂ€lt parallele Silbentrennung auf Vektor Rotation relativ zum Ursprung in einem Winkel und Reflexionen relativ zu einer Linie . Solche Bewegungen nennt man elementar. Die Zusammensetzung der beiden SĂ€tze gehört zu unserer Gruppe und ist manchmal elementar. So zum Beispiel zwei aufeinanderfolgende Spiegelreflexionen relativ zu geraden Linien und Drehen Sie sich in einem bestimmten Winkel um ein bestimmtes Zentrum (ĂŒberzeugen Sie sich selbst):
Sicher haben Sie schon vermutet, dass eine solche Gruppe von Bewegungen bildet euklidische Geometrie. Beim Strecken bleibt jedoch nicht der Abstand zwischen zwei Punkten erhalten, sondern deren Beziehung. Daher eine Gruppe von Bewegungen, obwohl es in unser Schema aufgenommen werden sollte, aber nur als Untergruppe.
Die Geometrie, die zu uns passt, basiert auf einer Dehnungsgruppe zu dem zusĂ€tzlich zu den obigen Bewegungen die Homothetik hinzugefĂŒgt wird nach Koeffizienten .
Nun, der letzte. Das umgekehrte Element muss in der Gruppe vorhanden sein. Und deshalb gibt es eine neutrale (oder Single) das Àndert nichts. Zum Beispiel
bedeutet zuerst einstrecken
mal und dann in
.
Jetzt können wir die Dehnung in gruppentheoretischer Sprache beschreiben, wobei der Mauszeiger fest bleibt:
Anmerkung 1Im allgemeinen Fall sind Umordnungen von Aktionen nicht kommutativ (Sie können zuerst Ihren Mantel und dann Ihr Hemd ausziehen, aber nicht umgekehrt).
DrĂŒcken Sie die Bewegung aus und und ihre Zusammensetzung als Funktionen eines Vektors im Code
type Scalar = number; type Vec = [number, number]; type Action<A = Vec | Scalar> = (z: Vec) => (v: A) => Vec;
Schmerz 1Es ist sehr seltsam, dass in JavaScript keine OperatorĂŒberladung auftritt. Es scheint, dass es bei einer derart weit verbreiteten Verwendung von Vektor- und Rastergrafiken viel bequemer ist, mit Vektoren oder komplexen Zahlen in einer "klassischen" Form zu arbeiten. Die Aktionskonzepte wĂŒrden arithmetische Operationen ersetzen. So zum Beispiel Rotation um einen Vektor an der Ecke wĂŒrde auf triviale Weise ausgedrĂŒckt werden:
Leider folgt JS nicht der Entwicklung der wunderbaren pythagoreischen Idee âDie Welt ist eine Zahlâ, im Gegensatz zu zumindest Python.
Beachten Sie, dass wir bisher mit einer Gruppe kontinuierlicher Transformationen gearbeitet haben. Der Computer arbeitet jedoch nicht mit kontinuierlichen Mengen. Daher verstehen wir nach Poincare eine kontinuierliche Gruppe als eine unendliche Gruppe diskreter Operationen. Nachdem wir die Geometrie herausgefunden haben, sollten wir uns der RelativitÀt der Bewegung zuwenden.
Kosmologie der Leinwand. Modulares Raster
Als Menschheit ist seit einem Jahrhundert die Expansion des Universums bekannt. Wenn wir entfernte Objekte - Galaxien und Quasare - beobachten, registrieren wir die Verschiebung des elektromagnetischen Spektrums zu lĂ€ngeren Wellen - die sogenannte kosmologische Rotverschiebung. Jede Messung verbindet den Beobachter, das Beobachtete und die Messmittel, in Bezug auf die wir unsere Messungen durchfĂŒhren. Ohne Messinstrumente ist es unmöglich, invariante Beziehungen in der Natur herzustellen, d. H. Die Geometrie des Universums zu bestimmen. Die Geometrie verliert jedoch ihre Bedeutung ohne Beobachtbarkeit. In unserer Aufgabe ist es also schön, Orientierungspunkte wie Galaxien zu haben, in Bezug auf deren Licht wir die RelativitĂ€t der Bewegung unserer Leinwand bestimmen können. Eine solche Struktur kann ein periodisches Gitter sein, das sich jedes Mal teilt, wenn der Raum zweimal erweitert wird.
Da das Gitter periodisch ist, ist es zweckmĂ€Ăig, eine modulare Algebra anzuwenden. Wir werden also als Gruppe agieren auch auf dem Torus . Da der Monitorbildschirm keine durchgehende Ebene ist, sondern ein ganzzahliges Gitter (Wir vernachlĂ€ssigen jetzt, dass es endlich ist), dann die Aktion der Gruppe muss auf einem ganzzahligen Torus berĂŒcksichtigt werden wo - quadratische RippengröĂe ::

Wenn wir also unseren Torus ein fĂŒr alle Mal in der NĂ€he des Ursprungs fixieren, werden wir alle weiteren Berechnungen daran durchfĂŒhren. Verbreiten Sie es dann mit Standardmethoden fĂŒr die Canvas-Bibliothek. So sieht eine Bewegung mit einem Pixel aus:

Offensichtlich ist die Standardoperation, das Modul x% p zu nehmen, fĂŒr uns nicht geeignet, da sie negative Werte des Arguments in negative Werte ĂŒbersetzt, aber es gibt keine auf dem ganzzahligen Torus. Schreiben Sie Ihre Funktion ::
const mod = (x, p) => x >= 0 ? Math.round(x) % p : p + Math.round(x) % p;
Nun zurĂŒck zur Endzustandsmaschine und
definiere esDie FSM-Klasse wird bequem von EventEmitter geerbt, wodurch wir die Möglichkeit haben, sie zu abonnieren.
class FSM<States> extends EventEmitter { static get TRANSITION() { return '__transition__'; } state: States; scheme: Scheme<States>; constructor(state: States, scheme: Scheme<States>) { super(); this.state = state; this.scheme = scheme; this.on(FSM.TRANSITION, event => this.emit(this.state, event)); } get isActive(): boolean { return typeof(this.scheme[this.state]) === 'object'; } dispatch(event: Event) { if (this.isActive) { const transition = this.scheme[this.state].find(({ where }) => where(event).every(domen => domen) ); if (transition) { this.state = transition.to; this.emit(FSM.TRANSITION, event); } } } }
Definieren Sie als NĂ€chstes ein Ăbergangsschema, erstellen Sie eine ZeichenflĂ€che und
initialisiere alles. canvas = document.getElementById('canvas'); ctx = canvas.getContext('2d');
AnschlieĂend sollten Sie die Rendering-Funktion festlegen, die erforderlichen Anfangswerte festlegen und die StatusĂ€nderung abonnieren. Betrachten Sie den interessantesten Teil des Codes:
fsm.on('zooming', (event: WheelEvent) => {
Erstens erweitern wir nicht um den Koeffizienten k, sondern um ein VerhĂ€ltnis nk / k. Dies liegt daran, dass der m-Schritt unserer Abbildung an einem festen Punkt liegt ausgedrĂŒckt als
oder relativ zu den Anfangswerten
Offensichtlich das Produkt Es gibt eine nichtlineare Funktion des Iterationsschritts, die entweder sehr schnell gegen Null konvergiert oder mit kleinen anfÀnglichen Abweichungen ins Unendliche ablÀuft.
Wir fĂŒhren die Variable g ein, die ein MaĂ fĂŒr die Verdoppelung unserer Leinwand ist. Offensichtlich nimmt es in einem bestimmten Intervall einen konstanten Wert an. LinearitĂ€t erreichen Wir verwenden eine homogene Substitution
Dann werden alle Mitglieder der Arbeit auĂer dem ersten und dem letzten reduziert:
Ferner reduziert der Phasensprung g die Expansionsrate derart, dass sich die fraktale Struktur, die sich vor uns entfaltet, immer linear bewegt. Somit erhalten wir eine ungefÀhre Variation des Hubble-Potenzgesetzes der Expansion des Universums.
Es bleibt die Grenzen der Genauigkeit unseres Modells zu verstehen.
Quantenschwankungen. 2-adic Zahlenfeld
Das VerstĂ€ndnis des Messprozesses fĂŒhrte zum Konzept einer reellen Zahl. Heisenbergs Unsicherheitsprinzip weist auf seine Grenzen hin. Ein moderner Computer arbeitet nicht mit reellen Zahlen, sondern mit Maschinenwörtern, deren LĂ€nge von der KapazitĂ€t des Prozessors bestimmt wird. Maschinenwörter bilden ein Feld von 2-adischen Zahlen und werden als bezeichnet wo - WortlĂ€nge. Der Messprozess wird in diesem Fall durch den Berechnungsprozess ersetzt und ist einer nicht-archimedischen Metrik zugeordnet:
Somit hat unser Modell eine Berechnungsgrenze. Die EinschrĂ€nkungen sind im IEEE_754- Standard beschrieben. Ab einem bestimmten Zeitpunkt ĂŒberschreitet unsere BasisgröĂe die Genauigkeitsgrenze, und bei der Aufnahme des Moduls werden Fehler erzeugt, die pseudozufĂ€lligen Sequenzen Ă€hneln. Dies wird einfach durch Löschen der Zeile ĂŒberprĂŒft
if (g < min || g > max) return;
Die endgĂŒltige Grenze wird in unserem Fall nach der semi-empirischen Methode berechnet, da wir mit mehreren Parametern arbeiten.
Fazit
Auf den ersten Blick scheinbar weit entfernte Theorien sind also im Browser auf der Leinwand verbunden. Die Konzepte von Aktion, Messung und Berechnung sind eng miteinander verbunden. Das Problem der Kombination ist immer noch nicht gelöst.
ErgebnisPSEs war klar, dass der Quellcode klein sein wĂŒrde, also leitete ich die Entwicklung in der gewöhnlichen index.html. WĂ€hrend ich diesen Artikel schrieb, fĂŒgte ich die Eingabe hinzu, die ich auf dem TypeScript-Spielplatz getestet habe.