Tree Gen 1.37.0: Obstbaum-Preset + Card-Groesse gegen die Baumhoehe

1) Neues Preset "obstbaum" (Apfel/Kirsche/Birne)
   Eigenes Preset statt Umbau von "baum": der wird anderswo verwendet, und ein
   Hochstamm ist keine Standard-Silhouette.

   Der Zweck ist funktional, nicht bloss optisch: die Frucht-Streuung setzt
   Fruechte auf die AUSSENHAUT der Krone. Am generischen baum war der
   Kronenradius 0.78 m bei 1.28 m Card-Diagonale - die Cards waren groesser als
   die Krone, es gab keine Haut.

   Proportion belegt (Hochstamm): Kronenansatz 180-220 cm, Krone selbst 3-4 m.
   Ergibt 4.8 m Gesamthoehe und knapp 40 % Ansatz - genau das Band der
   Spielseite.

   Gemessen ueber alle NEUN Seeds der Spielseite: Hoehe 4.91-5.12 m,
   Kronenradius 1.83-2.05 m, Ansatz 38-40 %, Kronenradius/Card 2.15-2.42,
   3560-3824 Tris. Deckelung auf 420 Cards ueber LEAF_STUFEN_PRESET, sonst
   4304-4400 Tris.

   Zwei Fallen, gemessen statt vermutet: "Height" ist die STAMMlaenge, nicht die
   Baumhoehe (Height 4.8 ergab 5.4-6.2 m), und der Kronenansatz zaehlt gegen die
   GESAMThoehe (Branch Start 0.37 ergab 29 %).

   test_obstbaum.py prueft die funktionale Bedingung ueber alle neun Seeds. B/H
   ist BEWUSST nicht hart geprueft: B/H 0.55-0.75 und Kronenradius >= 1.80 bei
   4-5 m Hoehe lassen zusammen nur ein 4 % breites Fenster, waehrend die Streuung
   von Seed zu Seed 12 % betraegt. Vorrang hat der Kronenradius.

2) Card-Groesse misst sich an der BAUMHOEHE
   Mit 0.45/0.63/0.82/1.00 schrumpfte die Card um Faktor 2.2, der Baum um 6.2.
   Gemessen an der Birke 27/14/11/9 % der Baumhoehe, an der Eiche 33 gegen 11 -
   der Setzling trug relativ dreimal so grosses Laub wie der Altbaum.
   Jetzt 0.24/0.46/0.75/1.00: 14/10/10/9 %. Nicht ganz flach, ein junger Baum
   traegt seine Bueschel relativ groeber; auf Altbaum-Proportion gezwungen
   (0.18) wird er kahl.

   test_blatt_bueschel prueft die Spreizung jetzt direkt und ueber Inseln statt
   ueber Face-Index-Paare.

   HINWEIS an die Spielseite: der Addon-Pfad skaliert S1 auf 0.45 der
   S4-Groesse, auf BEIDEN Wegen nachgemessen (Einzelcard und Sammlung). Die
   dort gemessenen 0.76 entstehen also erst danach - bitte growth_t am
   S1-Objekt direkt vor treegen_leaves pruefen, das muss 0.0 sein.

25 Tests bestehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 13:00:35 +02:00
co-authored by Claude Opus 4.8
parent 499d507e28
commit 4aaf3231ef
9 changed files with 341 additions and 12 deletions
+63
View File
@@ -193,6 +193,69 @@ if kanten and kanten[0] > 0:
"steht auf %.0f%% - die Wachstums-Kopplung greift nicht"
% (100 * anteil, 100 * s.leaf_size_growth))
# --- 3z) Card-Groesse gegen die BAUMHOEHE --------------------------------
# Die absolute Diagonale sagt nichts: sie haengt daran, wie gross die Card im
# Atlas gezeichnet ist. Aussagekraeftig ist ihr Verhaeltnis zur Baumhoehe, und
# das war schief. Gemessen an der Birke: S1 27 %, S4 9 % - der Setzling trug
# relativ dreimal so grosses Laub wie der Altbaum und sah aus wie ein Laubpaket
# auf einem Stiel (an der Eiche 33 % gegen 11 %).
#
# Gemessen wird ueber INSELN, nicht ueber Face-Index-Paare. Die naheliegende
# Annahme "je zwei aufeinanderfolgende Faces sind eine Card" haelt nach dem
# Realize nicht zwingend - beide Wege stimmten hier zwar ueberein, aber die
# Insel-Zaehlung braucht die Annahme gar nicht erst.
print("")
print("%-18s %6s %9s %9s" % ("Stufe", "Cards", "Diagonale", "% der Hoehe"))
verhaeltnisse = []
for st in staemme:
bl = bpy.data.objects.get(st.name + "_Leaf")
if bl is None:
continue
dg = bpy.context.evaluated_depsgraph_get()
ev = bl.evaluated_get(dg)
m = ev.to_mesh()
nachbarn = {}
for e in m.edges:
a, b = e.vertices
nachbarn.setdefault(a, []).append(b)
nachbarn.setdefault(b, []).append(a)
gesehen, diagonalen = set(), []
for start in range(len(m.vertices)):
if start in gesehen:
continue
stapel, gruppe = [start], []
gesehen.add(start)
while stapel:
v = stapel.pop()
gruppe.append(v)
for w in nachbarn.get(v, ()):
if w not in gesehen:
gesehen.add(w)
stapel.append(w)
pts = [m.vertices[i].co for i in gruppe]
diagonalen.append(max((a - b).length for a in pts for b in pts))
ev.to_mesh_clear()
if not diagonalen:
continue
zs = [v.co.z for v in st.data.vertices]
hoehe = max(zs) - min(zs)
mittel = sum(diagonalen) / len(diagonalen)
anteil = 100.0 * mittel / hoehe
verhaeltnisse.append(anteil)
print("%-18s %6d %9.3f %10.0f%%" % (st.name, len(diagonalen), mittel, anteil))
if len(verhaeltnisse) >= 2:
spreizung = max(verhaeltnisse) / min(verhaeltnisse)
print("Setzling traegt relativ %.1fx groessere Cards als der Altbaum"
% spreizung)
# Ein junger Baum DARF seine Bueschel relativ groeber tragen - aber nicht
# dreimal so grob. Zwingt man ihn auf Altbaum-Proportion, wird er kahl;
# auch das ist durchgemessen. Faktor 2.0 ist die Grenze.
if spreizung > 2.0:
fails.append("Card-Groesse relativ zur Baumhoehe spreizt um Faktor "
"%.1f ueber die Stufen - der Setzling wird ein Laubpaket"
% spreizung)
# --- 3a) Hoehen-Filter: der Fuss bleibt frei -----------------------------
# Der Radius-Filter allein reicht nicht. Er trennt duenn von dick, aber ein
# junger Stamm ist UEBERALL duenn - gemessen sassen beim Setzling 44 % der