Tree Gen 1.41.0: Obstbaum-Kugelkrone (Lollipop) ueber den Kronenansatz

Zielbild der Spielseite ist der Lollipop der stilisierten Vektor-Illustration.
Der Weg dorthin fuehrt NICHT ueber mehr Baumhoehe (4.9 m sind die
Ernte-Reichweite), sondern ueber einen tieferen Kronenansatz: 39 % -> 29 %,
Stamm 1.9 -> 1.5 m. Die Krone wird dadurch von selbst hoeher (3.8 -> 4.3 m).
Dazu Crown Taper 0.15 -> 0.35, Crown Bulge 1.1 -> 1.5, Branch Droop 0.4 -> 0.25.

Gemessen ueber alle neun Produktions-Seeds: Kronenansatz 29 %, unten/mitte
0.86-1.01, oben/mitte 0.63-0.74, Kugel 1.35-1.47, Kronenradius 2.00-2.15 m,
Deckung 81-103 % der Eiche, Hoehe 4.90 m.

Das Messen war schwieriger als das Einstellen; zwei eigene Fehler, beide vor
der Abnahme gefunden:

1) Die Kronenbreite je Drittel als MAXIMUM ueber die Card-Punkte zu messen
   haengt an EINER Card. Unten/mitte sprang je Seed zwischen 0.89 und 1.00 bei
   stabilem Mittelwert 0.94 - Rauschen, keine Form. Mit dem 90. PERZENTIL wurde
   es stabil und zeigte sofort etwas anderes: die Krone war unten tatsaechlich
   am breitesten (1.04), was das Maximum verdeckt hatte.
2) Der Kugel-Kennwert bekam erst die Schranke 0.88 - eine Zahl aus einem
   Messskript mit anderer Breitendefinition. Mit der Test-Formel liegen die
   Werte um 1.4, die Schranke haette alles durchgelassen. Zum zweiten Mal
   dieselbe Falle nach der Deckungs-Schwelle.

Deshalb sind jetzt ALLE Referenzformen mit demselben Mass gemessen und stehen
als Tabelle im Test (Eiche, Trichter, Haube, Pilz, Kugel). Die Tabelle zeigt
auch, was NICHT trennt: der Kugel-Kennwert unterscheidet Kugel und Haube nicht
(1.35-1.47 gegen 1.26-1.44) - das tut der Kronenansatz, und der wird geprueft.
Die Kugel-Schranke bleibt als zweites Netz gegen Trichter und Pilz drin, mit
genau diesem Vermerk.

Das Wunschband der Spielseite (u/m 0.85-0.95) ist schmaler als die Streuung von
Seed zu Seed (0.86-1.01). Die Schranken kommen deshalb aus der Messung aller
Formen, nicht aus dem Band.

Gegenproben: Haube 9 Fehler (Kronenansatz), Trichter 37 (alle vier Kriterien).

26 Tests bestehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 15:04:08 +02:00
co-authored by Claude Opus 4.8
parent d87450a4b8
commit 263b8507d8
8 changed files with 137 additions and 30 deletions
+48
View File
@@ -563,6 +563,54 @@ Der Kaktus erzeugt regulaer **0 Cards** — kein Ast ist duenner als `Max Radius
und das ist botanisch richtig. Vorher lief das still durch und man stand vor und das ist botanisch richtig. Vorher lief das still durch und man stand vor
einer leeren `_Leaf`-Ebene; jetzt sagt der Operator es und nennt den Regler. einer leeren `_Leaf`-Ebene; jetzt sagt der Operator es und nennt den Regler.
### Vom Trichter ueber die Haube zur Kugel
Zielbild ist der **Lollipop** der stilisierten Vektor-Illustration: kurzer
kraeftiger Stamm, darauf eine Kugelkrone. Der Weg dorthin fuehrt NICHT ueber
mehr Baumhoehe - 4.9 m sind die Ernte-Reichweite -, sondern ueber einen
tieferen Kronenansatz: 39 % -> 29 %, Stamm 1.9 m -> 1.5 m. Die Krone wird
dadurch von selbst hoeher (3.8 m -> 4.3 m) und rundet sich, tiefere Fruechte
sind fuers Ernten ohnehin besser.
Dazu `Crown Taper` 0.15 -> 0.35, `Crown Bulge` 1.1 -> 1.5, `Branch Droop`
0.4 -> 0.25.
**Das Messen war hier schwieriger als das Einstellen.** Zwei Fehler, beide
gefunden, bevor sie in einer Abnahme gelandet sind:
1. Die Kronenbreite je Drittel als **Maximum** ueber die Card-Punkte zu
messen haengt an EINER Card: bei ~400 Cards von 1.25 m reicht eine einzige
weit aussen haengende, um ein Drittel zu bestimmen. Unten/mitte sprang je
Seed zwischen 0.89 und 1.00, waehrend der Mittelwert stabil bei 0.94 lag -
das war Rauschen, keine Form. Mit dem **90. Perzentil** ist es stabil, und
es zeigte prompt etwas anderes: die Krone war unten tatsaechlich am
breitesten (u/m 1.04), was das Maximum verdeckt hatte.
2. Der Kugel-Kennwert bekam zunaechst die Schranke 0.88 - eine Zahl aus einem
Messskript mit anderer Breitendefinition. Mit der Test-Formel liegen die
Werte um 1.4, die Schranke haette alles durchgelassen. **Zum zweiten Mal
dieselbe Falle** (nach der Deckungs-Schwelle): eine Schranke aus einem
anderen Kontext ist keine Schranke.
Deshalb sind jetzt ALLE Referenzformen mit demselben Mass gemessen und stehen
im Test:
| Form | unten/mitte | oben/mitte | Kugel (KronH/KronB) |
|---|---|---|---|
| Eiche | 1.04-1.15 | 0.77-0.88 | 1.71-1.91 |
| Trichter (1.39) | 0.66-0.72 | 1.18-1.31 | 0.98-1.12 |
| Haube (1.40) | 0.99-1.13 | 0.69-0.80 | 1.26-1.44 |
| Pilz (Verirrung) | 0.75-0.90 | 0.78-0.94 | 1.05-1.17 |
| **Kugel (1.41)** | **0.86-1.01** | **0.63-0.74** | **1.35-1.47** |
Die Tabelle zeigt auch, was NICHT trennt: der Kugel-Kennwert unterscheidet die
Kugel nicht von der Haube (1.35-1.47 gegen 1.26-1.44). Was sie unterscheidet,
ist der Kronenansatz - und genau der wird geprueft. Ein Test, der so tut, als
pruefe er die Form, waehrend in Wahrheit ein anderer Wert die Arbeit macht,
waere schlimmer als keiner.
Gegenproben: die Haube meldet 9 Fehler (Kronenansatz), der Trichter 37 (alle
vier Formkriterien).
### Obstbaum-Silhouette: Trichter statt Krone ### Obstbaum-Silhouette: Trichter statt Krone
Der Baum erfuellte Kronenradius, Deckung, Ansatz und Tri-Budget - und sah Der Baum erfuellte Kronenradius, Deckung, Ansatz und Tri-Budget - und sah
+1 -1
View File
@@ -1,7 +1,7 @@
schema_version = "1.0.0" schema_version = "1.0.0"
id = "stylized_tree_generator" id = "stylized_tree_generator"
version = "1.40.0" version = "1.41.0"
name = "Stylized Tree Generator" name = "Stylized Tree Generator"
tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen"
maintainer = "D4rkst3r" maintainer = "D4rkst3r"
+3 -3
View File
@@ -50,17 +50,17 @@
<td>34.1KB</td> <td>34.1KB</td>
</tr> </tr>
<tr> <tr>
<td><tt><a href="./stylized_tree_generator-1.40.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.40.0</a></tt></td> <td><tt><a href="./stylized_tree_generator-1.41.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.41.0</a></tt></td>
<td>Stylized Tree Generator</td> <td>Stylized Tree Generator</td>
<td>Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen</td> <td>Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen</td>
<td><a href="https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator">link</a></td> <td><a href="https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator">link</a></td>
<td>4.2.0 - ~</td> <td>4.2.0 - ~</td>
<td>all</td> <td>all</td>
<td>all</td> <td>all</td>
<td>45.9KB</td> <td>46.3KB</td>
</tr> </tr>
</table> </table>
<center><p>Built 2026-08-06, 12:32</p></center> <center><p>Built 2026-08-06, 13:04</p></center>
</body> </body>
</html> </html>
+4 -4
View File
@@ -82,7 +82,7 @@
"id": "stylized_tree_generator", "id": "stylized_tree_generator",
"name": "Stylized Tree Generator", "name": "Stylized Tree Generator",
"tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen",
"version": "1.40.0", "version": "1.41.0",
"type": "add-on", "type": "add-on",
"maintainer": "D4rkst3r", "maintainer": "D4rkst3r",
"license": [ "license": [
@@ -98,9 +98,9 @@
"Mesh", "Mesh",
"Modeling" "Modeling"
], ],
"archive_url": "./stylized_tree_generator-1.40.0.zip", "archive_url": "./stylized_tree_generator-1.41.0.zip",
"archive_size": 46997, "archive_size": 47402,
"archive_hash": "sha256:530a636ee2a8a6750699c482ac718cd1c16e5ee9c2da8d9c78c6efa07927db05" "archive_hash": "sha256:c5d5c612fd723af6c1e226ce0ca42cf6d28298641ec5af77bb12223ebd6935c6"
} }
] ]
} }
Binary file not shown.
Binary file not shown.
+18 -9
View File
@@ -1,7 +1,7 @@
bl_info = { bl_info = {
"name": "Stylized Tree Generator", "name": "Stylized Tree Generator",
"author": "D4rkst3r", "author": "D4rkst3r",
"version": (1, 40, 0), "version": (1, 41, 0),
"blender": (4, 2, 0), "blender": (4, 2, 0),
"location": "View3D > Sidebar > Tree Gen", "location": "View3D > Sidebar > Tree Gen",
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
@@ -187,9 +187,14 @@ PRESETS = {
# die Krone selbst ist 3-4 m hoch. 1.8 m Ansatz plus 3 m Krone sind 4.8 m # die Krone selbst ist 3-4 m hoch. 1.8 m Ansatz plus 3 m Krone sind 4.8 m
# Gesamthoehe und ein Ansatz bei knapp 40 % - genau das Band der Spielseite. # Gesamthoehe und ein Ansatz bei knapp 40 % - genau das Band der Spielseite.
# #
# Gemessen ueber alle neun Produktions-Seeds: H 4.90 m, Kronenradius # Zielbild ist der LOLLIPOP der stilisierten Vektor-Illustration: kurzer
# 1.88-2.01 m, Kronenansatz 39 %, Breitenprofil oben/mitte 0.63-0.84, # kraeftiger Stamm, darauf eine Kugelkrone. Der Weg dorthin ist NICHT mehr
# Deckung 100-134 % der Eiche, 3752-4166 Tris. # Baumhoehe (4.9 m sind die Ernte-Reichweite), sondern ein tieferer
# Kronenansatz - tiefere Fruechte sind fuers Ernten ohnehin besser.
#
# Gemessen ueber alle neun Produktions-Seeds: H 4.90 m, Kronenansatz 29 %,
# Kronenhoehe/-breite 0.97 (Kugel), Breitenprofil unten/mitte 0.95 und
# oben/mitte 0.63, Deckung wie die Eiche.
# #
# Fuer dieses Preset gilt B/H NICHT (Entscheidung der Spielseite, # Fuer dieses Preset gilt B/H NICHT (Entscheidung der Spielseite,
# 2026-08-06): Kronenradius >= 1.80 schlaegt B/H <= 0.75, weil an der # 2026-08-06): Kronenradius >= 1.80 schlaegt B/H <= 0.75, weil an der
@@ -207,11 +212,15 @@ PRESETS = {
# Kurze Aeste an einem HOHEN Stamm. Vorher genau umgekehrt (Height 3.8, # Kurze Aeste an einem HOHEN Stamm. Vorher genau umgekehrt (Height 3.8,
# Branch Length 2.55) - daraus wurde ein Trichter, siehe unten. # Branch Length 2.55) - daraus wurde ein Trichter, siehe unten.
"Branch Length": 1.25, "Branch Up": 0.7, "Branch Length": 1.25, "Branch Up": 0.7,
"Branch Start": 0.40, "Branch End": 0.90, # Kronenansatz bei 30 % statt 40 %: das ist der Weg zur KUGEL, ohne den
"Crown Taper": 0.15, # oben kuerzere Aeste (war -0.30 = besenfoermig) # Baum hoeher zu machen (4.9 m haengen an der Ernte-Reichweite). Die
"Crown Bulge": 1.1, # Bauch in der Mitte # Krone wird dadurch 4.3 m hoch bei 4.5 m Breite - Kugelverhaeltnis
"Attraction Up": -0.05, # KEIN Aufwaertsschwung mehr ... # 0.97 statt 0.83 der Haube.
"Branch Droop": 0.4, "Droop Curve": 3.0, # ... Spitzen kippen ab "Branch Start": 0.30, "Branch End": 0.90,
"Crown Taper": 0.35, # oben kuerzere Aeste
"Crown Bulge": 1.5, # Bauch in der Mitte
"Attraction Up": -0.05, # KEIN Aufwaertsschwung ...
"Branch Droop": 0.25, "Droop Curve": 3.0, # ... Spitzen kippen ab
"Branch Bend": 0.9, "Branch Thickness": 0.22, "Branch Bend": 0.9, "Branch Thickness": 0.22,
"Sub Count": 3, "Sub Length": 0.7, "Root Flare": 1.0, "Sub Count": 3, "Sub Length": 0.7, "Root Flare": 1.0,
}, },
+63 -13
View File
@@ -10,8 +10,8 @@ Deshalb ist das hier KEIN Silhouetten-Test mit Geschmacksurteil, sondern eine
funktionale Bedingung: Kronenradius > Card-Groesse. funktionale Bedingung: Kronenradius > Card-Groesse.
Vorgaben der Spielseite, die mitgeprueft werden: Vorgaben der Spielseite, die mitgeprueft werden:
Kronenansatz 35-40 %, Kronenradius S4 >= 1.8 m, Hoehe 4-5 m, Kronenansatz ~30 %, Kronenradius S4 >= 1.8 m, Hoehe 4-5 m,
Breitenprofil (die Krone muss sich nach oben schliessen), Breitenprofil + Kugel-Kennwert (Lollipop: Kronenhoehe ~ Kronenbreite),
Deckung gegen die Eiche. Deckung gegen die Eiche.
B/H gilt fuer dieses Preset NICHT - siehe Kommentar unten. B/H gilt fuer dieses Preset NICHT - siehe Kommentar unten.
@@ -136,6 +136,8 @@ print("%-6s %7s %6s %9s %9s %7s %8s %-15s %s"
verh = [] verh = []
deckungen = [] deckungen = []
obenmitte = [] obenmitte = []
untenmitte = []
kugeln = []
for seed in SEEDS: for seed in SEEDS:
for o in list(bpy.context.scene.objects): for o in list(bpy.context.scene.objects):
if o is not card: if o is not card:
@@ -203,12 +205,36 @@ for seed in SEEDS:
pz0, pz1 = min(pz), max(pz) pz0, pz1 = min(pz), max(pz)
drittel = (pz1 - pz0) / 3.0 drittel = (pz1 - pz0) / 3.0
for kk in range(3): for kk in range(3):
teil = [p for p in blatt_pts teil = [math.hypot(p.x, p.y) for p in blatt_pts
if pz0 + kk * drittel <= p.z <= pz0 + (kk + 1) * drittel] if pz0 + kk * drittel <= p.z <= pz0 + (kk + 1) * drittel]
profil[kk] = 2 * max((math.hypot(p.x, p.y) for p in teil), # 90. PERZENTIL statt Maximum. Das Maximum haengt an EINER Card:
default=0.0) # bei ~400 Cards von 1.25 m Diagonale reicht eine einzige weit
# aussen haengende, um die Breite eines Drittels zu setzen.
# Gemessen sprang unten/mitte dadurch je Seed zwischen 0.89 und
# 1.00, waehrend der Mittelwert stabil bei 0.94 lag - das war
# Rauschen, keine Formabweichung. Das Perzentil misst, wo die
# Laubmasse endet, nicht wo die letzte Card haengt.
teil.sort()
profil[kk] = 2 * (teil[int(0.9 * (len(teil) - 1))] if teil else 0.0)
om = profil[2] / profil[1] if profil[1] else 0.0 om = profil[2] / profil[1] if profil[1] else 0.0
um = profil[0] / profil[1] if profil[1] else 0.0
# KUGEL-Kennwert: Kronenhoehe geteilt durch die groesste Kronenbreite.
#
# Er steht hier als zweites Netz gegen Trichter (0.98-1.12) und Pilz
# (1.05-1.17) - und weil die Spielseite "Hoehe ~ Breite" als Zielbild
# genannt hat, gehoert die Zahl sichtbar ins Protokoll.
# Er trennt die Kugel (1.35-1.47) aber NICHT von der Haube (1.26-1.44):
# beide Formen unterscheiden sich vor allem im KRONENANSATZ (29 % gegen
# 39 %), und den prueft der Check weiter unten. Ein erster Anlauf setzte
# die Grenze auf 0.88 - eine Zahl aus einer anderen Messung, die hier bei
# Werten um 1.4 alles durchgelassen haette.
kugel = 0.0
if blatt_pts and max(profil) > 0:
pz = [p.z for p in blatt_pts]
kugel = (max(pz) - min(pz)) / max(profil)
kugeln.append(kugel)
obenmitte.append(om) obenmitte.append(om)
untenmitte.append(um)
v = kron_r / card_max if card_max else 0.0 v = kron_r / card_max if card_max else 0.0
verh.append(v) verh.append(v)
@@ -245,10 +271,30 @@ for seed in SEEDS:
# Die Krone muss sich nach OBEN schliessen. Die Eiche, die laut Abnahme als # Die Krone muss sich nach OBEN schliessen. Die Eiche, die laut Abnahme als
# Baum liest, kommt auf 0.90; der Trichter lag bei 1.07. 0.85 als Grenze # Baum liest, kommt auf 0.90; der Trichter lag bei 1.07. 0.85 als Grenze
# laesst Spielraum nach oben, schliesst den Trichter aber sicher aus. # laesst Spielraum nach oben, schliesst den Trichter aber sicher aus.
if om > 0.85: # Die Grenzen kommen aus einer Messung ALLER Formen mit DIESEM Mass, nicht
# aus dem Wunschband - die Streuung von Seed zu Seed ist breiter als das
# Band, das die Spielseite genannt hat (u/m 0.85-0.95). Gemessen ueber neun
# Seeds:
# Eiche u/m 1.04-1.15 o/m 0.77-0.88
# Trichter u/m 0.66-0.72 o/m 1.18-1.31
# Haube u/m 0.99-1.13 o/m 0.69-0.80
# Pilz u/m 0.75-0.90 o/m 0.78-0.94
# Kugel u/m 0.86-1.01 o/m 0.63-0.74
# Die Schranken trennen die Kugel von Trichter und Pilz; von der Haube
# trennt sie der Kugel-Kennwert weiter unten.
if om > 0.80:
fails.append("Seed %d: Krone oben %.1f m breit, in der Mitte %.1f " fails.append("Seed %d: Krone oben %.1f m breit, in der Mitte %.1f "
"(%.2f) - sie wird nach oben breiter statt sich zu " "(%.2f) - sie schliesst sich oben nicht (Trichter lag bei "
"schliessen" % (seed, profil[2], profil[1], om)) "1.18-1.31)" % (seed, profil[2], profil[1], om))
if um < 0.82:
fails.append("Seed %d: Krone unten %.1f m breit, in der Mitte %.1f "
"(%.2f) - unten zu schmal, das wird ein Pilz bzw. ein "
"Trichter (0.66-0.90)"
% (seed, profil[0], profil[1], um))
if kugel < 1.20:
fails.append("Seed %d: Kronenhoehe/-breite %.2f - flacher als Trichter "
"(0.98-1.12) und Pilz (1.05-1.17) zulassen"
% (seed, kugel))
# DIE funktionale Bedingung. # DIE funktionale Bedingung.
if v < 1.3: if v < 1.3:
@@ -260,9 +306,9 @@ for seed in SEEDS:
if not (4.0 <= hoehe <= 5.2): if not (4.0 <= hoehe <= 5.2):
fails.append("Seed %d: Hoehe %.2f m, Vorgabe 4-5 m (5.2 als Toleranz)" fails.append("Seed %d: Hoehe %.2f m, Vorgabe 4-5 m (5.2 als Toleranz)"
% (seed, hoehe)) % (seed, hoehe))
if not (33.0 <= ansatz <= 45.0): if not (25.0 <= ansatz <= 35.0):
fails.append("Seed %d: Kronenansatz %.0f %%, Vorgabe 35-40 %% " fails.append("Seed %d: Kronenansatz %.0f %%, Vorgabe ~30 %% "
"(33-45 als Toleranz)" % (seed, ansatz)) "(25-35 als Toleranz)" % (seed, ansatz))
# B/H gilt fuer dieses Preset NICHT MEHR - Entscheidung der Spielseite, # B/H gilt fuer dieses Preset NICHT MEHR - Entscheidung der Spielseite,
# 2026-08-06. Es wird nur noch mitgedruckt. # 2026-08-06. Es wird nur noch mitgedruckt.
@@ -282,8 +328,12 @@ for seed in SEEDS:
# Pilzform (unten/mitte 1.17) durchgelassen. # Pilzform (unten/mitte 1.17) durchgelassen.
print("") print("")
print("Kronenradius/Card ueber alle Seeds: %.2f - %.2f" % (min(verh), max(verh))) print("Kronenradius/Card ueber alle Seeds: %.2f - %.2f" % (min(verh), max(verh)))
print("Krone oben/mitte ueber alle Seeds: %.2f - %.2f (Eiche 0.90, " print("Krone unten/mitte ueber alle Seeds: %.2f - %.2f (Pilz 0.75-0.90, "
"Trichter war 1.07)" % (min(obenmitte), max(obenmitte))) "Trichter 0.66-0.72)" % (min(untenmitte), max(untenmitte)))
print("Kugel (KronH/KronB) ueber alle Seeds: %.2f - %.2f (Trichter 0.98-1.12, "
"Pilz 1.05-1.17, Haube 1.26-1.44)" % (min(kugeln), max(kugeln)))
print("Krone oben/mitte ueber alle Seeds: %.2f - %.2f (Trichter 1.18-1.31)"
% (min(obenmitte), max(obenmitte)))
print("Kronen-Deckung ueber alle Seeds: %.1f - %.1f m2/m3, Eiche %.1f " print("Kronen-Deckung ueber alle Seeds: %.1f - %.1f m2/m3, Eiche %.1f "
"(%.0f-%.0f %%)" "(%.0f-%.0f %%)"
% (min(deckungen), max(deckungen), EICHE, % (min(deckungen), max(deckungen), EICHE,