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
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
Der Baum erfuellte Kronenradius, Deckung, Ansatz und Tri-Budget - und sah
+1 -1
View File
@@ -1,7 +1,7 @@
schema_version = "1.0.0"
id = "stylized_tree_generator"
version = "1.40.0"
version = "1.41.0"
name = "Stylized Tree Generator"
tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen"
maintainer = "D4rkst3r"
+3 -3
View File
@@ -50,17 +50,17 @@
<td>34.1KB</td>
</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>Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen</td>
<td><a href="https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator">link</a></td>
<td>4.2.0 - ~</td>
<td>all</td>
<td>all</td>
<td>45.9KB</td>
<td>46.3KB</td>
</tr>
</table>
<center><p>Built 2026-08-06, 12:32</p></center>
<center><p>Built 2026-08-06, 13:04</p></center>
</body>
</html>
+4 -4
View File
@@ -82,7 +82,7 @@
"id": "stylized_tree_generator",
"name": "Stylized Tree Generator",
"tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen",
"version": "1.40.0",
"version": "1.41.0",
"type": "add-on",
"maintainer": "D4rkst3r",
"license": [
@@ -98,9 +98,9 @@
"Mesh",
"Modeling"
],
"archive_url": "./stylized_tree_generator-1.40.0.zip",
"archive_size": 46997,
"archive_hash": "sha256:530a636ee2a8a6750699c482ac718cd1c16e5ee9c2da8d9c78c6efa07927db05"
"archive_url": "./stylized_tree_generator-1.41.0.zip",
"archive_size": 47402,
"archive_hash": "sha256:c5d5c612fd723af6c1e226ce0ca42cf6d28298641ec5af77bb12223ebd6935c6"
}
]
}
Binary file not shown.
Binary file not shown.
+18 -9
View File
@@ -1,7 +1,7 @@
bl_info = {
"name": "Stylized Tree Generator",
"author": "D4rkst3r",
"version": (1, 40, 0),
"version": (1, 41, 0),
"blender": (4, 2, 0),
"location": "View3D > Sidebar > Tree Gen",
"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
# Gesamthoehe und ein Ansatz bei knapp 40 % - genau das Band der Spielseite.
#
# Gemessen ueber alle neun Produktions-Seeds: H 4.90 m, Kronenradius
# 1.88-2.01 m, Kronenansatz 39 %, Breitenprofil oben/mitte 0.63-0.84,
# Deckung 100-134 % der Eiche, 3752-4166 Tris.
# Zielbild ist der LOLLIPOP der stilisierten Vektor-Illustration: kurzer
# kraeftiger Stamm, darauf eine Kugelkrone. Der Weg dorthin ist NICHT mehr
# 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,
# 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,
# Branch Length 2.55) - daraus wurde ein Trichter, siehe unten.
"Branch Length": 1.25, "Branch Up": 0.7,
"Branch Start": 0.40, "Branch End": 0.90,
"Crown Taper": 0.15, # oben kuerzere Aeste (war -0.30 = besenfoermig)
"Crown Bulge": 1.1, # Bauch in der Mitte
"Attraction Up": -0.05, # KEIN Aufwaertsschwung mehr ...
"Branch Droop": 0.4, "Droop Curve": 3.0, # ... Spitzen kippen ab
# Kronenansatz bei 30 % statt 40 %: das ist der Weg zur KUGEL, ohne den
# Baum hoeher zu machen (4.9 m haengen an der Ernte-Reichweite). Die
# Krone wird dadurch 4.3 m hoch bei 4.5 m Breite - Kugelverhaeltnis
# 0.97 statt 0.83 der Haube.
"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,
"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.
Vorgaben der Spielseite, die mitgeprueft werden:
Kronenansatz 35-40 %, Kronenradius S4 >= 1.8 m, Hoehe 4-5 m,
Breitenprofil (die Krone muss sich nach oben schliessen),
Kronenansatz ~30 %, Kronenradius S4 >= 1.8 m, Hoehe 4-5 m,
Breitenprofil + Kugel-Kennwert (Lollipop: Kronenhoehe ~ Kronenbreite),
Deckung gegen die Eiche.
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 = []
deckungen = []
obenmitte = []
untenmitte = []
kugeln = []
for seed in SEEDS:
for o in list(bpy.context.scene.objects):
if o is not card:
@@ -203,12 +205,36 @@ for seed in SEEDS:
pz0, pz1 = min(pz), max(pz)
drittel = (pz1 - pz0) / 3.0
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]
profil[kk] = 2 * max((math.hypot(p.x, p.y) for p in teil),
default=0.0)
# 90. PERZENTIL statt Maximum. Das Maximum haengt an EINER Card:
# 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
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)
untenmitte.append(um)
v = kron_r / card_max if card_max else 0.0
verh.append(v)
@@ -245,10 +271,30 @@ for seed in SEEDS:
# 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
# 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 "
"(%.2f) - sie wird nach oben breiter statt sich zu "
"schliessen" % (seed, profil[2], profil[1], om))
"(%.2f) - sie schliesst sich oben nicht (Trichter lag bei "
"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.
if v < 1.3:
@@ -260,9 +306,9 @@ for seed in SEEDS:
if not (4.0 <= hoehe <= 5.2):
fails.append("Seed %d: Hoehe %.2f m, Vorgabe 4-5 m (5.2 als Toleranz)"
% (seed, hoehe))
if not (33.0 <= ansatz <= 45.0):
fails.append("Seed %d: Kronenansatz %.0f %%, Vorgabe 35-40 %% "
"(33-45 als Toleranz)" % (seed, ansatz))
if not (25.0 <= ansatz <= 35.0):
fails.append("Seed %d: Kronenansatz %.0f %%, Vorgabe ~30 %% "
"(25-35 als Toleranz)" % (seed, ansatz))
# B/H gilt fuer dieses Preset NICHT MEHR - Entscheidung der Spielseite,
# 2026-08-06. Es wird nur noch mitgedruckt.
@@ -282,8 +328,12 @@ for seed in SEEDS:
# Pilzform (unten/mitte 1.17) durchgelassen.
print("")
print("Kronenradius/Card ueber alle Seeds: %.2f - %.2f" % (min(verh), max(verh)))
print("Krone oben/mitte ueber alle Seeds: %.2f - %.2f (Eiche 0.90, "
"Trichter war 1.07)" % (min(obenmitte), max(obenmitte)))
print("Krone unten/mitte ueber alle Seeds: %.2f - %.2f (Pilz 0.75-0.90, "
"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 "
"(%.0f-%.0f %%)"
% (min(deckungen), max(deckungen), EICHE,