Tree Gen 1.40.0: Obstbaum-Krone schliesst sich oben (Trichter behoben)

Der Baum erfuellte Kronenradius, Deckung, Kronenansatz und Tri-Budget - und sah
trotzdem falsch aus. Sichtbar wurde es erst am BREITENPROFIL der Krone in
Dritteln (unten/mitte/oben, Messung der Spielseite nachgestellt):
  Eiche      3.0 / 2.8 / 2.6   oben/mitte 0.90  (liest sich als Baum)
  Obstbaum   3.3 / 4.5 / 4.7   oben/mitte 1.07  (Trichter)

Kein einzelner Regler hat das geloest: Crown Taper (-0.10 bis +0.50), Branch
Droop (0.6-1.2), Crown Bulge (1.0-1.8) und Attraction Up einzeln durchgefahren
liessen oben/mitte bei rund 1.0 stehen. Der Grund ist strukturell: die
Kronenform folgt nicht der Astlaenge je Hoehe, sondern dem WEG DER ASTSPITZEN.
Solange die Aeste aufwaerts schwingen, landet das aeusserste Laub oben.

Loesung ist eine andere Bauform, kein Feintuning - kurze Aeste an einem hohen
Stamm statt langer Aeste an einem kurzen:
  Height 3.8 -> 4.9, Branch Length 2.55 -> 1.25, Branch Start 0.52 -> 0.40,
  Attraction Up +0.40 -> -0.05, Crown Taper -0.30 -> +0.15,
  Crown Bulge 0.60 -> 1.10, Branch Droop 0.35 -> 0.40.

Ueber alle neun Produktions-Seeds: oben/mitte 0.63-0.84, Kronenradius
1.88-2.01 m, Deckung 100-134 % der Eiche, 3752-4166 Tris, Ansatz 39 %.

Beim Einregeln kippte die Form zwischendurch in eine PILZform (unten am
breitesten) - deshalb steht das Profil jetzt als Test da. test_obstbaum prueft
oben/mitte <= 0.85. Gegenprobe mit den 1.39er Werten: neun Fehler bei 0.98-1.11.

B/H liegt jetzt bei 0.77-0.82 (Band der Spielseite 0.55-0.75). Bleibt bewusst
ungeprueft, wie beim Kronenradius entschieden: die Vorgaben Kronenradius >= 1.80
und B/H <= 0.75 sind bei 4-5 m Hoehe nur im Mittel gleichzeitig erfuellbar.

26 Tests bestehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 14:32:57 +02:00
co-authored by Claude Opus 4.8
parent 5a43ead8d1
commit 0b6f0deff9
8 changed files with 91 additions and 26 deletions
+38 -6
View File
@@ -127,12 +127,13 @@ print("")
# Birne 3/17/24. Ein Preset, das nur bei einem Seed passt, ist keines.
SEEDS = (1, 15, 22, 2, 16, 23, 3, 17, 24)
print("%-6s %7s %6s %9s %9s %9s %7s %9s"
% ("Seed", "Hoehe", "B/H", "KronR", "Card-Max", "KronR/Card", "Ansatz",
"m2/m3"))
print("%-6s %7s %6s %9s %9s %7s %8s %-15s %s"
% ("Seed", "Hoehe", "B/H", "KronR", "KronR/Cd", "Ansatz", "m2/m3",
"Profil u/m/o", "o/m"))
verh = []
deckungen = []
obenmitte = []
for seed in SEEDS:
for o in list(bpy.context.scene.objects):
if o is not card:
@@ -172,6 +173,7 @@ for seed in SEEDS:
ev = bl.evaluated_get(dg)
m2 = ev.to_mesh()
blatt_flaeche = sum(p.area for p in m2.polygons)
blatt_pts = [v.co.copy() for v in m2.vertices]
mitten = []
for grp in inseln(m2):
pts = [m2.vertices[i].co for i in grp]
@@ -186,13 +188,33 @@ for seed in SEEDS:
lz = [z for _x, _y, z in mitten]
kron_zyl = math.pi * lr ** 2 * (max(lz) - min(lz))
# BREITENPROFIL der Krone in Dritteln.
#
# Ohne diesen Check kann die Krone alle bisherigen Zahlen erfuellen und
# trotzdem falsch aussehen: der erste Obstbaum war ein TRICHTER - nach oben
# breiter, unten hohl (gemessen oben/mitte 1.07 gegen 0.90 der Eiche). Die
# Laubmasse sass an den top-aussen endenden Astspitzen. Kronenradius,
# Deckung und Tri-Zahl waren dabei alle in Ordnung.
profil = [0.0, 0.0, 0.0]
if blatt_pts:
pz = [p.z for p in blatt_pts]
pz0, pz1 = min(pz), max(pz)
drittel = (pz1 - pz0) / 3.0
for kk in range(3):
teil = [p 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)
om = profil[2] / profil[1] if profil[1] else 0.0
obenmitte.append(om)
v = kron_r / card_max if card_max else 0.0
verh.append(v)
deckung = blatt_flaeche / kron_zyl if kron_zyl else 0.0
deckungen.append(deckung)
print("%-6d %7.2f %6.2f %9.2f %9.2f %9.2f %6.0f%% %9.1f"
% (seed, hoehe, 2 * kron_r / hoehe, kron_r, card_max, v, ansatz,
deckung))
print("%-6d %7.2f %6.2f %9.2f %9.2f %6.0f%% %8.1f %.1f/%.1f/%.1f %.2f"
% (seed, hoehe, 2 * kron_r / hoehe, kron_r, v, ansatz, deckung,
profil[0], profil[1], profil[2], om))
# Kronen-DECKUNG: Blattflaeche je m3 Kronenvolumen.
#
@@ -218,6 +240,14 @@ for seed in SEEDS:
"Ast-Schlaeuche" % (seed, deckung, EICHE,
100 * deckung / EICHE))
# 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:
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))
# DIE funktionale Bedingung.
if v < 1.3:
fails.append("Seed %d: Kronenradius %.2f m bei %.2f m Card - die Krone "
@@ -241,6 +271,8 @@ for seed in SEEDS:
# funktionale (Kronenradius), weil an ihr die Frucht-Streuung haengt.
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("Kronen-Deckung ueber alle Seeds: %.1f - %.1f m2/m3, Eiche %.1f "
"(%.0f-%.0f %%)"
% (min(deckungen), max(deckungen), EICHE,