RadarChart: Multivariate Radial Metrics & Spider Webs

RadarChart (drawlib.charts.radar, also known as a spider web or star chart) evaluates multivariate entities across three or more symmetric radial dimensions emanating from a common center origin point. It is widely used for skill matrices, competitive benchmark profiles, and system architectural trade-off evaluations.

radar_1
RadarChart Radial Geometry: Polygon vs. Circular Contours
Source Code
from drawlib.canvas import save, setup
from drawlib.charts.radar import RadarChart
from drawlib.lines import line
from drawlib.shapes import circle
from drawlib.styles import Styles
from drawlib.text import text

setup(width=132, height=62)

dims = ["Scale", "Speed", "Security", "UX", "Uptime"]
vals_a = [90, 75, 85, 70, 95]
vals_b = [65, 90, 70, 88, 75]

# Left: Polygon Grid
poly_radar = RadarChart(
    categories=dims,
    axis_line_style=Styles.MutedDashed,
    axis_text_style=Styles.DarkBold.patch(text_size=10.0),
    grid_style=Styles.MutedThin,
    radius=16.5,
    min_value=0.0,
    max_value=100.0,
    levels=4,
    grid_shape="polygon",
    title="Polygon Grid (grid_shape='polygon')",
    title_style=Styles.BlackBold.patch(text_size=11.0),
)
poly_radar.add_series("Profile A", vals_a, style=Styles.PrimaryFlat, fill_alpha=0.28)
poly_radar.add_series("Profile B", vals_b, style=Styles.SecondaryNeutral, fill_alpha=0.25, line_style="dashed")
poly_radar.draw(xy=(4.0, 5.0))

# Right: Circular Grid with Scale Labels
circ_radar = RadarChart(
    categories=dims,
    axis_line_style=Styles.MutedDashed,
    axis_text_style=Styles.DarkBold.patch(text_size=10.0),
    grid_style=Styles.MutedThin,
    scale_text_style=Styles.Muted.patch(text_size=10.0),
    scale_format="{:.0f}",
    radius=16.5,
    min_value=0.0,
    max_value=100.0,
    levels=4,
    grid_shape="circle",
    title="Circular Grid (grid_shape='circle')",
    title_style=Styles.BlackBold.patch(text_size=11.0),
)
circ_radar.add_series("Profile A", vals_a, style=Styles.PrimaryFlat, fill_alpha=0.28)
circ_radar.add_series("Profile B", vals_b, style=Styles.SecondaryNeutral, fill_alpha=0.25, line_style="dashed")
circ_radar.draw(xy=(58.0, 5.0))

# Callout annotations on Right RadarChart using exact center coordinates
rw, rh = circ_radar.get_size()
rcx = 58.0 + rw / 2.0
rcy = 5.0 + (rh - 6.0) / 2.0

circle((rcx, rcy), radius=0.85, style=Styles.DangerFlat)
line((rcx + 0.8, rcy - 0.3), (105.0, 18.0), style=Styles.DangerBold)
text((106.0, 18.0), "min_value\n(Center Origin)", style=Styles.DangerBold.patch(text_size=10.0, halign="left"))

line((rcx + 3.0, rcy + 8.5), (105.0, 41.0), arrow_head="<-", style=Styles.DarkBold)
text((106.0, 41.0), "90° Top Spoke\n(scale_text_style)", style=Styles.DarkBold.patch(text_size=10.0, halign="left"))

circ_radar.draw_legend(xy=(45.0, 3.5), text_style=Styles.DarkBold.patch(text_size=10.0), orientation="horizontal")
save()

1. Core Concepts & Basic Usage

RadarChart plots closed multivariate polygons across three or more symmetric spokes (categories, axis_line_style), anchoring by default at the Bottom-Left (x, y) corner of its container box (or directly on the radar hub when align="center"). You can switch between straight spider webs (grid_shape="polygon") and polar rings (grid_shape="circle"), customize scale ticks (configure_axis), vertex markers (point_shape), and stroke patterns (line_style), and animate radial bloom (draw_ratio):

radar_2
Basic Usage & Parameter Tour: Spokes & Series, Contours & Scale, Markers & Strokes, and Bloom & Align (APNG Animation)
  • Spokes, Overlaid Series & Anchor (categories, add_series, fill_alpha): Define 3+ symmetric radial dimensions (90° top spoke progressing clockwise), overlay translucent series polygons (fill_alpha=0.25) anchored at bottom-left xy, and pair with chart.draw_legend(...).
  • Polygonal vs. Circular Contours & Scale (grid_shape, configure_axis, scale_text_style): Choose "polygon" spider webs or "circle" polar contours across levels rings, and tune radial bounds (min_value, max_value, scale_format) via configure_axis(...).
  • Vertex Markers, Stroke Patterns & Labels (point_shape, line_style, value_text_style): Differentiate baseline vs. focal profiles with line_style ("solid", "dashed", "dotted", "dashdot"), point_shape ("circle", "square", "none"), and optional vertex labels (value_text_style).
  • Radial Bloom, Visibility & Hub Pinning (draw_ratio, show, align="center"): Expand series outward from the hub via draw_ratio ("bottom_to_top" or "left_to_right"), hide individual profiles via series.show = False, or pass align="center" to pin xy=(cx, cy) at the radar hub.

2. Polygonal Grid Architecture Benchmark

Polygonal gridlines (grid_shape="polygon") align with radial spokes, making it easy to compare architectural trade-offs across non-functional requirements:

from drawlib.canvas import save, setup
from drawlib.charts.radar import RadarChart
from drawlib.styles import Styles

setup(width=105, height=88)

chart = RadarChart(
    categories=["Scalability", "Reliability", "Security", "Maintainability", "Latency"],
    axis_line_style=Styles.MutedDashed,
    axis_text_style=Styles.BlackBold.patch(text_size=10.5),
    grid_style=Styles.MutedThin,
    value_text_style=Styles.BlackBold.patch(text_size=10.0),
    radius=24.0,
    min_value=0.0,
    max_value=100.0,
    levels=5,
    grid_shape="polygon",
    title="System Architecture Trade-off Analysis",
    title_style=Styles.BlackBold.patch(text_size=13.0),
    value_format="{:.0f}",
)
chart.add_series("Microservices", [95, 80, 75, 85, 60], style=Styles.PrimaryFlat, fill_alpha=0.3)
chart.add_series(
    "Monolith",
    [60, 90, 85, 70, 95],
    style=Styles.SecondaryNeutral,
    fill_alpha=0.3,
    line_style="dashed",
    point_shape="square",
)

chart.draw(xy=(8.0, 8.0))
chart.draw_legend(xy=(72.0, 55.0), text_style=Styles.Black.patch(text_size=10.5))
save()
radar_3
System Architecture Non-Functional Analysis

3. Circular Grid Product Benchmark

Using grid_shape="circle" with scale_text_style and configure_axis(...) renders smooth concentric rings with numeric scale labels:

from drawlib.canvas import save, setup
from drawlib.charts.radar import RadarChart
from drawlib.styles import Styles

setup(width=100, height=85)

chart = RadarChart(
    categories=["UX Design", "Performance", "Battery Life", "Camera Quality", "Ecosystem", "Price"],
    axis_line_style=Styles.MutedDashed,
    axis_text_style=Styles.BlackBold.patch(text_size=10.5),
    grid_style=Styles.MutedThin,
    scale_text_style=Styles.Muted.patch(text_size=10.0),
    radius=24.0,
    levels=4,
    grid_shape="circle",
    title="Flagship Smartphone Benchmark",
    title_style=Styles.BlackBold.patch(text_size=13.0),
)
chart.configure_axis(min_value=0.0, max_value=10.0, levels=4, scale_format="{:.1f}")
chart.add_series("Device Pro Max", [9.2, 9.5, 8.8, 9.6, 9.0, 6.5], style=Styles.PrimaryFlat)
chart.add_series("Device Ultra", [8.5, 9.2, 9.4, 9.2, 8.2, 7.8], style=Styles.SecondaryNeutral, line_style="dashed")

chart.draw(xy=(13.0, 15.0))
chart.draw_legend(xy=(22.0, 10.0), text_style=Styles.Black.patch(text_size=10.5), orientation="horizontal")
save()
radar_4
Device Feature Matrix with Circular Contours

4. API Reference

Constructor (RadarChart)

All constructor arguments are keyword-only (*):

from drawlib.charts.radar import DrawDirection, FormatterType, GridShape, RadarChart, Series

chart = RadarChart(
    *,
    categories: list[str],
    axis_line_style: Style,
    radius: float = 25.0,
    min_value: float = 0.0,
    max_value: float | None = None,
    levels: int = 5,
    grid_shape: GridShape = "polygon",
    axis_text_style: Style | None = None,
    grid_style: Style | None = None,
    scale_text_style: Style | None = None,
    scale_format: FormatterType = None,
    value_text_style: Style | None = None,
    value_format: FormatterType = None,
    background_style: Style | None = None,
    title: str = "",
    title_style: Style | None = None,
    width: float | None = None,
    height: float | None = None,
)
Parameter Type Default Description
categories list[str] Required Radial dimension names around the perimeter (minimum 3 required; raises ValueError if < 3).
axis_line_style Style Required Mandatory style anchor for radial spoke lines.
radius float 25.0 Outer radius of the radar web in canvas units.
min_value float 0.0 Baseline data value at the central origin point.
max_value float | None None Outer boundary scale value. If None, computed automatically from series values.
levels int 5 Number of concentric grid rings (minimum 1).
grid_shape "polygon" | "circle" "polygon" Concentric grid contour geometry (GridShape).
axis_text_style Style | None None Style for outer perimeter category labels. If None, category labels are omitted.
grid_style Style | None None Style for concentric grid rings. If None, grid rings are omitted.
scale_text_style Style | None None Style for numeric scale labels along the top spoke. If None, scale labels are omitted.
scale_format FormatterType None Format string or Callable[[float], str] for concentric ring scale labels.
value_text_style Style | None None Style for numeric labels drawn next to series vertices. If None, vertex values are omitted.
value_format FormatterType None Format string or Callable[[float], str] for series vertex values.
background_style Style | None None Style for the outer chart background card. If None, card is omitted.
title str "" Chart title text rendered at the top of the container.
title_style Style | None None Typography style for title. Required for the title to render.
width / height float | None None Explicit container dimensions. If None, computed automatically from radius and title.

Methods & Properties

  • configure_axis(*, min_value: float | None = None, max_value: float | None = None, levels: int | None = None, scale_format: FormatterType = None) -> RadarChart: Updates radial scale bounds, concentric contour count, and scale label formatting in place (returns self for method chaining).
  • add_series(name: str, values: list[float], style: Style, fill_alpha: float = 0.25, line_width: float = 2.0, line_style: LineStyle = "solid", point_shape: PointShape = "circle", point_size: float = 0.8, legend_text_style: Style | None = None, *, show: bool = True, draw_ratio: float = 1.0, draw_direction: DrawDirection = "bottom_to_top") -> Series: Registers a closed radar polygon series and returns the mutable Series instance.
    • fill_alpha: Transparency of the polygon fill (0.0 to 1.0; defaults to 0.25).
    • line_width / line_style: Perimeter stroke width (2.0) and pattern ("solid", "dashed", "dotted", "dashdot").
    • point_shape / point_size: Vertex marker shape ("circle", "square", "none") and radius (0.8).
    • draw_ratio & draw_direction: Partial spatial rendering ("bottom_to_top" expands all vertices radially outward from the center; "left_to_right" sweeps spoke-by-spoke around the perimeter).
  • get_size() -> tuple[float, float]: Returns the computed (width, height) bounding box of the chart container.
  • draw(xy: tuple[float, float] = (0.0, 0.0), *, radius: float | None = None, width: float | None = None, height: float | None = None, align: Literal["bottom_left", "center"] = "bottom_left", scale: float = 1.0) -> None: Renders the chart anchored at xy ("bottom_left" by default or "center"), with optional temporary radius/width/height overrides and uniform scale.
  • draw_legend(xy: tuple[float, float], text_style: Style, orientation: Literal["vertical", "horizontal"] = "vertical", swatch_width: float = 2.4, swatch_height: float = 1.2, item_gap: float = 4.0, *, scale: float = 1.0) -> None: Renders the decoupled series legend at xy.
  • Properties:
    • chart.categories -> list[str]: Returns a copy of the radial dimension names.
    • chart.series -> list[Series]: Returns a copy of registered Series objects.