No description
  • JavaScript 78%
  • Kotlin 13.8%
  • CSS 5.7%
  • HTML 1.3%
  • Python 1.2%
Find a file
2026-10-25 13:02:06 +01:00
app Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
Archive Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
gradle Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
tests Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
web Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
.gitignore Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
build.gradle.kts Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
COPYRIGHT Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
gradle.properties Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
gradlew Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
gradlew.bat Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
icon-fdroid.png Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
LICENSE Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
make-icon.py Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
make-toene.py Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
README.md Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
settings.gradle.kts Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00
stammlisten.json Stand vom 2026-10-25 2026-10-25 13:02:06 +01:00

Dunkelkammer

Damit ein Abzug reproduzierbar bleibt. Android-App auf der gemeinsamen Archiv-Schale, dazu der Bereich dunkelkammer im Archiv (Archive/server/app/bereiche/dunkelkammer.py).

Zweck ist Reproduzieren, nicht Vergleichen — Negative und Abzüge vergleicht man mit Lupe und Leuchtpult. Was hier steht, ist das, was nicht auf die Rückseite des Papiers passt.

Vier Arten Eintrag

Anders als die übrigen Apps hält diese vier Arten in einer Datei, unterschieden am Feld art:

Art Was es ist
material Filme, Papiere, Entwickler — die Stammliste
rezept eine benannte Filmentwicklung („HP5 auf 800")
kontaktabzug ein Bogen: unscharfe Zeit, Orte, Filmrezept — beim Planfilm ein Blatt samt Aufnahme
abzug ein einzelner Abzug: Papier, Filterung, Zeit am Vergrößerer

Zwei Regeln, die man beim Lesen kennen muss:

  • Eingefroren wird gerechnet, nicht gespeichert. Ein Rezept ist eingefroren, sobald ein Kontaktabzug darauf verweist. Ein Flag könnte zwischen Telefon und Archiv auseinanderlaufen, ein Verweis nicht.
  • Material entsteht durch Tippen. Wer ein Papier einmal einträgt, findet es danach in der Auswahl — es gibt keine Vorratsmaske.

Der Abzug (Etappe 9)

Ein Abzug ist ein Protokoll, kein Plan. Er wird nicht vorher gewählt, sondern hinterher abgeschrieben — deshalb rät die App nichts, schlägt nichts vor und rechnet nichts um. Keine Ilford-Tabelle, keine Gradationsstufe, keine Umrechnung von einem Format aufs andere. Vorbelegt ist einzig das Gerät.

  • Alles ist Text außer der Grundbelichtungszeit. Gelb, Magenta und die Säulenhöhe werden von den Rädern abgeschrieben und nie verrechnet; als Zahlenfelder holten sie auf Android nur den Ziffernblock ohne Komma. Auf die Grundzeit legt Etappe 10 die Prozente eines Bereichs — sie ist eine Zahl.
  • „Negativ" ist eine gerechnete Gruppe, kein Eintrag: derselbe Bogen, dieselbe Nummer. Verglichen wird schreibungsblind, sortiert natürlich („2" vor „12"). Dieselbe Denkweise wie beim Einfrieren.
  • Zwei Bildrollen, abzug und saeulenskala. Getrennt, weil das Foto der Skala eine Ablesehilfe ist und kein Bild des Werks — in einem Stapel wäre nach einem Jahr nicht mehr zu sagen, welches welches ist.
  • Maske und Blatt trennen die Vergrößererwerte sichtbar ab (Konzept § 5.5). Objektiv, Blende und Zeit gibt es an der Kamera und am Vergrößerer; hier gibt es nur die des Vergrößerers, und das steht dabei.
  • Im Archivbaum liegen die Abzüge nicht unter ihrem Kontaktabzug, sondern in abzuege/<jahr>/<nummer>-<titel> — sonst hätte der Aufräumer sie weggeräumt.

Der Kontaktabzug (überarbeitet in v1.5.0)

Ein Bogen ist ein Film, und die Entwicklung wird an ihm eingetragen, nicht anderswo ausgewählt. Wer den Film aus der Dose nimmt, hat kein Rezept — er hat einen Film, einen Entwickler und eine Zeit. Also stehen die Felder in der Maske des Bogens, in der Ordnung des Arbeitsablaufs: Film (mit ISO), Entwickler (mit Ansatz), dann die Parameter.

  • Das Rezept entsteht daraus, wie Material durch Tippen entsteht. Der Kurzname bildet sich von selbst („Ilford Delta 100 auf 100 in Kodak D-76 1+1") und bleibt änderbar; wer einen tippt, behält ihn.
  • Wer schon eines hat, nimmt es oben — und diese Auswahl schrumpft, sobald ein Film dasteht. Über die Jahre werden es sonst hundert.
  • Ändern gilt für diesen Bogen. Verweist nur er auf das Rezept, ändert es sich mit; benutzen es mehrere, wird abgewandelt statt geändert — sonst behauptete die Änderung, deren Filme seien anders entwickelt worden. Das ist „eingefroren" von der anderen Seite, mit dem Ausweg, den eine Sperre braucht.
  • In der Ansicht steht der Inhalt, nicht der Name. „Rezept · Delta 100" sagt beim Nachschlagen nichts; gebraucht werden Entwickler, Ansatz, Temperatur und Zeit. Der Kurzname steht als Verweis dabei.
  • Die Abzüge dieses Bogens stehen als Bilderraster, nach Negativ gebündelt. Abzüge unterscheidet man am Motiv, nicht an einer Reihe unterstrichener Titel.
  • „Stilllegen" gibt es hier nicht. Es nimmt einen Eintrag aus den Auswahllisten — dafür muss er in einer stehen, und das tun nur Material und Rezepte.

Unvollständig bleibt am Bogen (v1.5.1)

Manchmal sind nicht mehr alle Parameter der Filmentwicklung bekannt, etwa die Zeit. Dann soll nicht automatisch ein Rezept entstehen, aber die Daten bleiben beim Kontakteintrag.

Der Bogen hat dafür einen Entwurf (entwicklung): dieselben Felder wie ein Rezept, aber am Bogen und nur für ihn. Entweder Rezept oder Entwurf, nie beides — sonst gäbe es zwei Wahrheiten über dieselbe Entwicklung, und beim Nachschlagen läse man die falsche. Wer die Werte braucht, fragt entwicklungVon(k) und nicht finde(k.rezept).

Die Grenze zwischen beiden ist die Vollständigkeit und wird nach jedem Tastendruck neu gezogen: Film, Entwickler und Kippzeit müssen dastehen. Sind sie da, entsteht ein Rezept (oder es wird ein gleiches wiederverwendet); fehlt wieder eines, geht alles zurück in den Entwurf — ohne Verlust. Der Rezepteintrag verschwindet dabei nur, wenn er niemandem sonst gehört und nie im Archiv war.

Ein Entwurf taugt trotzdem für die Uhr und für die Rotationsrechnung: wer eine Kippzeit notiert hat, will damit rechnen.

Grad Celsius stehen nicht im Wert (v1.5.1)

Die Temperatur ist immer in Celsius; getippt wird nur die Zahl.

Getippt wird 20, daneben steht °C, gespeichert wird 20, angezeigt 20 °C (gradWert/gradText). Alte Bestände mit „20 °C" im Wert werden beim Laden geradegezogen. Was nicht ins Muster passt — „20–22", „Raumtemperatur", „68 F" — bleibt wörtlich stehen und wird nicht heimlich zu Celsius erklärt. tests/kippzeit-gleich.mjs hält App und Archiv darin zusammen.

Ein Blatt Planfilm (Etappe 11 e, seit v1.13.0 / v1.13.1)

Ein Bogen ist ein Film — außer beim Planfilm. Bei Planfilm 4×5 inch und größer gibt es für jedes Blatt einen eigenen Kontaktabzug. Damit ist so ein Eintrag ein einzelnes Negativ, und erst daraus folgt der Rest.

  • Das Filmformat ist der Schalter, nicht bloß eine Angabe. filmformat steht als Knopfreihe da (135 · 120 · 9×12 · 4×5″ · 13×18 · 18×24 · Eigenes); die Planfilm-Formate zeigen darunter den Block „Wie aufgenommen wurde". Welches Format ein Blatt ist, sagt die Leiter FILMFORMATE in web/stammlisten.js — es wird nicht gerechnet: „4×5 und größer" über Zoll und Zentimeter auszurechnen hielte 9×12 für Rollfilm, weil die Zahl kleiner ist. Ein getipptes Format gilt als Planfilm. Kein Format heißt „nicht gesagt", und damit verhält sich der ganze Altbestand wie vorher.
  • Drei Aufnahmefelder, alles Freitext in seiner Schreibweise: brennweite („210mm"), blende („f:22"), belichtungszeit („1/4s") — wie die aufnahmen in Motive, damit man nicht zweierlei tippen lernt. Gerechnet wird damit nichts: die f/-Leiter und ihre gerundeten Etiketten gehören dem Vergrößerer. Kein Verweis auf ein Objektiv — die Sorte objektiv im Bestand sind die Vergrößerungsobjektive samt ihren Blendenreihen.
  • belichtungszeit heißt nicht zeit. zeit ist am Bogen der Monat („3/2026"), aus dem Jahresfach und Liste ihr Jahr rechnen. In Motive ist genau diese Verwechslung einmal passiert.
  • motiv steht im Namen vorn. Vier Blätter von einem Nachmittag hießen sonst alle „9/2026 · Hamburg" — und eine Kopie wäre von ihrer Vorlage nicht zu unterscheiden. In der Liste trägt die Unterzeile eines Blattes die Aufnahme („210mm · f:22 · 1/4s") vor dem Rezept: die Belichtungsreihe unterscheidet sich in nichts anderem.
  • Kopieren gibt es am Kontaktabzug und am Abzug — für zwei Belichtungen, die nur um eine Blende (oder Zeit) verschieden sind. Mit kommen alle Angaben und der Rezeptverweis; nicht mit kommen Fotos, eingezeichnete Bereiche, die Abzüge eines Bogens, die Papierkorbmarke und die Archivbuchführung. Letztere ist die scharfe: erbt die Kopie archiv, behauptet sie, sie sei der schon hochgeladene Eintrag, und der nächste Abgleich schreibt sie über das Original. Die Liste steht mit Begründung je Feld in NICHT_MITKOPIEREN. Kein „(Kopie)" im Namen — die Kopie öffnet sich sofort in der Maske, Schreibzeiger auf dem Feld, das sich ändert.

Im Archiv liegen dieselben Felder in dk_kontakt, sind dort geradezuziehen (Schreibweise) und stehen in Liste und Blatt. Die Planfilm-Leiter baut der Server nicht nach: er zeigt die Aufnahme, sobald etwas dasteht.

Bewegung, Vorlauf und der Zeitrechner (Etappe 11 f, seit v1.14.0)

Drei Dinge, und alle drei hängen an derselben Ecke.

Kippen vs. Rotation ist die obere Wahl, kontinuierlich eine Facette von Kippen. Rotation heißt nicht kontinuierlich; Rotation ist Rotation, und die Zeit ist die Kippzeit − 15 %. Also ist die Maske jetzt verschachtelt: unter „Wie bewegt wurde" steht Kippen/Rotation, und nur unter Kippen die Frage, wie gekippt wird (im Intervall / kontinuierlich) samt Agitation. Bei Rotation steht dort nur die Rechnung.

Was man sich denken kann, steht nicht da (v1.14.1): eine Zeile „Agitation — die Maschine dreht" stand einen Tag lang im Blatt und ist wieder weg. In der Maske bleibt ein Satz, aber nur die Hälfte, die nicht zu erraten ist — dass Kippschema und Agitation am Rezept stehen bleiben. Dort ist gerade etwas verschwunden, und die Frage lautet dann, ob es gelöscht wurde.

  • Das Rezept wird dabei nicht angefasst. Kippschema und Agitation gehören beim Kippen ins Rezept (KONZEPT § 5), und ein zweiter Bogen kann dasselbe Rezept gekippt haben. Rotation zeigt sie deshalb nur nicht; geschrieben wird nichts. Das ist die Prüfung, an der dieser Umbau hängt.
  • Die Uhr schlägt bei Rotation kein Intervall mehr vor. Die Dauer bleibt (sie ist die Kippzeit minus Abzug), aber ein Ton alle 60 s wäre die Aufforderung zu etwas, das niemand tun muss.

Der Vorlauf (agitationVorlauf, Sekunden): etwa die erste Minute kontinuierlich kippen, danach 10 s pro Minute, oder danach 10 s alle 30 s. Ein Ja/Nein über die ganze Zeit konnte das nicht sagen. Leer heißt „kein Vorlauf" und liest sich wie vorher. In die Uhr geht er ausdrücklich nicht ein — einfach halten.

Der Zeitrechner (⏱ im Kopf, „Zeit rechnen" am Bogen) rechnet eine Kette von Prozentabzügen auf eine Zeit. Ein Fall aus der Praxis: bei 20 °C 10 Minuten kippen; weil es Planfilm war, direkt −10 % Zeit, davon nochmal −15 % wegen Rotation.

10:00  − 10 % Planfilm = 9:00  − 15 % Rotation = 7:39     (insgesamt −23,5 %)
  • Die Kette ist der Kern. −10 % und danach −15 % sind nicht −25 %: wer die Prozente addiert, rechnet 7:30 statt 7:39. Deshalb steht jede Stufe einzeln da, mit ihrem Zwischenergebnis, und der Gesamtabzug darunter.
  • Gerundet wird nach jeder Stufe (floor(x + 0.5)), damit in jeder Zeile die Zahl steht, die man auch von Hand hinschreiben würde.
  • Am Bogen steht die Stufe, die der Bogen schon kennt, fest da („Rotation −15 %, vom Bogen") — so wird sie weder doppelt gerechnet noch vergessen. Knöpfe gibt es für das, was sich wiederholt (Planfilm, Rotation), dazu eine freie Zeile mit eigener Beschriftung.
  • Nichts davon wird gespeichert. Das Ergebnis stellt die Uhr, oder es geht als Satz in die Notiz des Bogens („10:00 − 10 % Planfilm = 9:00 …") — damit im Protokoll steht, wie die Zeit zustande kam. Eine abgelegte Kette wäre ein zweites Rezept neben dem Rezept.

Im Archiv steht der Vorlauf am Rezept und am Bogen; am Rotationsbogen steht dort ebenfalls kein Kippschema. Und der Zurück-Pfeil führt in den Reiter zurück, aus dem der Eintrag kommt (gerechnet aus seiner Art, damit er auch aus einem Lesezeichen trägt) — vorher landete man in der Startansicht.

Kerben am Planfilm (v1.15.0)

Planfilm trägt keine Beschriftung. Welcher Film ein Blatt ist, sagt nur die Kerbe an der Kante, und die Kerbe sagt auch, wo die Schicht liegt: Blatt hochkant, Kerben oben rechts, dann zeigt die Schicht zu dir. Die Liste zum Anschauen filtert vorweg nach dem, was als Material eingetragen ist.

  • Menü → „Kerben am Planfilm", vorgewählt „Meine Filme": jeder Film unter Material (auch stillgelegte) mit seiner Kerbe. Was der Katalog nicht kennt, steht mit Namen unter „Nicht im Katalog", statt still zu fehlen. „Alle" zeigt den ganzen Katalog nach Art (Schwarzweiß, Farbnegativ, Dia).
  • Am Blatt eines Films steht seine Kerbe und der Weg in den Katalog.
  • Im Kopf steht kein fünfter Knopf: dort ist mit Uhr, f/, ⏱ und ☰ kein Platz mehr, ohne die Überschrift zu verdrängen.
  • Im Archiv dieselbe Liste unter /b/dunkelkammer/kerben (Weg bei den Filmen und am Filmblatt), am großen Schirm mehrere Kanten nebeneinander.

Eine Datei für beide: der Katalog und die Zeichnung stehen in der Schale, Archive/web/kerben.js. Die App hat sie in ihren Assets, das Archiv liefert sie unter /schale/. Zwei Abschriften liefen beim ersten Nachtrag auseinander.

Woher: 66 Einträge von C222 (c222.tv/notch-codes, CC BY 4.0; gesammelt aus Kodak F-3 2003, Dataguide 1994, Photoguide 1981, Data Book 1956, Ilford 2017), die Formen aus deren Schrift „NotchCodes" (SIL OFL) als Pfade nachgezeichnet. Dazu 13 Fuji, Agfa und Forte, abgelesen bei Photon Detector aus Bildchen von 54×9 Punkten. Art und Reihenfolge der Kerben stimmen, die Lage auf etwa eine Stelle; die Anzeige sagt „Lage ungefähr" dazu. Fomapan und Adox fehlen: Foma kerbt, soweit bekannt, nur ein V für die Schicht, für Adox war nichts zu finden.

Zugeordnet wird über Namensschlüssel je Eintrag (erkennt) und den Hersteller, sofern er eingetragen ist („Fujifilm" gilt als „Fuji"). „Tri-X 400" findet beide Tri-X-Kerben, weil es ihn als Blatt nur als 320 gibt; HP5+, FP4+ und Delta 100 finden je beide Ilford-Größen (bis 10×12″ flach, darüber rund).

Nach Format gefiltert (v1.15.1): Ilford kerbt bis 10×12″ anders als darüber. Unter „Meine Filme" und am Filmblatt steht nur die Kerbe, die zu den Formaten passt, in denen der Film benutzt wird — am Film eingetragen und an den Bögen. Weiß der Film selbst nichts, gelten die Planfilmformate des ganzen Bestands (etwa: nur 4×5 als Planfilm im Gebrauch). Das Blatt sagt dazu, wonach es filtert. formatklasse liest Zoll, wo ein Zollzeichen steht oder ein Zollformat gemeint ist, sonst Zentimeter — „cm" schlägt das Zollzeichen, denn in der Saat steht „10,2×12,7 cm (4×5")".

Unter dem eigenen Film heißt ein Eintrag nur noch nach dem, was ihn unterscheidet („Blätter bis 10×12″"); zweimal „Ilford Delta 100 Professional" unter „Ilford Delta 100" sah aus wie ein Duplikat. Echte Paare bleiben: Tri-X 320TXP (2003) und die ältere TXT-Kerbe — ein Blatt aus alter Packung kann so gekerbt sein.

Die eigene Kerbe (v1.16.0): ein Feld kerbe am Film, das den Katalog für diesen Film ersetzt, und eingeben in App und Archiv.

  • Editor (kerbenEditor in der Schale, von beiden benutzt): oben die ganze Kante, darunter zehn Stellen und die 17 Formen. Stelle antippen, dann Form — die Auswahl rückt weiter, so geht man die Kante von links zur Ecke durch. Darunter die Katalogkerben dieses Films zum Übernehmen, ohne Formatfilter: beim Korrigieren will man gerade auch die andere sehen. In der App steht er in der Maske des Films, im Archiv ersetzt er das Textfeld kerbe beim Bearbeiten.
  • Korrigieren in der Liste (selbst auswählen, was dort gezeigt wird und was nicht): unter dem eigenen Film trägt jede Katalogkerbe „Das ist meine" — das schreibt dasselbe Feld, danach steht nur noch sie da, und „Zurück zum Katalog" leert es wieder.
  • Gültig ist eine Kerbe aus zehn Zeichen der Formen, nicht lauter gerade Kante (kerbeGueltig / kerbe_bereinigt im Archiv; eine Prüfung vergleicht die Zeichen beider Seiten). Im Archiv steht sie in dk_material.kerbe — der Fingerabdruck übers Schema baut einen alten Index von selbst neu.

Rezeptfamilien (v1.5.0)

„Alle Parameter sind gleich, nur Temperatur und Zeit nicht — das ist wahrscheinlich dasselbe Rezept, nur mit anderer Temperatur."

Familie = Film + ISO + Entwickler + Ansatz. Sie wird gerechnet, nicht gespeichert — dieselbe Denkweise wie beim „Negativ" (Bogen + Nummer) und beim Einfrieren (jemand verweist darauf). Kein neues Feld, kein zweiter Ort, an dem etwas auseinanderlaufen könnte.

Der Reiter „Rezepte" zeigt eine Zeile je Familie mit den Temperaturen darunter; Antippen klappt die Varianten in der Liste auf, nicht in einem weiteren Blatt. Jede Variante bleibt ein eigener Eintrag, und das muss sie auch: eine benutzte Variante ist eingefroren, ihre Nachbarin nicht.

Gerechnet wird bei der Temperatur nichts. Die Zeiten je Temperatur werden ausgetestet und nebeneinandergestellt, nicht aus 20 °C hochgerechnet — eine Faustformel wäre hier dasselbe wie die Ilford-Filtertabelle am Vergrößerer: ein Plan, wo ein Protokoll hingehört.

Die Uhr (Etappe 8 b)

Mehrere benannte Uhren gleichzeitig, jede mit Gesamtdauer und Intervall, Ton und Vibration je Uhr schaltbar. Sie wohnt in Kotlin (TimerUhr.kt) und nicht in der Seite: ein setTimeout in einer WebView stirbt, sobald der Bildschirm ausgeht — und genau dann liegt das Telefon dunkel neben der Dose.

  • Kein Vordergrunddienst, sondern AlarmManager plus eine Benachrichtigung, die von selbst herunterzählt (setUsesChronometer + setChronometerCountDown
    • VISIBILITY_PUBLIC). Ein Dienst bräuchte seit Android 14 einen foregroundServiceType, für den es „Timer" gar nicht gibt.
  • Gespeichert wird ein Zeitpunkt, nie eine Restdauer. Eine Restdauer steht still, sobald der Prozess stirbt.
  • Ton über die Wecker-Nutzung der AudioAttributes, sonst bleibt es im Lautlos-Modus stumm.
  • Der Vorschlag kommt aus dem Rezept des Kontaktabzugs: Kippzeit als Dauer, Agitation als Intervall, Rotation zieht ihre Prozente ab. Alles überschreibbar — die Uhr taugt auch als bloße Stoppuhr.
  • Das Intervall ist eine Knopfreihe, keine Eingabe (seit v1.4.0): jede Minute · alle 30 s · nur am Ende · eigenes. Es ist fast immer eins der ersten beiden, und „01:00" zu tippen war der lästige Teil. „Eigenes" bleibt stehen, damit die Abkürzung kein Zaun wird. Die Dauer bleibt getippt — in Sekunden müsste man 15 Minuten im Kopf umrechnen.
  • Im Archiv gibt es sie nicht: dort wird nachgeschlagen, nicht entwickelt.

Abwedeln und Nachbelichten (Etappe 10, seit v1.6.0)

Bereiche werden ins Foto des Abzugs gezeichnet — freihand, mit dem Finger, im Betrachter („✎ Bereich zeichnen"). Vier Sätze, die man kennen muss:

  • Der Umriss hängt am Bild, nicht am Eintrag. Er gilt in den Koordinaten genau dieses Fotos; ein zweites Foto desselben Abzugs ist anders angeschnitten, und ein am Eintrag hängender Umriss läge darüber daneben.
  • Relative Koordinaten (0..1), nichts wird ins JPEG gebrannt. Deshalb sieht das Overlay am PC aus wie am Telefon: beide Seiten benutzen dieselbe Datei, Archive/web/bereiche.js.
  • Eingetippt wird in Sekunden, gespeichert wird die getippte Zahl. Die andere rechnet bereichszahlen aus der Grundzeit. Korrigiert man die Grundzeit, bleibt der getippte Wert stehen und der andere zieht nach. Wer auf Prozent umschaltet, nimmt den gerechneten Wert mit — ab dann gilt der.
  • Das Vorzeichen sagt die Art. Nachbelichten ist plus, Abwedeln minus; ein zweites Feld daneben könnte ihm widersprechen.

Unter 2 s Abweichung wird der Bereich blass und gestrichelt: das kriegt keine Hand hin. Unter 5 s Grundbelichtung sagt es der Rechner für die ganze Zeile.

Objektiv und Blende (seit v1.7.0)

Das Objektiv kommt aus einer Auswahlliste, nach Brennweite geordnet (50 · 50 · 80 · 150 · 150). Am Vergrößerer sucht man über die Brennweite; alphabetisch stünden die beiden 150er mitten zwischen den 50ern, weil fast alle „Componon" heißen. Die Brennweite kommt aus dem Feld, sonst aus dem Namen — die halben Einträge heißen schlicht „50mm" und haben kein Feld. Bei gleicher Brennweite entscheidet der angezeigte Name, nicht der Hersteller.

Am Abzug steht ein schlankes <select>; die Wahl setzt die Blendenreihe darunter und zeigt Brennweite, Blendenspanne und Notiz. „Anderes eintragen …" macht das Vorschlagsfeld auf, damit ein sechstes Objektiv nicht den Entwickler braucht.

Warum keine Knopfreihe wie beim Intervall der Uhr: die trägt vier kurze Wörter, aber keine Eigennamen. „Componon 150 mm (alt)" macht aus fünf Knöpfen ein waagerecht scrollendes Band — am Gerät gesehen, nicht am Schreibtisch.

Halbe Objektive aufräumen. Bis v1.7.1 war das Objektiv ein Textfeld; objektiveNachziehen machte aus jedem getippten Namen einen Eintrag ohne Brennweite und Blenden. Die stehen in der Liste getrennt unter „Ohne Angaben", und ein Weg „Aufräumen" hängt ihre Abzüge auf ein richtiges Objektiv um oder wirft sie weg. Zusammengelegt wird nichts von selbst — ob „50mm" das Rodagon oder das Componon meint, weiß nur der Mensch.

Das Objektiv ist ein Eintrag, keine Zeile Text — vierte Sorte Material neben Film, Papier und Entwickler. Der Grund ist die Blende: nur am Objektiv kann stehen, welche Blenden es überhaupt gibt (Offenblende bis kleinste), und daraus wird am Abzug eine Knopfreihe. Neue entstehen durch Tippen, wie ein Papier; eine Korrektur am Objektiv gilt rückwirkend für jeden Abzug, der darauf verweist.

Läuft eine Blende ohne Raster, steht in der Knopfreihe „Eigenes", damit die Abkürzung kein Zaun wird.

Alte Abzüge, an denen „80 mm" noch als Text steht, hängt objektiveNachziehen beim Laden auf einen Eintrag um. Einmalig, still und suchend vor dem Anlegen.

Der Belichtungsrechner

Klein, und er rechnet ausdrücklich nicht von einem Format aufs andere — die Grundzeit wird je Säulenhöhe per Teststreifen ermittelt und getippt. Er zeigt dieselbe Belichtung bei jeder anderen Blende, dazu die Bereiche dieses Abzugs mit ihren Sekunden bei dieser Blende — genau dort wird sichtbar, dass eine Blendenänderung das Abwedeln unmöglich macht.

Eine Stufe ist exakt eine Verdopplung, und darum wird nicht mit der aufgedruckten Zahl gerechnet. Auf dem Objektiv steht „11", gemeint ist 8·√2 = 11,31; die Reihe 2 · 2,8 · 4 · 5,6 · 8 · 11 · 16 · 22 · 32 · 45 besteht aus gerundeten Etiketten. Mit (11/8)² käme der Faktor 1,89 heraus statt 2 — ein Viertel Blende daneben, im Abzug sichtbar. echteBlende schnappt deshalb auf Drittelstufen (2^(k/6)), damit auch ein „f/6,3" stimmt, und lässt in Ruhe, was gar nicht auf die Leiter passt.

Eine Zeile antippen setzt Blende und Grundzeit am Abzug — und die Bereiche rechnen mit. Das ist der Unterschied, der leicht untergeht:

  • die Grundzeit berichtigen heißt „ich hatte mich vertippt" → die getippte Zahl eines Bereichs bleibt stehen, die gerechnete zieht nach;
  • die Blende wechseln heißt „dasselbe Licht, anders verteilt" → der Anteil bleibt stehen, aus 3 s auf 10 s werden 6 s auf 20 s.

In Prozent getippte Bereiche brauchen dafür nichts; nur die in Sekunden getippten werden gestreckt. Die Reihe selbst ändert sich beim Wechseln nie — f/8 mit 10 s und f/11 mit 20 s ergeben dieselbe Tabelle.

Er steht an zwei Stellen: am Abzug (mit Grundzeit und Blende vorbelegt) und als f/ im Kopf, weil es beim Teststreifen noch keinen Eintrag gibt.

Löschen, Papierkorb, Rückweg (v1.10.0, erweitert in v1.11.0)

Gelöscht ist ausgeblendet: der Eintrag steht in keiner Liste mehr, aber die Angaben und die Fotos bleiben, und im Papierkorb (Menü) holt ein Griff ihn zurück. Endgültig weggeworfen wird nur dort, und nur mit Rückfrage.

Was auf dem Telefon gar nicht mehr steht, holt „Archiv → Aus dem Archiv holen": das Blatt zeigt, was im Archiv liegt und hier fehlt, und gibt einen ganzen Eintrag samt Fotos wieder her. Seit v1.11.0 auch alle auf einmal — der Fall „neues Telefon", wo jeder Eintrag ein fehlender ist. Zwei Regeln dabei:

  • Material und Rezepte zuerst, dann was darauf verweist. Am Ergebnis ändert das nichts (Verweise gehen über die Kennung), am Zusehen schon.
  • Die Saat weicht dem Geholten. Beim ersten Start legt die App die Stammlisten an; sonst stünde „Ilford ID-11" danach doppelt da. Weichen darf nur, was nachweislich unangetastet ist: gesät (saat), nie im Archiv gewesen, von nichts verwiesen. Im Archiv ausgeblendete Einträge gehen beim Holen aller nicht mit — einzeln schon.
  • Unberührte Saat wartet auf nichts (seit v1.12.0). Sie steht in keiner Warteschlange und geht bei „Alles hochladen" nicht mit — sonst entschiede die Reihenfolge (erst hochladen oder erst holen) darüber, ob im Archiv hinterher alles doppelt steht. Genau das ist am 2026-08-25 passiert. Sobald ein Rezept darauf verweist oder jemand ein Feld ändert, fällt der Merker und der Eintrag geht seinen gewöhnlichen Weg.

Was schon doppelt dasteht, räumt Menü → „Material aufräumen" weg: es zeigt jeden mehrfach vergebenen Namen und wirft auf Knopfdruck die gesäten, von nichts benutzten Zeilen in den Papierkorb — von wo aus die nächste Übergabe sie auch im Archiv ausblendet. Ganz aus dem Archiv nimmt sie dort der Papierkorb (/papierkorb → „Endgültig wegwerfen"); die Dateien wandern dabei nach .abgelegt/ und nicht in den Abfall.

Am Rechner nachtragen (v1.17.0, Etappe 11 h)

Das Telefon erfasst im Labor, der Rechner recherchiert und trägt nach — und seit v1.17.0 kommt das Nachgetragene auch wieder an. Zuschnitt und Entscheidungen: ZUSCHNITT-11H.html.

  • Neues aus dem Archiv. Beim Start sieht die App im Archiv nach, wenn Netz da ist (ohne Netz: nichts, keine Meldung). Gibt es etwas, trägt der Menüknopf einen Punkt und das Menü „Aus dem Archiv holen (3)". Das Blatt hat zwei Gruppen: Neu im Archiv (fehlt hier) und Im Archiv geändert. Was hier seit dem letzten Hochladen unverändert ist, übernimmt ein Druck — einzeln oder alle. Wurde ein Eintrag auf beiden Seiten geändert, zeigt ein Vergleich je Feld, was hier und was im Archiv steht, und man wählt.
  • Die Sperre danach. Übernommen wird mit der Fassung des Archivs; die App schickt sie beim nächsten Hochladen als grundlage mit, und das Archiv nimmt den Eintrag ohne „Trotzdem übergeben" an. Fotos, die hier schon liegen, werden dabei nicht noch einmal geholt.
  • Neues Material am Rechner (im Archiv unter Material): Sorte, Hersteller, Name — danach die Maske mit Kerben-Editor. Am Telefon steht es unter „Neu im Archiv".
  • Datenblätter. Im Archiv am Blatt eines Materials: PDF ablegen (auch hineinziehen) oder von einer Adresse holen lassen. Sie kommen mit dem Eintrag aufs Telefon; jede Seite wird schon beim Holen als Bild gezeichnet, im Labor wird nichts mehr gerechnet. Gelesen im eigenen Betrachter (schale/dokument.js, gebaut wie in der Tickets-App): Seiten untereinander, zwei Finger zoomen um die Stelle unter den Fingern, Doppeltipp holt heran, und das Datenblatt öffnet dort, wo man zuletzt war. Rezept, Bogen und Abzug haben je einen Griff „Datenblatt Film / Entwickler / Papier".

Ausrollen: erst das Archiv, dann die App.

Es bleibt ein PDF (v1.18.0). Über den Seitenbildern liegt die Textschicht von pdf.js (web/pdfjs/, 6.3, Apache 2.0): markieren und kopieren wie in einem PDF-Leser, die Schicht zoomt mit. Ab etwa doppelter Bildschärfe zeichnet pdf.js den sichtbaren Ausschnitt scharf nach, nie die ganze Seite. „Öffnen mit …" gibt das Original an eine andere PDF-App. Datenblätter tragen eine Sprache (im Archiv gewählt), die neben dem Titel steht.

Nachbehandlung: Tonen als Fassung (v1.19.0)

Ein identischer Abzug, am Ende mit Selen getont, ist praktisch derselbe Abzug, nur getont.

Die Grenze ist der Fixierer. Was bis dahin geschieht — Papier, Filter, Grundzeit, Bereiche, Entwickler —, ist der Abzug; ändert sich davon etwas, ist es ein neuer. Was danach kommt, ist eine Fassung im selben Eintrag (fassungen am Abzug). Wie viele Blätter mit denselben Werten belichtet wurden, ändert daran nichts.

  • Eine Fassung hat Bäder, nicht ein Feld: Sepia ist Bleiche und dann Toner, eine geteilte Tonung sind zwei Bäder. Jedes zeigt auf ein Mittel, dazu Verdünnung und Zeit als Text („1+10", „2 Min").
  • Die Mittel sind Material der neuen Sorte nachbehandlung, mit eigenem Band. Nicht „Toner", weil die Bleiche dazugehört. Am Mittel steht die Wirkung (Selen · Gold · Schwefel · Sepia · Kupfer · Bleiche · Sonstiges) und seine Verdünnungen, die am Abzug als Vorschlag erscheinen. Neue Mittel entstehen durch Tippen wie ein Papier.
  • Der Name der Fassung wird gerechnet aus den Wirkungen ihrer Mittel („Selen", „Selen + Gold"). Er steht als Marke in der Liste und findet sich über die Suche.
  • Fotos hängen an der Fassung, nicht am Abzug. Gibt es nur die getonte Fassung, hat der Abzug eben kein eigenes Foto; das Deckbild kommt dann von der Fassung. „Nebeneinander ansehen" legt im Betrachter das Foto des Abzugs und das erste jeder Fassung hintereinander.
  • Kopieren nimmt keine Fassung mit: eine Kopie ist eine neue Belichtung und war noch in keinem Toner.

Im Archiv steht die Fassung am Blatt des Abzugs unter „Nachbehandlung", ihre Fotos als eigene Rolle fassung (fassung-<n>-<m>.jpg). Bearbeitet wird sie nur am Telefon. Die Wirkungen stehen in WIRKUNGEN in App und Archiv; ein Test im Archiv vergleicht beide Listen.

Fotos hinaus, Umrisse aus (v1.19.1). Im Betrachter stehen an jedem Foto „Teilen" (Teilen-Blatt) und „In Galerie" (Kopie nach Bilder/Dunkelkammer über MediaStore, ab Android 10, ohne Berechtigung). So kommt ein Foto auch über „Aus der Galerie" in einen anderen Eintrag. Umrisse fürs Abwedeln und Nachbelichten lassen sich mit „Umrisse aus" ausblenden; beim Zeichnen sind sie wieder da. Beides steckt in der gemeinsamen Schale (bildTeilen, bildInGalerie, betrachter.js).

Bauen

git submodule update --init          # die gemeinsame Schale
./gradlew :app:assembleRelease

web/ wird vor jedem Bau in die Assets kopiert, die Web-Schale aus Archive/web als ganzer Ordner dazu. assets/ nicht von Hand pflegen.

Prüfen

node tests/probe.mjs             # Rauchtest der Seite in Chromium
node tests/kippzeit-gleich.mjs   # rechnen App und Archiv gleich?
(cd Archive/server && python3 -m pytest)

tests/kippzeit-gleich.mjs lässt die echten Fassungen gegeneinander laufen — die JS-Seite in Chromium, die Python-Seite im Unterprozess. Ein hier nachgebauter Rechner wäre eine dritte Fassung und würde nichts beweisen.

Die Stammlisten

stammlisten.json ist die Saat, web/stammlisten.js ihre Fassung für die App. In dieser Fassung ist die Saat leer: die App startet ohne Material, jeder legt seins selbst an. Eine Beispielsaat für die Tests liegt in tests/stammlisten-beispiel.js. Sie wird einmal eingespielt, beim ersten Start. Danach ist sie bedeutungslos: was fehlt, entsteht durch Tippen, und was falsch steht, wird am Materialblatt berichtigt — nicht in der Datei.