DiscoPygal Solver Viewer: Developer Architecture Manual¶
The DiscoPygal Solver Viewer is a PySide6-based graphical frontend for the discopygal Python framework. It provides interactive scene loading, dynamic solver configuration, process-isolated path planning execution, and multi-robot path animation.
Because the backend relies on heavy geometric algorithms and C++ CGAL bindings, the viewer uses an out-of-process concurrency model with a specialized inter-process communication (IPC) serialization layer to keep the user interface responsive.
System Architecture & Concurrency Model¶
Path planning on complex geometric arrangements is computationally intensive and can block the Qt event loop if run synchronously. To maintain UI responsiveness, the application isolates solver execution in a separate operating system process managed via Python’s multiprocessing library.
Process-Isolated Concurrency Model & IPC Queues¶
Process Lifecycle & IPC Mechanism¶
Initialization: The GUI thread invokes
SolverViewerGUI.solve_thread(), which serializes the active scene into a dictionary (scene_dict) and gathers user-defined parameters viaget_solver_args().Execution Isolation: A
multiprocessing.Processtarget is initialized withsolve_process().Log Streaming: Standard output inside the worker process is redirected to a
ChildWriterproxy class, which pushes text log chunks into a thread-safelog_queue. The GUI polls this queue asynchronously to update the log dock.Result Marshalling: Upon completion of
solver.solve(), generated geometric paths, graphs, and planar arrangements are converted into primitive standard Python data structures and pushed ontoresult_queue.
Dynamic Solver Discovery & Trait Filtering¶
The viewer ensures structural compatibility between loaded geometric scenes and selected solvers prior to execution.
Trait Extraction & Matching Logic¶
Scenes define operational constraints based on robot geometries and fleet size. Before displaying available solvers in the GUI dropdown, SolverDialog executes a compatibility check:
Scene Analysis (``extract_scene_traits``): * Inspects
scene.robotsto map concrete classes (RobotDisc,RobotPolygon,RobotRod) to trait capabilities (Trait.ACCEPTS_DISC_ROBOT, etc.). * Inspectslen(scene.robots)to derive cardinality traits (Trait.ACCEPTS_SINGLE_ROBOT,Trait.ACCEPTS_MULTIPLE_ROBOTS, etc.).Solver Trait Verification (``solver_matches_scene``): * Inspects the
_traitsclass attribute on candidate solvers viaget_solver_class(). * Verifies that the solver’s traits cover all required scene capabilities.
discopygal_scene ──► extract_scene_traits() ──► Required Trait Set
│
Available Solvers ──► solver_matches_scene() ────────┴──► Active Dropdown List
Dynamic Import Pipeline¶
Custom solvers can be imported at runtime via import_solver_file(). The module is loaded dynamically using importlib.import_module().
Data Serialization & CGAL Marshalling Protocol¶
Because native C++ CGAL geometric objects (such as Point_2, Polygon_2, or arrangements) cannot be directly pickled across Python process boundaries, the viewer marshals all CGAL objects into standard Python dictionaries prior to transmission over multiprocessing.Queue.
┌─────────────────────────────────┐
│ Worker Process Completion │
└────────────────┬────────────────┘
│
┌───────────────────┴───────────────────┐
│ │
▼ ▼
Path / Metadata Objects Graph & Arrangement
│ │
serialize_paths() conversions.graph_to_dict()
│ conversions.arrangement_to_dict()
│ │
└───────────────────┬───────────────────┘
│
▼
Serialized Result Tuple pushed to Queue
Result Payload Contract¶
The payload returned from the worker process via result_queue adheres to the following tuple signatures:
Success Payload:
("RESULT", serialized_paths, serialized_graph, serialized_arrangement)Failure Payload:
("ERROR", exception_type, exception_value, formatted_traceback_string)
Robot Identity Reconstruction¶
When path dictionaries return to the main process, each path must be mapped back to its corresponding Robot object instance inside the PySide6 scene graph.
┌───────────────────────────────┐
│ Received Serialized Path │
└───────────────┬───────────────┘
│
Has 'obj_id' in Metadata?
/ \
Yes / \ No
/ \
▼ ▼
Primary Match Geometric Fallback
(Direct ID) (Coordinate Matching)
Primary Identity Matching (match_path_robot)¶
During serialization, serialize_paths() captures the robot’s unique memory ID (_obj_id). The GUI matches this key against robot.data['_obj_id'] for each robot active in discopygal_scene.robots.
Geometric Fallback Matching (match_scene_robot)¶
If obj_id is absent (such as in external third-party path representations), match_scene_robot() performs a spatial boundary comparison using start
and end
path coordinates. Match validation applies an absolute tolerance threshold:

5. Rendering & Parallel Animation Engine¶
The viewer includes an animation framework built on Qt’s animation properties (QPropertyAnimation, QParallelAnimationGroup) to simulate multi-robot trajectory execution simultaneously.
Segment Animation Dispatch¶
For each segment from path vertex
to
, the animation system calculates motion parameters and instantiates property drivers:
Translation Segments: Driven by
linear_translation_animation()when coordinates update without orientation changes.Rotational Segments: Driven by
segment_angle_animation()when orientation (theta) keys are present in vertex metadata.Speed Evaluation: Segment durations are calculated in milliseconds using Euclidean distances:

Multi-Robot Synchronization: Individual segment animations for all robots are bundled into a
QParallelAnimationGroup. These groups are appended to a queue viaqueue_animation()and executed in sequence viaplay_queue().
- discopygal_tools.solver_viewer.solver_viewer_main.start_gui(scene: Scene = None, solver: Solver = None, solver_file: str = None) None¶
Start solver_viewer tool
See also Solver Viewer - From script