Tree Gen 1.38.0: ein Weg zum Blattwerk, generischer Baum entsaeult

1) Zwei-Pfade-Fehler behoben (von der Spielseite gemeldet)
   Blattwerk entstand an zwei Stellen - Haekchen "_Leaf anlegen" beim Erzeugen
   und Knopf "Blattwerk streuen" - und nur der zweite rechnete die
   Wachstums-Staffel. Am Modifier nachgemessen trug der S1-Setzling aus dem
   Create-Pfad Size 0.85, Max Radius 0.04, Min Height 0.25: die Altbaum-Werte.

   Zum dritten Mal dieselbe Fehlerklasse (nach Stufen-Deckung und build_group):
   zwei Quellen fuer dieselbe Frage. Die Staffel liegt jetzt in make_leaves
   selbst und zieht growth_t/preset vom Baum-Objekt - ein Aufrufer KANN sie
   nicht mehr vergessen. Darueber blattwerk_streuen() als der eine Einstieg
   fuer beide Knoepfe, inklusive der Dichte-Regelung, die vorher ebenfalls nur
   der Operator hatte. Nebenbei bekommt der Create-Pfad damit auch die
   Card-SAMMLUNG, die ihm bisher fehlte.

   test_zwei_pfade.py prueft die DIFFERENZ zwischen beiden Wegen, nicht die
   absoluten Werte - ein Test, der nur einen Weg anschaut, findet so etwas nie.
   Plus die Gegenprobe im Test selbst: waeren beide Wege gleich falsch, muss
   auffallen, dass S1 und S4 dieselbe Card-Groesse tragen.
   Gegenprobe gefahren: mit dem alten Create-Pfad meldet der Test 10 Fehler und
   reproduziert exakt die gemeldeten 0.85 / 0.04 / 0.25.

2) Generischer "baum" entsaeult
   Gemessen B/H 0.18 ueber fuenf Seeds - schmaler als jedes andere Laubgehoelz.
   Ursache dieselbe wie bei der Birke: Branch Up 1.3 aus den DEFAULTS. Zielband
   der Spielseite 0.45-0.60, jetzt 0.55 (0.52-0.56).
   Die DEFAULTS bleiben unangetastet - sie sind die Socket-Vorgaben, an denen
   die haendischen Regler haengen. Nur "baum" bekommt eigene Werte.

26 Tests bestehen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 13:23:53 +02:00
co-authored by Claude Opus 4.8
parent 4aaf3231ef
commit 9bcb80765a
8 changed files with 269 additions and 61 deletions
+32
View File
@@ -563,6 +563,38 @@ 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 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. einer leeren `_Leaf`-Ebene; jetzt sagt der Operator es und nennt den Regler.
### Zwei Knoepfe, ein Weg
Blattwerk entsteht an zwei Stellen: ueber das Haekchen "_Leaf anlegen" beim
Erzeugen und ueber den Knopf "Blattwerk streuen". Nur der zweite rechnete die
Wachstums-Staffel. Von der Spielseite am Modifier nachgemessen trug der
S1-Setzling aus dem Create-Pfad **Size 0.85, Max Radius 0.04, Min Height 0.25** -
exakt die Altbaum-Werte.
Das ist zum dritten Mal dieselbe Fehlerklasse (nach der Stufen-Deckung und
`build_group`): **zwei Quellen fuer dieselbe Frage**. Die Staffel liegt deshalb
jetzt in `make_leaves` selbst, die sie aus `growth_t` und `preset` am
Baum-Objekt zieht - ein Aufrufer KANN sie nicht mehr vergessen. Darueber sitzt
`blattwerk_streuen()` als der eine Einstieg, den beide Knoepfe benutzen; dort
haengt auch die Dichte-Regelung, die vorher ebenfalls nur der Operator hatte.
`tests/test_zwei_pfade.py` prueft nicht die absoluten Werte, sondern die
DIFFERENZ zwischen beiden Wegen - ein Test, der nur einen Weg anschaut, findet
so etwas grundsaetzlich nie. Zusaetzlich prueft er, dass sich S1 und S4
ueberhaupt unterscheiden: waeren beide Wege gleich falsch, waere der Vergleich
allein zufrieden.
### Der generische Baum war auch eine Saeule
Gemessen Breite/Hoehe **0.18** ueber fuenf Seeds - schmaler als jedes andere
Laubgehoelz im Satz. Ursache dieselbe wie bei der Birke: `Branch Up` 1.3 aus den
DEFAULTS. Zielband der Spielseite 0.45-0.60, gemessen jetzt **0.55**
(0.52-0.56).
Die DEFAULTS bleiben unangetastet - sie sind die Socket-Vorgaben der
Node-Gruppe, an denen jeder haendische Regler haengt. Nur `baum` bekommt eigene
Werte; alle anderen Presets ueberschreiben `Branch Up` ohnehin.
### Obstbaum: die Krone muss groesser sein als eine Card ### Obstbaum: die Krone muss groesser sein als eine Card
Das Preset ist nicht nur eine Silhouette, es ist eine FUNKTIONALE Bedingung. Das Preset ist nicht nur eine Silhouette, es ist eine FUNKTIONALE Bedingung.
+1 -1
View File
@@ -1,7 +1,7 @@
schema_version = "1.0.0" schema_version = "1.0.0"
id = "stylized_tree_generator" id = "stylized_tree_generator"
version = "1.37.0" version = "1.38.0"
name = "Stylized Tree Generator" name = "Stylized Tree Generator"
tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen" tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen"
maintainer = "D4rkst3r" maintainer = "D4rkst3r"
+3 -3
View File
@@ -50,17 +50,17 @@
<td>34.1KB</td> <td>34.1KB</td>
</tr> </tr>
<tr> <tr>
<td><tt><a href="./stylized_tree_generator-1.37.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.37.0</a></tt></td> <td><tt><a href="./stylized_tree_generator-1.38.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.38.0</a></tt></td>
<td>Stylized Tree Generator</td> <td>Stylized Tree Generator</td>
<td>Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen</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><a href="https://git.d4rkst3r.de/D4rkst3r/stylized-rock-generator">link</a></td>
<td>4.2.0 - ~</td> <td>4.2.0 - ~</td>
<td>all</td> <td>all</td>
<td>all</td> <td>all</td>
<td>44.2KB</td> <td>45.0KB</td>
</tr> </tr>
</table> </table>
<center><p>Built 2026-08-06, 10:59</p></center> <center><p>Built 2026-08-06, 11:22</p></center>
</body> </body>
</html> </html>
+4 -4
View File
@@ -82,7 +82,7 @@
"id": "stylized_tree_generator", "id": "stylized_tree_generator",
"name": "Stylized Tree Generator", "name": "Stylized Tree Generator",
"tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen", "tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen",
"version": "1.37.0", "version": "1.38.0",
"type": "add-on", "type": "add-on",
"maintainer": "D4rkst3r", "maintainer": "D4rkst3r",
"license": [ "license": [
@@ -98,9 +98,9 @@
"Mesh", "Mesh",
"Modeling" "Modeling"
], ],
"archive_url": "./stylized_tree_generator-1.37.0.zip", "archive_url": "./stylized_tree_generator-1.38.0.zip",
"archive_size": 45213, "archive_size": 46032,
"archive_hash": "sha256:189494bb426bb757671a8da62baf1fb341e6147532a1d0a7eb7d9a7a745d15f3" "archive_hash": "sha256:0d65140f04d1ba8289a72c69c44c95b8e7b7300681695b43c7cba7a8f2ec6c9b"
} }
] ]
} }
Binary file not shown.
Binary file not shown.
+102 -53
View File
@@ -1,7 +1,7 @@
bl_info = { bl_info = {
"name": "Stylized Tree Generator", "name": "Stylized Tree Generator",
"author": "D4rkst3r", "author": "D4rkst3r",
"version": (1, 37, 0), "version": (1, 38, 0),
"blender": (4, 2, 0), "blender": (4, 2, 0),
"location": "View3D > Sidebar > Tree Gen", "location": "View3D > Sidebar > Tree Gen",
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen", "description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
@@ -86,7 +86,21 @@ DEFAULTS = {
# Presets: nur die Abweichungen von DEFAULTS. # Presets: nur die Abweichungen von DEFAULTS.
PRESETS = { PRESETS = {
"baum": {}, # Der generische Laubbaum war eine SAEULE: gemessen Breite/Hoehe 0.18 ueber
# fuenf Seeds, schmaler als jedes andere Laubgehoelz hier. Ursache war
# dieselbe wie bei der Birke - "Branch Up" 1.3 aus den DEFAULTS liess die
# Aeste fast senkrecht nach oben zeigen und am Stamm kleben.
#
# Zielband der Spielseite: 0.45-0.60, "zwischen Birke (0.51) und einem
# klassischen Parkbaum".
#
# Die DEFAULTS bleiben unangetastet - sie sind die Socket-Vorgaben der
# Node-Gruppe, an denen jeder haendische Regler haengt. Nur "baum" bekommt
# eigene Werte; alle anderen Presets ueberschreiben Branch Up ohnehin.
"baum": {
"Branch Up": 0.5, "Branch Length": 2.1,
"Attraction Up": 0.15, # spreizen statt hochschwingen
},
# Palme - am TreeIt-Referenzbaum ausgemessen (palm07_0 + frond stem_1): # Palme - am TreeIt-Referenzbaum ausgemessen (palm07_0 + frond stem_1):
# Stamm 4.33 m, Radius 0.133 unten -> ~0.086 Mitte -> 0.106 OBEN # Stamm 4.33 m, Radius 0.133 unten -> ~0.086 Mitte -> 0.106 OBEN
# (Kronenschaft!), 11 Seiten, nur 150 Tris. # (Kronenschaft!), 11 Seiten, nur 150 Tris.
@@ -1753,8 +1767,46 @@ def _dichte_einregeln(context, ob, md, ids, ziel, start_dichte, pro_stueck,
return bestes[1], bestes[2] return bestes[1], bestes[2]
def make_leaves(context, tree_obj, card_obj, name=None, card_coll=None, **overrides): def make_leaves(context, tree_obj, card_obj, name=None, card_coll=None,
"""Erzeugt/aktualisiert ein <Baum>_Leaf-Objekt mit dem Blattwerk-Modifier.""" stufen=True, **overrides):
"""Erzeugt/aktualisiert ein <Baum>_Leaf-Objekt mit dem Blattwerk-Modifier.
Die WACHSTUMS-STAFFEL sitzt hier drin, nicht beim Aufrufer. Uebergeben
werden die Grundwerte (Size, Max Radius); welche Stufe daraus wird, zieht
diese Funktion selbst aus `growth_t` und `preset` am Baum-Objekt.
Warum hier und nicht beim Aufrufer: es gab zwei Wege zum Blattwerk - den
Knopf "Blattwerk streuen" und das Haekchen "_Leaf anlegen" beim Erzeugen -
und nur der erste rechnete die Staffel. Gemessen am S1-Modifier des
Create-Pfads: Size 0.85, Max Radius 0.04, Min Height 0.25, also exakt die
Altbaum-Werte. Das ist dieselbe Fehlerklasse wie bei der Stufen-Deckung und
bei build_group: zwei Quellen fuer dieselbe Frage. Solange die Staffel hier
liegt, KANN ein Aufrufer sie nicht vergessen.
`stufen=False` schaltet sie ab - fuer Aufrufer, die bewusst rohe Werte
setzen.
"""
if stufen:
# Stufe aus dem OBJEKT lesen, nicht aus dem Kontext: der Blattwerk-Lauf
# kommt spaeter und getrennt vom Erzeugen.
gt = float(tree_obj.get("growth_t", 1.0))
_ziel, g_anteil, h_anteil = _stufen_anteile(gt, tree_obj.get("preset"))
# 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. Boden bei LEAF_RADIUS_YOUNG, weil der
# reine Stammradius-Faktor (0.064 bei t = 0) unter die duennsten Zweige
# fiel und der Setzling wieder kahl stand (gemessen 0 Cards).
r_faktor = LEAF_RADIUS_YOUNG + (1.0 - LEAF_RADIUS_YOUNG) * gt
basis = dict(LEAF_DEFAULTS)
basis.update(overrides)
overrides["Size"] = basis["Size"] * g_anteil
overrides["Max Radius"] = basis["Max Radius"] * r_faktor
overrides["Min Height"] = h_anteil
name = name or (tree_obj.name + "_Leaf") name = name or (tree_obj.name + "_Leaf")
ob = bpy.data.objects.get(name) ob = bpy.data.objects.get(name)
if ob is None: if ob is None:
@@ -1784,6 +1836,41 @@ def make_leaves(context, tree_obj, card_obj, name=None, card_coll=None, **overri
return ob return ob
def blattwerk_streuen(context, tree_obj, s):
"""DER Weg zum Blattwerk - fuer beide Knoepfe derselbe.
Streut die Cards und regelt die Dichte auf die Ziel-Cardzahl der Stufe ein.
Wer Blattwerk erzeugt, ruft das hier und sonst nichts: die frueheren zwei
Wege liefen genau so lange auseinander, bis es jemandem im Spiel auffiel.
Gibt (Blatt-Objekt, gezaehlte Cards, Ziel-Cards) zurueck; ohne Regelung
sind die letzten beiden None.
"""
lf = make_leaves(context, tree_obj, s.leaf_card,
card_coll=s.leaf_cards,
Density=s.leaf_density,
Size=s.leaf_size,
**{"Size Var": s.leaf_size_var,
"Max Radius": s.leaf_max_radius,
"Tilt": s.leaf_tilt, "Seed": s.seed})
# Dichte auf die ZIEL-CARDZAHL einregeln. Muss NACH dem Anlegen passieren,
# weil dafuer gemessen werden muss, was tatsaechlich herauskommt - die
# Zweigflaeche faellt je Preset voellig anders aus.
gt = float(tree_obj.get("growth_t", 1.0))
ziel_cards, _g, _h = _stufen_anteile(gt, tree_obj.get("preset"))
if not ziel_cards:
return lf, None, None
md = lf.modifiers.get("GN_Leaves")
ng = md.node_group if md is not None else None
if ng is None:
return lf, None, None
pro = _cards_pro_stueck(s.leaf_card, s.leaf_cards)
dichte, ist = _dichte_einregeln(context, lf, md, leaf_socket_ids(ng),
ziel_cards, s.leaf_density, pro)
print("[TreeGen] %s: %d Cards (Ziel %d) bei Dichte %.0f"
% (tree_obj.name, ist, ziel_cards, dichte))
return lf, ist, ziel_cards
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@@ -2909,48 +2996,10 @@ class TREEGEN_OT_leaves(Operator):
total_tree = total_leaf = 0 total_tree = total_leaf = 0
for t in trees: for t in trees:
try: try:
# Bueschelgroesse mit dem Wachstum skalieren: ein Setzling # Staffel und Dichte-Regelung stecken in blattwerk_streuen -
# traegt kleinere Bueschel. Baeume ohne Wachstums-Stufe haben # damit laeuft dieser Knopf durch dieselbe Stelle wie das
# growth_t = 1.0 und bleiben unveraendert. # Haekchen beim Erzeugen.
gt = float(t.get("growth_t", 1.0)) lf, _ist, _ziel = blattwerk_streuen(context, t, s)
ziel_cards, g_anteil, h_anteil = _stufen_anteile(
gt, t.get("preset"))
# 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=s.leaf_density,
Size=s.leaf_size * g_anteil,
**{"Size Var": s.leaf_size_var,
"Max Radius": s.leaf_max_radius * r_faktor,
"Min Height": h_anteil,
"Tilt": s.leaf_tilt, "Seed": s.seed})
# Dichte auf die ZIEL-CARDZAHL einregeln. Muss nach dem Anlegen
# passieren, weil dafuer gemessen werden muss, was tatsaechlich
# herauskommt - die Zweigflaeche faellt je Preset voellig
# anders aus.
if ziel_cards:
md = lf.modifiers.get("GN_Leaves")
ng = md.node_group if md is not None else None
if ng is not None:
pro = _cards_pro_stueck(s.leaf_card, s.leaf_cards)
dichte, ist = _dichte_einregeln(
context, lf, md, leaf_socket_ids(ng),
ziel_cards, s.leaf_density, pro)
print("[TreeGen] %s: %d Cards (Ziel %d) bei Dichte %.0f"
% (t.name, ist, ziel_cards, dichte))
except Exception as exc: # noqa: BLE001 except Exception as exc: # noqa: BLE001
self.report({'WARNING'}, "%s: %s" % (t.name, exc)) self.report({'WARNING'}, "%s: %s" % (t.name, exc))
continue continue
@@ -3071,13 +3120,13 @@ class TREEGEN_OT_create(Operator):
for ob in created: for ob in created:
if s.make_leaf: if s.make_leaf:
if s.leaf_card is not None and s.leaf_card.type == 'MESH': if s.leaf_card is not None and s.leaf_card.type == 'MESH':
# Card gesetzt -> Blattwerk direkt streuen # Card gesetzt -> Blattwerk direkt streuen, ueber
extra.append(make_leaves( # DIESELBE Stelle wie der Knopf "Blattwerk streuen".
context, ob, s.leaf_card, # Vorher stand hier ein eigener make_leaves-Aufruf mit
Density=s.leaf_density, Size=s.leaf_size, # rohem s.leaf_size - die Wachstums-Staffel fehlte
**{"Size Var": s.leaf_size_var, # komplett, gemessen trug der S1-Setzling
"Max Radius": s.leaf_max_radius, # Altbaum-Bueschel.
"Tilt": s.leaf_tilt, "Seed": s.seed})) extra.append(blattwerk_streuen(context, ob, s)[0])
else: else:
# keine Card -> leeres _Leaf zum Selbermodellieren # keine Card -> leeres _Leaf zum Selbermodellieren
extra.append(_add_layer(context, ob, "_Leaf")) extra.append(_add_layer(context, ob, "_Leaf"))
+127
View File
@@ -0,0 +1,127 @@
"""Beide Wege zum Blattwerk muessen dasselbe ergeben.
Es gibt zwei Knoepfe, die Blattwerk erzeugen:
* "_Leaf anlegen" beim Erzeugen des Baums (TREEGEN_OT_create)
* "Blattwerk streuen" auf einer Auswahl (TREEGEN_OT_leaves)
Nur der zweite rechnete die Wachstums-Staffel. Von der Spielseite am Modifier
nachgemessen: der S1-Setzling aus dem Create-Pfad trug Size 0.85, Max Radius
0.04, Min Height 0.25 - exakt die Altbaum-Werte.
Das ist dieselbe Fehlerklasse, die vorher schon die Stufen-Deckung und
build_group erwischt hat: ZWEI QUELLEN fuer dieselbe Frage. Ein Test, der nur
einen der beiden Wege prueft, findet so etwas nie - deshalb prueft dieser hier
die DIFFERENZ zwischen ihnen und nicht die absoluten Werte.
Aufruf: blender --background --factory-startup --python tests/test_zwei_pfade.py
"""
import os
import sys
import importlib.util
import bpy
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
spec = importlib.util.spec_from_file_location(
"tg", os.path.join(ROOT, "stylized_tree_generator.py"))
tg = importlib.util.module_from_spec(spec)
sys.modules["tg"] = tg
spec.loader.exec_module(tg)
tg.register()
fails = []
for o in list(bpy.data.objects):
bpy.data.objects.remove(o, do_unlink=True)
me = bpy.data.meshes.new("Leaf_Card")
me.from_pydata([(-0.5, 0, 0), (0, 0, 0.7), (0.5, 0, 0), (0, 0, -0.1)],
[], [(0, 1, 2), (0, 2, 3)])
me.update()
card = bpy.data.objects.new("Leaf_Card", me)
bpy.context.scene.collection.objects.link(card)
s = bpy.context.scene.tree_gen_settings
s.leaf_card = card
s.apply_modifier = True
s.preset = "birke"
s.variant = 1
s.use_growth = True
s.stages = 4
# Die Werte, die die Staffel anfasst. "Density" steht bewusst NICHT dabei: sie
# wird nach dem Streuen per Bisektion eingeregelt und darf sich zwischen zwei
# Laeufen minimal unterscheiden.
GESTAFFELT = ("Size", "Max Radius", "Min Height")
def werte(leaf_obj):
md = leaf_obj.modifiers.get("GN_Leaves")
ids = tg.leaf_socket_ids(md.node_group)
return {k: round(float(md[ids[k]]), 6) for k in GESTAFFELT if k in ids}
def bauen(mit_haken):
for o in list(bpy.context.scene.objects):
if o is not card:
bpy.data.objects.remove(o, do_unlink=True)
s.art_name = "P" if mit_haken else "Q"
s.make_leaf = mit_haken
bpy.ops.object.treegen_create()
staemme = 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)
if not mit_haken:
for o in bpy.context.scene.objects:
o.select_set(o in staemme)
bpy.context.view_layer.objects.active = staemme[0]
bpy.ops.object.treegen_leaves()
out = []
for st in staemme:
bl = bpy.data.objects.get(st.name + "_Leaf")
out.append((st.name, float(st.get("growth_t", 1.0)),
werte(bl) if bl else None))
return out
erzeugt = bauen(True) # Haekchen "_Leaf anlegen"
operator = bauen(False) # Knopf "Blattwerk streuen"
print("%-5s %7s | %-34s | %-34s"
% ("Stufe", "growth", "beim Erzeugen", "per Operator"))
for (n1, gt, w1), (n2, _gt, w2) in zip(erzeugt, operator):
def zeig(w):
if w is None:
return "keine Blattebene"
return " ".join("%s %.3f" % (k[:3], w[k]) for k in GESTAFFELT if k in w)
gleich = "" if w1 == w2 else " <-- WEICHT AB"
print("%-5s %7.2f | %-34s | %-34s%s"
% (n1.split("_")[-1], gt, zeig(w1), zeig(w2), gleich))
if w1 is None or w2 is None:
fails.append("%s: eine der beiden Ebenen fehlt" % n1)
continue
for k in GESTAFFELT:
if k not in w1 or k not in w2:
continue
if abs(w1[k] - w2[k]) > 1e-6:
fails.append("%s: '%s' ist beim Erzeugen %.4f, per Operator %.4f - "
"die Wachstums-Staffel greift nur auf einem Weg"
% (n1, k, w1[k], w2[k]))
# Und die Staffel muss ueberhaupt greifen: waeren beide Wege gleich FALSCH,
# waere der Vergleich oben zufrieden. Also zusaetzlich, dass sich S1 von S4
# unterscheidet.
if len(erzeugt) >= 2 and erzeugt[0][2] and erzeugt[-1][2]:
s1, s4 = erzeugt[0][2], erzeugt[-1][2]
if s1.get("Size") is not None and abs(s1["Size"] - s4["Size"]) < 1e-6:
fails.append("S1 und S4 tragen dieselbe Card-Groesse (%.3f) - die "
"Staffel greift auf KEINEM der beiden Wege" % s1["Size"])
print("")
if fails:
print("ERGEBNIS: %d FEHLER" % len(fails))
for f in fails:
print(" - " + f)
sys.exit(1)
print("ERGEBNIS: ALLE CHECKS OK")