Tree Gen 1.47.0: Hochstamm auf jeder Wachstums-Stufe

Bis 1.46 war am Obstbaum nur S4 kalibriert. Im Welt-Test ist der User auf einen
S3 gestossen, der sich als duenne Astgabel mit Laubklumpen las. Gemessen lag
der Kronenansatz bei 10 / 16 / 22 / 29 % ueber die Stufen - den freien Stamm
gab es nur ganz am Ende.

Ursache ist der allgemeine Setzlings-Faktor 0.35 auf Branch Start. Der bildet
die Selbstbeschneidung eines WILDEN Baums ab: junge Baeume tragen ihre Aeste
tiefer und asten sich erst spaeter auf. Ein Hochstamm wird aber GEZOGEN - die
Baumschule veredelt hoch und nimmt die unteren Triebe weg, der freie Stamm ist
von Anfang an da.

GROWTH_OVERRIDES["obstbaum"] = {"Branch Start": 1.0} - jetzt 29 % auf allen
vier Stufen. test_obstbaum prueft die ganze Wachstumsreihe; Gegenprobe ohne die
Ausnahme: drei Fehler.

Definitions-Hinweis fuer den Abgleich mit der Spielseite: dort wird "Ansatz"
ueber den tiefsten BLATT-Vertex gemessen, hier ueber den tiefsten AST-Vertex.
Die Spielseite sah deshalb einen Einbruch in der Mitte (33/17/13/21), wir eine
monotone Reihe - dasselbe Preset, zwei Definitionen, derselbe echte Befund.

27 Tests bestehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 13:33:43 +02:00
co-authored by Claude Opus 4.8
parent 65feb71e56
commit 1a1c3a5c59
8 changed files with 102 additions and 9 deletions
+25
View File
@@ -702,6 +702,31 @@ Gegenprobe ohne die Boden-Klemmung: vier Fehler.
Drei bestehende Tests zaehlten die Huelle als Render-Mesh mit und schlugen zu
Recht an - sie filtern jetzt `UCX_`.
### Der Hochstamm muss auf JEDER Stufe stehen
Bis 1.46 war am Obstbaum nur S4 kalibriert. Im Welt-Test ist der User auf einen
S3 gestossen, der sich als duenne Astgabel mit Laubklumpen las statt als
Obstbaeumchen. Gemessen ueber die Stufen lag der Kronenansatz bei
**10 / 16 / 22 / 29 %** - den freien Stamm gab es nur ganz am Ende.
Ursache ist der allgemeine Setzlings-Faktor 0.35 auf `Branch Start`. Der bildet
die Selbstbeschneidung eines WILDEN Baums ab: junge Baeume tragen ihre Aeste
tiefer und asten sich erst spaeter auf. Beim Obstbaum stimmt das nicht - ein
**Hochstamm wird gezogen, nicht gewachsen**: die Baumschule veredelt hoch und
nimmt die unteren Triebe weg, der freie Stamm ist von Anfang an da. Genau daran
haengt der Hochstamm-Charakter, und genau den verlor der Baum in den
Zwischenstufen.
`GROWTH_OVERRIDES["obstbaum"] = {"Branch Start": 1.0}` - jetzt 29 % auf allen
vier Stufen. `tests/test_obstbaum.py` prueft das ueber die ganze Wachstumsreihe;
Gegenprobe ohne die Ausnahme: drei Fehler.
Nebenbei ein Definitions-Hinweis, der beim Vergleich mit der Spielseite wichtig
war: dort wird "Ansatz" ueber den tiefsten BLATT-Vertex gemessen, hier ueber den
tiefsten AST-Vertex. Die Spielseite sah deshalb einen Einbruch in der Mitte
(33/17/13/21 %), wir eine monotone Reihe - dasselbe Preset, zwei Definitionen.
Der Befund stimmte trotzdem: die Zwischenstufen hatten keinen freien Stamm.
### Blattwerk als Halbkugel-Bueschel, und groessere Baeume
Drei Aenderungen, die zusammengehoeren.
+1 -1
View File
@@ -1,7 +1,7 @@
schema_version = "1.0.0"
id = "stylized_tree_generator"
version = "1.46.1"
version = "1.47.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.5KB</td>
</tr>
<tr>
<td><tt><a href="./stylized_tree_generator-1.46.1.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.46.1</a></tt></td>
<td><tt><a href="./stylized_tree_generator-1.47.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.47.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>53.0KB</td>
<td>53.3KB</td>
</tr>
</table>
<center><p>Built 2026-08-07, 10:40</p></center>
<center><p>Built 2026-08-07, 11:33</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.46.1",
"version": "1.47.0",
"type": "add-on",
"maintainer": "D4rkst3r",
"license": [
@@ -98,9 +98,9 @@
"Mesh",
"Modeling"
],
"archive_url": "./stylized_tree_generator-1.46.1.zip",
"archive_size": 54254,
"archive_hash": "sha256:5860c1f85899dea531bcb44e88060fe61f44906aabf924dd37b8109f81fd364a"
"archive_url": "./stylized_tree_generator-1.47.0.zip",
"archive_size": 54533,
"archive_hash": "sha256:0c1ba47d4fe89d1c45dbff090d04feaa136bea349cd30386127bc70ab2acd8eb"
}
]
}
Binary file not shown.
Binary file not shown.
+12 -1
View File
@@ -1,7 +1,7 @@
bl_info = {
"name": "Stylized Tree Generator",
"author": "D4rkst3r",
"version": (1, 46, 1),
"version": (1, 47, 0),
"blender": (4, 2, 0),
"location": "View3D > Sidebar > Tree Gen",
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
@@ -1267,6 +1267,17 @@ GROWTH_OVERRIDES = {
# 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},
# Ein HOCHSTAMM wird gezogen, nicht gewachsen. Der allgemeine
# Setzlings-Faktor 0.35 auf "Branch Start" bildet die Selbstbeschneidung
# eines WILDEN Baums ab - junge Baeume tragen ihre Aeste tiefer und asten
# sich erst spaeter auf. Beim Obstbaum stimmt das nicht: die Baumschule
# veredelt hoch und nimmt die unteren Triebe weg, der freie Stamm ist von
# Anfang an da. Genau daran haengt der Hochstamm-Charakter.
#
# Gemessen ohne diese Ausnahme (Kronenansatz je Stufe): 10 / 16 / 22 / 29 %.
# Nur S4 war kalibriert; im Spiel las sich ein S3 als duenne Astgabel mit
# Laubklumpen statt als Obstbaeumchen - gemeldet aus dem Welt-Test.
"obstbaum": {"Branch Start": 1.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
+57
View File
@@ -372,6 +372,63 @@ for seed in SEEDS:
# Die Form sichert ohnehin das BREITENPROFIL schaerfer ab, als B/H es je
# konnte: B/H haette sowohl den Trichter (oben/mitte 1.07) als auch die
# Pilzform (unten/mitte 1.17) durchgelassen.
# --- Der HOCHSTAMM muss auf JEDER Wachstums-Stufe stehen -----------------
#
# Bis 1.46 war nur S4 kalibriert. Im Spiel ist der User auf einen S3 gestossen,
# der sich als duenne Astgabel mit Laubklumpen las statt als Obstbaeumchen -
# gemeldet aus dem Welt-Test. Gemessen lag der Kronenansatz bei 10 / 16 / 22 /
# 29 % ueber die Stufen: den freien Stamm gab es nur ganz am Ende.
#
# Ursache war der allgemeine Setzlings-Faktor auf "Branch Start", der die
# Selbstbeschneidung eines WILDEN Baums abbildet. Ein Hochstamm wird aber
# gezogen: die Baumschule veredelt hoch und nimmt die unteren Triebe weg, der
# freie Stamm ist von Anfang an da. Deshalb GROWTH_OVERRIDES.
print("")
print("%-6s %7s %7s %7s" % ("Stufe", "Hoehe", "Cards", "Ansatz"))
for o in list(bpy.context.scene.objects):
if o is not card:
bpy.data.objects.remove(o, do_unlink=True)
s.preset = "obstbaum"
s.art_name = "Stufen"
s.variant = 1
s.seed = 1
s.use_growth = True
s.stages = 4
bpy.ops.object.treegen_create()
_st = sorted((o for o in bpy.context.scene.objects
if o.type == 'MESH' and o is not card
and not o.name.endswith(("_Leaf", "_Frucht"))), key=lambda o: o.name)
for o in bpy.context.scene.objects:
o.select_set(o in _st)
bpy.context.view_layer.objects.active = _st[0]
bpy.ops.object.treegen_leaves()
for _k, _x in enumerate(_st, 1):
_dg = bpy.context.evaluated_depsgraph_get()
_ev = _x.evaluated_get(_dg)
_m = _ev.to_mesh()
_vs = [v.co.copy() for v in _m.vertices]
_a = _m.attributes.get("ast_id")
_aid = [_a.data[i].value for i in range(len(_vs))] if _a else [0] * len(_vs)
_ev.to_mesh_clear()
_z0 = min(v.z for v in _vs)
_h = max(v.z for v in _vs) - _z0
_astz = [v.z for v, i in zip(_vs, _aid) if i > 0]
_an = 100.0 * (min(_astz) - _z0) / _h if _astz else 0.0
_bl = bpy.data.objects.get(_x.name + "_Leaf")
_n = 0
if _bl is not None:
_ev = _bl.evaluated_get(_dg)
_m2 = _ev.to_mesh()
_n = len(_m2.polygons) // 2
_ev.to_mesh_clear()
print("%-6s %7.2f %7d %6.0f%%" % ("S%d" % _k, _h, _n, _an))
if not (23.0 <= _an <= 35.0):
fails.append("S%d: Kronenansatz %.0f %% - der Hochstamm muss auf JEDER "
"Stufe stehen, nicht nur bei S4 (Vorgabe ~29 %%)"
% (_k, _an))
if _n == 0:
fails.append("S%d: keine Cards" % _k)
print("")
print("Kronenradius/Card ueber alle Seeds: %.2f - %.2f" % (min(verh), max(verh)))
print("Krone unten/mitte ueber alle Seeds: %.2f - %.2f (Pilz 0.75-0.90, "