Visualization Backends ====================== Pylinkage provides multiple visualization and export backends: - **Matplotlib**: Animations and static plots (default) - **Plotly**: Interactive HTML visualizations - **drawsvg**: Publication-quality SVG output - **DXF**: 2D CAD export for AutoCAD/CNC (requires ``pylinkage[cad]``) - **STEP**: 3D CAD interchange format (requires ``pylinkage[cad]``) This tutorial covers each backend with practical examples. .. figure:: /../assets/visualization_comparison.png :width: 800px :align: center :alt: Visualization backends comparison Comparison of the three visualization backends: Matplotlib (animations), Plotly (interactive HTML), and drawsvg (publication SVG). Quick Reference --------------- .. list-table:: :header-rows: 1 :widths: 20 30 50 * - Backend - Best For - Output Formats * - Matplotlib - Quick visualization, GIF animations - PNG, GIF, PDF, interactive window * - Plotly - Interactive exploration, web embedding - HTML, PNG, PDF, SVG * - drawsvg - Publications, precise vector graphics - SVG, PNG, PDF * - DXF - 2D CAD, laser cutting, CNC - DXF (AutoCAD compatible) * - STEP - 3D CAD, machining, 3D printing - STEP/STP (ISO 10303) Matplotlib Backend ------------------ The default backend for quick visualization and animations. Basic Visualization ^^^^^^^^^^^^^^^^^^^ .. code-block:: python import pylinkage as pl from pylinkage.visualizer import show_linkage # Create a four-bar linkage crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1, name="Crank") output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1, name="Output") linkage = pl.Linkage(joints=(crank, output), name="Four-bar") # Quick visualization (opens matplotlib window) show_linkage(linkage) Static Frame Visualization ^^^^^^^^^^^^^^^^^^^^^^^^^^ Show the linkage at a specific position: .. code-block:: python from pylinkage.visualizer import show_linkage import matplotlib.pyplot as plt # Show without animation fig, ax = show_linkage(linkage, animated=False) # Customize the plot ax.set_title("Four-bar Linkage - Initial Position") ax.grid(True, alpha=0.3) ax.set_aspect('equal') plt.tight_layout() plt.savefig("linkage_static.png", dpi=150) plt.show() **Result**: A static image showing the linkage in its initial configuration. Animated GIF Output ^^^^^^^^^^^^^^^^^^^ Create animated GIFs for documentation or presentations: .. code-block:: python from pylinkage.visualizer import show_linkage # Create animated GIF show_linkage( linkage, save_path="four_bar_animation.gif", fps=24, # Frames per second duration=3000, # Total duration in ms loci=True, # Show joint paths ) print("Animation saved to four_bar_animation.gif") **Result**: An animated GIF showing the linkage cycling through its motion. Customizing Appearance ^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python from pylinkage.visualizer import show_linkage import matplotlib.pyplot as plt fig, ax = show_linkage( linkage, # Display options loci=True, # Show joint trajectories show_legend=True, # Add legend title="Custom Four-bar", # Style options joint_color='#E63946', # Joint marker color link_color='#1D3557', # Link line color locus_color='#A8DADC', # Trajectory line color joint_size=80, # Marker size # Animation options (if animated=True) animated=True, interval=50, # ms between frames ) plt.show() Showing Multiple Linkages ^^^^^^^^^^^^^^^^^^^^^^^^^ Compare different configurations: .. code-block:: python import pylinkage as pl from pylinkage.visualizer import show_linkage import matplotlib.pyplot as plt # Create two different four-bars def make_fourbar(d0, d1, name): crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1) output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=d0, distance1=d1) return pl.Linkage(joints=(crank, output), name=name) linkage1 = make_fourbar(3, 1, "Short rocker") linkage2 = make_fourbar(3, 2, "Long rocker") # Plot side by side fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5)) show_linkage(linkage1, ax=ax1, animated=False, loci=True) ax1.set_title(linkage1.name) show_linkage(linkage2, ax=ax2, animated=False, loci=True) ax2.set_title(linkage2.name) plt.tight_layout() plt.savefig("comparison.png", dpi=150) plt.show() Plotly Backend -------------- Interactive HTML visualizations ideal for web embedding and exploration. Basic Interactive Plot ^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python import pylinkage as pl from pylinkage.visualizer import plot_linkage_plotly # Create linkage crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1, name="Crank") output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1, name="Output") linkage = pl.Linkage(joints=(crank, output), name="Four-bar") # Create interactive plot fig = plot_linkage_plotly(linkage) # Display in notebook or browser fig.show() # Save to HTML fig.write_html("interactive_linkage.html") **Result**: An interactive HTML page where you can: - Zoom and pan - Hover over joints for coordinates - Toggle visibility of elements - Rotate through animation frames Animation with Slider ^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python from pylinkage.visualizer import plot_linkage_plotly fig = plot_linkage_plotly( linkage, show_loci=True, # Show joint trajectories show_slider=True, # Add frame slider frame_count=50, # Number of animation frames title="Interactive Four-bar", ) fig.write_html("animated_linkage.html") **Result**: HTML with a slider to scrub through the animation manually. Customizing Plotly Appearance ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python from pylinkage.visualizer import plot_linkage_plotly import plotly.graph_objects as go fig = plot_linkage_plotly( linkage, # Colors joint_color='red', link_color='blue', locus_color='rgba(0, 255, 0, 0.5)', # Sizes joint_size=15, link_width=4, # Layout title="Styled Four-bar", width=800, height=600, ) # Further customization using plotly API fig.update_layout( plot_bgcolor='white', paper_bgcolor='white', font=dict(family="Arial", size=14), ) fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='lightgray') fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='lightgray') fig.show() Embedding in Web Pages ^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python from pylinkage.visualizer import plot_linkage_plotly fig = plot_linkage_plotly(linkage, show_loci=True) # Get HTML div for embedding div_html = fig.to_html(include_plotlyjs='cdn', full_html=False) # Write to file with custom wrapper full_html = f"""
Use the controls to explore the mechanism.
""" with open("embedded_linkage.html", "w") as f: f.write(full_html) drawsvg Backend --------------- Publication-quality vector graphics for papers and documentation. Basic SVG Output ^^^^^^^^^^^^^^^^ .. code-block:: python import pylinkage as pl from pylinkage.visualizer import save_linkage_svg # Create linkage crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1, name="Crank") output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1, name="Output") linkage = pl.Linkage(joints=(crank, output), name="Four-bar") # Save as SVG save_linkage_svg(linkage, "linkage.svg") **Result**: A crisp SVG file that scales perfectly at any resolution. Customizing SVG Style ^^^^^^^^^^^^^^^^^^^^^ .. code-block:: python from pylinkage.visualizer import save_linkage_svg save_linkage_svg( linkage, "styled_linkage.svg", # Dimensions width=400, height=300, margin=20, # Colors (CSS color strings) joint_color='#2E86AB', link_color='#A23B72', ground_color='#F18F01', locus_color='#C73E1D', # Stroke widths link_width=3, locus_width=1.5, # Joint markers joint_radius=8, # Show elements show_loci=True, show_labels=True, show_ground=True, ) Multi-Frame SVG ^^^^^^^^^^^^^^^ Show multiple positions in one image: .. code-block:: python from pylinkage.visualizer import save_linkage_svg_multiframe import pylinkage as pl crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1) output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1) linkage = pl.Linkage(joints=(crank, output)) # Show 5 evenly spaced positions save_linkage_svg_multiframe( linkage, "multiframe.svg", num_frames=5, frame_opacity=0.3, # Transparency of intermediate frames highlight_first=True, # Make first frame solid highlight_last=True, # Make last frame solid ) **Result**: SVG showing the linkage at multiple positions, ideal for illustrating motion. SVG for LaTeX ^^^^^^^^^^^^^ Generate SVGs optimized for LaTeX documents: .. code-block:: python from pylinkage.visualizer import save_linkage_svg save_linkage_svg( linkage, "latex_figure.svg", width=300, # Points (LaTeX-friendly) height=200, font_family="serif", # Match LaTeX fonts font_size=10, show_labels=True, label_offset=12, ) # Include in LaTeX: # \begin{figure} # \centering # \includesvg{latex_figure} # \caption{Four-bar linkage mechanism} # \end{figure} CAD Export ---------- Export linkages to industry-standard CAD formats for fabrication and 3D modeling. .. note:: CAD export requires optional dependencies. Install with: .. code-block:: bash pip install pylinkage[cad] This installs ``ezdxf`` (for DXF) and ``build123d`` (for STEP). DXF Export (2D CAD) ^^^^^^^^^^^^^^^^^^^ Export to DXF format for AutoCAD, CNC machines, and laser cutters: .. code-block:: python import pylinkage as pl from pylinkage.visualizer import save_linkage_dxf, plot_linkage_dxf # Create linkage crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1, name="Crank") output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1, name="Output") linkage = pl.Linkage(joints=(crank, output), name="Four-bar") # Save to DXF file save_linkage_dxf(linkage, "linkage.dxf") # Or get the ezdxf Drawing object for further customization doc = plot_linkage_dxf(linkage) doc.saveas("custom_linkage.dxf") **DXF Layers**: The exported DXF contains organized layers: - ``LINKS`` - Link bar geometry (white) - ``JOINTS`` - Joint symbols (red) - ``GROUND`` - Ground/fixed support symbols (gray) - ``CRANKS`` - Crank/motor symbols (green) Customizing DXF Output ^^^^^^^^^^^^^^^^^^^^^^ Control dimensions and export a specific frame: .. code-block:: python from pylinkage.visualizer import save_linkage_dxf # Run simulation to get all positions loci = list(linkage.step()) # Export frame 25 with custom dimensions save_linkage_dxf( linkage, "frame25.dxf", loci=loci, frame_index=25, # Export this frame (0 = first) link_width=0.5, # Link bar width in world units joint_radius=0.2, # Joint symbol radius ) STEP Export (3D CAD) ^^^^^^^^^^^^^^^^^^^^ Export to STEP format for 3D CAD applications (FreeCAD, SolidWorks, Fusion 360): .. code-block:: python import pylinkage as pl from pylinkage.visualizer import save_linkage_step, build_linkage_3d # Create linkage crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1, name="Crank") output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1, name="Output") linkage = pl.Linkage(joints=(crank, output), name="Four-bar") # Save to STEP file (dimensions auto-scaled to fit linkage) save_linkage_step(linkage, "linkage.step") # Or get the build123d Compound for further manipulation model = build_linkage_3d(linkage) model.export_step("custom_linkage.step") **3D Geometry**: The STEP export creates: - Stadium-shaped link bars (rounded rectangles extruded in Z) - Holes at joint locations for pin connections - Cylindrical pins at each joint - Ground symbols for fixed supports Customizing STEP Dimensions ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use ``LinkProfile`` and ``JointProfile`` to control 3D geometry: .. code-block:: python from pylinkage.visualizer import ( save_linkage_step, LinkProfile, JointProfile, ) # Define custom link cross-section link_profile = LinkProfile( width=10.0, # Link bar width (mm or your units) thickness=3.0, # Extrusion depth in Z fillet_radius=0.5, # Edge rounding (0 for sharp) ) # Define custom joint pins joint_profile = JointProfile( radius=2.0, # Pin radius length=5.0, # Pin length in Z ) # Export with custom profiles save_linkage_step( linkage, "machined_linkage.step", link_profile=link_profile, joint_profile=joint_profile, frame_index=0, # Which position to export include_pins=True, # Include joint pins ) Exporting Multiple Frames ^^^^^^^^^^^^^^^^^^^^^^^^^ Export different positions of the mechanism: .. code-block:: python from pylinkage.visualizer import save_linkage_step # Pre-compute trajectory loci = list(linkage.step()) # Export key positions for i, frame_idx in enumerate([0, 25, 50, 75]): save_linkage_step( linkage, f"linkage_position_{i}.step", loci=loci, frame_index=frame_idx, ) print(f"Exported frame {frame_idx} to linkage_position_{i}.step") CAD Export Workflow ^^^^^^^^^^^^^^^^^^^ A typical workflow from simulation to fabrication: .. code-block:: python import pylinkage as pl from pylinkage.visualizer import ( show_linkage, save_linkage_svg, save_linkage_dxf, save_linkage_step, LinkProfile, ) # 1. Design and simulate crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1, name="Crank") output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1, name="Output") linkage = pl.Linkage(joints=(crank, output)) loci = list(linkage.step()) # 2. Quick visualization to verify show_linkage(linkage, loci=loci) # 3. Publication figure (SVG) save_linkage_svg(linkage, "documentation/linkage.svg", show_loci=True) # 4. 2D CAD for laser cutting (DXF) save_linkage_dxf(linkage, "fabrication/linkage_2d.dxf", loci=loci) # 5. 3D CAD for machining/printing (STEP) profile = LinkProfile(width=10, thickness=3) save_linkage_step( linkage, "fabrication/linkage_3d.step", loci=loci, link_profile=profile, ) print("Export complete! Files ready for fabrication.") PSO Visualization ----------------- Visualize particle swarm optimization progress: .. code-block:: python import pylinkage as pl from pylinkage.visualizer import ( plot_pso_convergence, plot_pso_particles, create_pso_dashboard, ) # Create and optimize linkage crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1) output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1) linkage = pl.Linkage(joints=(crank, output)) @pl.kinematic_minimization def fitness(loci, **kwargs): output_path = [step[-1] for step in loci] bbox = pl.bounding_box(output_path) return bbox[2] - bbox[0] # Minimize height bounds = pl.generate_bounds(linkage.get_num_constraints()) # Run optimization with history tracking results, history = pl.particle_swarm_optimization( eval_func=fitness, linkage=linkage, bounds=bounds, n_particles=30, iters=50, return_history=True, ) # Plot convergence fig = plot_pso_convergence(history) fig.savefig("convergence.png") # Plot particle distribution fig = plot_pso_particles(history, iteration=25) fig.savefig("particles_iter25.png") # Create full dashboard fig = create_pso_dashboard(linkage, history, results) fig.savefig("pso_dashboard.png", dpi=150) Visualization with Kinematics ----------------------------- Show velocity vectors alongside the linkage: .. figure:: /../assets/visualization_velocity.png :width: 600px :align: center :alt: Velocity vectors visualization Linkage with velocity vectors shown at each joint, computed from the angular velocity of the input crank. .. code-block:: python from pylinkage.visualizer import show_kinematics, animate_kinematics import pylinkage as pl crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.1, distance=1) output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=3, distance1=1) linkage = pl.Linkage(joints=(crank, output)) # Set angular velocity linkage.set_input_velocity(crank, omega=10.0) # Show single frame with velocity vectors fig = show_kinematics( linkage, frame_index=10, show_velocity=True, velocity_scale=0.05, # Scale factor for arrow length velocity_color='red', ) fig.savefig("velocities.png") # Animated with velocities animate_kinematics( linkage, show_velocity=True, save_path="velocity_animation.gif", ) Choosing the Right Backend -------------------------- **Use Matplotlib when:** - You need quick visualization during development - You want animated GIFs for documentation - You're working in Jupyter notebooks - You need PDF output for simple figures **Use Plotly when:** - You want interactive exploration - You're building web applications - You need to embed in HTML pages - Users need to zoom/pan/hover **Use drawsvg when:** - You're writing academic papers - You need precise vector graphics - You want to edit the output in Inkscape/Illustrator - You need consistent styling across figures **Use DXF when:** - You need to import into AutoCAD or similar 2D CAD software - You're preparing files for laser cutting or CNC machining - You need layered 2D technical drawings - You want to edit geometry in CAD software **Use STEP when:** - You need to import into 3D CAD software (FreeCAD, SolidWorks, Fusion 360) - You're preparing files for 3D printing or machining - You want to visualize the linkage as physical parts - You need to integrate with other 3D models Example: Complete Visualization Workflow ---------------------------------------- .. code-block:: python import pylinkage as pl from pylinkage.visualizer import ( show_linkage, plot_linkage_plotly, save_linkage_svg, ) # Create an optimized linkage crank = pl.Crank(0, 1, joint0=(0, 0), angle=0.31, distance=1, name="A") output = pl.Revolute(3, 2, joint0=crank, joint1=(3, 0), distance0=2.5, distance1=1.5, name="B") linkage = pl.Linkage(joints=(crank, output), name="Optimized Four-bar") # 1. Quick check with Matplotlib show_linkage(linkage, loci=True) # 2. Interactive exploration with Plotly fig = plot_linkage_plotly(linkage, show_loci=True, show_slider=True) fig.write_html("explore.html") print("Open explore.html in browser for interactive view") # 3. Publication figure with drawsvg save_linkage_svg( linkage, "figure1.svg", width=400, height=300, show_loci=True, show_labels=True, joint_color='black', link_color='black', locus_color='gray', ) print("Publication figure saved to figure1.svg") # 4. Animation for presentation show_linkage( linkage, save_path="presentation.gif", fps=30, loci=True, ) print("Animation saved to presentation.gif") Next Steps ---------- - :doc:`getting_started` - Basic linkage creation - :doc:`kinematics_optimization` - Velocity visualization - See :py:mod:`pylinkage.visualizer` for complete API reference