Add 4 new topology generators to SyntheticAssemblyGenerator:
- generate_tree_assembly: random spanning tree with configurable branching
- generate_loop_assembly: closed ring producing overconstrained data
- generate_star_assembly: hub-and-spoke topology
- generate_mixed_assembly: tree + loops with configurable edge density
Each accepts joint_types as JointType | list[JointType] for per-joint
type sampling.
Add complexity tiers (simple/medium/complex) with predefined body count
ranges via COMPLEXITY_RANGES dict and ComplexityTier type alias.
Update generate_training_batch with 7-way generator selection,
complexity_tier parameter, and generator_type field in output dicts.
Extract private helpers (_random_position, _random_axis,
_select_joint_type, _create_joint) to reduce duplication.
44 generator tests, 130 total — all passing.
Closes#7