Tree Gen 1.27.0: Blatt-Cards aus einer Collection (Punkt 3)
"Leaf Card" kann jetzt eine COLLECTION sein: je Blattpunkt wird zufaellig eine Card gezogen (Pick Instance, seed-stabil). Damit legt der Nutzer 4-6 Diamond-Cards mit unterschiedlichen Atlas-UVs an und die Krone mischt sie. Rueckwaertskompatibel - ein einzelnes Objekt geht weiter, die Sammlung hat Vorrang. Umsetzung: Collection Info mit Separate Children (sonst kaeme die Sammlung als ein Klumpen an und Pick Instance haette nichts zum Auswaehlen), Geometry-Switch zwischen Einzel- und Sammlungspfad, Instanz-Index aus einem Random Value, dessen Obergrenze aus der TATSAECHLICHEN Anzahl kommt - eine feste Obergrenze wuerde bei weniger Cards Luecken erzeugen. Geometry Nodes kann einen leeren Collection-Zeiger nicht selbst pruefen, deshalb setzt make_leaves einen "Use Collection"-Schalter. Beweisfuehrung im Test: die Zufalls-Groesse wird ABGESCHALTET, dann kommt jede Groessenabweichung nur von der Card-Variante und die Anzahl verschiedener Face-Flaechen ist ein exakter Zaehler. Gemessen: Einzel-Card 2 Flaechen (ein Card-Paar), Sammlung mit 4 Cards 10 Flaechen - alle Varianten kommen vor. Gleicher Seed identisch, anderer Seed anders. Nebenbei: die Vorpruefung des Blattwerk-Operators war eine zusammengesetzte Bedingung, die bei gesetzter Sammlung ohne Einzel-Card auf None zugriff. Neu: tests/test_blatt_varianten.py. 20 Tests bestehen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -352,6 +352,39 @@ Gras-, Blumen- und Farn-Bueschel. Der Kern ist die **Wind-UV**:
|
||||
|
||||
## Stylized Tree Generator
|
||||
|
||||
### Spielfertiger Stand-Baum (Faellsystem)
|
||||
|
||||
Knopf **Spielfertig exportieren** im Export-Panel: Stamm und `_Leaf`-Ebene
|
||||
werden zu **einem** Mesh mit **zwei Material-Slots** verschmolzen
|
||||
(Slot 0 = `M_Bark`, Slot 1 = `M_Foliage`). Die getrennten Objekte im Blend
|
||||
bleiben die Arbeitsebene — verschmolzen wird nur die Export-Kopie. Ursprung
|
||||
Bbox-Unterseite Mitte, FBX-Einstellungen wie in der Fels-Pipeline.
|
||||
|
||||
**Kugel-Normalen** (Haken `Kugel-Normalen auf Blaetter`, Standard an): alle
|
||||
Blatt-Normalen zeigen radial vom Kronen-Schwerpunkt nach aussen. Ohne das wirkt
|
||||
jede Card wie ein eigenes flaches Blech und die Krone flimmert im Licht. Der
|
||||
Schwerpunkt kommt **nur aus den Blatt-Faces** — der Stamm wuerde die Mitte nach
|
||||
unten ziehen und die Kugel verkippen.
|
||||
|
||||
> Bewusst direkt ueber `normals_split_custom_set` statt ueber den
|
||||
> Normal-Edit-Modifier: der muesste vor dem Export angewendet werden, und
|
||||
> angewendete Modifier plus Custom Normals ist genau die Kombination, bei der
|
||||
> der FBX-Export gerne flachbuegelt. Am **reimportierten** FBX gemessen:
|
||||
> mittlere Radialitaet **1.000**, 100 % der 510 Blatt-Loops ueber 0.8.
|
||||
> Gegenprobe Rinde 0.043.
|
||||
|
||||
**Card-Varianten:** `Card-Sammlung` nimmt eine Collection mit 4–6 Diamond-Cards
|
||||
unterschiedlicher Atlas-UVs; je Blattpunkt wird eine zufaellig gezogen
|
||||
(seed-stabil). Eine einzelne Card geht weiter — die Sammlung hat Vorrang.
|
||||
|
||||
Zwei Fallen beim Bauen:
|
||||
|
||||
- Ueber `bmesh.from_mesh` zusammenzufuegen haengt die Geometrie zwar an, wirft
|
||||
aber die **Material-Indizes** weg: danach lagen alle 748 Faces auf Index 0.
|
||||
Blenders `join` fuehrt die Slot-Listen zusammen und rechnet die Indizes um.
|
||||
- `join` loescht das zweite Objekt; jede gehaltene Referenz darauf ist danach
|
||||
tot. Die Aufraeumliste fuehrt deshalb **Namen** statt Objekte.
|
||||
|
||||
### Proportionen: gemessen, nicht geschaetzt
|
||||
|
||||
Drei Presets sahen falsch aus. Die Ursachen wurden gemessen bzw. belegt, nicht
|
||||
@@ -572,6 +605,8 @@ gefiltert `.\run_tests.ps1 -Filter panels`.
|
||||
|
||||
| Datei | prueft |
|
||||
|---|---|
|
||||
| `tests/test_baum_export.py` | Stand-Baum: ein Mesh, zwei Slots, Ursprung, **Kugel-Normalen im exportierten FBX** |
|
||||
| `tests/test_blatt_varianten.py` | Card-Sammlung: alle Varianten werden gezogen, seed-stabil, Einzelobjekt weiter moeglich |
|
||||
| `tests/test_proportionen.py` | Baum-Proportionen: Palmenkrone, Strauch-Breite, Setzling-Schlankheit, Branch Count 0 |
|
||||
| `tests/test_bruchstuecke.py` | Bruchstuecke: Volumen 100 % des Muttersteins, dichte Topologie, genau ein Erz-Stueck, Material-Slots, Ursprung, UCX je Teil |
|
||||
| `tests/test_schnellstart.py` | Schnellstart: jede Sorte liefert ohne Nacharbeit Geometrie + Slot, Erz-Sorte ueber der Dichte-Schwelle, Dissolve-Bereich streut je Stein |
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
schema_version = "1.0.0"
|
||||
|
||||
id = "stylized_tree_generator"
|
||||
version = "1.26.0"
|
||||
version = "1.27.0"
|
||||
name = "Stylized Tree Generator"
|
||||
tagline = "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen"
|
||||
maintainer = "D4rkst3r"
|
||||
|
||||
Vendored
+3
-3
@@ -50,17 +50,17 @@
|
||||
<td>34.1KB</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><tt><a href="./stylized_tree_generator-1.26.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.26.0</a></tt></td>
|
||||
<td><tt><a href="./stylized_tree_generator-1.27.0.zip?repository=.%2Findex.json&blender_version_min=4.2.0">stylized_tree_generator-1.27.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>27.6KB</td>
|
||||
<td>28.6KB</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<center><p>Built 2026-08-06, 04:42</p></center>
|
||||
<center><p>Built 2026-08-06, 04:48</p></center>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Vendored
+4
-4
@@ -82,7 +82,7 @@
|
||||
"id": "stylized_tree_generator",
|
||||
"name": "Stylized Tree Generator",
|
||||
"tagline": "Parametrische Baeume, Palmen, Bueschen mit Wachstums-Stufen",
|
||||
"version": "1.26.0",
|
||||
"version": "1.27.0",
|
||||
"type": "add-on",
|
||||
"maintainer": "D4rkst3r",
|
||||
"license": [
|
||||
@@ -98,9 +98,9 @@
|
||||
"Mesh",
|
||||
"Modeling"
|
||||
],
|
||||
"archive_url": "./stylized_tree_generator-1.26.0.zip",
|
||||
"archive_size": 28299,
|
||||
"archive_hash": "sha256:8e6cf3131a36efe840d0f59802003a3cb198b611a405c53e6428f2a99029b0c9"
|
||||
"archive_url": "./stylized_tree_generator-1.27.0.zip",
|
||||
"archive_size": 29267,
|
||||
"archive_hash": "sha256:daeb815cde034e527cdffb0e76a980e964f228b7a0f39070ba78477be9fc3e57"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
+85
-10
@@ -1,7 +1,7 @@
|
||||
bl_info = {
|
||||
"name": "Stylized Tree Generator",
|
||||
"author": "D4rkst3r",
|
||||
"version": (1, 26, 0),
|
||||
"version": (1, 27, 0),
|
||||
"blender": (4, 2, 0),
|
||||
"location": "View3D > Sidebar > Tree Gen",
|
||||
"description": "Parametrischer Baum-/Palmen-/Busch-Generator (Geometry Nodes) mit Wachstums-Stufen",
|
||||
@@ -1176,6 +1176,7 @@ LEAF_DEFAULTS = {
|
||||
"Size Var": 0.35,
|
||||
"Tilt": 0.5, # wie stark die Cards aus der Senkrechten kippen (rad)
|
||||
"Seed": 0,
|
||||
"Use Collection": False,
|
||||
}
|
||||
|
||||
|
||||
@@ -1211,6 +1212,11 @@ def build_leaf_group(rebuild=False):
|
||||
|
||||
add_in("Tree", 'NodeSocketObject')
|
||||
add_in("Leaf Card", 'NodeSocketObject')
|
||||
# Mehrere Atlas-Varianten: die Collection liefert je Punkt eine zufaellig
|
||||
# gewaehlte Card. "Use Collection" wird von make_leaves gesetzt - Geometry
|
||||
# Nodes kann einen leeren Collection-Zeiger nicht selbst pruefen.
|
||||
add_in("Leaf Cards", 'NodeSocketCollection')
|
||||
add_in("Use Collection", 'NodeSocketBool', False)
|
||||
add_in("Density", 'NodeSocketFloat', LEAF_DEFAULTS["Density"], 0.0, 20000.0)
|
||||
add_in("Max Radius", 'NodeSocketFloat', LEAF_DEFAULTS["Max Radius"], 0.001, 0.5)
|
||||
add_in("Size", 'NodeSocketFloat', LEAF_DEFAULTS["Size"], 0.01, 5.0)
|
||||
@@ -1228,6 +1234,23 @@ def build_leaf_group(rebuild=False):
|
||||
card = N("GeometryNodeObjectInfo"); card.location = (-700, -200)
|
||||
L(V["Leaf Card"], _sock(card, "Object"))
|
||||
|
||||
# Separate Children = jedes Objekt der Collection wird eine EIGENE Instanz.
|
||||
# Ohne das kaeme die Collection als ein einziger Klumpen an und "Pick
|
||||
# Instance" haette nichts zum Auswaehlen.
|
||||
coll = N("GeometryNodeCollectionInfo"); coll.location = (-700, -420)
|
||||
L(V["Leaf Cards"], _sock(coll, "Collection"))
|
||||
try:
|
||||
_sock(coll, "Separate Children").default_value = True
|
||||
_sock(coll, "Reset Children").default_value = True
|
||||
except (AttributeError, KeyError):
|
||||
pass
|
||||
|
||||
quelle = N("GeometryNodeSwitch"); quelle.location = (-500, -300)
|
||||
quelle.input_type = 'GEOMETRY'
|
||||
L(V["Use Collection"], _sock(quelle, "Switch"))
|
||||
L(_out(card, "Geometry"), _sock(quelle, "False"))
|
||||
L(_out(coll, "Instances", "Geometry"), _sock(quelle, "True"))
|
||||
|
||||
# Auswahl: nur duenne Aeste (uv_r < Max Radius)
|
||||
na = N("GeometryNodeInputNamedAttribute"); na.location = (-700, 20)
|
||||
na.data_type = 'FLOAT'
|
||||
@@ -1272,7 +1295,35 @@ def build_leaf_group(rebuild=False):
|
||||
|
||||
inst = N("GeometryNodeInstanceOnPoints"); inst.location = (100, 200)
|
||||
L(_out(dist, "Points"), _sock(inst, "Points"))
|
||||
L(_out(card, "Geometry"), _sock(inst, "Instance"))
|
||||
L(_out(quelle, "Output"), _sock(inst, "Instance"))
|
||||
try:
|
||||
L(V["Use Collection"], _sock(inst, "Pick Instance"))
|
||||
except (KeyError, RuntimeError):
|
||||
pass
|
||||
|
||||
# Zufaelliger Index je Punkt, aus der tatsaechlichen Anzahl abgeleitet -
|
||||
# eine feste Obergrenze wuerde bei weniger Cards Luecken erzeugen.
|
||||
dsize = N("GeometryNodeAttributeDomainSize"); dsize.location = (-320, -560)
|
||||
try:
|
||||
dsize.component = 'INSTANCES'
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
L(_out(coll, "Instances", "Geometry"), _sock(dsize, "Geometry"))
|
||||
dmax = N("ShaderNodeMath"); dmax.location = (-150, -560)
|
||||
dmax.operation = 'SUBTRACT'; dmax.inputs[1].default_value = 1.0
|
||||
try:
|
||||
L(_out(dsize, "Instance Count", "Point Count"), dmax.inputs[0])
|
||||
except (KeyError, RuntimeError):
|
||||
dmax.inputs[0].default_value = 1.0
|
||||
ridx = N("FunctionNodeRandomValue"); ridx.location = (100, -560)
|
||||
ridx.data_type = 'INT'
|
||||
L(V["Seed"], _sock(ridx, "Seed"))
|
||||
ridx.inputs[4].default_value = 0
|
||||
L(dmax.outputs[0], ridx.inputs[5])
|
||||
try:
|
||||
L(ridx.outputs["Value"], _sock(inst, "Instance Index"))
|
||||
except (KeyError, RuntimeError):
|
||||
pass
|
||||
L(rrot.outputs["Value"], _sock(inst, "Rotation"))
|
||||
L(rsc.outputs[1], _sock(inst, "Scale"))
|
||||
|
||||
@@ -1287,7 +1338,7 @@ def leaf_socket_ids(ng):
|
||||
if getattr(so, "in_out", "") == 'INPUT'}
|
||||
|
||||
|
||||
def make_leaves(context, tree_obj, card_obj, name=None, **overrides):
|
||||
def make_leaves(context, tree_obj, card_obj, name=None, card_coll=None, **overrides):
|
||||
"""Erzeugt/aktualisiert ein <Baum>_Leaf-Objekt mit dem Blattwerk-Modifier."""
|
||||
name = name or (tree_obj.name + "_Leaf")
|
||||
ob = bpy.data.objects.get(name)
|
||||
@@ -1306,6 +1357,10 @@ def make_leaves(context, tree_obj, card_obj, name=None, **overrides):
|
||||
ids = leaf_socket_ids(ng)
|
||||
md[ids["Tree"]] = tree_obj
|
||||
md[ids["Leaf Card"]] = card_obj
|
||||
# Rueckwaertskompatibel: ohne Collection bleibt alles wie bisher.
|
||||
if "Leaf Cards" in ids:
|
||||
md[ids["Leaf Cards"]] = card_coll
|
||||
overrides.setdefault("Use Collection", card_coll is not None)
|
||||
vals = dict(LEAF_DEFAULTS); vals.update(overrides)
|
||||
for k, v in vals.items():
|
||||
if k in ids:
|
||||
@@ -1484,8 +1539,15 @@ class TreeGenSettings(PropertyGroup):
|
||||
)
|
||||
leaf_card: PointerProperty(
|
||||
name="Blatt-Card", type=bpy.types.Object,
|
||||
description=("Objekt, das als Blatt/Billboard gestreut wird (z.B. dein "
|
||||
"'Diamon Plane'). Leer = _Leaf bleibt leer zum Selbermodellieren"),
|
||||
description=("Einzelne Card, die gestreut wird (z.B. dein 'Diamon "
|
||||
"Plane'). Leer = _Leaf bleibt leer zum Selbermodellieren. "
|
||||
"Wird ignoriert, sobald eine Card-Sammlung gesetzt ist"),
|
||||
)
|
||||
leaf_cards: PointerProperty(
|
||||
name="Card-Sammlung", type=bpy.types.Collection,
|
||||
description=("Sammlung mehrerer Cards mit unterschiedlichen Atlas-UVs. "
|
||||
"Je Blattpunkt wird zufaellig eine gewaehlt (Seed-stabil) - "
|
||||
"damit mischt die Krone 4-6 Varianten statt einer"),
|
||||
)
|
||||
leaf_density: FloatProperty(
|
||||
name="Dichte", default=150.0, min=0.0, max=20000.0,
|
||||
@@ -1755,8 +1817,13 @@ class TREEGEN_OT_leaves(Operator):
|
||||
|
||||
def execute(self, context):
|
||||
s = context.scene.tree_gen_settings
|
||||
if s.leaf_card is None or s.leaf_card.type != 'MESH':
|
||||
self.report({'ERROR'}, "Bitte zuerst eine Blatt-Card waehlen (Mesh-Objekt).")
|
||||
# Card-Sammlung hat Vorrang; ohne sie muss die Einzel-Card ein Mesh sein.
|
||||
if s.leaf_cards is not None:
|
||||
if not [o for o in s.leaf_cards.objects if o.type == 'MESH']:
|
||||
self.report({'ERROR'}, "Die Card-Sammlung enthaelt keine Meshes.")
|
||||
return {'CANCELLED'}
|
||||
elif s.leaf_card is None or s.leaf_card.type != 'MESH':
|
||||
self.report({'ERROR'}, "Bitte eine Blatt-Card oder eine Card-Sammlung waehlen.")
|
||||
return {'CANCELLED'}
|
||||
trees = [o for o in context.selected_objects
|
||||
if o.type == 'MESH' and not o.name.endswith(("_Leaf", "_Frucht"))]
|
||||
@@ -1768,6 +1835,7 @@ class TREEGEN_OT_leaves(Operator):
|
||||
for t in trees:
|
||||
try:
|
||||
lf = make_leaves(context, t, 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,
|
||||
@@ -1969,9 +2037,12 @@ class VIEW3D_PT_tree_leaves(_TreeSub):
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
s = context.scene.tree_gen_settings
|
||||
layout.prop(s, "leaf_card")
|
||||
layout.prop(s, "leaf_cards")
|
||||
row = layout.row()
|
||||
row.active = s.leaf_cards is None
|
||||
row.prop(s, "leaf_card")
|
||||
col = layout.column(align=True)
|
||||
col.active = s.leaf_card is not None
|
||||
col.active = s.leaf_card is not None or s.leaf_cards is not None
|
||||
col.prop(s, "leaf_density")
|
||||
col.prop(s, "leaf_size")
|
||||
col.prop(s, "leaf_size_var")
|
||||
@@ -1980,7 +2051,11 @@ class VIEW3D_PT_tree_leaves(_TreeSub):
|
||||
col.prop(s, "leaf_budget")
|
||||
layout.separator()
|
||||
layout.operator("object.treegen_leaves", icon='OUTLINER_OB_POINTCLOUD')
|
||||
if s.leaf_card is None:
|
||||
if s.leaf_cards is not None:
|
||||
n = len([o for o in s.leaf_cards.objects if o.type == 'MESH'])
|
||||
layout.label(text="%d Card-Variante(n), zufaellig je Blatt" % n,
|
||||
icon='INFO')
|
||||
elif s.leaf_card is None:
|
||||
layout.label(text="Ohne Card bleibt _Leaf leer", icon='INFO')
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
"""Punkt 3: Blatt-Cards aus einer COLLECTION statt aus einem Einzelobjekt.
|
||||
|
||||
Damit kann der Nutzer 4-6 Diamond-Cards mit unterschiedlichen Atlas-UVs anlegen
|
||||
und die Krone mischt sie. Rueckwaertskompatibel: ein einzelnes Objekt geht
|
||||
weiter.
|
||||
|
||||
Beweisfuehrung: die Zufalls-Groesse wird ABGESCHALTET (Size Var 0). Dann kommt
|
||||
jede Groessenabweichung ausschliesslich von der Card-Variante, und die Anzahl
|
||||
verschiedener Face-Flaechen ist ein exakter Zaehler fuer die benutzten Varianten.
|
||||
Mit eingeschalteter Groessenstreuung waere das Mass unbrauchbar - alles streut.
|
||||
|
||||
Aufruf: blender --background --factory-startup --python tests/test_blatt_varianten.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)
|
||||
|
||||
|
||||
def card(name, groesse):
|
||||
"""Diamond-Card in definierter Groesse - die Groesse ist hier der Marker."""
|
||||
me = bpy.data.meshes.new(name)
|
||||
g = groesse
|
||||
me.from_pydata([(-g, 0, 0), (0, 0, g * 1.4), (g, 0, 0), (0, 0, -g * 0.2)],
|
||||
[], [(0, 1, 2), (0, 2, 3)])
|
||||
me.update()
|
||||
ob = bpy.data.objects.new(name, me)
|
||||
bpy.context.scene.collection.objects.link(ob)
|
||||
return ob
|
||||
|
||||
|
||||
coll = bpy.data.collections.new("Leaf_Cards")
|
||||
bpy.context.scene.collection.children.link(coll)
|
||||
einzel = card("Card_Single", 0.5)
|
||||
GROESSEN = (0.30, 0.45, 0.60, 0.75)
|
||||
for i, g in enumerate(GROESSEN):
|
||||
c = card("Card_%02d" % (i + 1), g)
|
||||
bpy.context.scene.collection.objects.unlink(c)
|
||||
coll.objects.link(c)
|
||||
|
||||
s = bpy.context.scene.tree_gen_settings
|
||||
s.preset = 'baum'
|
||||
s.use_growth = False
|
||||
s.apply_modifier = True
|
||||
s.art_name = "Eiche"
|
||||
s.variant = 1
|
||||
s.leaf_size_var = 0.0 # entscheidend: Groesse kommt NUR von der Variante
|
||||
|
||||
bpy.ops.object.treegen_create()
|
||||
baum = [o for o in bpy.context.scene.objects
|
||||
if o.type == 'MESH' and o.name.startswith("Eiche_01")
|
||||
and not o.name.endswith(("_Leaf", "_Frucht"))][0]
|
||||
|
||||
|
||||
def streuen(collection, einzelcard, seed):
|
||||
s.leaf_cards = collection
|
||||
s.leaf_card = einzelcard
|
||||
s.seed = seed
|
||||
for o in bpy.context.scene.objects:
|
||||
o.select_set(o is baum)
|
||||
bpy.context.view_layer.objects.active = baum
|
||||
bpy.ops.object.treegen_leaves()
|
||||
bl = bpy.data.objects.get(baum.name + "_Leaf")
|
||||
dg = bpy.context.evaluated_depsgraph_get()
|
||||
ev = bl.evaluated_get(dg)
|
||||
me = ev.to_mesh()
|
||||
flaechen = sorted({round(p.area, 5) for p in me.polygons})
|
||||
n = len(me.polygons)
|
||||
ev.to_mesh_clear()
|
||||
return n, flaechen
|
||||
|
||||
|
||||
print("%-26s %7s %s" % ("Variante", "Faces", "verschiedene Flaechen"))
|
||||
|
||||
# --- 1) Rueckwaertskompatibel: Einzelobjekt ------------------------------
|
||||
n1, f1 = streuen(None, einzel, 0)
|
||||
print("%-26s %7d %d" % ("Einzel-Card", n1, len(f1)))
|
||||
if n1 == 0:
|
||||
fails.append("Einzel-Card erzeugt keine Blaetter - Rueckwaertskompatibilitaet weg")
|
||||
# Eine Card = zwei unterschiedlich grosse Tris = genau zwei Flaechen.
|
||||
if len(f1) > 3:
|
||||
fails.append("Einzel-Card liefert %d verschiedene Flaechen, erwartet <= 3 - "
|
||||
"die Groessenstreuung ist nicht wirklich aus" % len(f1))
|
||||
|
||||
# --- 2) Collection: alle Varianten kommen vor ----------------------------
|
||||
n2, f2 = streuen(coll, None, 0)
|
||||
print("%-26s %7d %d" % ("Collection (%d Cards)" % len(GROESSEN), n2, len(f2)))
|
||||
if n2 == 0:
|
||||
fails.append("Collection erzeugt keine Blaetter")
|
||||
# Vier Cards mit je zwei Tris = acht verschiedene Flaechen, wenn wirklich alle
|
||||
# vier gezogen werden. Weniger heisst: es kommen nicht alle Varianten vor.
|
||||
erwartet = 2 * len(GROESSEN)
|
||||
if len(f2) < erwartet:
|
||||
fails.append("Nur %d verschiedene Flaechen, erwartet %d - es werden nicht "
|
||||
"alle %d Card-Varianten gezogen"
|
||||
% (len(f2), erwartet, len(GROESSEN)))
|
||||
|
||||
# --- 3) Seed-stabil ------------------------------------------------------
|
||||
n3, f3 = streuen(coll, None, 0)
|
||||
print("%-26s %7d %d" % ("gleicher Seed", n3, len(f3)))
|
||||
if (n3, f3) != (n2, f2):
|
||||
fails.append("Gleicher Seed liefert ein anderes Ergebnis - nicht stabil")
|
||||
|
||||
n4, f4 = streuen(coll, None, 7)
|
||||
print("%-26s %7d %d" % ("anderer Seed", n4, len(f4)))
|
||||
if (n4, f4) == (n2, f2):
|
||||
fails.append("Anderer Seed liefert dasselbe Ergebnis - der Seed wirkt nicht")
|
||||
|
||||
# --- 4) Collection hat Vorrang vor der Einzel-Card -----------------------
|
||||
n5, f5 = streuen(coll, einzel, 0)
|
||||
print("%-26s %7d %d" % ("beides gesetzt", n5, len(f5)))
|
||||
if len(f5) < erwartet:
|
||||
fails.append("Mit gesetzter Einzel-Card faellt die Collection zurueck - "
|
||||
"die Sammlung muss Vorrang haben")
|
||||
|
||||
print("")
|
||||
if fails:
|
||||
print("ERGEBNIS: %d FEHLER" % len(fails))
|
||||
for x in fails:
|
||||
print(" - " + x)
|
||||
sys.exit(1)
|
||||
print("ERGEBNIS: ALLE CHECKS OK")
|
||||
Reference in New Issue
Block a user