lrg + ledchainarrangement - Graphik-Subsystem für SmartLED-Ketten und Matrizen
LED-Simulator verfügbar ab Firmware-Version 2.7.0/2.7.0.32
Ab 2.7.0/2.7.0.32 gibt es einen eingebauten LED-Simulator, der die Arbeit mit dem p44lrgrahics-Subsystem sehr erleichtert, da er es ermöglicht, die gesamte Ansichtshierarchie eines LED-Setups zu überprüfen, noch bevor irgendwelche LEDs tatsächlich angeschlossen sind. Der Simulator kann über die entsprechende Schaltfläche "LED Sim..." auf der Registerkarte "System" oder über die verpixelte Schaltfläche unten rechts in der IDE aufgerufen werden. Beim Aufruf zeigt der Simulator das gleiche Bild wie die angeschlossenen LEDs, aber jede Ansicht kann auch einzeln über das Pop-up-Menü oben links betrachtet werden.
Was ist lrg?
lrg ist die Abkürzung für "low resolution graphics", und innerhalb von p44script der Name des globalen Objekts, mit dem das Graphik-Subsystem p44lrgrpahics angesprochen wird. p44lrgrpahics stellt eine Graphik-Umgebung zur Verfügung, mit der komplexe Effekte auf LED-Ketten und Matrizen erstellt und auch animiert werden können. Dazu bietet es eine Hierarchie von Views, die nebeneinander oder auch übereinander (mit variabler Durchsichtigkeit) angeordnet und bewegt werden können.
Ein Lauflicht-Effekt etwa würde in p44lrgrpahics mit einem Hintergrund-View realisiert, über dem das eigentliche Lauflicht als zweiter View bewegt wird.
Was ist ein ledchainarrangement?
SmartLED-Ketten bestehend aus einzeln addressierbaren LEDs (WS281x, SK681x etc.) können verschiedenartig zu Bändern oder Flächen kombiniert werden. Z.B. lassen sich LED-Streifen auf einem Zylinder aufwickeln, oder in Zick-Zack-Anordnung auf einer Fläche.
Ein ledchainarrangement umfasst eine oder mehrere Abschnitte von LED-Ketten, und arrangiert sie logisch innerhalb eines Rechtecks von Pixeln (u.U. mit Lücken). Dieses logische Rechteck bildet dann den rootview für das lrg Graphiksystem. Jede angeschlossene LED erhält damit eine eindeutige Koordinate (x und y). Der rootview (üblicherweise ein stack) kann dann weitere Views beliebig in diesem Rechteck platzieren, verschieben, ein- und ausblenden, etc.
Das ledchainarrangement eines P44-xx-Geräts wird entweder über die Kommandozeile mit der --ledchain
-Option angegeben, oder via p44script mit addledchain()
(s. unten für Details) definiert, üblicherweise aus dem mainscript heraus.
View-Typen
Es gibt verschiedene Typen von Views (Details zur Konfiguration s. unten):
Allgemeine Views
- plain
- Auf diesem einfachsten View sind alle anderen Typen aufgebaut. Ein plain view umfasst ein Rechteck mit einer Hintergrund- und einer Vordergrund-Farbe.
- S. unten für Beschreibung aller Eigenschaften von plain, die alle anderen Views ebenso haben, weil sie auf plain aufgebaut sind.
- canvas
- Auf diesem View können programmatisch einzelne Pixel gesetzt (gezeichnet) werden.
- S. unten für Beschreibung der spezifischen Eigenschaften von canvas
- text
- Stellt einen Text in Pixelschrift (verschiedene Fonts in unterschiedlichen Grössen wählbar).
- S. unten für Beschreibung der spezifischen Eigenschaften von text
- image
- Zeigt ein Bild (das im PNG-Format vorliegen muss) an.
- S. unten für Beschreibung der spezifischen Eigenschaften von image
- scroller
- Kann einen anderen View mit einstellbarer Geschwindigkeit bewegen (scrollen). Die Bewegungsschritte können kleiner als eine LED sein, dann werden die Zwischenschritte durch Helligkeitsverteilung auf mehrere Pixel simuliert (Anti-Aliasing). Mit dem scroller und einem text-View sind z.B. Laufschriften realisierbar. Der scroller kann einen Inhalt in einer Endlosschlaufe scrollen, oder auch einen Event auszulösen, wenn z.B. eine Laufschrift neuen Inhalt zur Darstellung braucht.
- S. unten für Beschreibung der spezifischen Eigenschaften von scroller
- stack
- Dient dazu, mehrere andere Views in Schichten (Layers) übereinander oder auch nebeneinander anzuordnen.
- S. unten für Beschreibung der spezifischen Eigenschaften von stack
- sequencer
- Dient dazu, mehrere andere Views (z.B. Bilder) in einer zeitlichen Abfolge (Steps) anzuzeigen, einmalig oder wiederholt. Die einzelnen Schritte können verschiedene Anzeigedauer sowie Ein- und Ausblendzeiten haben.
- S. unten für Beschreibung der spezifischen Eigenschaften von sequencer
Spezialisierte Views
- lightspot
- Simuliert einen Licht-"Spot", eine runde oder ovale Fläche mit verschiedenen Möglichkeiten der Farb- und Helligkeitsverteilung rund um die Mitte. Damit sind Farbverläufe, weiche Kanten etc. machbar. Der lightspot ist die Basis des "Moving feature light with effects" das in P44-DSB/LC-Geräten mit SmartLED-Anschluss verfügbar ist.
- S. unten für Beschreibung der spezifischen Eigenschaften von lightspot
- epx
- Spielt Animationen ab, die im Expressive pixels IoT animation format-JSON-Format erstellt wurden.
- S. unten für Beschreibung der spezifischen Eigenschaften von epx
- life
- Stellt Conway's "Game of Life" dar. Das Alter der Zellen kann farblich dargestellt werden.
- S. unten für Beschreibung der spezifischen Eigenschaften von life
- torch
- Eine einfach Fackel/Feuer-Simulation, die gut auf rund aufgewickelten LED-Ketten aussieht. Dieser View hat sich aus dem Projekt MessageTorch von 2014 entwickelt.
- S. unten für Beschreibung der spezifischen Eigenschaften von torch
Aufsetzen der SmartLED-Ketten in LEDChainArrangements
Der Ledchain-Initialisierungsstring
Je nach Hardware verfügt ein Gerät über einen oder mehrere Ausgänge, an die jeweils eine LED-Kette mit einzel addressierbaren LEDs, wie WS28xx und ähnliche, angeschlossen werden können. Wie die LEDs in einer Kette auf die Koordinaten des rootview (Rechteck, das alle LEDs umfasst) abgebildet werden, bestimmt der Initialisierungsstring:
[ledtype:[leddevicename:]]numberOfLeds:[x:dx:y:dy:firstoffset:betweenoffset][XYSA][W#whitecolor]
Hinweis
Wenn der Initialisierungsstring bei der --ledchain
Kommandozeilenoption nur none
lautet, dann wird beim Start nur ein leeres ledchainarrangement angelegt; die LED-Ketten können nachträglich mit der addledchain()-Funktion hinzugefügt werden.
Die einzelnen Elemente sind
- ledtype
-
Gibt den Typ und weitere Parameter für die Ansteuerung der LEDs an. Normalerweise in der Form
chip.layout
, für genauere Kontrolle der Parameter kann es auchchip.layout.tmaxpassive.maxretries
sein.- chip
- Typ des LED-chips. Derzeit unterstützt: WS2811, WS2812, WS2813, WS2815, P9823, SK6812.
- layout
- Kanalzuordnung. Derzeit unterstützt: RGB, GRB, RGBW, GRBW, RBG, GBR, BRG, BGR, RBGW, GBRW, BRGW, BGRW. Das häufigste Layout bei WS28xx-Chips ist GRB.
- tmaxpassive
- maximale Pausenzeit zwischen zwei Bits in µS. Wenn der Wert nicht angegeben wird oder auf 0 gesetzt, wird der Standardwert des jeweiligen Chips verwendet (10..80µS). Bei unvermitteltem Flackern, besonders bei alten WS2812-Chips, kann es helfen, die Pause zu verkürzen.
- maxretries
- Maximale Anzahl von Versuchen, die Daten für ein Update zu senden. Je nach Treiber und Interrupt-Situation auf dem Zielsystem muss ein Update abgebrochen werden, bevor alle LEDs der Kette aktualisiert werden konnten. In diesem Fall wird das Update wiederholt, aber nicht öfters als maxretries (Standardwert: 3).
Aus Rückwärtskompatibilitätsgründen sind auch folgende ledtype-Werte (ohne separate chip/layout-Angabe) noch unterstützt: SK6812, P9823, WS2812, WS2813, WS2815_RGB.
- leddevicename
- Der Name des LED-Devices/Kanals, an dem die LED-Kette angeschlossen ist. Auf MT7688-OpenWrt-Plattformen mit p44-ledchain Kerneltreiber heissen die devices für die maximal 4 PWM-Ausgänge /dev/ledchain0 bis /dev/ledchain3. Auf ESP32 muss der Name des GPIO (bis zu 8 Kanäle möglich) angegeben werden: z.B. gpio22. Auf RaspberryPi ist der leddevicename optional (dann wird GPIO18 via PWM verwendet), kann aber als gpio21 (via PCM) oder gpio10 (via SPI) angegeben werden.
- numberOfLeds
- Die Anzahl LEDs in der Kette, die angesteuert werden sollen. Die Kette kann auch kürzer oder länger sein, aber wenn die Stromverbrauchsberechunng/-Begrenzung verwendet wird, muss die tatsächliche Anzahl LEDs übereinstimmen.
- x, dx, y, dy
- (optional) Diese Werte definieren den Koordinatenbereich im rootview, den diese LED-Kette abdeckt. Die X-Richtung ist die Laufrichtung der Kette, Y kommt zum Zug wenn eine Kette zu einer Fläche angeordnet wird, z.B. aufgerollt auf einen Zylinder, oder im Zickzack auf einer Fläche (s. XYSA-Flags). Fehlen diese Angaben, wird die ganze Kette in X-Richtung ab x=0 und y=0 gemappt.
- firstoffset
- (optional) Gibt die Anzahl nicht angesteuerter LEDs am Anfang der Kette an. Dies kann verwendet werden, wenn z.B. von einer Kette die ersten LEDs aus Montagegründen gar nicht sichtbar sind, und wenn von derselben LED-Kette verschiedene Abschnitte auf verschiedene Koordinaten gelegt werden sollen.
- betweenoffset
- (optional) Bei flächigen Matrix-Anordnungen, wo die LED-Kette zick-zack-förmig geführt wird, sind üblicherweise auch zwischen den Reihen eine oder mehrere LEDs nicht sichtbar. Mit diesem Parameter können diese aus der Matrix ausgenommen werden
- XYSA
-
(optional) Flags für die Anordnung:
- X invertiert die X-Koordinate
- Y invertiert die Y-Koordinate
- S vertauscht X und Y-Koordinate ("Swap")
- A ändert die X-Laufrichtung für jede Y-Reihe ("Alternate") für Zickzack-Anordnungen.
- W#whitecolor
- (optional) Bei Ketten mit weisser vierter LED kann hier die Farbe und Helligkeit, die der Weisskanal erzeugt, angegeben werden (im Webcolor-Format, also etwa
W#AA8
oderW#A5A582
). Damit kann für unterschiedliche RGBW-Ketten korrekte Farbwiedergabe erreicht werden. Ohne Angabe wird der Weisskanal als reinweiss mit doppelter Helligkeit gegenüber den R,G,B-Kanälen angenommen.
Beispiele
Eine übliche WS2813-LED-Kette mit 200 LEDs kann einfach mit Namen und Anzahl LEDs in x-Richtung ab Koordinate x=0, y=0 eingebunden werden:
WS2813.GRB:/dev/ledchain0:200
Auf dem RaspberryPi (nur ein Ausgang, leddevicename irrelevant) reicht sogar:
WS2813.GRB:200
Um die Laufrichtung umzukehren (x=0 für letzte LED in der Kette, nicht für die erste LED):
WS2813.GRB:/dev/ledchain0:200:X
Wenn die Kette aber auf einem Zylinder aufgerollt wird, so dass 20 Windungen à 10 LEDs entstehen, und dies als 10x20-Matrix angesteuert werden soll, z.B. für die Verwendung eines torch-Views (s. Anleitung hier):
WS2813.GRB:/dev/ledchain0:200:0:20:0:10
Nun aber eine etwas anspruchsvollere Anordnung:
Hier sehen wir eine Kette von 106 LEDs, die am Anfang 3 inaktive LEDs hat, nachher eine 14x6-Fläche bildet (mit jeweils zwei inaktiven LEDs in den Knicks), und am Ende noch 9 LEDs in Y-Richtung wie eine Kerze auf den Kuchen setzt.
Die 6x14-Fläche kann so beschrieben werden:
WS2813.GRB:/dev/ledchain0:106:0:14:0:6:3:2:XA
Das X-Flag ist notwendig weil die erste Zeile auf der X-Achse rückwärts geht, das A-Flag, weil die Zeilenrichtung sich jede Zeile ändert.
Um nun die 9 "Kerzen"-LEDs auf die Koordinaten x=6, y=6..14 zu legen, braucht es einen weiteren Initialisierungsstring, der sich auf einen anderen Abschnitt der gleichen LED-Kette (gleicher leddevicename) bezieht:
WS2813.GRB:/dev/ledchain0:106:6:1:6:9:97:S
Hier sind die ersten 97 LEDs inaktiv (diese werden ja von der ersten Definition mit der 6*14-Matrix abgedeckt). Weil die LEDs in diesem Abschnitt in Y-Richtung (statt X) laufen, braucht es das S-Flag.
Verwendung auf der Commandline
Bei Geräten und Installationen mit einem festen LED-Layout, z.B. wenn die LEDs untrennbarer Teil des Geräts sind, kann es sinnvoll sein dass das Layout beim Start des Geräts schon definiert ist. In diesem Fall kann ein LED-Chain Initialisierungsstring und zusätzliche Paramter für die Leistungsbeschränkung oder das Timing direkt auf der command line übergeben werden:
vdcd --ledchain WS2813.GRB:/dev/ledchain0:200 --ledpowerlimit 6000 --ledrefresh 20
Die Option --ledpowerlimit xx
beschränkt den Gesamtverbrauch (ungefähr) auf xx Milliwatt, indem alle LEDs entsprechend gedimmt werden, falls das anzuzeigendene Pixelmuster zuviel Helligkeit verlangt. Das funktioniert nur richtig, wenn sowohl der LED-Typ als auch die effektiv angeschlossene Anzahl LEDs korrekt angegeben werden.
Mit der Option --ledrefresh yy
kann das minimale Refreshintervall yy in Millisekunden angegeben werden. Bei sehr langen LED-Ketten muss dieses möglicherweise höher gesetzt werden, damit es nicht flackert.
Auf diese Weise ist in P44-xx-Geräte mit LED-Ausgang üblicherweise eine Default-Anordnung für LED-Ketten vordefiniert. Zum Beispiel ist beim Raspberry-Pi-Image P44-DSB-X standardmässig an GPIO18 eine LED-Kette von 255 LEDs WS2812/13 mit GRB-Layout definiert, was z.B. für einfache Lichtstreifenbeleuchtungen ausreicht.
Falls beim Start aber noch gar keine LED-Chain definiert werden soll (sondern erst später mit p44script, s. unten), ist das möglich durch Angabe von none
:
vdcd --ledchain none
Verwendung in p44script
Sobald das LED-Layout komplexer wird, kann die Definition auch über p44script erfolgen. Um die richtige Definition der Abschnitte zuerst auszuprobieren, kann das auch interaktiv und mehrfach hintereinander im REPL/IDE geschehen. Ist das korrekte Layout gefunden, kann das Setup ins mainscript kopiert werden, so dass es beim Start des Geräts jeweils ausgeführt wird. Es gibt eine Reihe von Funktionen für das Management von LED-Ketten, s. Script-Referenz.
Eine typische Einricht-Sequenz im mainscript kann z.B. so aussehen:
// bestehende Definition löschen
removeledchains()
// drei Ketten, die zusammen ein schmales Rechteck von 600*3 Pixel bilden
addledchain("WS2815.RGB:/dev/ledchain0:800:0:600:0:1")
addledchain("WS2815.RGB:/dev/ledchain1:800:0:600:1:1")
addledchain("WS2815.RGB:/dev/ledchain2:800:0:600:2:1")
// root-view in der Grösse anpassen
lrg.configure(ledchaincover()).set("fullframe",true)
// Konfiguration des rootview ausgeben zur Kontrolle
lrg.status()
Der rootview selber wird durch das Ändern der LED-Chain-Anordnungen nicht verändert, auch nicht in der Grösse. Der lrg.configure(...)
-Befehl dient dazu, dass die Grösse der neuen Anordnung angepasst wird (d.h. dem Umrissrechteck ledchaincover()
entspricht, das alle definierten LED-Chain-Abschnitte umfasst)
Um dann zu sehen, ob die LEDs alle funktionieren, kann beispielsweise die Hintergrundfarbe des root view gesetzt werden:
lrg.set('bgcolor','#800') // ganzer Hintergrund rot (halbe Helligkeit)
lrg.set('bgcolor','#000') // wieder aus
View-Konfiguration
Wie oben beschrieben, gibt es verschiedene View-Typen. Jeder View-Typ hat gewisse Eigenschaften unveränderlich eingebaut (gemäss seinem Typ), aber alle Views müssen z.B. in ihrer Grösse, Position, Farbe, u.v.a.m konfiguriert werden. Als lrgraphics entwickelt wurde (für dieses Projekt), gab es noch kein p44script, und die ganze Konfiguration der Views wurde in JSON-Files festgelegt. Deshalb kann eine ganze View-Hierarchie auch jetzt noch komplett in JSON formuliert und in den root-view geladen werden.
Mit p44script wurden die View-Eigenschaften direkter zugänglich, zuerst mit makeview()
, configure()
und set()
und seit Firmware 2.7.0.x/1.7.0.x dadurch, dass views direkt Objekte in p44script sind und die Eigentschaften als Objekt-Felder zur Verfügung stehen.
Die Beispiele in dieser Dokumentation verwenden deshalb die eine oder andere Art von Zugriff auf view-Eigenschaften.
JSON-Konfiguration
Es gibt ein paar Möglichkeiten, JSON-Konfiguration ganz direkt aus einem File einzulesen, ganz ohne p44script (z.B. durch Angabe eines Filenamens beim Erstellen einer LEDChain-Leuchte im Feld "Feature config", s. auch Beispiele). In der Regel aber wird die Funktion configure() verwendet, um in JSON formulierte Konfiguration einzulesen:
lrg.configure({ "bgcolor":"#300" })
Hier wird eine Eigenschaft des rootview, bgcolor, gesetzt.
Mit der status()-Funktion kann der Zustand eines Views abgerufen und inspiziert werden. Da lrg den rootview repräsentiert, kann mit
lrg.status()
die gesamte aktuelle View-Hierarchie abgerufen werden.
Die schon oben verwendete Funktion set macht im Prinzip dasselbe wie configure(), aber ist praktischer für das Setzen einzelner Werte:
lrg.set("bgcolor", hsv(120,0.4,0.5))
Hier wird der Farbwert (leichtes grün) mit der hsv-Funktion aus dem Farbradwinkel (hue, 120°), der Sättigung (40%) und der Helligkeit (50%) berechnet.
Die JSON-Darstellung ist praktisch, um kompliziertere Setups als Ganzes zu definieren und einzulesen. Zum Beispiel braucht es für eine Laufschrift einen scroller, in dem ein text eingebunden ist. Auf P44-LC-xx-Geräten gibt es eine entsprechende Konfiguration als Resource namens plan44text.json, also lässt sich ein Laufschriftbeispiel erzeugen mit:
var laufschrift = makeview("plan44text.json")
Um die Laufschrift (oder sonst einen mit makeview erzeugten View) sichtbar zu machen, muss er als Ebene zum rootview (welcher vom Typ stack ist) hinzugefügt werden:
lrg.addview(laufschrift)
Damit wird die Laufschrift sichtbar, oder zumindest der Teil davon, der tatsächlich von LEDs abgedeckt ist. Die Beispielkonfiguration hat die Grösse 20 x 10, also braucht es mindestens 10 Reihen a 20 LEDs für diesen Fall.
Um den Inhalt des JSON zu sehen und ggf. angepasst zu verwenden, kann der Inhalt des Files abgerufen werden:
// Fixe, endlos wiederholende Laufschrift
readfile('+/plan44text.json')
// Vorlage für Laufband-Anzeige mit beliebigem Inhalt (auf dem Stack "CONTENT"), endlos wiederholend
readfile('+/repeatingticker.json')
// Vorlage für Laufband-Anzeige, die programmatisch von wechselndem
// Input gespiesen wird mit `on(scroller.empty()) { ... }`
readfile('+/ondemandticker.json')
Direktzugriff auf View-Eigenschaften als Objekt-Felder
Seit Firmware 2.7.0/2.7.0.34 (bzw. 1.7.0/1.7.0.34 für P44-LC) sind die views als strukturierte Objekte in p44script abgebildet, also können sie auch direkt zugewiesen werden, was noch einfacher ist als set()
zu verwenden:
lrg.bgcolor = hsv(120,0.4,0.5)
Ebenso können die aktuellen Werte einzeln abgerufen werden:
log("Die Hintergrundfarbe is aktuell %s", lrg.bgcolor)
Auch die status()-Funktion ist meist nicht mehr notwendig, anstatt zu schreiben lrg.status()
reicht es jetzt im REPL der IDE-Konsole einfach einzugeben:
// ganze View-Hierarchie
lrg
// ein spezifischer view anhand seines Labels
lrg.findview('labelname')
// der View einer LEDChain-Leuchte namens 'Lampe'
device('Lampe').view
View-Eigenschaften
Gemeinsame Eigenschaften
Alle Views, inklusive plain haben die folgende Eigenschaften:
type
- diese Eigenschaft is nur lesbar und gibt den view-Typ als String an. Wenn mit
makeview()
oderconfigure()
neue Views erzeugt werden, muss diese Eigenschaft in der Konfiguration auf den gewünschten View-Typ gesetzt werden. label
- ein String um dem View einen Namen zu geben - mit diesem Namen kann der View in einer Hierarchie mit
findview()
wieder gefunden werden. id
- ein String der die view-Instanz (also die aktuell existierende Version im Speicher) identifiziert, in einer Form wie 'V_7758DBC0', mit der der View auch mit
findview()
gefunden werden kann. - Die ID ist nach jedem Neustart oder Neu-Erzeugen verschieden, eignet sich also nur als temporäres Erkennungmerkmal (das z.B. der LED-Simulator intern verwendet). Die id kann nicht gesetzt werden. Um Views persistent wiederauffindbar zu machen, gibt es das label, s. oben.
alpha
- Die Undurchsichtigkeit (opaqueness) des ganzen Views. 0=vollkommen durchsichtig, unsichtbar, 255=komplett undurchsichtig
- Anmerkung: auch ein undurchsichtiger View (alpha=255) kann teilweise oder komplett durchsichtige Pixel beinhalten. Die Durchsichtigkeit des ganzen Views wird mit der Durchsichtigkeit der Pixel kombiniert bei der Anzeige.
- Wenn in der P44-Weboberfläche "Ledchain"-Leuchten definiert werden, dann wird die
alpha
-Eigenschaft vom Helligkeits-Regler der Leuchte gesteuert. visible
- Gibt true zurück wenn alpha nicht Null ist, ansonsten false.
- Wenn visible ein Wert zugewiesen wird, der nicht false ist, wird alpha auf 255 gesetzt, andernfalls auf 0.
bgcolor
- Die Hintergrundfarbe des Views, also die Farbe aller Pixel, auf denen kein Inhalt angezeigt wird.
-
Die Farbe wird als String im Web-Color-Format angegeben. Folgende Formate sind unterstützt:
#rrggbb
- wobei rr,gg,bb zweistellige Hexadezimalzahlen 00..FF (dezimal: 0..255) sind, die die Farbanteile rot, grün und blau darstellen.
#rgb
- dies ist eine Kurzschreibweise für #rrggbb, d.h.
#123
bedeutet#112233
#aarrggbb
- wie #rrggbb, wobei aber aa eine vierte Hexadezimalzahl 00..FF (dezimal: 0..255) ist, welche den Alpha-Wert (die Undurchsichtigkeit) angibt. Wird der Alphawert nicht angegeben, so wird er als FF (=undurchsichtig) angenommen.
#112233
ist also gleichbedeutend mit#FF112233
. #argb
- dies ist eine Kurzschreibweise für #aarrggbb, d.h.
#D123
bedeutet#dd112233
color
- Die Vordergrundfarbe (im gleichen Format wie bgcolor). Bei Views vom Typ plain ist das die Farbe, in der das content-Reckteck (s. unten) dargestellt wird. Bei anderen View-Typen wird color entweder als Basisfarbe für die Darstellung verwendet (lightspot, torch, text) oder ist nicht relevant, weil der content eigene Farben hat (image, stack, scroller, sequencer).
- Wenn in der P44-Weboberfläche "Ledchain"-Leuchten definiert werden, dann wird die
color
-Eigenschaft von den Farb-Reglern der Leuchte gesteuert. x, y, dx, dy
- Diese vier Werte bilden das frame (den Rahmen) für den View.
- x und y geben an, wo in Bezug auf den übergeordneten View die linke untere Ecke dieses Views positioniert ist.
- dx und dy geben die Breite und Länge des frames an.
content_x, content_y, content_dx, content_dy
- Diese vier Werte bilden das content- (Inhalts-) Rechteck des Views.
- content_x und content_y geben an, wo in Bezug auf den Nullpunkt des frames der Nullpunit des contents positioniert ist (bei rechteckigem Content die linke untere Ecke).
-
content_dx und content_dy geben die Grösse des contents an. Bei rechteckigem Content ist das die Breite und Länge des Umrissrechtecks, aber es kann auch eine andere sinnvolle Dimension sein, z.B. sind es bei lightspot die Halbachsen der Leuchtpunkt-Ellipse. Je nach View-Typ wird die content-Grösse auch indirekt vom Inhalt bestimmt, etwa bei image oder text views. Beim plain view wird damit einfach das Rechteck definiert, das in der Vordergrundfarbe (color) dargestellt werden soll.
framing
-
Diese Eigenschaft bestimmt, wie der Rahmen funktioniert. Es ist ein String, in dem eines oder mehrere (mit
|
getrennt, keine Spaces!) der folgenden Flags angegeben sind:none
- Kein framing, der Inhalt wird weder beschnitten noch repetiert
clipXmin, clipXmax, clipX, clipYmin, clipYmax, clipY, clipXY
- Inhalt, der über das frame hinausreicht, wird vom Rahmen beschnitten. Das kann für jede Seite des Rahmens separat (Xmin, Xmax, Ymin, Ymax), für eine Richtung (X,Y) oder für den ganzen Rahmen gemeinsam (XY) angegeben werden.
repeatXmin, repeatXmax, repeatX, repeatYmin, repeatYmax, repeatY, repeatXY
-
Der Inhalt des frame wird in die angegebene Richtung(en) wiederholt. Damit lassen sich Hintergrundmuster oder sich "endlos" wiederholende Laufschriftem etc. realisieren.
orientation
- Hiermit wird die grundsätzliche Ausrichtung des content im Bezug auf das frame bestimmt, und ermöglicht, den content zu spiegeln und in 90-Grad Schritten zu drehen.
- Diese Eigenschaft ist dafür da, die grundsätzliche Ausrichtung des content im Pixelraster zu bestimmen. Mit rotation, scrollX,Y und zoomX,Y kann die Darstellung genauer angepasst werden.
-
Der Wert ist ein String, in dem eines oder mehrere (mit
|
getrennt, keine Spaces!) der folgenden Flags angegeben sind:right
- die "normale" Orientierung, X-Achse waagrecht von links nach rechts und Y-Achse senkrecht von unten nach oben.
left, down, up
- Andere Richtungen der X-Achse
swapXY
- X und Y-Koordinaten vertauscht
flipX, flipY
- X- oder Y-Koordinate gespiegelt
rotation
- Drehwinkel in Grad (0..360) um den der content um seinen Nullpunkt (content_x, content_y) gedreht werden soll, in Uhrzeigersinn
scroll_x, scroll_y
- Verschiebung des content, relativ zu seinem Nullpunkt (content_x, content_y). Die Verschiebung wird nach der Rotation angewendet, d.h. die X-Scrollrichtung ist um den in rotation angegebenen Winkel verdreht zur Waagrechten. Damit lassen sich z.B. schräge Laufschriften realisieren.
- Die Scrollwerte müssen nicht ganzzahlig sein, es ist möglich den Inhalt um Bruchteile von Pixeln (Subpixel) zu verschieben. Das Graphiksystem berechnet die Resultatpixel als Durchschnitt der angeschnittenen Pixel (Anti-Aliasing).
zoom_x, zoom_y
-
Vergrösserungsfaktor für den content, bzw. Verkleinerungsfaktor wenn <1.
Subpixel-Berechnung braucht Performance
Nicht-ganzzahlige Werte für scroll_x/y, Werte ungleich 1 für zoom_x,y und Werte ungleich 0 für rotation schalten eine aufwendigere Pixelberechung ein, die für einen angezeigten Pixel verschiedene Anteile von content-Pixeln einbeziehen muss, und deshalb zusätzliche Performance braucht. Diese Einstellungen sollten also nur verwendet werden, wenn es visuell Sinn macht.
timingpriority
- Wenn dieser Wert auf true gesetzt ist, hat der View "Timing-Priorität", das heisst, auf ihm passierende Änderungen werden möglichst sofort angezeigt.
- Das kann bei verschachtelten Scrollern oder Animationen auf Scrollern problematisch sein, weil die zwei Bewegungen kombiniert eine zu hohe Update-Rate der LEDs bedingen, welche die Hardware nicht umsetzen kann - dann sieht das Ergebnis ruckelig aus. In solchen Fällen kann in untergeordnetem Views timingpriority auf false gesetzt werden, damit die übergeordnete Bewegung, z.B. eine Laufschrift, die Update-rate allein bestimmt und dadurch ruckelfrei erscheint. Das Timing untergeordneter Bewegungen wird dann dem übergeordneten, priorisierten Timing angepasst, d.h. es ist nicht mehr ganz exakt - das ist aber gerade in schnell laufenden Scrollern nicht wahrnehmbar (ganz im Gegensatz zum Ruckeln des Scrollers selber, wenn er keine Priorität hat).
subsampling
- Wenn dieser Wert auf true gesetzt ist (was der Default ist), werden die Farben für ein angezeigtes Pixel bei nicht-ganzzahligem scroll_x,y, zoom_x,y!=1 oder rotation!=0 aus den Flächenanteilen der betroffenen content-Pixel berechnet, was zu einer schön kontinuierlich wirkenden Darstellung führt. Diese Rechnung ist aber relativ aufwendig, und kann mit subsampling=false ausgeschaltet werden. Ob das Sinn macht hängt von der gewünschten Optik ab - aber z.B. bei verschachtelten Views kann es sein, dass es reicht, wenn der oberste View subsampling macht, wenn die darin verschachtelten das nicht machen ist das u.U. wenig sichtbar, spart aber Performance.
invertalpha
- Wenn dieser Wert auf true gesetzt ist, wird die Transparenz-Anteil der content-Pixel invertiert. Das kann besonders im Zusammenspiel mit
contentismask
verwendet werden contentismask
- Wenn dieser Wert auf true gesetzt ist, wird nur Transparenz-Anteil der content-Pixel verwendet, die Farbe aller Pixel wird aus der color-Einstellung genommen. Damit kann z.B. ein Bild oder Text als Maske verwendet werden, um von einem dahinter liegenden view (in einem stack) gewisse Pixel abzudecken und andere durchscheinen zu lassen.
sizetocontent
- Wenn dieser Wert auf true gesetzt ist, und das content-Rechteck verändert wird, so wird die frame-Grösse (dx, dy) des Views an den content angepasst. Das kann bei Views, wo der Inhalt die Grösse vorgibt (Bilder, Text) nützlich sein.
autoadjust
- Wenn dieser nicht
none
ist, dann wird dieser View in Position und Grösse angepasst, wenn der parent-View (stack, scroller oder sequencer) seine Geometrie ändert. -
Der Wert ist ein String, in dem eines oder mehrere (mit
|
getrennt, keine Spaces!) der folgenden Flags angegeben sind:none
- (Standardwert) keine automatische Anpassung.
fillX, fillY, fillXY
- Grösse wird in der entsprechenden Richtung an die Grösse des parent-View angepasst
noadjust
- Wenn dieses Flag zusätzlich gesetzt wird, wird zwar das frame angepasst, nicht aber nachher der content auf die Grösse des Frames gesetzt.
z_order
- Dieser Wert hat nur eine Bedeutung, wenn der View ein Subview von einem stack ist. Dann werden die Subviews in der Reihenfolge von z_order aufeinandergelegt, der Subview mit dem höchsten z_order ist der vorderste (und verdeckt, je nach Transparenz, die dahinterliegenden).
Gemeinsame Methoden
In p44script sind Views Objekte, die auch einige Methoden haben:
configure(json)
,configure(resource_file)
,set(prop, val)
,findview(viewlabel)
,addview(view)
parent()
,remove()
,animator('propertyname')
,stopanimations()
,reset()
,clear()
- Dies sind die grundsätzlichen Methoden zum Arbeiten mit Views aus p44script, und sind in der p44script-Kurzreferenz beschrieben zusammen mit den globalen Funktionen zur Ansteuerung von LED-Ketten.
Für das Folgende sei v
ein View:
v.render()
- Stösst eine Aktualisierung der LED-Anzeige an. Normalerweise geschieht dies automatisch, die Funktion existiert vor allem zu Debugzwecken.
v.fullframe()
- Passt das content-Rechteck an, so dass es das ganze frame ausfüllt.
v.content_position(rel_x, rel_y, centered)
- Positioniert das content-Rechteck relativ zur Grösse des frames. rel_x und rel_y geben im Bereich -1 bis 1 die Position innerhalb des frames an, Werte >1 oder <-1 bewegen den content aus dem frame heraus. Wenn centered true ist, dann wird die Mitte des content-Rechtecks positioniert, ansonsten der Ursprung (linke untere Ecke). rel_x oder rel_y kann auch als null übergeben werden, wenn die entsprechende Koordinate nicht verändert werden soll.
v.content_size(rel_dx, rel_dy)
- Justiert die content-Grösse relativ zur Grösse des frame. rel_dx und rel_dy geben für Breite und Höhe separat den Skalierungsfaktor im Verhältnis zum frame an, wenn beide 1 sind, bekommt der content die gleiche Grösse wie das frame. rel_dx oder rel_dy kann auch als null übergeben werden, wenn nur eine Dimension in der Grösse angepasst werden soll.
v.get(x, y)
- Liest den aktuellen Farbwert des Pixels an Position x, y (in frame-Koordinaten, also relativ zur linken unteren Ecke).
v.animations()
- Gibt einen Array mit allen auf dem View aktiven Animatoren zurück.
Color Effect
Es gibt keinen separaten "Color Effect" View, aber lightspot
, canvas
und text
haben denselben "Color Effect" Farbgradienten-Generator zugrunde, der folgende Eigenschaften hat:
brightness_gradient
,hue_gradient
,saturation_gradient
- Anteil der vollen Skala (0..1 bei Saturierung und Helligkeit, 0..360 bei hue) um die sich der entsprechende Wert in einem Effektzyklus (s.
effect_cycles
) ändern soll, ausgehend von der eingestellten Vordergrundfarbe (color). brightness_mode
,hue_mode
,saturation_mode
-
Verhalten der jeweiligen Farbkomponente im Gradienten. Der Wert ist ein String, in dem eines oder mehrere (mit
|
getrennt, keine Spaces!) der folgenden Flags angegeben sind:none
- Ausgeschaltet, Farbkomponente wird nicht verändert im Verlauf des Gradienten.
square
- Rechteck-Übergang bei 50%.
linear
,sin
,cos
,log
,exp
- Kurvenform des Übergangs.
norepeat
,cyclic
,oscillating
,unlimited
- Repetieroptionen der Kurve.
inverted
- Invertierung der Kurvenform.
effect_cylces
-
Wieviele Zyklen angewendet werden zur Berechnung des Gradienten über die Grösse des content. Damit lassen sich Wiederholungen für wellenförmige Farbeffekte etc. erreichen.
Die folgende Abbildung zeigt den Aufbau zwei verschiedener Gradienten:
transparent_fade
- wenn true, dann steuert die Helligkeits-Komponente (
brightness
) die Transparenz des Pixels, d.h. bei Null Helligkeit wird der Pixel durchsichtig, andernfalls bleibt der Pixel undurchsichtig und wird bei Helligkeit Null (deckend) schwarz. radial
- wie der Farbeffekt sich "ausbreitet": Wenn true, dann wird der Gradient vom content-Ursprung her radial verwendet, d.h. der Verlauf ist abhängig von der Distanz zum Ursprung und sieht demzufolge kreis/ovalförmig aus. Ansdernfalls breitet sich der Effekt entlang der X-Achse aus.
effect_zoom
- Skalierung/Begrenzung des sichtbaren Effekts gegenüber der content-Grösse. Wenn negativ, dann ist der Zoom unendlich bzw. der Effekt breitet sich soweit aus wie es das framing des Views erlaubt.
effect_wrap
- Wenn true, wird der Effekt bei grösserem Zoom wiederholt, andernfalls bleibt die Farbe am Ende des Effekts stehen.
Lightspot
Lightspot baut auf dem ColorEffect auf (s. oben), und verwendet den Gradienten, um einen "Lichtpunkt" zu erzeugen. Anders als die meisten rechteckigen views verwendet lightspot die Grössenangaben des content (content_dx, content_dy) als Radien (Halbachsen) für den Lichtpunkteffekt (Ausdehnung des Gradienten).
effect_zoom
kann aber die effektive Grösse des Lichtpunkts beschneiden (<1) oder ausdehnen (>1), und kann als eine Art "Blende" betrachtet werden.
Text
Der Textview kann einen Text in Pixelschrift in verschiedenen Fonts und mit den Einfärbungsoptionen des color effects, z.B. mit einem Farbgradient, darstellen. Wenn kein Farbeffekt konfiguriert ist, erscheint der ganze Text in der Vordergrundfarbe (color
).
text
- String für den anzuzeigenden Text. Nicht alle Fonts unterstützen alle Sonderzeichen, unbekannte Zeichen werden als Rechteck angezeigt. Der Text ist immer einzeilig, Steuerzeichen werden als unbekannte Zeichen angezeigt.
font
- Name der zu verwendenden Schriftart. Folgende Schriften sind eingebaut:
3x3
,5x5
,5x7
,5x8
,m3x6
,m5x7
,m6x11
,bios
,sixtyfour
,vcr_osd_mono
. Zusätzliche Schriftarten können mithilfe vonloadfont
aus.lrgf
-Dateien geladen werden. Die Namen aller verfügbaren Schriftarten können mithilfe der p44script-Funktionlrgfonts()
abgerufen werden. spacing
- Anzahl leerer Pixel zwischen zwei Zeichen. Standard = 2.
bolden
- Wenn >0 wird ein Fettschrift-Effekt erzeugt, indem die Schriftvorlage bolden mal um jeweils ein Pixel verschoben aufeinandergelegt wird.
stretch
- Wenn >0 wird jede Pixelspalte der Schriftvorlage stretch Male repetiert, was die Schrift in die Breite zieht.
collapsed
- Wenn auf true gesetzt, fällt der Text (und das content-Rechteck) auf Null Grösse zusammen, und wird daher unsichtbar. Im Unterschied zu
visible==false
ändert sich dadurch die Grösse, was bei aneinandergereihten Textviews eine Rolle spielen kann. loadfont(fontname)
- Lädt eine Schriftart aus einer
.lrgf
-Schriftartendatei (ein einfaches Format, das auf dem p44utils TLV-Serializer basiert und von der FontHexer2 App erstellt werden kann).
Canvas
Ein Canvas-View ist eine Zeichenfläche, auf der die Farbe jedes einzelnen Pixels im content-Reckteck einzeln gesetzt werden kann. Wird die Grösse des content-Recktecks verändert, werden alle Pixel gelöscht (transparent gemacht). Pixel ausserhalb des content-Rechtecks erscheinen in der Hintergrundfarbe (bgcolor
)
dot(x, y)
- Mit dieser Methode wird der Pixel an der content-Koordinate (x, y) auf die aktuell eingestellte Vordergrundfarbe (s.
color
) gesetzt. line(x0, y0, x1, y1)
- Zeichnet eine gerade Linie von (x0,y0) nach (x1,y1). Die Pixel der Linie werden auf die aktuell eingestellte Vordergrundfarbe (s.
color
) gesetzt, bzw. falls konfiguriert, mit den Einfärbungsoptionen des color effects, z.B. mit einem Farbgradient entlang der Linie, dargestellt. copy([sourceview ,] x, y, dx, dy, dest_x, dest_y [, transparentonly [, fromcontent])
- Kopiert die Pixel im Rechteck (x,y,dx,dy) aus dem Canvas selbst oder, wenn sourceview angegeben ist, aus einem anderen View, zum Punkt (dest_x,dest_y) im Canvas. Wenn transparentonly gesetzt ist, werden vollständig transparente Pixel nicht kopiert. Wenn fromcontent gesetzt ist, gibt das Quellrechteck Content-Koordinaten anstelle von Frame-Koordinaten an, so dass die Quellpixel nicht von Content-Offsets, Zooming, Scrolling und Rotation beeinflusst werden.
pixels
- gibt die Anzahl Pixel der Zeichenfäche zurück (content_dx * content_dy)
bytes
- gibt die Anzahl Bytes zurück, die die Zeichenfläche im Speicher belegt.
Image
Dieser View-Typ kann ein PNG-Bild laden und darstellen. - Wenn die Grösse des content kleiner als das Bild ist, wird es entsprechende oben und rechts beschnitten. - Wenn die content-Grösse aber grösser als das Bild eingestellt wird, dann wird die oberste Pixelzeile des Bildes nach oben und die am rechten Rand nach rechts dupliziert. Damit kann z.B. ein Streifenmuster als platzsparendes Bild von 1 pixel Höhe gezeichnet werden, kann aber dennoch eine höhere Fläche ausfüllen.
loadimage(filepath)
-
Diese Methode liest ein PNG-Image vom angegebenen filepath in den View ein, und setzt die Grösse (content_dx, content_dy) entsprechend der Grösse des Bildes.
Achtung, Speicherbedarf
Ein geladenes PNG-Bild braucht im Speicher für jedes Pixel 4 Bytes. Wenn grosse Bilder geladen werden, kann die Anwendung schnell an ihre Speichergrenzen kommen. Bei der niedrigen Auflösung einer LED-Pixel-Anzeige sind grosse Bilder auch nicht sinnvoll. Es empfiehlt sich, zu verwendende PNG-Bilder in einem Bildbearbeitungsprogramm exakt auf die notwendige Auflösung herunterzuskalieren, bevor sie verwendet werden (auch wenn mit
zoom_x
/zoom_y
Skalierung innerhalb lrgraphics möglich ist). -
Ohne absolute Pfadangabe oder spezielles Prefix wird filepath als relativ zum p44script-Resourceverzeichnis (ein Verzeichnis im nicht-schreibbaren Bereich, das einige vordefinierte Bilder enthält) aufgefasst. Folgende speziellen Prefixe können verwendet werden:
_/tempfile
: bedeutet, dass tempfile relativ zum (füchtigen) p44script Temporärverzeichnis zu verwenden ist.+/file
: bedeutet, dass file relativ zum schreibbaren p44script-Datenverzeichnis im Flash-Speicher der Anwendung zu verwenden ist.
image_dx
,image_dy
- nur lesbar, gibt die effektive Grösse des geladenen Bildes wieder.
bytes
- nur lesbar, gibt den Speicherbedarf in Bytes des geladenen Bildes an.
Stack
Der stack (Stapel) ist ein sogenannter Container-View, d.h. er enthält andere Views, die seinen Inhalt bestimmen. Jeder dieser subviews wird anhand seines frame-Ursprungs (x, y) relativ zum Ursprung des contents des Stacks angezeigt. Wenn sich die Views überlappen, dann bestimmt z_order, in welcher Reihenfolge die Views übereinandergestapelt sind.
Ein häufiger Anwendungsfall für LED-Anzeigen sind Laufschriften, deshalb unterstützt der der stack nicht nur Übereinanderstapeln, sondern auch automatisches Aneinanderrreihen von Teilstücken, wie sie etwa in einer fortlaufend mit neuem Inhalt gefütterten Laufschrift/Graphik notwendig ist, und ebenso das automatische Entfernen von Teilstücken, die in einem scroller (s. auch dort) schon aus dem Blickfeld gescrollt sind und nicht mehr benötigt werden.
layers
- Ein Array, der die Ebenen ("Layer") des Stacks enthält, aufsteigend sortiert nach deren z_order ("vorderster" View zuletzt im Array). Dieser Array kann nicht direkt verändert werden, nur via pushview(), addview(), popview() etc.
positioningmode
-
Bestimmt die Positionierung von views die mit pushview() oder addview() hinzugefügt werden. Der Wert ist ein String, in dem eines oder mehrere (mit
|
getrennt, keine Spaces!) der folgenden Flags angegeben sind:appendLeft
,appendRight
,appendBottom
,appendTop
- Neu hinzugefügte views werden an der angegebenen Seite an die schon im stack befindlichen views angereiht.
- Bei purge() werden die ausserhalb liegenden Views auf der Gegenseite entfernt, also: beim Anfügen rechts mit appendright fallen bei purge() die am weitesten links liegenden views weg.
noAdjust
- Wenn dieses Flag gesetzt ist, wird die content-Grösse nicht verändert, auch wenn sich die frame-Grösse ändert
fillX
,fillY
,fillXY
- Diese Flags bewirken, dass neu hinzugefügte views in der Breite, Höhe oder beidem der content-Grösse des Stacks gleichgesetzt werden, um diesen auszufüllen.
pushview(view [,spacing [, fullframe]])
addview(view)
- Fügt den view dem stack-Inhalt hinzu. addview(view) ist dasselbe wie pushview(view, 0, false).
- Es hängt von positioningmode ab, wie der view positioniert wird. Wenn positioningmode gleich
none
odernoAdjust
ist, dann wird der view einfach an die Position (relativ zum content-Ursprung des Stacks) gesetzt, die durch view.x und view.y vorgegeben wird. - Wenn ein positioningmode gewählt ist, bei dem Views aneinandergereiht werden, gibt spacing an, mit wievielen Pixeln Abstand das geschieht.
- Ist fullframe gesetzt, dann wird der content des hinzugefügten views an den frame angepasst (das ist nützlich für den Fall, dass beim Hinzufügen der frame des hinzugefügten views verändert wird z.B. wegen positioningmode == 'fill…', und der Inhalt entsprechend angepasst werden soll)
popview()
- entfernt den "vordersten" subview vom stack. Das ist nicht notwendigerweise der zuletzt hinzugefügte, sondern der mit der höchsten z_order, da der stack immer in dieser Ordnung gestapelt ist.
- Um andere Positionen zu entfernen, muss die remove()-Methode des zu entfernenden Views aufgerufen werden.
purge(dx, dy, completely)
- Entfernt alle subviews aus dem stack, die nicht innerhalb des Rechtecks (dx,dy) Platz haben (bis auf einen - der letzte View im Stack wird nie entfernt, auch wenn er keinen Platz hat). Das Rechteck wird gemessen von der Kante aus, die mit append… in positioningmode angegeben ist, die Views fallen also an der Gegenseite weg. Das ist der Mechanismus, den man für fortlaufende Inhalte in einer Laufschrift braucht.
- Wenn completely = true ist, werden nur views zurückbehalten, die komplett in (dx,dy) Platz haben, andernfalls auch solche, von denen mindestens ein Teilbereich noch innerhalb (dx,dy) ist.
- Der purge-Mechanismus kann auch vom scroller verwendet werden, um automatisch herausgescrollte, nicht mehr benötigte Inhalte aus dem Stack zu entfernen, s. dort.
Scroller
Ein Scroller kann einen anderen view, den scrolledview, kontinuierlich scrollen, indem er seine eignenen scroll_y und scroll_y-Eigenschaften automatisch in einem bestimmtem Zeitintervall erhöht oder verringert.
Beim scroller ist der content, der mit scroll_x,y verschoben wird (ggf. zuvor auch mit zoom_x,y skaliert und mit rotation gedreht) ein gesamter View. Das heisst insbesondere, dass für endlos scrollende Inhalte das framing des scrolledview z.B. auf repeatX gesetzt werden kann, so dass auch aus einem kleinen scrolledview ein (nahezu) endlos langes Band mit repetierendem Inhalt wird.
Weil aber bei blossem Hochzählen der Scroll-Offset immer grösser würde, hat scroller zusätzlich einen Mechanismus, der den Scroll-Offset immer wieder mal "zurückspult", aber auf eine Weise, dass dies komplett unsichtbar ist (indem das "Zurückspulen" um ein ganzzahliges Vielfaches des Inhalts passiert). Das ist in der Verwendung von scroller meist egal, aber wenn der aktuelle scroll_x,y abgefragt wird muss man sich bewusst sein, dass dieser auch zwischendurch rückwärts zur Scrollrichtung springen kann.
Die sichtbare Scrollgeschwindigkeit setzt sich zusammen aus den Anzahl Pixeln (auch Bruchteile) Bewegung pro Schritt (step_x, step_y), und dem Zeit-Interval (interval) zwischen zwei Schritten. Bei langsamer Scrollgeschwindigkeit (weniger 1 Pixel pro 50mS) lohnt es sich, nicht das interval zu erhöhen, sondern die Schritte kleiner (<1, Subpixel) zu machen - weil dann kontinuierliche Übergänge gerechnet werden und der Eindruck einer sanften Bewegung entsteht, und nicht ein langsames pixelweises Hopsen.
Für Laufschriften/Graphiken mit wechselndem Inhalt, etwa ein News-Ticker etc. kann scroller via empty()/alertempty() einen Event aussenden, wenn der "Vorrat" an Inhalt (Views auf einem Stack, s. pushview() und positioningmode im stack View) ausgeht. Mit einem Handler on(v.empty()) { ... }
kann dann neuer Inhalt zur richtigen Zeit bereitgestellt werden.
scrolledview
- dies ist der View, der gescrollt wird. Das kann ein einfacher View sein, oder auch ein Stack, wenn der zu scrollende Inhalt selber aus mehreren Views (etwa Graphiken + Texte gemischt) besteht.
step_x
,step_y
- Veränderung von scroll_x bzw. scroll_y, die pro interval geschehen soll.
interval
- Zeit-Intervall zwischen zwei Scroll-Schritten (in Sekunden)
steps
- Anzahl verbleibender Scroll-Schritte, nur lesbar. Wenn ein Scrolling mit einer fixen Anzahl von Schritten gestartet wurde (s. startscroll()), gibt dieser Wert an, wieviele Schritte bis zum Stop noch verbleiben. Ist dieser Wert 0, dann ist der Scroller gestoppt (s. stopscroll()). Ist der Wert negativ, läuft der Scroller ohne Limit solange, bis stopscroll() aufgerufen wird.
syncscroll
- Wenn dieser Wert true ist, dann wird die Scrolldistanz über die Zeit exakt synchronisiert, d.h. wenn das System wegen temporärer Überlastung (z.B. wegen anderer Systemaktivitäten) mit der Bildberechnung nicht nachkommt, dann kann der Scoller vorwärts "hüpfen", um eine Verzögerung wettzumachen. Das ist z.B. wichtig wenn mehrere Geräte eine gemeinsame grosse Laufschrift steuern, und deshalb die Scrolldistanzen auch durch solche Störungen nicht auseinanderdriften dürfen. Wird syncscroll ausgeschaltet, versucht der scroller nicht, aufzuholen, und es gibt keine "Hüpfer", dafür aber Zeitabweichungen (was bei einem Einzelscroller meist kein Problem ist).
autopurge
- Wenn dieses Flag true ist, und der scrolledview ein stack ist, dann verwendet der scroller den purge()-Mechanismus des stacks automatisch, um diejenigen Views, die aus dem Bild gescrollt sind, zu entfernen. Das ist sehr praktisch für eine Laufschrift mit fortwährend änderndem Inhalt - dazu braucht es nur einen empty()-Handler (s. unten), der neue Views "on demand" produziert; autopurge erledigt die Entsorgung automatisch.
Für das Folgende sei s
ein Scroller:
s.startscroll(stepX, stepY, interval)
s.startscroll(stepX, stepY, interval, roundoffsets [,numsteps [,syncstart]])
- Startet den Scroller mit entsprechenden Werten für step_x, step_y und interval.
- Wenn roundoffset nicht false ist, dann wird scroll_x und scroll_y vor dem Start auf ganzahlige Pixel gerundet.
- Wenn numsteps angegeben wird und grösser 0 ist, läuft der Scroller die angegebene Anzahl Schritte und stoppt dann.
- Mit syncstart kann entweder eine absolute Zeit (als Sekunden seit 1.1.1970, s. epochtime()) oder, wenn <86400 ein Synchronisationszeitpunkt wie "zur nächsten vollen Viertelstunde" (_syncstart = 1560) angegeben werden, zu dem der Scroll startet. Dies ist nützlich um mehrere Scroller, die u.a. auf verschiedene Geräte verteilt sind, exakt zum selben Zeitpunkt zu starten. Ohne Angabe von syncstart* startet der Scroller sofort.
s.stopscroll()
- Stoppt den Scroller sofort.
s.purge()
- ruft auf dem scrolledview (wenn dieser ein stack ist) die Methode purge(dx,dy) auf, mit dx/dy so dass schon durchgescrollte Views entfernt werden. Wenn autopurge gesetzt ist, geschieht das automatisch und purge() muss nicht direkt aufgerufen werden.
s.remainingpixels()
- gibt ein Objekt mit zwei Feldern x und y zurück, welche angeben, wieviele Pixel "Reserve" der scrolledview noch hat, bevor sich der Scroller anfängt zu leeren (weil nichts mehr nachkommt). Negative Werte geben an, wieviele Pixel bereits leer sind weil nichts nachkommt. Wenn die Reserve "endlos" ist, weil entweder in die Richtung gar nicht gescrollt wird, oder weil der scrolledview ein framing mit repeatX/Y hat, dann ist das entsprechende Feld null.
s.remainingtime()
- berechnet, wieviele Sekunden lang der Scroller noch "Vorrat" hat (s. auch remainingpixels()), bevor er sich anfängt zu leeren, bzw. wenn negativ, vor wieviel Zeit der Vorrat ausgegangen ist. Wenn der Scrollinhalt (wegen repeatX/Y) endlos ist, wird null zurückgegeben (vor x.7.0.37: -0.000001).
s.empty()
-
gibt true zurück wenn der Vorrat an Anzeigematerial (das beim Scrollen neu sichtbar wird) erschöpft ist, also genau ab dann, wenn remainingtime() null wird. Die Besonderheit bei empty() ist aber, dass es eine Eventsource ist, welche in handler mit
on(v.empty()) { ... code ... }
verwendet werden kann, um in dem Moment, in dem dem Scroller der Vorrat ausgeht, Code auszuführen der neuen Inhalt produziert und mit pushview() hinzufügt:// Annahme: eine Leuchte 'Ticker' existiert, die mit dem // "Feature-Config" "ondemandticker.json" erstellt wurde. var scroller = device('Ticker').view on (scroller.empty()) { // Neuen Textview erstellen mit aktuellem Datum/Uhrzeit var t = makeview({ type:'text', text: " +++ Es ist jetzt " + formattime(), // Datum / Uhrzeit color: hsv(random(0,359,1)), // Zufällige Farbe sizetocontent: true }) // Hinzufügen scroller.scrolledview.pushview(t) // Ab jetzt wieder warnen, wenn erneut leer scroller.alertempty(true) } // Handler aufrufen wenn View leer scroller.alertempty(true) scroller.startscroll(1,0,0.05)
s.alertempty(enable)
- Damit empty() ein Ereignis auslöst, muss der Mechanismus jeweils "scharf geschalten" werden mit alertempty(true). Ansonsten würde der Handler die ganze Zeit aufgerufen, wenn nicht immer sofort genug Inhalt nachgeladen wird (was u.U. gar nicht möglich ist). Sobald der Handler einmal aufgerufen wurde, wird alertempty automatisch auf false gesetzt, und muss für die nächste "Runde" erneut freigeschaltet werden - z.B. als letzte Aktion im Handler (wie im Beispiel oben gezeigt).
Sequencer
Der sequencer ist ein View, der eine Anzahl anderer Views wie eine Diashow hintereinander ein/ausblenden kann. Dazu können mit pushstep() Views zusammen mit einer Anzeigezeit und optional einer Ein- und Ausblendzeit hinzugefügt werden. Mit start wird die Sequenz gestartet.
steps
- Dieses Feld ist nur lesbar und gibt einen Array mit den aktuellen steps des sequencers zurück, je als ein Objekt mit 4 Feldern view, showtime, fadeintime, fadeouttime.
Für das Folgende sei s
ein Sequencer:
s.pushstep(view, showtime [, fadeintime [, fadeouttime]]
- Fügt einen weiteren Schritt hinzu. view ist der zu zeigende View, showtime die Anzeigezeit in Sekunden.
- Optional kann eine Ein- und Ausblendzeit (fadeintime, fadeouttime) angegeben werden, innert der das alpha des views zwischen 0 und 255 bewegt wird.
s.start()
s.start(repeat)
- Startet die Sequenz. Wenn repeat true ist, dann läuft die Sequenz in einer Schleife, bis sie gestoppt wird. Andernfalls stoppt die Sequenz nach dem letzten Schritt automatisch.
s.stop()
- Stoppt die Sequenz, nicht aber laufende Animationen auf dem angezeigten View, insbesondere nicht eine laufende Ein- oder Ausblendanimation.
s.stopanimations()
- Stoppt die Sequenz und alle Animationen auf dem aktuell angezeigten View.
EPX (Expressive pixels)
Expressive Pixels ist ein in 2020 mit viel Trara vorgestelltes Format für LED-Pixelanimationen von Microsoft Research. Es ist einfach zu implementieren,und grundsätzlich genau für Pixelmatrizen wie p44lrgraphics geeignet, deshalb gibts den epx-View. Leider ist das Projekt bei Microsoft komplett eingeschlafen, es hat sich seit 2020 überhaupt gar nichts mehr getan. Die Windows-App dazu soll es noch geben.
Der epx-View kann "expressive pixel" Animationen im JSON-Format einlesen und darstellen. Die Breite des Inhalts (content_dx) muss passend zur Breite des epx eingestellt werden, das JSON-Format enthält diese Information nicht.
run
- Diese Eigenschaft muss auf true gesetzt werden um die Animation zu starten. Sie geht automatisch wieder auf false wenn die Animation endet.
loadepx(json)
loadepx(jsonfile)
- lädt eine Expressive Pixels Animation entweder aus dem direkt angegebenen (JSON)Objekt, oder wenn ein Filename übergeben wird, aus dem entsprechenden File.
Life (Conway's Game of Live)
Stellt Conway's "Game of Life" dar. Anfangs ist das Feld (content_dx,dy) leer, es muss einmal addrandom() oder placepattern() aufgerufen werden, damit etwas sichtbar wird.
generationinterval
- Zeit zwischen zwei Zell-Generation in Sekunden.
minstatic
- Wieviele Zyklen keine Bewegung auf dem Feld geschehen darf, wenn die Anzahl Zellen unter minpopulation ist, bevor automatisch einige Zellen gesetzt werden. Dieser Wert ist dazu da, bei leerem oder fast leerem Feld schnell neue Zellen einzustreuen, damit es nicht langweilig wird.
maxstatic
- Wieviele Zyklen keine Bewegung auf dem Feld geschehen darf, wenn die Anzahl Zellen über minpopulation ist, bevor automatisch einige Zellen gesetzt werden. Dieser Wert ist dazu da, bei einem Feld mit möglicherweise interessanten statischen Mustern diese eine Weile stehenzulassen, bevor neue Zellen eingestreut werden.
minpopulation
- Unter welcher Anzahl Zellen der minstatic-Wert gilt.
addrandom()
addrandom(max)
addrandom(min, max)
- Streut zufällig neue Zellen ca. in der angegebenen Anzahl, bzw. ohne Angabe automatisch passend für die Feldgrösse, um das Spiel neu zu beleben.
placepattern(pattern)
placepattern(pattern, x, y, orientation)
- setzt ein vordefiniertes (wohlbekanntes) Muster auf das Feld. pattern kann einer der folgenden Strings sein:
dot
,blinker
,toad
,beacon
,pentadecathlon
,rpentomino
,diehard
,acorn
,glider
. - x und y geben an wo das Zentrum des Musters plaziert werden soll, ohne Angabe wird es zufällig plaziert. Die orientation kann ein Wert von 0 bis 3 sein für die 4 möglichen Ausrichtungen.
Torch
Die "Torch" ist eine einfache Feuer-Animation, die mit einer auf oder in einem Zylinder aufgewickelten LED-Kette eine hübsche "Fackel" ergibt. Die Fackel gibt es als P44-LC-TORCH als Bausatz zu kaufen oder kann mit einem RaspberryPi selber gebaut werden.
torch hat eine Menge Parameter, man kann die Animation durch Experimentieren auch soweit verändern, dass sie als Regensimulation o.ä. brauchbar ist ;-) Grundsätzlich ist es eine Art Funkensimulation, jeder Punkt hat eine gewisse Energie und transferiert diese in jedem Zyklus aufwärts und seitwärts, und verliert Energie. Die Punkte werden dann entsprechend der Energie mit dem darunterliegenden coloreffect-Gradienten eingefärbt.
cycletime
- [Standard: 0.025] Zykluszeit für die Animation in Sekunden.
flame_min
- [Standard: 100] Minimale Energie in der "Flamme" (unterste Reihen).
flame_max
- [Standard: 220] Maximale Energie in der "Flamme" (unterste Reihen).
flame_height
- [Standard: 1] Höhe der "Flamme" (wieviele unterste Reihen).
spark_probability
- [Standard: 2] Wahrscheinlichkeit, dass sich aus dem "Feuer" ein "Funke" nach oben löst.
spark_min
- [Standard: 200] Minimale Start-Energie eines Funkens.
spark_max
- [Standard: 255] Maximale Start-Energie eines Funkens.
spark_tfr
- [Standard: 40] Energie-Menge (prozentual: 0=nichts, 255=alles) die der Funken nach oben mitträgt.
spark_cap
- [Standard: 200] Energie-Menge (prozentual: 0=nichts, 255=alles) die der Funken selber behält.
up_rad
- [Standard: 40] Ausstrahlung nach oben.
side_rad
- [Standard: 35] Ausstrahlung seitwärts.
heat_cap
- [Standard: 0] "Wärmekapazität" der unsichtbaren Punkte.
hotspark_min
- [Standard: 250] Schwellwert für die Darstellung als "heisser" Funke.
hotsparkcolor
- [Standard: '#AAAAFAFF'] Farbe für "heisse Funken". Normale Funken haben color als Basisfarbe mit dem Gradienten gemäss coloreffect.
hotsparkinc
- [Standard: '#000005FF'] Zusätzliche Farbe bei maximal heissem (255) Punkt.