diff --git a/restricted/aussenplan.php b/restricted/aussenplan.php index 74b5e4d..9cd32ae 100644 --- a/restricted/aussenplan.php +++ b/restricted/aussenplan.php @@ -73,6 +73,49 @@ const PLAN_STAUCHUNG = 0.55; */ const PLAN_UEBERHOEHUNG = 1.4; +/** + * Hellster Wert, den eine Flaeche annehmen darf. + * + * Aus den Renderings der Innengeschosse gemessen: dort geht kein einziger + * Bildpunkt ueber 200. Reines Weiss kommt nicht vor, und genau das laesst die + * Bilder ruhig wirken. Ohne diesen Deckel steht der Aussenplan grell neben + * ihnen. + */ +const PLAN_LICHT = 200; + +/** + * Wohin abgedunkelt wird - nicht nach Schwarz, sondern ins Blaugraue. + * + * Ebenfalls gemessen: im Vorbild ist die hellste Stufe genau neutral, und mit + * abnehmender Helligkeit waechst der Blauanteil auf rund 30 Stufen an. Das ist + * kein Stilmittel, sondern Physik - eine Flaeche, die keine Sonne abbekommt, + * bekommt ihr Licht vom Himmel, und der ist blau. + */ +const PLAN_SCHATTEN = [40, 62, 98]; + +/** + * Woher das Licht kommt, als Richtung in der Grundflaeche - x nach Osten, + * y nach Sueden. Westsuedwest, also Nachmittagssonne. + * + * Nicht genau aus Suedwesten, obwohl von dort geschaut wird: Dann laegen die + * beiden sichtbaren Seiten gleich hell, und die Kante zwischen ihnen waere + * verschwunden. Etwas nach Westen versetzt trennt sie sich. + */ +const PLAN_LICHTRICHTUNG = [-0.85, 0.53]; + +/** Wieviel Licht eine Flaeche bekommt, die von der Sonne abgewandt ist. */ +const PLAN_UMGEBUNG = 0.58; + +/** + * Licht auf einer waagerechten Flaeche - Boden, Terrasse, Wandoberkante. + * + * Etwas weniger als eine gut zur Sonne stehende Wand. Bei rund 40 Grad + * Sonnenhoehe bekommen beide fast dasselbe ab, und die Wand einen Hauch mehr. + * Mit vollem Licht waren Boden und Terrassen das Hellste im Bild und zogen den + * Blick auf sich; im Vorbild sind es die Waende. + */ +const PLAN_WAAGERECHT = 0.88; + /** So groß ist die Zeichenfläche, in die alles hineinpassen muss. */ const PLAN_BREITE = 400; const PLAN_HOEHE = 300; @@ -126,7 +169,7 @@ function aussenFlaechen() // Die Anlage liegt auf der Suedschraege. Die Wiederkehr wird danach // gezeichnet und deckt die Mitte ab - genau wie auf dem Luftbild, wo // die Module links und rechts von ihr liegen. - ["raum" => null, "z" => 0, "hoehe" => 6, "farbe" => "#d5d9df", + ["raum" => null, "z" => 0, "hoehe" => 6, "farbe" => "#c8c8c8", "dachfarbe" => "#9aa3ad", "dach" => ["art" => "sattel", "hoehe" => 3], "pv" => "S", "ecken" => [[36.8, 17.2], [39.2, 31.2], [24.2, 33.8], [21.8, 19.8]]], @@ -140,7 +183,7 @@ function aussenFlaechen() // // Breite und Tiefe sind vom Luftbild geschaetzt - wenn es genauer sein // soll, klick die Flaeche in tools/lageplan.php ab. - ["raum" => null, "z" => 6, "hoehe" => 0, "farbe" => "#d5d9df", + ["raum" => null, "z" => 6, "hoehe" => 0, "farbe" => "#c8c8c8", "dachfarbe" => "#9aa3ad", "dach" => ["art" => "sattel", "hoehe" => 1.3, "achse" => "NS"], "ecken" => [[34.7, 32.0], [28.7, 33.0], [27.5, 25.6], [33.4, 24.6]]], @@ -169,7 +212,7 @@ function aussenFlaechen() // an der westlichen setzt der OG-Carport an. Ausgeschrieben, weil bei // einem fast quadratischen Grundriss sonst die zweite Nachkommastelle // entscheidet, welche Seite die laengere ist. - ["raum" => "Garage", "z" => 0, "hoehe" => 2.5, "farbe" => "#c9ced5", + ["raum" => "Garage", "z" => 0, "hoehe" => 2.5, "farbe" => "#c8c8c8", "dachfarbe" => "#7d9470", "tor" => "S", "dach" => ["art" => "sattel", "hoehe" => 1.5, "achse" => "NS"], "ecken" => [[12.2, 19.2], [13.3, 25.4], [19.6, 24.3], [18.5, 18.0]]], @@ -298,14 +341,54 @@ function aussenMitte($punkte) return [$sx / $n, $sy / $n]; } -/** Eine Farbe abdunkeln - die Seitenflächen liegen im Schatten. */ +/** + * Eine Farbe ins Licht setzen - Faktor 1 volle Beleuchtung, kleiner ist + * Schatten. + * + * Zwei Dinge unterscheiden das vom blossen Abdunkeln, und beide sind aus den + * Renderings der Innengeschosse abgelesen: + * + * - Der Deckel. Keine Flaeche wird heller als PLAN_LICHT, auch wenn die + * Grundfarbe es waere. Der Farbton bleibt dabei erhalten, weil alle drei + * Kanaele mit demselben Wert heruntergezogen werden. + * - Die Richtung. Abgedunkelt wird nicht nach Schwarz, sondern nach + * PLAN_SCHATTEN. Deshalb wird eine Flaeche blauer, je weniger Licht sie + * bekommt - der farbtreue Weg dorthin sah daneben stumpf aus. + */ function aussenSchatten($hex, $faktor) { $n = hexdec(ltrim($hex, "#")); + $kanal = [($n >> 16) & 255, ($n >> 8) & 255, $n & 255]; + + $spitze = max($kanal); + if ($spitze > PLAN_LICHT) { + foreach ($kanal as $i => $wert) { + $kanal[$i] = $wert * PLAN_LICHT / $spitze; + } + } + + $f = max(0.0, min(1.0, $faktor)); return sprintf("#%02x%02x%02x", - (int) round(($n >> 16) * $faktor), - (int) round((($n >> 8) & 255) * $faktor), - (int) round(($n & 255) * $faktor)); + (int) round($kanal[0] * $f + PLAN_SCHATTEN[0] * (1 - $f)), + (int) round($kanal[1] * $f + PLAN_SCHATTEN[1] * (1 - $f)), + (int) round($kanal[2] * $f + PLAN_SCHATTEN[2] * (1 - $f))); +} + +/** + * Wie hell eine senkrechte Flaeche mit dieser Aussennormalen liegt. + * + * Bisher bekam jede Aussenwand denselben Wert, egal wohin sie zeigte. Der Plan + * sah dadurch flach aus, und nachgemessen fehlte ihm genau das obere Drittel + * der Helligkeitsskala, das die Renderings haben. Eine Lichtrichtung fuellt es. + */ +function aussenBeleuchtung($nx, $ny) +{ + $laenge = hypot($nx, $ny); + if ($laenge <= 0) { + return 1.0; // waagerecht, volle Beleuchtung von oben + } + $anteil = ($nx * PLAN_LICHTRICHTUNG[0] + $ny * PLAN_LICHTRICHTUNG[1]) / $laenge; + return PLAN_UMGEBUNG + (1 - PLAN_UMGEBUNG) * max(0.0, $anteil); } /** @@ -542,7 +625,8 @@ function aussenZeichnung() }; // Aussenseite $seiten[] = [$viereck($von, $z, $bis, $z, $bis, $zBis, $von, $zVon), - 0.72, "Wand " . $k["richtung"] . " aussen"]; + aussenBeleuchtung($k["nx"], $k["ny"]), + "Wand " . $k["richtung"] . " aussen"]; if ($dicke > 0) { // Innenseite - umgekehrter Umlauf, damit sie zaehlt, wenn // man durch die Oeffnung hineinsieht. @@ -552,12 +636,21 @@ function aussenZeichnung() // Oberkante $seiten[] = [$viereck($von, $zVon, $bis, $zBis, $innenBis, $zBis, $innenVon, $zVon), - 0.86, "Wand " . $k["richtung"] . " Oberkante"]; + PLAN_WAAGERECHT, + "Wand " . $k["richtung"] . " Oberkante"]; // Die beiden Schnittkanten an den Enden der Wand + // Eine Schnittkante steht quer zur Wand und bekommt deshalb + // ihr eigenes Licht. Mit einem festen Wert sprangen die + // Ecken je nach Wandhelligkeit als Pfosten hervor. + $laengsL = hypot($bis[0] - $von[0], $bis[1] - $von[1]); + $lx = $laengsL > 0 ? ($bis[0] - $von[0]) / $laengsL : 0; + $ly = $laengsL > 0 ? ($bis[1] - $von[1]) / $laengsL : 0; $seiten[] = [$viereck($innenVon, $z, $von, $z, $von, $zVon, $innenVon, $zVon), - 0.66, "Wand " . $k["richtung"] . " Schnitt A"]; + aussenBeleuchtung(-$lx, -$ly), + "Wand " . $k["richtung"] . " Schnitt A"]; $seiten[] = [$viereck($bis, $z, $innenBis, $z, $innenBis, $zBis, $bis, $zBis), - 0.66, "Wand " . $k["richtung"] . " Schnitt B"]; + aussenBeleuchtung($lx, $ly), + "Wand " . $k["richtung"] . " Schnitt B"]; } $etwasSichtbar = false; foreach ($seiten as $seite) { @@ -655,7 +748,9 @@ function aussenZeichnung() $first[$r], ]; if (aussenSichtbar($giebel)) { - $polygone[] = ["punkte" => $giebel, "farbe" => aussenSchatten($farbe, 0.86)]; + $polygone[] = ["punkte" => $giebel, + "farbe" => aussenSchatten($farbe, + aussenBeleuchtung($k["nx"], $k["ny"]))]; } } foreach ($seiten as $r) { @@ -683,7 +778,7 @@ function aussenZeichnung() if (aussenSichtbar($schraege)) { $polygone[] = ["punkte" => $schraege, "farbe" => aussenSchatten($f["dachfarbe"] ?? "#9aa3ad", - $r === "S" || $r === "W" ? 1.0 : 0.82)]; + aussenBeleuchtung($k["nx"], $k["ny"]))]; // Module auf genau dieser Schraege. Eingezogen wird hier // im Bild und nicht in Metern: die Flaeche ist schon // projiziert, und auf ihrer Ebene bleibt der Einzug auch @@ -721,12 +816,20 @@ function aussenZeichnung() aussenPunkt($einpassung, $pkt[0], $pkt[1], $zOben[$i]), ]; if (aussenSichtbar($stirn)) { + // Die Stirn laeuft um den ganzen Koerper und zeigt + // dabei in alle Richtungen - ein fester Wert liesse + // die abgewandte Seite so hell wie die besonnte. + $sdx = $dachEcken[$j][0] - $pkt[0]; + $sdy = $dachEcken[$j][1] - $pkt[1]; $polygone[] = ["punkte" => $stirn, - "farbe" => aussenSchatten($f["dachfarbe"] ?? $farbe, 0.78)]; + "farbe" => aussenSchatten($f["dachfarbe"] ?? $farbe, + aussenBeleuchtung($sdy, -$sdx))]; } } } - $polygone[] = ["punkte" => $deckPunkte, "farbe" => $f["dachfarbe"] ?? $farbe]; + $polygone[] = ["punkte" => $deckPunkte, + "farbe" => aussenSchatten($f["dachfarbe"] ?? $farbe, + PLAN_WAAGERECHT)]; $deck = $dachEcken; }