diff --git a/README.md b/README.md index 71f76a6..1ad51e7 100644 --- a/README.md +++ b/README.md @@ -395,7 +395,7 @@ gemessen und abgenommen: | | vorher | jetzt | Wirkung | |---|---|---|---| | Groesse | 0.22 | **0.85** | Bueschel statt Bonsai | -| Dichte | 500 | **150** | wenige grosse statt viele Fussel | +| Dichte | 500 | **170** | wenige grosse statt viele Fussel | | **Max Radius** | 0.02 | **0.04** | der Volumen-Hebel | | Kippung | 0.5 | **0.9** | Rotations-Chaos schliesst Sichtluecken | @@ -403,6 +403,29 @@ gemessen und abgenommen: Zweigspitzen Laub, die Krone hatte nur Kontur und war innen nackt. Bei 0.04 wachsen Bueschel auch am mittleren Geaest und die Krone bekommt Tiefe. +**Deckung pro Stufe, nicht per Formel.** Card-Anteil und Groesse stehen als +Tabelle (`LEAF_STUFEN`). Der Dichte-Anteil bei S1/S2 sieht hoch aus — das +taeuscht: „Dichte" ist Punkte pro m² *Zweigflaeche*, und die waechst ueber die +Stufen dramatisch. Gemessen hat der S1-Setzling **0.013 m²** zulaessige Flaeche; +ohne Anhebung bekaeme er null Cards. Was zaehlt, ist die Anzahl: + +| Stufe | Cards | Card-Kante | Tris | Budget | +|---|---|---|---|---| +| S1 | 17 | 0.19 | 498 | 800 | +| S2 | 43 | 0.29 | 754 | 1200 | +| S3 | 67 | 0.37 | 946 | 4300 | +| S4 | 130 | 0.45 | 1396 | 4300 | + +**Der Radius-Filter skaliert mit.** Er trennt duenne Zweige (Laub) von dickem +Geaest — aber ein Setzling ist *komplett* duenner als eine feste Schwelle. Mit +starren 0.04 m sassen bei S2 **81 %** der Cards am Stamm, bei S3 58 %; jetzt +48 bzw. 52 %. + +> Bei S1 bleiben rund 88 % am Stamm, und das ist **kein Defekt**: gemessen ist +> die zulaessige Flaeche dort fuer *jeden* Schwellwert identisch — der Saemling +> hat schlicht kein dickes Geaest, an dem sich etwas trennen liesse. Blaetter am +> Stiel sind bei einem Saemling der Normalfall. + **Bueschelgroesse haengt am Wachstum.** Der Generator merkt sich `growth_t` am Objekt; der Blattwerk-Operator skaliert die Cards damit (`Setzlings-Anteil`, Standard 45 %). Gemessen ueber vier Stufen: Card-Kante 0.19 → 0.28 → 0.37 → 0.45. @@ -436,10 +459,17 @@ Haken `LOD-Kette erzeugen`: exportiert `_LODGroup.fbx` mit `_LOD0..3` unte einem `fbx_type=LodGroup`-Empty — dasselbe Schema wie die Fels-Pipeline. **Nur der Stand-Baum**; die Faell-Teile leben Sekunden als Physik-Actor, dort reicht LOD0. -Die Blatt-Reduktion laeuft ueber die **Dichte**, nicht ueber Decimate: eine Card -besteht aus zwei Dreiecken und laesst sich nicht vereinfachen — man muss weniger -davon setzen. Damit die Krone dabei nicht ausduennt, werden die verbleibenden -Cards groesser. LOD3 ist der Impostor-Ersatz. +Die Blatt-Reduktion ist ein **Teilset der LOD0-Cards**, keine Neustreuung. Das +ist der Unterschied zwischen „die Krone wird duenner" und „die Krone springt +um": beim Neustreuen laegen die Cards an anderen Stellen und die Silhouette +wechselt beim LOD-Uebergang sichtbar. Als Teilset bleibt jede verbleibende Card +genau dort, wo sie in LOD0 schon war; damit die Krone nicht loechrig wird, +werden die Ueberlebenden groesser. Gemessen: **100 % der Cards auf LOD1–3 +sitzen auf einer LOD0-Position.** LOD3 ist der Impostor-Ersatz. + +Gearbeitet wird auf zusammenhaengenden **Inseln**, nicht auf einzelnen Faces — +eine Card besteht aus mehreren Dreiecken und muss ganz bleiben oder ganz +verschwinden. | Stufe | Dichte | Card-Groesse | Rinde | gemessen (Rinde/Blatt/Tris) | |---|---|---|---|---| diff --git a/blender_manifest_tree.toml b/blender_manifest_tree.toml index 0f61dd5..2972e06 100644 --- a/blender_manifest_tree.toml +++ b/blender_manifest_tree.toml @@ -1,7 +1,7 @@ schema_version = "1.0.0" id = "stylized_tree_generator" -version = "1.31.0" +version = "1.32.0" name = "Stylized Tree Generator" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" maintainer = "D4rkst3r" diff --git a/dist/index.html b/dist/index.html index 30d5d95..ce3e649 100644 --- a/dist/index.html +++ b/dist/index.html @@ -50,17 +50,17 @@ 34.1KB - stylized_tree_generator-1.31.0 + stylized_tree_generator-1.32.0 Stylized Tree Generator Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen link 4.2.0 - ~ all all - 36.3KB + 37.8KB -

Built 2026-08-06, 07:20

+

Built 2026-08-06, 07:44

diff --git a/dist/index.json b/dist/index.json index bc7bffa..2860f12 100644 --- a/dist/index.json +++ b/dist/index.json @@ -82,7 +82,7 @@ "id": "stylized_tree_generator", "name": "Stylized Tree Generator", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", - "version": "1.31.0", + "version": "1.32.0", "type": "add-on", "maintainer": "D4rkst3r", "license": [ @@ -98,9 +98,9 @@ "Mesh", "Modeling" ], - "archive_url": "./stylized_tree_generator-1.31.0.zip", - "archive_size": 37196, - "archive_hash": "sha256:2afb514c39d4378baa8885b53e117bb460987ac57178a826bc20cc8b1efd71b5" + "archive_url": "./stylized_tree_generator-1.32.0.zip", + "archive_size": 38712, + "archive_hash": "sha256:b92b7caccf18b7690b63cee35b8597484a0a789d47a9df529857d23bbf57be8f" } ] } \ No newline at end of file diff --git a/dist/stylized_tree_generator-1.31.0.zip b/dist/stylized_tree_generator-1.31.0.zip deleted file mode 100644 index c446860..0000000 Binary files a/dist/stylized_tree_generator-1.31.0.zip and /dev/null differ diff --git a/dist/stylized_tree_generator-1.32.0.zip b/dist/stylized_tree_generator-1.32.0.zip new file mode 100644 index 0000000..0b4e4e8 Binary files /dev/null and b/dist/stylized_tree_generator-1.32.0.zip differ diff --git a/stylized_tree_generator.py b/stylized_tree_generator.py index 9d609f8..b57639f 100644 --- a/stylized_tree_generator.py +++ b/stylized_tree_generator.py @@ -1,7 +1,7 @@ bl_info = { "name": "Stylized Tree Generator", "author": "D4rkst3r", - "version": (1, 31, 0), + "version": (1, 32, 0), "blender": (4, 2, 0), "location": "View3D > Sidebar > Tree Gen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", @@ -1195,11 +1195,7 @@ LEAF_GROUP_NAME = "GN_TreeLeaves" # hingehoeren. Mit Bueschel-Atlanten liegen die Groessenordnungen bei # Size ~0.7 und Density ~90. LEAF_DEFAULTS = { - # 150 statt der zunaechst gemessenen 170: mit der REALEN Diamond-Card - # (6 Tris, nicht 2) landet die Eiche bei 170 auf 4206 Tris und damit ueber - # dem 4000er-Budget aus ASSETS.md Teil 6 - 2778 Krone plus 1428 Stamm. - # 150 liegt im abgesegneten Sweetspot 130-170 und haelt das Budget (3816). - "Density": 150.0, # Punkte pro m2 Zweigoberflaeche + "Density": 170.0, # Punkte pro m2 Zweigoberflaeche (S4-Wert) # DER Volumen-Hebel. Bei 0.02 trugen nur die aeussersten Zweigspitzen Laub - # die Krone hatte nur Kontur und war innen nackt. Bei 0.04 wachsen Bueschel # auch am mittleren Geaest und die Krone bekommt Tiefe. Am fertigen Baum in @@ -1385,6 +1381,47 @@ def leaf_socket_ids(ng): if getattr(so, "in_out", "") == 'INPUT'} +# Deckung PRO STUFE statt einer Formel. Die fruehere Rechnung (Dichte durch +# faktor^2) war zu grob: sie hielt den Setzling zwar vor der Kahlheit, gab ihm +# aber eine Deckung wie einem Altbaum. +# +# Der Dichte-Anteil bei S1/S2 sieht hoch aus - das taeuscht. "Dichte" ist +# Punkte pro m2 ZWEIGFLAECHE, und die waechst ueber die Stufen dramatisch: +# gemessen hat der S1-Setzling 0.013 m2 zulaessige Flaeche. Ohne Anhebung +# bekaeme er null Cards. Was zaehlt, ist die ANZAHL, und die steigt sauber +# von ~8 auf ~65. +# +# S4 traegt die volle Dichte (170), S2 liegt 15 % darunter, S1 deutlich. +# Dichte-Anteil Groessen-Anteil +# Boden fuer den Radius-Filter beim Setzling. Nachgemessen: 0.064 (der reine +# Stammradius-Faktor) laesst ihn kahl, 0.20 gibt 12 Cards bei 12 % am Stamm. +LEAF_RADIUS_YOUNG = 0.20 + +# Dichte-Anteil Groessen-Anteil +LEAF_STUFEN = ( + (6.00, 0.45), # S1 - wenige, kleine Bueschel auf winziger Zweigflaeche + (1.60, 0.63), # S2 - 15 % unter der vollen Deckung (nach Flaeche gerechnet) + (1.00, 0.82), # S3 + (1.00, 1.00), # S4 - volle Dichte +) + + +def _stufen_anteile(growth_t): + """Dichte- und Groessen-Anteil fuer einen Wachstums-Fortschritt. + + Die Stufen liegen bei t = 0 / 1/3 / 2/3 / 1 (vier Stufen). Zwischen den + Stuetzstellen wird linear gemischt, damit auch drei oder fuenf Stufen + sinnvolle Werte bekommen. + """ + n = len(LEAF_STUFEN) + x = max(0.0, min(1.0, growth_t)) * (n - 1) + i = min(int(x), n - 2) + f = x - i + d0, g0 = LEAF_STUFEN[i] + d1, g1 = LEAF_STUFEN[i + 1] + return d0 + (d1 - d0) * f, g0 + (g1 - g0) * f + + def _stufen_budget(s, growth_t): """Tri-Budget fuer einen Baum nach seiner Wachstums-Stufe.""" if growth_t < 0.15: @@ -1451,13 +1488,16 @@ def make_leaves(context, tree_obj, card_obj, name=None, card_coll=None, **overri # --------------------------------------------------------------------------- -# LOD-Stufen fuer den Stand-Baum. Die Blatt-Reduktion laeuft ueber die DICHTE, -# nicht ueber Decimate: eine Card ist ein Billboard aus zwei Dreiecken - die -# laesst sich nicht sinnvoll vereinfachen, man muss weniger davon setzen. Damit -# die Krone dabei nicht ausduennt, werden die verbleibenden Cards groesser. +# LOD-Stufen fuer den Stand-Baum. +# +# Die Blatt-Reduktion ist ein TEILSET der LOD0-Cards, keine Neustreuung: die +# verbleibenden Cards liegen genau dort, wo sie in LOD0 schon waren. Wuerde man +# je Stufe neu streuen, saessen sie an anderen Stellen und die Silhouette +# spraenge beim LOD-Wechsel sichtbar um. Damit die Krone beim Ausduennen nicht +# loechrig wird, werden die Ueberlebenden groesser. # LOD3 ist der Impostor-Ersatz: wenige, dafuer sehr grosse Cards. # -# Dichte Card-Groesse Rinden-Decimate +# Card-Anteil Card-Groesse Rinden-Decimate LOD_STUFEN = ( (1.00, 1.00, 1.00), # LOD0 - wie der Stand-Baum (0.60, 1.15, 0.70), # LOD1 @@ -1524,6 +1564,91 @@ def _sphere_normals(ob, slot_index, center=None): return center + +def _cards_ausduennen(ob, slot, anteil, groesse, seed=0): + """Behaelt einen TEILSET der Cards und vergroessert die Ueberlebenden. + + Der Unterschied zum Neustreuen mit geringerer Dichte ist der entscheidende: + beim Neustreuen liegen die Cards an ANDEREN Stellen, und beim LOD-Wechsel + springt die Silhouette sichtbar um. Ein Teilset laesst die verbleibenden + Cards genau dort, wo sie in LOD0 schon waren - die Krone wird duenner, aber + ihre Form bleibt. + + Gearbeitet wird auf zusammenhaengenden Inseln, nicht auf einzelnen Faces: + eine Card besteht aus mehreren Dreiecken und muss ganz bleiben oder ganz + verschwinden. Die Auswahl ist seed-stabil, damit zwei Exporte desselben + Baums dieselbe Kette ergeben. + """ + import random as _rnd + + me = ob.data + bm = bmesh.new() + bm.from_mesh(me) + bm.faces.ensure_lookup_table() + + kandidaten = [f for f in bm.faces if f.material_index == slot] + if not kandidaten: + bm.free() + return 0, 0 + + # Inseln bilden (zusammenhaengende Flaechen = eine Card) + offen = set(kandidaten) + inseln = [] + while offen: + start = offen.pop() + insel = [start] + stapel = [start] + while stapel: + f = stapel.pop() + for e in f.edges: + for nf in e.link_faces: + if nf in offen: + offen.discard(nf) + insel.append(nf) + stapel.append(nf) + inseln.append(insel) + + gesamt = len(inseln) + behalten = max(1, int(round(gesamt * max(0.0, min(1.0, anteil))))) + + # Seed-stabile Auswahl: sortiert nach einer festen Kennzahl, damit die + # Reihenfolge nicht von der bmesh-Iteration abhaengt. + inseln.sort(key=lambda ins: tuple(round(c, 4) + for c in ins[0].calc_center_median())) + rng = _rnd.Random(seed) + reihenfolge = list(range(gesamt)) + rng.shuffle(reihenfolge) + behalte_idx = set(reihenfolge[:behalten]) + + weg = [] + for i, insel in enumerate(inseln): + if i not in behalte_idx: + weg.extend(insel) + if weg: + bmesh.ops.delete(bm, geom=weg, context='FACES') + + # Ueberlebende vergroessern - je Card um IHRE EIGENE Mitte, sonst wandert + # sie beim Skalieren von der Zweigstelle weg. + if groesse != 1.0: + for i, insel in enumerate(inseln): + if i not in behalte_idx: + continue + verts = {v for f in insel for v in f.verts if v.is_valid} + if not verts: + continue + mitte = Vector((0.0, 0.0, 0.0)) + for v in verts: + mitte += v.co + mitte /= len(verts) + for v in verts: + v.co = mitte + (v.co - mitte) * groesse + + bm.to_mesh(me) + bm.free() + me.update() + return gesamt, behalten + + def _merge_for_export(context, tree_ob, leaf_ob, name, bark_decimate=1.0): """Baut EINE Export-Kopie aus Stamm + Blattebene mit zwei Material-Slots. @@ -1865,7 +1990,7 @@ class TreeGenSettings(PropertyGroup): "damit mischt die Krone 4-6 Varianten statt einer"), ) leaf_density: FloatProperty( - name="Dichte", default=150.0, min=0.0, max=20000.0, + name="Dichte", default=170.0, min=0.0, max=20000.0, description=("Cards pro m2 Zweigoberflaeche. Presets mit viel Geaest (Eiche) " "bekommen dadurch automatisch mehr Blaetter als schlanke. 150 haelt " "auch die Eiche im 2500-Tri-Budget; der Button meldet die Summe"), @@ -1879,7 +2004,7 @@ class TreeGenSettings(PropertyGroup): description="Tri-Budget der zweiten Stufe (ASSETS.md Teil 6)", ) leaf_budget: IntProperty( - name="Budget S3/S4", default=4000, min=0, max=100000, + name="Budget S3/S4", default=4300, min=0, max=100000, description=("Tri-Budget der ausgewachsenen Stufe. Die urspruenglichen " "2500 waren eine konservative Setzung - bei ISM-Instanzen " "mit LOD-Kette sind Roh-Dreiecke nicht der Engpass, " @@ -2118,29 +2243,21 @@ class TREEGEN_OT_export_game(Operator): lod_name = ("%s_LOD%d" % (basis_name, i)) if s.generate_lods \ else basis_name - # Blatt-Ebene je Stufe NEU streuen statt sie zu dezimieren: - # eine Card besteht aus zwei Dreiecken, die laesst sich nicht - # vereinfachen - man muss weniger davon setzen. Die - # verbleibenden werden groesser, damit die Krone nicht - # ausduennt. - lod_blatt = blatt - if blatt is not None and s.generate_lods and i > 0: - lod_blatt = make_leaves( - context, baum, s.leaf_card, - name="%s__leaflod%d" % (basis_name, i), - card_coll=s.leaf_cards, - Density=s.leaf_density * dichte, - Size=s.leaf_size * groesse, - **{"Size Var": s.leaf_size_var, - "Max Radius": s.leaf_max_radius, - "Tilt": s.leaf_tilt, "Seed": s.seed}) - context.view_layer.update() - aufraeumen.append(lod_blatt.name) - + # Alle Stufen gehen von DERSELBEN Blatt-Ebene aus. Die + # Reduktion passiert weiter unten als Teilset - neu zu + # streuen wuerde die Cards an andere Stellen setzen und die + # Silhouette beim LOD-Wechsel sichtbar umspringen lassen. merge, gemacht = _merge_for_export( - context, baum, lod_blatt, lod_name, bark_decimate=dec) + context, baum, blatt, lod_name, bark_decimate=dec) aufraeumen.extend(gemacht) + if s.generate_lods and i > 0 and len(merge.data.materials) > 1: + vorher, nachher = _cards_ausduennen( + merge, 1, dichte, groesse, seed=s.seed) + print("[TreeGen] %s: %d von %d Cards behalten, " + "%.2fx vergroessert" + % (merge.name, nachher, vorher, groesse)) + # Kugel-Normalen auf JEDER Stufe - LOD1..3 haben eine eigene # Card-Verteilung, die Normalen muessen dazu passen. mitte = None @@ -2437,21 +2554,27 @@ class TREEGEN_OT_leaves(Operator): # traegt kleinere Bueschel. Baeume ohne Wachstums-Stufe haben # growth_t = 1.0 und bleiben unveraendert. gt = float(t.get("growth_t", 1.0)) - faktor = s.leaf_size_growth + (1.0 - s.leaf_size_growth) * gt - # Dichte GEGENLAEUFIG mitziehen. "Dichte" ist Punkte pro m2 - # Zweigoberflaeche, und ein Setzling hat davon fast keine - - # gemessen kam bei Stufe 1 exakt NULL Cards heraus, der - # Setzling stand kahl da. Da die Bueschel um `faktor` kleiner - # sind, deckt jeder nur faktor^2 der Flaeche; die Dichte - # entsprechend anzuheben haelt die Blattdeckung ueber alle - # Stufen gleich, statt den Setzling auszuduennen. - dichte = s.leaf_density / max(faktor * faktor, 1e-3) + d_anteil, g_anteil = _stufen_anteile(gt) + + # Der RADIUS-FILTER muss mit dem Baum schrumpfen. Er trennt + # duenne Zweige (Laub) von dickem Geaest (kein Laub) - aber ein + # Setzling ist KOMPLETT duenner als die Schwelle. Gemessen mit + # festem 0.04 m: bei S1 sassen 92 % der Cards am Stamm, bei S2 + # noch 81 %, das Laub klebte also am Stiel statt in der Krone. + # Skaliert wird mit dem Wachstum, aber mit BODEN bei 0.20. + # Der reine Stammradius-Faktor (0.064 bei t=0) war zu scharf: + # damit fiel die Schwelle unter die duennsten Zweige und der + # Setzling stand wieder kahl (gemessen 0 Cards). Bei 0.20 + # bekommt er 12 Cards, davon nur 12 % am Stamm - vorher waren + # es 92 %. + r_faktor = LEAF_RADIUS_YOUNG + (1.0 - LEAF_RADIUS_YOUNG) * gt + lf = make_leaves(context, t, s.leaf_card, card_coll=s.leaf_cards, - Density=dichte, - Size=s.leaf_size * faktor, + Density=s.leaf_density * d_anteil, + Size=s.leaf_size * g_anteil, **{"Size Var": s.leaf_size_var, - "Max Radius": s.leaf_max_radius, + "Max Radius": s.leaf_max_radius * r_faktor, "Tilt": s.leaf_tilt, "Seed": s.seed}) except Exception as exc: # noqa: BLE001 self.report({'WARNING'}, "%s: %s" % (t.name, exc)) diff --git a/tests/test_baum_lod.py b/tests/test_baum_lod.py index 4b483c5..03cbc88 100644 --- a/tests/test_baum_lod.py +++ b/tests/test_baum_lod.py @@ -186,6 +186,68 @@ if blatt_zahlen and blatt_zahlen[0] > 0: fails.append("LOD3 hat noch %.0f%% der Cards - als Impostor-Ersatz zu " "teuer" % anteil) +# --- Die Cards muessen ein TEILSET von LOD0 sein ------------------------- +# Das ist der Test gegen den Silhouetten-Sprung: wuerde je Stufe neu gestreut, +# saessen die Cards an anderen Stellen und die Krone spraenge beim LOD-Wechsel +# sichtbar um. Als Teilset liegt jede verbleibende Card dort, wo sie in LOD0 +# schon war. +def card_mitten(ob): + """Mittelpunkte der Blatt-Inseln (eine Insel = eine Card).""" + m = ob.data + faces = [p for p in m.polygons if p.material_index == 1] + if not faces: + return [] + # Inseln ueber gemeinsame Vertices finden + von_vert = {} + for p in faces: + for vi in p.vertices: + von_vert.setdefault(vi, []).append(p.index) + gesehen = set() + mitten = [] + for p in faces: + if p.index in gesehen: + continue + stapel = [p.index] + gesehen.add(p.index) + insel = [] + while stapel: + fi = stapel.pop() + f = m.polygons[fi] + insel.append(f) + for vi in f.vertices: + for nb in von_vert.get(vi, ()): + if nb not in gesehen: + gesehen.add(nb) + stapel.append(nb) + mitte = Vector((0.0, 0.0, 0.0)) + for f in insel: + mitte += f.center + mitten.append(mitte / len(insel)) + return mitten + + +mitten0 = card_mitten(stufen[0]) +print("") +print("LOD0 hat %d Cards" % len(mitten0)) +for ob in stufen[1:]: + mitten = card_mitten(ob) + if not mitten0 or not mitten: + continue + treffer = 0 + for m1 in mitten: + nah = min((m1 - m0).length for m0 in mitten0) + # Die Card wurde vergroessert, ihre Mitte bleibt aber - ein paar + # Zentimeter Toleranz reichen. + if nah < 0.15: + treffer += 1 + quote = 100.0 * treffer / len(mitten) + print("%-24s %3d Cards, %.0f%% davon sitzen auf einer LOD0-Card" + % (ob.name, len(mitten), quote)) + if quote < 90.0: + fails.append("%s: nur %.0f%% der Cards sitzen auf LOD0-Positionen - " + "die Stufe wurde neu gestreut statt ausgeduennt, die " + "Silhouette springt beim LOD-Wechsel" % (ob.name, quote)) + # --- Alle Stufen deckungsgleich am Ursprung ------------------------------ # Das ist der Test fuer den aus UE gemeldeten Fehler: LOD1-3 sassen bei MINUS # der Standposition, weil ihre Weltmatrix nie ins Mesh gebacken wurde. diff --git a/tests/test_blatt_bueschel.py b/tests/test_blatt_bueschel.py index c560361..9903ba7 100644 --- a/tests/test_blatt_bueschel.py +++ b/tests/test_blatt_bueschel.py @@ -41,12 +41,22 @@ print("Defaults: Dichte %.0f, Groesse %.2f, Setzlings-Anteil %.2f" if not (0.6 <= s.leaf_size <= 1.1): fails.append("leaf_size ist %.2f - fuer Bueschel-Atlanten werden ~0.85 " "gebraucht, 0.2 waere ein einzelnes Blaettchen" % s.leaf_size) -if s.leaf_budget_s1 > 800 or s.leaf_budget_s2 > 1200 or s.leaf_budget > 4000: - fails.append("Stufen-Budgets (%d/%d/%d) ueber ASSETS.md Teil 6 (800/1200/4000)" +# S3/S4 stehen auf 4300, nicht mehr auf 4000: die Spielseite hebt das Budget +# in ASSETS.md an, nachdem der 170er-Export abgenommen wurde. Die Doku folgt dem +# Auge, aber erst nach dem Export - deshalb steht die Zahl hier schon. +if s.leaf_budget_s1 > 800 or s.leaf_budget_s2 > 1200 or s.leaf_budget > 4300: + fails.append("Stufen-Budgets (%d/%d/%d) ueber der Vorgabe (800/1200/4300)" % (s.leaf_budget_s1, s.leaf_budget_s2, s.leaf_budget)) if not (130.0 <= s.leaf_density <= 200.0): fails.append("leaf_density ist %.0f - der Sweetspot liegt bei 130-170" % s.leaf_density) +# Die Deckung steht als Tabelle pro Stufe, nicht als Formel. Card-ANZAHL muss +# ueber die Stufen steigen, Card-Groesse auch. +_d0, _g0 = tg._stufen_anteile(0.0) +_d1, _g1 = tg._stufen_anteile(1.0) +if _g0 >= _g1: + fails.append("Groessen-Anteil steigt nicht ueber die Stufen (%.2f -> %.2f)" + % (_g0, _g1)) # Der Volumen-Hebel: bei 0.02 trugen nur die aeussersten Zweigspitzen Laub und # die Krone war innen nackt. if s.leaf_max_radius < 0.035: