diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..16fb93f
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,36 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.1.0] - 2025-08-14
+
+### Added
+- **Lines function**: New functionality for drawing lines on basketball courts
+ - Support for basic lines, comet lines (increasing width), and transparent lines (increasing opacity)
+ - Colormap integration for lines
+ - Coordinate transformation support for all court orientations
+ - Basketball-specific applications (pass visualization, player movement tracking)
+ - Comprehensive error handling and validation
+ - Performance optimized using matplotlib's LineCollection
+- **Documentation**: Complete usage guide and examples in README.md
+- **Testing**: 12 comprehensive test cases covering all functionality
+- **Examples**: New example scripts demonstrating Lines usage
+
+### Technical Details
+- New module: `mplbasketball.lines`
+- New function: `Lines()` with extensive parameter support
+- Integration with existing coordinate transformation system
+- Compatible with all court types (NBA, WNBA, NCAA, FIBA)
+
+## [1.0.0] - Initial Release - 2024-09-07
+
+### Added
+- Court class for 2D basketball court visualization
+- Court3D class for 3D basketball court visualization
+- Support for multiple court types (NBA, WNBA, NCAA, FIBA)
+- Coordinate transformation utilities
+- Integration with matplotlib functions
+- Comprehensive documentation and examples
diff --git a/README.md b/README.md
index c79227f..ccc236e 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
**A Python plotting library to visualize basketball data, created by the Sport Performance Lab (SPL) at Maple Leaf Sports & Entertainment (MLSE), Toronto, Canada.**
+[](https://badge.fury.io/py/mplbasketball)
+
[Sport Performance Lab (SPL)](https://www.mlsedigital.com/innovation-initiatives/sport-performance-lab) is a Research and Development group that works across all MLSE teams on strategic research initiatives in sports analytics and player performance.
# Quick start
@@ -54,6 +56,7 @@ Currently, you can use `mplbasketball` to
4. [FIBA](https://nz.basketball/wp-content/uploads/2020/02/FIBA-Basketball-Court-Dimensions.pdf)
2. View data in different orientations orientation (horizontal, vertical, and also normalized to left/right/up/down). The [`utils.transform`](./mplbasketball/utils.py) function makes going between orientations extremely easy and seamless.
3. Easily interface with existing `matplotlib` functions.
+4. Draw lines with various effects (comet, transparent, colormap) using the `Lines` function
# Before you begin
@@ -253,6 +256,29 @@ ax.imshow(heatmap_2.T, extent=extent_2, origin='lower', cmap='cividis', zorder=-
+## The Lines function
+
+The `Lines` function provides a way to draw lines on basketball courts with various visual effects. It's particularly useful for visualizing passes, player movements, and other line-based data.
+
+### Basic usage
+
+```python
+from mplbasketball import Court, Lines
+
+court = Court(court_type="nba", origin="center")
+fig, ax = court.draw(orientation="h")
+
+# Draw a simple line
+Lines(0, 0, 20, 20, color="red", linewidth=3, ax=ax)
+
+# Draw multiple lines
+Lines([-20, 20], [10, -10], [20, -20], [-10, 10],
+ color=["red", "blue"], linewidth=3, ax=ax)
+```
+
+
+
+
# Documentation
Full documentation coming soon. In the meantime, check out the examples in this README, as well as some of our [examples](https://github.com/mlsedigital/mplbasketball/tree/main/examples)!
diff --git a/examples/plot_lines.py b/examples/plot_lines.py
new file mode 100644
index 0000000..c715401
--- /dev/null
+++ b/examples/plot_lines.py
@@ -0,0 +1,93 @@
+"""
+Example demonstrating the lines functionality in mplbasketball.
+
+This example shows how to use the Lines function to draw various types of lines
+on basketball courts, including basic lines, comet lines, transparent lines,
+and lines with colormaps. This version simulates a high-intensity basketball game
+with many passes and movements.
+"""
+
+import matplotlib.pyplot as plt
+import numpy as np
+
+from mplbasketball import Court, Lines
+
+
+def generate_pass_data(n_passes=200):
+ """Generate realistic basketball pass data."""
+ passes = []
+
+ # Define court boundaries (NBA court in feet)
+ court_width = 94
+ court_height = 50
+
+ for _ in range(n_passes):
+ # Only assist passes
+ # Short to medium passes
+ x1 = np.random.uniform(-40, 40)
+ y1 = np.random.uniform(-20, 20)
+ x2 = x1 + np.random.uniform(-15, 15)
+ y2 = y1 + np.random.uniform(-15, 15)
+
+ # Ensure coordinates are within court bounds
+ x1 = np.clip(x1, -47, 47)
+ y1 = np.clip(y1, -25, 25)
+ x2 = np.clip(x2, -47, 47)
+ y2 = np.clip(y2, -25, 25)
+
+ passes.append((x1, y1, x2, y2, 'completed'))
+
+ return passes
+
+
+def main():
+ # Create a court
+ court = Court(court_type="nba", origin="center")
+ fig, ax = court.draw(orientation="h", showaxis=False)
+
+ # Generate pass data
+ print("Generating pass data...")
+ passes = generate_pass_data(n_passes=300)
+
+ # Define styles for completed passes
+ completed_style = {
+ 'color': 'gold',
+ 'linewidth': 2,
+ 'comet': True,
+ 'transparent': False
+ }
+
+ # Draw passes
+ print("Drawing passes...")
+ for x_start, y_start, x_end, y_end, pass_type in passes:
+ style = completed_style.copy()
+
+ # Add some randomness to make it look more realistic
+ x_start += np.random.normal(0, 1)
+ y_start += np.random.normal(0, 1)
+ x_end += np.random.normal(0, 1)
+ y_end += np.random.normal(0, 1)
+
+ # Draw the pass
+ Lines(x_start, y_start, x_end, y_end, ax=ax, **style)
+
+
+
+ # Add title and adjust layout
+ plt.title("Basketball Game Simulation - Completed Passes\nUsing mplbasketball Lines Function",
+ fontsize=14, fontweight='bold', color='white')
+
+ # Add legend
+ legend_elements = [
+ plt.Line2D([0], [0], color='gold', linewidth=3, label='Completed Passes')
+ ]
+
+ ax.legend(handles=legend_elements, loc='upper right',
+ bbox_to_anchor=(1.15, 1), fontsize=10)
+
+ plt.tight_layout()
+ plt.show()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/figs/completed_passes.png b/figs/completed_passes.png
new file mode 100644
index 0000000..5285f95
Binary files /dev/null and b/figs/completed_passes.png differ
diff --git a/mplbasketball/__init__.py b/mplbasketball/__init__.py
index 0da6075..a89da8d 100644
--- a/mplbasketball/__init__.py
+++ b/mplbasketball/__init__.py
@@ -1,4 +1,5 @@
from .court import Court
from .court3d import Court3D
+from .lines import Lines
-__all__ = ["Court", "Court3D"]
+__all__ = ["Court", "Court3D", "Lines"]
diff --git a/mplbasketball/lines.py b/mplbasketball/lines.py
new file mode 100644
index 0000000..9145dd5
--- /dev/null
+++ b/mplbasketball/lines.py
@@ -0,0 +1,278 @@
+"""A module with functions for using LineCollection to create lines on basketball courts."""
+
+import warnings
+from typing import Literal
+
+import numpy as np
+from matplotlib import colormaps, rcParams
+from matplotlib.collections import LineCollection
+from matplotlib.colors import to_rgba_array
+from matplotlib.legend import Legend
+from matplotlib.legend_handler import HandlerLineCollection
+
+from mplbasketball.utils import transform
+
+__all__ = ['Lines']
+
+
+def Lines(xstart, ystart, xend, yend, color=None, n_segments=100, comet=False, transparent=False,
+ alpha_start=0.01, alpha_end=1, cmap=None, ax=None,
+ fr: Literal["h", "hl", "hr", "v", "vu", "vd"] = "h",
+ to: Literal["h", "hl", "hr", "v", "vu", "vd"] = "h",
+ origin: Literal["center", "top-left", "bottom-left", "top-right", "bottom-right"] = "center",
+ court_type: Literal["nba", "wnba", "ncaa", "fiba"] = "nba",
+ reverse_cmap=False, **kwargs):
+ """Plots Lines using matplotlib.collections.LineCollection on basketball courts.
+
+ This is a fast way to plot multiple lines without loops.
+ Also enables lines that increase in width or opacity by splitting
+ the line into n_segments of increasing width or opacity as the line progresses.
+
+ Parameters
+ ----------
+ xstart, ystart, xend, yend: array-like or scalar
+ Commonly, these parameters are 1D arrays.
+ These should be the start and end coordinates of the lines.
+ color : matplotlib color or sequence of colors, optional
+ Defaults to None. In that case the marker color is determined
+ by the value rcParams['lines.color']
+ n_segments : int, default 100
+ If comet=True or transparent=True this is used to split the line
+ into n_segments of increasing width/opacity.
+ comet : bool, default False
+ Whether to plot the lines increasing in width.
+ transparent : bool, default False
+ Whether to plot the lines increasing in opacity.
+ alpha_start: float, default 0.01
+ The starting alpha value for transparent lines, between 0 (transparent) and 1 (opaque).
+ If transparent = True the line will be drawn to
+ linearly increase in opacity between alpha_start and alpha_end.
+ alpha_end : float, default 1
+ The ending alpha value for transparent lines, between 0 (transparent) and 1 (opaque).
+ If transparent = True the line will be drawn to
+ linearly increase in opacity between alpha_start and alpha_end.
+ cmap : str, optional
+ A matplotlib cmap (colormap) name
+ ax : matplotlib.axes.Axes, optional
+ The axis to plot on.
+ fr : str, default 'h'
+ Current orientation of the data points. Can be one of 'h', 'hl', 'hr', 'v', 'vu', 'vd'.
+ to : str, default 'h'
+ Desired orientation of the data points. Can be one of 'h', 'hl', 'hr', 'v', 'vu', 'vd'.
+ origin : str, default 'center'
+ Origin of the court. Can be one of 'center', 'top-left', 'bottom-left', 'top-right', 'bottom-right'.
+ court_type : str, default 'nba'
+ Court type. Can be one of 'nba', 'wnba', 'ncaa', 'fiba'.
+ reverse_cmap : bool, default False
+ Whether to reverse the cmap colors.
+ If the court is horizontal and the y-axis is inverted then set this to True.
+ **kwargs :
+ All other keyword arguments are passed on to matplotlib.collections.LineCollection.
+
+ Returns
+ -------
+ LineCollection : matplotlib.collections.LineCollection
+
+ Examples
+ --------
+ >>> from mplbasketball import Court
+ >>> court = Court()
+ >>> fig, ax = court.draw()
+ >>> from mplbasketball.lines import Lines
+ >>> Lines(20, 20, 45, 80, comet=True, transparent=True, ax=ax)
+
+ >>> import matplotlib.pyplot as plt
+ >>> fig, ax = plt.subplots()
+ >>> Lines([0.1, 0.4], [0.1, 0.5], [0.9, 0.4], [0.8, 0.8], ax=ax)
+ """
+ if not isinstance(comet, bool):
+ raise TypeError("Invalid argument: comet should be bool (True or False).")
+ if not isinstance(transparent, bool):
+ raise TypeError("Invalid argument: transparent should be bool (True or False).")
+
+ if alpha_start < 0 or alpha_start > 1:
+ raise TypeError("alpha_start values should be within 0-1 range")
+ if alpha_end < 0 or alpha_end > 1:
+ raise TypeError("alpha_end values should be within 0-1 range")
+ if alpha_start > alpha_end:
+ msg = "Alpha start > alpha end. The line will increase in transparency nearer to the end"
+ warnings.warn(msg)
+
+ if 'colors' in kwargs:
+ warnings.warn("Lines method takes 'color' as an argument, 'colors' is ignored")
+ if color is not None and cmap is not None:
+ raise ValueError("Only use one of color or cmap arguments not both.")
+ if 'lw' in kwargs and 'linewidth' in kwargs:
+ raise TypeError("Lines got multiple values for 'linewidth' argument (linewidth and lw).")
+
+ # Handle linewidth parameter
+ if 'lw' in kwargs:
+ lw = kwargs.pop('lw', 5)
+ elif 'linewidth' in kwargs:
+ lw = kwargs.pop('linewidth', 5)
+ else:
+ lw = 5
+
+ # Convert inputs to numpy arrays
+ xstart = np.ravel(xstart)
+ ystart = np.ravel(ystart)
+ xend = np.ravel(xend)
+ yend = np.ravel(yend)
+ lw = np.ravel(lw)
+
+ # Validate array sizes
+ if (comet or transparent) and lw.size > 1:
+ msg = "Multiple linewidths with a comet or transparent Line is not implemented."
+ raise NotImplementedError(msg)
+
+ if color is None and cmap is None:
+ color = rcParams['lines.color']
+
+ if (comet or transparent) and cmap is None and to_rgba_array(color).shape[0] > 1:
+ msg = "Multiple colors with a comet or transparent Line is not implemented."
+ raise NotImplementedError(msg)
+
+ if xstart.size != ystart.size:
+ raise ValueError("xstart and ystart must be the same size")
+ if xstart.size != xend.size:
+ raise ValueError("xstart and xend must be the same size")
+ if ystart.size != yend.size:
+ raise ValueError("ystart and yend must be the same size")
+ if lw.size > 1 and lw.size != xstart.size:
+ raise ValueError("lw and xstart must be the same size")
+
+ if lw.size == 1:
+ lw = lw[0]
+
+ # Transform coordinates if needed
+ if fr != to:
+ xstart, ystart = transform(xstart, ystart, fr, to, origin, court_type)
+ xend, yend = transform(xend, yend, fr, to, origin, court_type)
+
+ # Handle comet effect
+ if comet:
+ lw = np.linspace(1, lw, n_segments)
+ handler_first_lw = False
+ else:
+ handler_first_lw = True
+
+ multi_segment = transparent is not False or comet is not False or cmap is not None
+
+ # Handle transparent effect
+ if transparent:
+ cmap = _create_transparent_cmap(color, cmap, n_segments, alpha_start, alpha_end)
+
+ if isinstance(cmap, str):
+ cmap = colormaps.get_cmap(cmap)
+
+ if cmap is not None:
+ handler_cmap = True
+ line_collection = _lines_cmap(xstart, ystart, xend, yend, lw=lw, cmap=cmap, ax=ax,
+ n_segments=n_segments, multi_segment=multi_segment,
+ reverse_cmap=reverse_cmap, **kwargs)
+ else:
+ handler_cmap = False
+ line_collection = _lines_no_cmap(xstart, ystart, xend, yend, lw=lw, color=color,
+ ax=ax, n_segments=n_segments,
+ multi_segment=multi_segment, **kwargs)
+
+ line_collection_handler = HandlerLines(numpoints=n_segments, invert_y=reverse_cmap,
+ first_lw=handler_first_lw, use_cmap=handler_cmap)
+
+ Legend.update_default_handler_map({LineCollection: line_collection_handler})
+ return line_collection
+
+
+def _create_transparent_cmap(color, cmap, n_segments, alpha_start, alpha_end):
+ """Create a transparent colormap for lines."""
+ if cmap is None:
+ color_rgba = to_rgba_array(color)[0]
+ colors = np.array([color_rgba] * n_segments)
+ colors[:, 3] = np.linspace(alpha_start, alpha_end, n_segments)
+ from matplotlib.colors import ListedColormap
+ return ListedColormap(colors)
+ else:
+ # If cmap is provided, create transparent version
+ if isinstance(cmap, str):
+ cmap = colormaps.get_cmap(cmap)
+ colors = cmap(np.linspace(0, 1, n_segments))
+ colors[:, 3] = np.linspace(alpha_start, alpha_end, n_segments)
+ from matplotlib.colors import ListedColormap
+ return ListedColormap(colors)
+
+
+def _create_segments(xstart, ystart, xend, yend, n_segments=100, multi_segment=False):
+ """Create line segments for LineCollection."""
+ if multi_segment:
+ x = np.linspace(xstart, xend, n_segments + 1)
+ y = np.linspace(ystart, yend, n_segments + 1)
+ points = np.array([x, y]).T
+ points = np.concatenate([points, np.expand_dims(points[:, -1, :], 1)], axis=1)
+ points = np.expand_dims(points, 1)
+ segments = np.concatenate([points[:, :, :-2, :],
+ points[:, :, 1:-1, :],
+ points[:, :, 2:, :]], axis=1)
+ segments = np.transpose(segments, (0, 2, 1, 3)).reshape((-1, 3, 2))
+ else:
+ segments = np.transpose(np.array([[xstart, ystart], [xend, yend]]), (2, 0, 1))
+ return segments
+
+
+def _lines_no_cmap(xstart, ystart, xend, yend, lw=None, color=None, ax=None,
+ n_segments=100, multi_segment=False, **kwargs):
+ """Create lines without colormap."""
+ segments = _create_segments(xstart, ystart, xend, yend,
+ n_segments=n_segments, multi_segment=multi_segment)
+ color = to_rgba_array(color)
+ if (color.shape[0] > 1) and (color.shape[0] != xstart.size):
+ raise ValueError("xstart and color must be the same size")
+ line_collection = LineCollection(segments, color=color, linewidth=lw, snap=False, **kwargs)
+ line_collection = ax.add_collection(line_collection)
+ return line_collection
+
+
+def _lines_cmap(xstart, ystart, xend, yend, lw=None, cmap=None, ax=None,
+ n_segments=100, multi_segment=False, reverse_cmap=False, **kwargs):
+ """Create lines with colormap."""
+ segments = _create_segments(xstart, ystart, xend, yend,
+ n_segments=n_segments, multi_segment=multi_segment)
+ if reverse_cmap:
+ cmap = cmap.reversed()
+ line_collection = LineCollection(segments, cmap=cmap, linewidth=lw, snap=False, **kwargs)
+ line_collection = ax.add_collection(line_collection)
+ extent = ax.get_ylim()
+ court_array = np.linspace(extent[0], extent[1], n_segments)
+ line_collection.set_array(court_array)
+ return line_collection
+
+
+class HandlerLines(HandlerLineCollection):
+ """Automatically generated by Lines() to allow use of linecollection in legend."""
+
+ def __init__(self, invert_y=False, first_lw=False, use_cmap=False,
+ marker_pad=0.3, numpoints=None, **kw):
+ HandlerLineCollection.__init__(self, marker_pad=marker_pad, numpoints=numpoints, **kw)
+ self.invert_y = invert_y
+ self.first_lw = first_lw
+ self.use_cmap = use_cmap
+
+ def create_artists(self, legend, artist, xdescent, ydescent,
+ width, height, fontsize, trans):
+ x = np.linspace(0, width, self.get_numpoints(legend) + 1)
+ y = np.zeros(self.get_numpoints(legend) + 1) + height / 2. - ydescent
+ points = np.array([x, y]).T.reshape(-1, 1, 2)
+ segments = np.concatenate([points[:-1], points[1:]], axis=1)
+ lw = artist.get_linewidth()
+ if self.first_lw:
+ lw = lw[0]
+ if self.use_cmap:
+ cmap = artist.cmap
+ if self.invert_y:
+ cmap = cmap.reversed()
+ line_collection = LineCollection(segments, lw=lw, cmap=cmap,
+ snap=False, transform=trans)
+ line_collection.set_array(x)
+ else:
+ line_collection = LineCollection(segments, lw=lw, colors=artist.get_colors()[0],
+ snap=False, transform=trans)
+ return [line_collection]
diff --git a/pyproject.toml b/pyproject.toml
index 3e1a4d2..ff5313f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mplbasketball"
-version = "1.0.0"
+version = "1.1.0"
description = "A Python plotting library for visualization of basketball data."
authors = ["Sreekar Voleti "]
license = "MIT"
diff --git a/tests/test_lines.py b/tests/test_lines.py
new file mode 100644
index 0000000..f86c036
--- /dev/null
+++ b/tests/test_lines.py
@@ -0,0 +1,123 @@
+"""Tests for the lines functionality in mplbasketball."""
+
+import numpy as np
+import pytest
+from matplotlib.collections import LineCollection
+
+from mplbasketball import Court, Lines
+
+
+class TestLines:
+ """Test class for lines functionality."""
+
+ def setup_method(self):
+ """Set up test fixtures."""
+ self.court = Court(court_type="nba", origin="center")
+ self.fig, self.ax = self.court.draw(orientation="h")
+
+ def test_basic_lines(self):
+ """Test basic line drawing."""
+ xstart, ystart = [0, 10], [0, 10]
+ xend, yend = [20, 30], [20, 30]
+
+ line_collection = Lines(xstart, ystart, xend, yend,
+ color="red", ax=self.ax)
+
+ assert isinstance(line_collection, LineCollection)
+ assert len(line_collection.get_paths()) == 2
+
+ def test_comet_lines(self):
+ """Test comet line drawing."""
+ line_collection = Lines(0, 0, 20, 20, comet=True,
+ color="blue", linewidth=5, ax=self.ax)
+
+ assert isinstance(line_collection, LineCollection)
+ # Comet lines should have multiple segments
+ assert len(line_collection.get_paths()) > 1
+
+ def test_transparent_lines(self):
+ """Test transparent line drawing."""
+ line_collection = Lines(0, 0, 20, 20, transparent=True,
+ color="green", alpha_start=0.1, alpha_end=1.0,
+ ax=self.ax)
+
+ assert isinstance(line_collection, LineCollection)
+ # Transparent lines should have multiple segments
+ assert len(line_collection.get_paths()) > 1
+
+ def test_colormap_lines(self):
+ """Test lines with colormap."""
+ line_collection = Lines([0, 10], [0, 10], [20, 30], [20, 30],
+ cmap="viridis", ax=self.ax)
+
+ assert isinstance(line_collection, LineCollection)
+ assert line_collection.get_cmap() is not None
+
+ def test_coordinate_transformation(self):
+ """Test coordinate transformation in lines."""
+ # Test transformation from half-court left to full court
+ line_collection = Lines([-10, -15], [5, -5], [10, 15], [-5, 5],
+ fr="hl", to="h", color="purple", ax=self.ax)
+
+ assert isinstance(line_collection, LineCollection)
+
+ def test_invalid_parameters(self):
+ """Test error handling for invalid parameters."""
+ # Test invalid comet parameter
+ with pytest.raises(TypeError):
+ Lines(0, 0, 20, 20, comet="invalid", ax=self.ax)
+
+ # Test invalid transparent parameter
+ with pytest.raises(TypeError):
+ Lines(0, 0, 20, 20, transparent="invalid", ax=self.ax)
+
+ # Test invalid alpha values
+ with pytest.raises(TypeError):
+ Lines(0, 0, 20, 20, transparent=True, alpha_start=-1, ax=self.ax)
+
+ with pytest.raises(TypeError):
+ Lines(0, 0, 20, 20, transparent=True, alpha_end=2, ax=self.ax)
+
+ def test_array_size_validation(self):
+ """Test validation of array sizes."""
+ # Test mismatched array sizes
+ with pytest.raises(ValueError):
+ Lines([0, 10], [0], [20, 30], [20, 30], ax=self.ax)
+
+ with pytest.raises(ValueError):
+ Lines([0, 10], [0, 10], [20], [20, 30], ax=self.ax)
+
+ def test_multiple_colors_with_comet(self):
+ """Test that multiple colors with comet raises error."""
+ with pytest.raises(NotImplementedError):
+ Lines([0, 10], [0, 10], [20, 30], [20, 30],
+ color=["red", "blue"], comet=True, ax=self.ax)
+
+ def test_multiple_linewidths_with_comet(self):
+ """Test that multiple linewidths with comet raises error."""
+ with pytest.raises(NotImplementedError):
+ Lines([0, 10], [0, 10], [20, 30], [20, 30],
+ linewidth=[2, 3], comet=True, ax=self.ax)
+
+ def test_color_and_cmap_conflict(self):
+ """Test that color and cmap cannot be used together."""
+ with pytest.raises(ValueError):
+ Lines(0, 0, 20, 20, color="red", cmap="viridis", ax=self.ax)
+
+ def test_linewidth_aliases(self):
+ """Test that both 'linewidth' and 'lw' work."""
+ line_collection1 = Lines(0, 0, 20, 20, linewidth=3, ax=self.ax)
+ line_collection2 = Lines(0, 0, 20, 20, lw=3, ax=self.ax)
+
+ assert isinstance(line_collection1, LineCollection)
+ assert isinstance(line_collection2, LineCollection)
+
+ def test_scalar_inputs(self):
+ """Test that scalar inputs work correctly."""
+ line_collection = Lines(0, 0, 20, 20, color="red", ax=self.ax)
+ assert isinstance(line_collection, LineCollection)
+ assert len(line_collection.get_paths()) == 1
+
+
+if __name__ == "__main__":
+ pytest.main([__file__])