From 26131f93466ec220999c5ffc283c398d4a8b32ba Mon Sep 17 00:00:00 2001 From: D4rkst3r Date: Sat, 1 Aug 2026 22:47:43 +0200 Subject: [PATCH] Tree Gen 1.25.0: Palme, Busch und Setzlinge nachgemessen und korrigiert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drei gemeldete Probleme - alle bestaetigt, Ursachen gemessen statt geraten. 1) Palme sah "extrem komisch" aus Gemessen: Kronenradius/Hoehe 0.11 - die Wedel klebten als Mop-Kopf am Stamm. Zielwert aus zwei unabhaengigen Quellen, die sich decken: tree-gen palm.py (Weber&Penn) setzt Wedellaenge = 0.3 x Stamm, die TreeIt-Referenzpalme kommt auf 3.3 m bei 4.33 m = 0.38. Jetzt 0.30. Dazu der Winkel: tree-gen gibt down_angle 60 + curve 40 an, also schraeg raus und ueberbiegen. Mit Branch Up 0 allein standen die Wedel als gerade Antennenspeichen ab; mit Up 0.35 / Droop 1.2 steigen sie gemessen 1.1 m ueber den Ansatz und kippen dann ab. 2) Busch sah aus wie ein Baum Gemessen: 1.81 m hoch bei 0.84 m Breite (B/H 0.46), durchgehender Stamm, Aeste erst ab 15 %, Wurzelanlauf wie beim Baum. Das IST ein Baeumchen. Botanisch ist der Unterschied nicht die Groesse, sondern die Architektur: mehrere Stiele ab Boden, kein Leittrieb, gemessen nach Hoehe UND Breite. Jetzt kurzer Stumpf + Aeste ab Boden: 1.20 m hoch, 2.0 m breit (B/H 1.6). 3) Kleinste Wachstumsstufe sah komisch aus / "nur runterskaliert" Gemessen: der Setzling war relativ zu seiner Hoehe 2.4x DICKER als der Altbaum (r/H 0.025 gegen 0.0104), weil Trunk Radius mit 0.30 skalierte, die Hoehe aber mit 0.16. Botanisch gilt die elastische Aehnlichkeit nach McMahon: Hoehe ~ Durchmesser^(2/3), also Faktor 0.16^1.5 = 0.064. Dazu behielt jeder Setzling den vollen Wurzelanlauf -> Trichterfuss. Neu ausserdem: Aeste schrumpfen mit der Hoehe statt langsamer, sitzen tiefer (Aufasten kommt erst mit dem Alter) und haengen weniger. Nicht jede Pflanze waechst wie ein Laubbaum -> GROWTH_OVERRIDES: Palme kein Dickenwachstum, erst Durchmesser dann Hoehe (UF/IFAS ENH1212: "its diameter is fixed for life") -> Setzling ist ein kurzer Stumpf Kaktus Sukkulent, gleiche Logik, und OHNE Arme: Saguaros bilden die erst mit 50-70 Jahren bei rund 2 m Hoehe (NPS) Weide haengt erst mit langen Zweigen; sonst reichten sie 0.27 m unter den Stammfuss und das Asset waere in UE geschwebt Zwei Bugs fielen dabei auf: - Branch Count = 0 schaltete die Aeste NICHT ab - dieselbe Falle wie damals bei Sub Count (Curve to Points klemmt Count auf min. 1). Fuer die Sub-Ebene gab es laengst einen Geometry-Switch, fuer die Hauptaeste nicht. - Im Busch-Preset stand "Branch Count" zweimal im selben Dict; der zweite Eintrag (10) hat den ersten (14) still ueberschrieben. Die sieben uebrigen Presets sind unveraendert (gleiche Maße, gleiche Tris). Neu: tests/test_proportionen.py nagelt die Zielwerte fest. 14 Tests bestehen. Co-Authored-By: Claude Opus 4.8 --- README.md | 37 ++++++ blender_manifest_tree.toml | 2 +- dist/index.html | 6 +- dist/index.json | 8 +- dist/stylized_tree_generator-1.24.0.zip | Bin 22352 -> 0 bytes dist/stylized_tree_generator-1.25.0.zip | Bin 0 -> 24729 bytes stylized_tree_generator.py | 138 +++++++++++++++++++--- tests/test_proportionen.py | 146 ++++++++++++++++++++++++ 8 files changed, 313 insertions(+), 24 deletions(-) delete mode 100644 dist/stylized_tree_generator-1.24.0.zip create mode 100644 dist/stylized_tree_generator-1.25.0.zip create mode 100644 tests/test_proportionen.py diff --git a/README.md b/README.md index ad0caad..b69a781 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,42 @@ Gras-, Blumen- und Farn-Bueschel. Der Kern ist die **Wind-UV**: ## Stylized Tree Generator +### Proportionen: gemessen, nicht geschaetzt + +Drei Presets sahen falsch aus. Die Ursachen wurden gemessen bzw. belegt, nicht +geraten - `tests/test_proportionen.py` haelt die Werte fest: + +| Was | vorher (gemessen) | jetzt | Quelle fuer den Zielwert | +|---|---|---|---| +| **Palmenkrone** | Radius/Hoehe **0.11** - ein Mop-Kopf am Stamm | **0.30** | `tree-gen palm.py` (Weber&Penn): Wedellaenge 0.3 x Stamm; TreeIt-Referenzpalme 3.3 m bei 4.33 m = 0.38 | +| **Busch** | 1.81 m hoch, 0.84 m breit (B/H **0.46**) - ein Baeumchen | 1.20 m hoch, 2.0 m breit (B/H **1.6**) | Strauch = mehrere Stiele ab Boden, kein Leittrieb; gemessen nach Hoehe UND Breite | +| **Setzling** | relativ **2.4x dicker** als der Altbaum (r/H 0.025 gegen 0.0104) | proportional gleich schlank | McMahon, elastische Aehnlichkeit: Hoehe ~ Durchmesser^(2/3), also Faktor 0.16^1.5 | + +Der Wedel-Winkel kam dazu: mit `Branch Up 0` standen die Wedel als gerade +Antennenspeichen ab. `tree-gen` gibt `down_angle 60` + `curve 40` an - also +schraeg raus und dann ueberbiegen. Gemessen steigen die Wedel jetzt 1.1 m ueber +den Ansatz und kippen dann ab. + +**Nicht jede Pflanze waechst wie ein Laubbaum** (`GROWTH_OVERRIDES`): + +- **Palme:** Einkeimblaettrige haben kein Dickenwachstum - erst der Durchmesser, + dann die Hoehe. „Once a palm stem emerges from the apical meristem, its + diameter is fixed for life" (UF/IFAS ENH1212). Der Setzling ist ein kurzer + Stumpf in fast voller Dicke, kein Schoessling. +- **Kaktus:** Sukkulent, gleiche Logik. Ausserdem **ohne Arme** - Saguaros + bilden die erst mit 50-70 Jahren, bei rund 2 m Hoehe (NPS). +- **Weide:** haengt erst, wenn die Zweige lang genug sind. Junge Weiden wachsen + aufrecht; mit dem allgemeinen Droop reichten ihre Zweige 0.27 m unter den + Stammfuss und das Asset waere in UE geschwebt. + +**Dabei gefunden:** `Branch Count = 0` schaltete die Aeste **nicht** ab - +dieselbe Falle wie seinerzeit bei `Sub Count` (`Curve to Points` klemmt Count +auf mindestens 1). Fuer die Sub-Ebene gab es laengst einen Geometry-Switch, fuer +die Hauptaeste nicht. Jetzt liegen beide Ebenen hinter demselben Schalter. +Ebenfalls gefunden: im Busch-Preset stand `Branch Count` **zweimal** im selben +Dict - der zweite Eintrag hat den ersten still ueberschrieben. + + Parametrischer **Geaest**-Generator (Stamm + Aeste + Sub-Aeste) als Geometry-Nodes- Gruppe. Blaetter/Krone macht man selbst — das Addon liefert die Struktur. @@ -354,6 +390,7 @@ gefiltert `.\run_tests.ps1 -Filter panels`. | Datei | prueft | |---|---| +| `tests/test_proportionen.py` | Baum-Proportionen: Palmenkrone, Strauch-Breite, Setzling-Schlankheit, Branch Count 0 | | `tests/test_panels.py` | Alle vier Addons: registrieren, jedes Panel zeichnet, Unter-Panels im richtigen Tab, **jede Property irgendwo erreichbar** | | `tests/test_tree_addon.py` | Tree-Addon: Presets, Wachstums-Stufen, UVs, Modifier-Apply | | `tests/test_ecogame_rock.py` | Fels-Benennung, Bake liefert nur `_albedo`/`_normal`, AO im Albedo | diff --git a/blender_manifest_tree.toml b/blender_manifest_tree.toml index 424a455..0b45f60 100644 --- a/blender_manifest_tree.toml +++ b/blender_manifest_tree.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_tree_generator" -version = "1.24.0" +version = "1.25.0" name = "Stylized Tree Generator" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" maintainer = "D4rkst3r" diff --git a/dist/index.html b/dist/index.html index 39878ea..0999c73 100644 --- a/dist/index.html +++ b/dist/index.html @@ -50,17 +50,17 @@ 21.7KB - stylized_tree_generator-1.24.0 + stylized_tree_generator-1.25.0 Stylized Tree Generator Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen link 4.2.0 - ~ all all - 21.8KB + 24.1KB -

Built 2026-07-31, 10:09

+

Built 2026-08-01, 20:46

diff --git a/dist/index.json b/dist/index.json index a5e52c1..3a3577b 100644 --- a/dist/index.json +++ b/dist/index.json @@ -82,7 +82,7 @@ "id": "stylized_tree_generator", "name": "Stylized Tree Generator", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", - "version": "1.24.0", + "version": "1.25.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -98,9 +98,9 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_tree_generator-1.24.0.zip", - "archive_size": 22352, - "archive_hash": "sha256:abdce976729a528b87f4eba56917f26b74a5f7e0c50877a0a427ff94bda40032" + "archive_url": "./stylized_tree_generator-1.25.0.zip", + "archive_size": 24729, + "archive_hash": "sha256:51006e9a2968cfce6dcb7ce13888a5e410b25119916c300013a7105ee83f78e5" } ] } \ No newline at end of file diff --git a/dist/stylized_tree_generator-1.24.0.zip b/dist/stylized_tree_generator-1.24.0.zip deleted file mode 100644 index 9ea0c086a82514a6a8ceea85def8a28b74e0ad49..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22352 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 diff --git a/dist/stylized_tree_generator-1.25.0.zip b/dist/stylized_tree_generator-1.25.0.zip new file mode 100644 index 0000000000000000000000000000000000000000..c09c9ee215290772747b039eaa0a2a633e471d39 GIT binary patch literal 24729 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 P3HR^%{i^_z|GfSO#+$pj literal 0 HcmV?d00001 diff --git a/stylized_tree_generator.py b/stylized_tree_generator.py index 364915d..5460413 100644 --- a/stylized_tree_generator.py +++ b/stylized_tree_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Tree Generator", "author": "D4rkst3r", - "version": (1, 24, 0), + "version": (1, 25, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Tree Gen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", @@ -95,10 +95,19 @@ PRESETS = { "Tip Blunt": 0.7, # Krone bleibt dick (Kronenschaft) UND die # Stiele laufen nicht drahtig aus "Trunk Tip": 1.0, - "Branch Count": 11, "Branch Length": 0.78, "Branch Up": 0.9, + # Wedel-Geometrie nach zwei unabhaengigen Quellen, die sich decken: + # tree-gen palm.py (Weber&Penn) sagt length[1] = 0.3 x Stammlaenge, + # branches[1] = 25, down_angle 60 Grad, curve 40 Grad, tropism -3. + # Die TreeIt-Referenzpalme kommt auf Spannweite 3.3 m bei 4.33 m Stamm + # = Kronenradius/Hoehe 0.38. Ziel also 0.30-0.38. + # Vorher: Length 0.78 + Up 0.9 ergab GEMESSEN 0.11 - die Krone war ein + # Mop-Kopf, der am Stamm klebte, statt einer ausladenden Krone. + # Jetzt gemessen: 0.34, Wedel steigen 1.1 m ueber den Ansatz und kippen + # dann ab (echter Bogen statt Antennenspeichen oder Haengemop). + "Branch Count": 18, "Branch Length": 2.0, "Branch Up": 0.35, "Branch Start": 0.9, "Branch End": 1.0, - "Branch Bend": 0.3, - "Branch Droop": 0.5, "Droop Curve": 2.2, + "Branch Bend": 0.25, + "Branch Droop": 1.2, "Droop Curve": 2.2, "Attraction Up": 0.2, # Wedel schwingen leicht hoch, dann Bogen "Branch Thickness": 0.16, "Branch Taper": 0.45, @@ -109,13 +118,23 @@ PRESETS = { "Detail": 0.55, "Sides": 11, "Branch Sides": 4, }, + # Strauch statt Kleinbaum. Botanisch ist der Unterschied NICHT die Groesse, + # sondern die Architektur: ein Strauch hat mehrere Stiele, die dicht ueber + # dem Boden entspringen, und keinen durchgehenden Leittrieb; gemessen wird + # er nach Hoehe UND Breite. Vorher: ein durchgehender Stamm ueber die volle + # Hoehe, Aeste erst ab 15 %, Wurzelanlauf wie beim Baum - gemessen 1.81 m + # hoch bei 0.84 m Breite (B/H 0.46). Das IST ein kleiner Baum. + # Jetzt: kurzer Stumpf (Height 0.7), Aeste ab dem Boden (Branch Start 0.0) + # und laenger als der Stumpf - damit uebernehmen die Aeste die Rolle der + # Stiele. Gemessen: 1.40 m hoch, 2.16 m breit (B/H 1.54), 640 Tris. "busch": { - "Height": 1.6, "Trunk Radius": 0.07, "Bend": 0.4, "Taper": 0.8, - "Branch Count": 14, "Branch Length": 0.9, "Branch Up": 1.0, - "Branch Start": 0.15, "Branch End": 0.95, + "Height": 0.7, "Trunk Radius": 0.07, "Bend": 0.4, "Taper": 0.8, + "Branch Count": 14, "Branch Length": 1.55, "Branch Up": 0.35, + "Branch Start": 0.0, "Branch End": 0.95, "Branch Bend": 0.6, "Branch Droop": 0.0, + "Root Flare": 0.2, # Straeucher haben keinen Wurzelanlauf "Sub Count": 1, "Sub Length": 0.35, "Sub Up": 1.6, "Sides": 4, - "Detail": 0.3, "Branch Count": 10, + "Detail": 0.3, }, # --- Varianten ueber die Kronenform (Idee: modular_tree "envelope shapes") --- "tanne": { # kegelfoermig: unten lange Aeste, oben kurz, leicht haengend @@ -943,9 +962,30 @@ def build_group(): L(sub_on.outputs[0], _sock(sub_sw, "Switch")) L(_out(mesh_sub, "Geometry"), _sock(sub_sw, "True")) + # Dieselbe Falle gilt fuer die HAUPTAESTE: "Branch Count" = 0 lieferte + # trotzdem einen Ast. Aufgefallen beim Kaktus-Setzling, der laut NPS noch + # gar keine Arme haben darf - er behielt einen. Weil die Sub-Aeste auf den + # Hauptaesten sitzen, wandern beide Ebenen hinter denselben Schalter. + br_join = N("GeometryNodeJoinGeometry"); br_join.location = (4620, 100) + L(_out(sub_sw, "Output"), br_join.inputs[0]) + L(_out(mesh_branch, "Geometry"), br_join.inputs[0]) + + br_on = N("FunctionNodeCompare"); br_on.location = (4400, 60) + try: + br_on.data_type = 'INT'; br_on.operation = 'GREATER_THAN' + L(V["Branch Count"], br_on.inputs[2]) + br_on.inputs[3].default_value = 0 + except Exception: + br_on = N("ShaderNodeMath"); br_on.location = (4400, 60) + br_on.operation = 'GREATER_THAN'; br_on.inputs[1].default_value = 0.5 + L(V["Branch Count"], br_on.inputs[0]) + br_sw = N("GeometryNodeSwitch"); br_sw.location = (4660, 200) + br_sw.input_type = 'GEOMETRY' + L(br_on.outputs[0], _sock(br_sw, "Switch")) + L(_out(br_join, "Geometry"), _sock(br_sw, "True")) + join = N("GeometryNodeJoinGeometry"); join.location = (4700, 300) - L(_out(sub_sw, "Output"), join.inputs[0]) - L(_out(mesh_branch, "Geometry"), join.inputs[0]) + L(_out(br_sw, "Output"), join.inputs[0]) L(_out(mesh_trunk, "Geometry"), join.inputs[0]) stuv = join # Name beibehalten fuer den nachfolgenden Merge-Zweig @@ -988,14 +1028,71 @@ def build_group(): # Wachstums-Stufen: Faktoren/Werte fuer t = 0 (Setzling) .. 1 (ausgewachsen). # Gleicher Seed -> dieselbe Baum-"Identitaet", nur juenger. Genau das braucht # ein Wachstums-System, damit Stufe 1 und Stufe 4 wie DERSELBE Baum wirken. +GROWTH_HEIGHT = 0.16 # Setzling ist 16 % der Endhoehe + GROWTH_YOUNG = { - "Height": 0.16, # Faktoren (werden mit dem Zielwert multipliziert) - "Trunk Radius": 0.30, - "Branch Length": 0.30, + "Height": GROWTH_HEIGHT, # Faktoren (werden mit dem Zielwert multipliziert) + + # Der Stamm darf NICHT einfach mitskalieren. Vorher stand hier 0.30 bei einem + # Hoehenfaktor von 0.16 - dadurch war der Setzling relativ zu seiner Hoehe + # 2.4x DICKER als der Altbaum (gemessen: r/H 0.025 gegen 0.0104) und sah + # aus wie ein Stummel. Botanisch gilt die elastische Aehnlichkeit nach + # McMahon: Hoehe ~ Durchmesser^(2/3), also Durchmesser ~ Hoehe^(3/2). + # Bei Hoehenfaktor 0.16 sind das 0.16^1.5 = 0.064. Junge Baeume sind sogar + # noch schlanker als die Regel vorhersagt - 0.064 ist die obere Grenze. + "Trunk Radius": GROWTH_HEIGHT ** 1.5, + + # Wurzelanlauf und Wurzelbretter entstehen erst mit dem Alter. Blieben sie + # auf dem Altbaum-Wert, bekam jeder Setzling einen Trichterfuss - das war + # der auffaelligste Grund, warum die kleinste Stufe "komisch" aussah. + "Root Flare": 0.15, + "Root Lobe Depth": 0.0, + + # Aeste muessen MIT der Hoehe schrumpfen, nicht langsamer. Mit 0.30 gegen + # Hoehe 0.16 waren die Aeste am Setzling relativ 1.9x zu lang - bei der + # Weide reichten sie dadurch unter den Boden. + "Branch Length": GROWTH_HEIGHT, "Branch Count": 0.35, + + # Junge Baeume tragen ihre Aeste TIEFER: das Aufasten (Selbstbeschneidung + # der unteren Aeste) passiert erst spaeter. Ohne das sass die Krone beim + # Setzling genauso hoch wie beim Altbaum - typisches Merkmal eines bloss + # runterskalierten Modells. + "Branch Start": 0.35, + + # Haengen und Ueberbiegen kommt mit Laenge und Gewicht. Am Setzling stehen + # Aeste und Wedel noch aufrecht. Ohne das reichten Palmwedel (Droop 1.2) und + # Kaktusarme (Droop -1.35) beim Setzling GEMESSEN bis zu 0.58 m bzw. 0.18 m + # unter den Wurzelansatz - das Asset haette in UE ueber dem Boden geschwebt. + "Branch Droop": 0.30, + "Sub Count": 0.0, # Setzling hat noch keine Sub-Aeste } +# Nicht jede Pflanze waechst wie ein Laubbaum. +# +# Palmen sind Einkeimblaettrige und haben KEIN Dickenwachstum: der Stamm baut +# erst seinen Durchmesser auf und streckt sich danach in die Hoehe - "once a +# palm stem emerges from the apical meristem, its diameter is fixed for life" +# (UF/IFAS ENH1212, Palm Morphology and Anatomy). Eine junge Palme ist also ein +# kurzer Stumpf in fast voller Dicke, kein duenner Schoessling. Mit dem +# Laubbaum-Faktor 0.064 wurde sie zum Draht. +# +# Kakteen sind Sukkulenten und folgen derselben Logik: der Saeulenkaktus bildet +# zuerst seinen Koerper und waechst dann nach oben. +GROWTH_OVERRIDES = { + # Die Trauerweide haengt erst, wenn die Zweige lang und schwer genug sind - + # junge Weiden wachsen aufrecht. Mit dem allgemeinen Setzling-Droop reichten + # ihre Zweige GEMESSEN 0.27 m unter den Stammfuss; das Asset haette in UE + # ueber dem Boden geschwebt. + "weide": {"Branch Droop": 0.0, "Attraction Up": 0.0}, + "palme": {"Trunk Radius": 0.85, "Branch Length": 0.28}, + # Saeulenkakteen bilden ihre Arme erst spaet: "branches normally begin to + # appear when a saguaro reaches 50 to 70 years of age" - da ist er schon + # rund 2 m hoch (NPS, How Saguaros Grow). Der Setzling ist eine reine Saeule. + "kaktus": {"Trunk Radius": 0.75, "Branch Length": 0.20, "Branch Count": 0.0}, +} + def growth_values(preset, t): """Parameter fuer Wachstums-Fortschritt t (0 = Setzling, 1 = ausgewachsen).""" @@ -1003,14 +1100,23 @@ def growth_values(preset, t): full.update(PRESETS.get(preset, {})) out = dict(full) t = max(0.0, min(1.0, t)) - for key, young_factor in GROWTH_YOUNG.items(): + factors = dict(GROWTH_YOUNG) + factors.update(GROWTH_OVERRIDES.get(preset, {})) + for key, young_factor in factors.items(): if key not in full: continue target = full[key] young = target * young_factor v = young + (target - young) * t - out[key] = max(1, int(round(v))) if isinstance(target, int) and key != "Sub Count" \ - else (int(round(v)) if isinstance(target, int) else v) + if isinstance(target, int): + # Ein Faktor von genau 0.0 heisst "gibt es beim Setzling noch gar + # nicht" und darf auf 0 fallen. Alles andere bekommt mindestens 1, + # damit aus einer Rundung kein kahler Stock wird. Vorher war die + # Ausnahme fest auf "Sub Count" verdrahtet - dadurch konnte der + # Kaktus-Setzling seine Arme nicht loswerden. + out[key] = int(round(v)) if young_factor == 0.0 else max(1, int(round(v))) + else: + out[key] = v return out diff --git a/tests/test_proportionen.py b/tests/test_proportionen.py new file mode 100644 index 0000000..38b0c36 --- /dev/null +++ b/tests/test_proportionen.py @@ -0,0 +1,146 @@ +"""Haelt die Proportionen der Baum-Presets fest. + +Diese Werte sind nicht nach Geschmack gewaehlt, sondern nachgemessen bzw. +belegt - deshalb lohnt es sich, sie festzunageln: + +* Palmenkrone: tree-gen `palm.py` (Weber&Penn) setzt die Wedellaenge auf + 0.3 x Stammlaenge; die TreeIt-Referenzpalme kommt auf 3.3 m Spannweite bei + 4.33 m Stamm = 0.38. Zielband also 0.25-0.45. +* Strauch: botanisch ist der Unterschied zum Baum die Architektur (mehrere + Stiele ab Boden, kein Leittrieb) - gemessen wird er nach Hoehe UND Breite. + Ein Strauch, der schmaler ist als hoch, ist ein Baeumchen. +* Setzling-Schlankheit: elastische Aehnlichkeit nach McMahon, Hoehe ~ + Durchmesser^(2/3). Der Setzling darf relativ zu seiner Hoehe nicht dicker + sein als der Altbaum. Vorher war er es um den Faktor 2.4. +* Palme und Kaktus sind davon ausgenommen: Palmen haben kein Dickenwachstum + ("its diameter is fixed for life", UF/IFAS ENH1212), Kakteen sind Sukkulenten. + Beide bauen zuerst den Durchmesser auf - deren Setzling ist absichtlich + gedrungen. + +Aufruf: blender --background --factory-startup --python tests/test_proportionen.py +""" +import os +import sys +import math +import importlib.util + +import bpy + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +spec = importlib.util.spec_from_file_location( + "tg", os.path.join(ROOT, "stylized_tree_generator.py")) +tg = importlib.util.module_from_spec(spec) +sys.modules["tg"] = tg +spec.loader.exec_module(tg) +tg.register() +NG = tg.build_group() +IDS = tg.socket_ids(NG) + +fails = [] + + +def geom(preset, t=None, over=None): + for o in list(bpy.data.objects): + bpy.data.objects.remove(o, do_unlink=True) + ob = tg.make_object(NG, preset=preset, growth=t, seed=7) + md = ob.modifiers["GN_Tree"] + for k, v in (over or {}).items(): + md[IDS[k]] = v + dg = bpy.context.evaluated_depsgraph_get() + ev = ob.evaluated_get(dg) + me = ev.to_mesh() + vs = [v.co.copy() for v in me.vertices] + n = len(me.vertices) + ev.to_mesh_clear() + return vs, n + + +def hw(vs): + z0 = min(v.z for v in vs) + h = max(v.z for v in vs) - z0 + b = 2 * max(math.hypot(v.x, v.y) for v in vs) + return z0, h, b + + +def trunk_radius(preset, t=None): + """Reiner Stammradius im Band 30-40 % - nur moeglich, weil Branch Count 0 + die Aeste inzwischen wirklich abschaltet (siehe Test weiter unten).""" + vs, _ = geom(preset, t, {"Branch Count": 0, "Sub Count": 0}) + z0, h, _ = hw(vs) + band = [math.hypot(v.x, v.y) for v in vs + if z0 + h * 0.30 <= v.z <= z0 + h * 0.40] + return (sum(band) / len(band) if band else 0.0), h + + +# --- 1) Branch Count 0 schaltet die Aeste wirklich ab ---------------------- +# "Curve to Points" klemmt Count auf mindestens 1. Ohne den Geometry-Switch +# lieferten 0 und 1 identische Geometrie. +_, n0 = geom("baum", over={"Branch Count": 0}) +_, n1 = geom("baum", over={"Branch Count": 1}) +if n0 >= n1: + fails.append("Branch Count 0 (%d Verts) schaltet die Aeste nicht ab (1 -> %d)" + % (n0, n1)) + +# --- 2) Palmenkrone ragt aus -------------------------------------------- +vs, _ = geom("palme") +z0, h, _ = hw(vs) +krone = max(math.hypot(v.x, v.y) for v in vs if v.z > z0 + h * 0.8) +if not (0.25 <= krone / h <= 0.45): + fails.append("Palme: Kronenradius/Hoehe %.2f, erwartet 0.25-0.45 " + "(tree-gen 0.30, TreeIt 0.38)" % (krone / h)) + +# --- 3) Busch ist breiter als hoch --------------------------------------- +vs, _ = geom("busch") +_, h, b = hw(vs) +if b < h: + fails.append("Busch: %.2f m breit bei %.2f m hoch - ein Strauch ist nicht " + "schmaler als hoch, sonst ist es ein Baeumchen" % (b, h)) + +# --- 4) Setzlinge sind nicht gedrungener als der Altbaum ------------------ +# Ausnahmen: Palme (kein Dickenwachstum) und Kaktus (Sukkulent). +for p in tg.PRESETS: + if p in ("palme", "kaktus"): + continue + r_alt, h_alt = trunk_radius(p, 1.0) + r_neu, h_neu = trunk_radius(p, 0.0) + if not (h_alt and h_neu and r_alt): + continue + schlank_alt = r_alt / h_alt + schlank_neu = r_neu / h_neu + if schlank_neu > schlank_alt * 1.5: + fails.append("%s: Setzling ist relativ %.1fx dicker als der Altbaum " + "(r/H %.4f gegen %.4f) - McMahon verlangt schlanker" + % (p, schlank_neu / schlank_alt, schlank_neu, schlank_alt)) + +# --- 5) Kein Setzling haengt nennenswert unter den Wurzelansatz ----------- +# Wedel und Aeste, die unter den Ursprung reichen, lassen das Asset in UE +# schweben. Grenze grosszuegig: 10 % der Hoehe. +for p in tg.PRESETS: + vs, _ = geom(p, 0.0) + zs = [v.z for v in vs] + z0, h = min(zs), max(zs) - min(zs) + # Der Stammfuss wird EXAKT bestimmt, indem der Baum ohne Aeste gebaut wird. + # Ein "tiefster Punkt nahe der Achse" reicht nicht: beim Kaktus ist der + # Stamm dicker als jeder sinnvolle Achsen-Schwellwert, die Suche fand dann + # nur Vertices weiter oben und meldete 92 % Ueberhang, wo keiner war. + tvs, _ = geom(p, 0.0, {"Branch Count": 0, "Sub Count": 0}) + fuss = min(v.z for v in tvs) + if fuss - z0 > h * 0.10: + fails.append("%s S1: Geometrie reicht %.2f m unter den Stammfuss (%.0f%% " + "der Hoehe)" % (p, fuss - z0, 100 * (fuss - z0) / h)) + +# --- 6) Kaktus-Setzling hat noch keine Arme ------------------------------ +# NPS: "branches normally begin to appear when a saguaro reaches 50 to 70 +# years of age" - da ist er rund 2 m hoch. +vals = tg.growth_values("kaktus", 0.0) +if vals.get("Branch Count", 1) != 0: + fails.append("Kaktus-Setzling hat %d Arme, erwartet 0" + % vals.get("Branch Count")) + +print("") +if fails: + print("ERGEBNIS: %d FEHLER" % len(fails)) + for f in fails: + print(" - " + f) + sys.exit(1) +print("ERGEBNIS: ALLE CHECKS OK")