How to Create Line Charts Using the Outfancy LineChart Class

Outfancy renders terminal-based line charts through the LineChart class defined in outfancy/chart.py, which manages data validation, coordinate scaling, and ASCII/Unicode rendering via the plot() and render() methods.

The outfancy library provides a lightweight solution for visualizing data directly in terminal environments. To create line charts using the outfancy LineChart class, you instantiate the class, feed datasets via the plot() method, and generate output through render(). This workflow separates data ingestion from rendering, allowing you to customize symbols, colors, and margins without modifying the core algorithm.

Instantiating the Chart Object

The LineChart class is implemented in outfancy/chart.py (line 11). When instantiated, it initializes storage for global extrema (x_min, x_max, y_min, y_max) and maintains a dataset_space list to hold plotted series. This state management enables automatic scaling across multiple data series without manual coordinate transformation.

Ingesting Data via the plot() Method

To add data, pass a list of (x, y) tuples to the plot(dataset) method. Internally, plot() validates the input through check_data_integrity (chart.py line 27) and extracts coordinate lists using get_list_of_elements. The method automatically updates the global minima and maxima, ensuring the subsequent rendering phase can scale the chart to fit the terminal dimensions precisely.

The Rendering Pipeline

The render() method orchestrates terminal output by executing a multi-stage pipeline:

  • Terminal sizing: Queries the current terminal dimensions using shutil.get_terminal_size() (chart.py line 16) to compute conversion factors (x_points_per_x_pixel, y_points_per_y_pixel).
  • Drawing surface: Instantiates a Window object from outfancy/window.py (line 6) to provide a mutable matrix representing the drawing surface.
  • Coordinate transformation: Transforms data series into screen coordinates, applying optional down-sampling via remove_unnecesary_points_before_plotting and gap-filling through linear interpolation when enabled.
  • Character selection: When slope_based_characters is True, get_char_slope (chart.py line 50) selects line characters (|, /, \, ) based on local slope, optionally wrapped in ANSI color codes.
  • Margin assembly: Constructs top, left, and bottom margins using create_top_margin, create_left_margin, and create_down_margin, which utilize widgets.create_matrix from outfancy/widgets.py (line 56) to generate axis grids.

The assembled Window renders to a final string via window.render() and returns to the caller.

Customization Options

The separation of concerns between LineChart, Window, and widgets allows extensive customization without core algorithm changes. Key configuration parameters include:

  • Point symbols: Override default characters using the point_list parameter in render().
  • Colorization: Enable ANSI colors with color=True and specify series-specific codes via color_number_list (e.g., 34 for blue, 31 for red).
  • Interpolation: Control automatic gap filling with the boolean interpolation parameter.
  • Slope-based rendering: Toggle slope_based_characters to replace custom symbols with slope-appropriate line art.
  • Margins: Adjust layout using left_margin_width, margin_down_height, and margin_top_height.

Practical Code Examples

Basic Single-Series Chart

Create a simple line chart by instantiating the class, plotting a dataset, and rendering:

from outfancy.chart import LineChart

# 1️⃣ Create the chart object

chart = LineChart()

# 2️⃣ Provide a dataset – list of (x, y) tuples

dataset = [(0, 2), (1, 5), (2, 3), (3, 8), (4, 6)]

# 3️⃣ Plot the data (you can call plot multiple times for more series)

chart.plot(dataset)

# 4️⃣ Render to a string and print

print(chart.render(plot_name='Demo Line Chart'))

Multi-Series with Custom Symbols and Colors

Plot multiple series with distinct visual styling:

from outfancy.chart import LineChart

chart = LineChart()

# Series A – blue circles

series_a = [(x, x**0.5) for x in range(0, 11)]
chart.plot(series_a)

# Series B – red triangles

series_b = [(x, 5 - 0.5 * x) for x in range(0, 11)]
chart.plot(series_b)

# Render with custom point characters and colourisation

print(
    chart.render(
        plot_name='Two Series',
        point_list=['⊙', '△'],          # custom symbols per series

        color=True,                    # enable ANSI colours

        color_number_list=[34, 31],    # 34=blue, 31=red

        interpolation=False,           # disable automatic gap filling

        slope_based_characters=False  # keep the chosen symbols unchanged

    )
)

Advanced Margin and Slope Configuration

Adjust layout dimensions and enable slope-based character replacement:

from outfancy.chart import LineChart

chart = LineChart()
chart.plot([(i, i*i) for i in range(0, 21)])

print(
    chart.render(
        plot_name='Quadratic',
        left_margin_width=10,
        margin_down_height=4,
        margin_top_height=2,
        slope_based_characters=True,   # let the class replace symbols by slopes

        background_point='·'           # light-dot background

    )
)

Summary

  • The LineChart class in outfancy/chart.py provides the primary interface for creating terminal-based line charts.
  • Data ingestion occurs through plot(), which validates input via check_data_integrity and updates global extrema for automatic scaling.
  • Rendering happens through render(), which calculates terminal-relative coordinates, manages a Window drawing surface from outfancy/window.py, and assembles margins using utilities from outfancy/widgets.py.
  • Customization options include slope-based character selection (|, /, \, ), ANSI color codes, interpolation control, and adjustable margin widths.
  • The architecture separates chart logic from terminal rendering, enabling flexible visual customization without core algorithm modification.

Frequently Asked Questions

How does LineChart handle automatic scaling?

The class tracks global minima and maxima (x_min, x_max, y_min, y_max) across all plotted series. During rendering, it queries the terminal size via shutil.get_terminal_size() and computes conversion factors (x_points_per_x_pixel, y_points_per_y_pixel) to map data coordinates to screen pixels automatically.

Can I plot multiple data series on the same chart?

Yes. Call plot(dataset) multiple times on the same LineChart instance before invoking render(). Each call appends to the internal dataset_space list. You can differentiate series using the point_list and color_number_list parameters in render() to assign unique symbols and colors per series.

What are slope-based characters and when should I use them?

When slope_based_characters=True, the get_char_slope method (chart.py line 50) analyzes the local slope between points and selects appropriate line drawing characters: vertical bars (|), forward slashes (/), backslashes (\), or horizontal em-dashes (). This creates smoother visual connections between points compared to static symbols, especially for steep gradients.

How do I disable interpolation between data points?

Pass interpolation=False to the render() method. By default, the renderer may fill gaps linearly, but setting this parameter to False ensures the chart displays only the exact data points provided without generating intermediate coordinate values.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →