Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use converter functions in renderers #294

Merged
merged 2 commits into from Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 4 additions & 4 deletions lib/contourpy/util/bokeh_util.py
Expand Up @@ -3,7 +3,7 @@
from typing import TYPE_CHECKING, cast

from contourpy import FillType, LineType
from contourpy.util.mpl_util import mpl_codes_to_offsets
from contourpy.convert import _offsets_from_codes

if TYPE_CHECKING:
from contourpy._contourpy import (
Expand All @@ -25,7 +25,7 @@ def filled_to_bokeh(
if points is None:
continue
if have_codes:
offsets = mpl_codes_to_offsets(offsets)
offsets = _offsets_from_codes(offsets)
xs.append([]) # New outer with zero or more holes.
ys.append([])
for i in range(len(offsets)-1):
Expand All @@ -39,7 +39,7 @@ def filled_to_bokeh(
for j in range(len(outer_offsets)-1):
if fill_type == FillType.ChunkCombinedCodeOffset:
codes = codes_or_offsets[outer_offsets[j]:outer_offsets[j+1]]
offsets = mpl_codes_to_offsets(codes) + outer_offsets[j]
offsets = _offsets_from_codes(codes) + outer_offsets[j]
else:
offsets = codes_or_offsets[outer_offsets[j]:outer_offsets[j+1]+1]
xs.append([]) # New outer with zero or more holes.
Expand Down Expand Up @@ -78,7 +78,7 @@ def lines_to_bokeh(
if points is None:
continue
if line_type == LineType.ChunkCombinedCode:
offsets = mpl_codes_to_offsets(offsets)
offsets = _offsets_from_codes(offsets)

for i in range(len(offsets)-1):
line = points[offsets[i]:offsets[i+1]]
Expand Down
115 changes: 14 additions & 101 deletions lib/contourpy/util/mpl_renderer.py
Expand Up @@ -8,8 +8,9 @@
import numpy as np

from contourpy import FillType, LineType
from contourpy.convert import convert_fill_type, convert_line_type
from contourpy.enum_util import as_fill_type, as_line_type
from contourpy.util.mpl_util import filled_to_mpl_paths, lines_to_mpl_paths, mpl_codes_to_offsets
from contourpy.util.mpl_util import filled_to_mpl_paths, lines_to_mpl_paths
from contourpy.util.renderer import Renderer

if TYPE_CHECKING:
Expand Down Expand Up @@ -373,100 +374,6 @@ def _arrow(
))
ax.plot(arrow[:, 0], arrow[:, 1], "-", c=color, alpha=alpha)

def _filled_to_lists_of_points_and_offsets(
self,
filled: cpy.FillReturn,
fill_type: FillType,
) -> tuple[list[cpy.PointArray], list[cpy.OffsetArray]]:
if fill_type == FillType.OuterCode:
if TYPE_CHECKING:
filled = cast(cpy.FillReturn_OuterCode, filled)
all_points = filled[0]
all_offsets = [mpl_codes_to_offsets(codes) for codes in filled[1]]
elif fill_type == FillType.ChunkCombinedCode:
if TYPE_CHECKING:
filled = cast(cpy.FillReturn_ChunkCombinedCode, filled)
all_points = [points for points in filled[0] if points is not None]
all_offsets = [mpl_codes_to_offsets(codes) for codes in filled[1] if codes is not None]
elif fill_type == FillType.OuterOffset:
if TYPE_CHECKING:
filled = cast(cpy.FillReturn_OuterOffset, filled)
all_points = filled[0]
all_offsets = filled[1]
elif fill_type == FillType.ChunkCombinedOffset:
if TYPE_CHECKING:
filled = cast(cpy.FillReturn_ChunkCombinedOffset, filled)
all_points = [points for points in filled[0] if points is not None]
all_offsets = [offsets for offsets in filled[1] if offsets is not None]
elif fill_type == FillType.ChunkCombinedCodeOffset:
if TYPE_CHECKING:
filled = cast(cpy.FillReturn_ChunkCombinedCodeOffset, filled)
all_points = []
all_offsets = []
for points, codes, outer_offsets in zip(*filled):
if points is None:
continue
if TYPE_CHECKING:
assert codes is not None and outer_offsets is not None
all_points += np.split(points, outer_offsets[1:-1])
all_codes = np.split(codes, outer_offsets[1:-1])
all_offsets += [mpl_codes_to_offsets(codes) for codes in all_codes]
elif fill_type == FillType.ChunkCombinedOffsetOffset:
if TYPE_CHECKING:
filled = cast(cpy.FillReturn_ChunkCombinedOffsetOffset, filled)
all_points = []
all_offsets = []
for points, offsets, outer_offsets in zip(*filled):
if points is None:
continue
if TYPE_CHECKING:
assert offsets is not None and outer_offsets is not None
for i in range(len(outer_offsets)-1):
offs = offsets[outer_offsets[i]:outer_offsets[i+1]+1]
all_points.append(points[offs[0]:offs[-1]])
all_offsets.append(offs - offs[0])
else:
raise RuntimeError(f"Rendering FillType {fill_type} not implemented")

return all_points, all_offsets

def _lines_to_list_of_points(
self, lines: cpy.LineReturn, line_type: LineType,
) -> list[cpy.PointArray]:
if line_type == LineType.Separate:
if TYPE_CHECKING:
lines = cast(cpy.LineReturn_Separate, lines)
all_lines = lines
elif line_type == LineType.SeparateCode:
if TYPE_CHECKING:
lines = cast(cpy.LineReturn_SeparateCode, lines)
all_lines = lines[0]
elif line_type == LineType.ChunkCombinedCode:
if TYPE_CHECKING:
lines = cast(cpy.LineReturn_ChunkCombinedCode, lines)
all_lines = []
for points, codes in zip(*lines):
if points is not None:
if TYPE_CHECKING:
assert codes is not None
offsets = mpl_codes_to_offsets(codes)
for i in range(len(offsets)-1):
all_lines.append(points[offsets[i]:offsets[i+1]])
elif line_type == LineType.ChunkCombinedOffset:
if TYPE_CHECKING:
lines = cast(cpy.LineReturn_ChunkCombinedOffset, lines)
all_lines = []
for points, all_offsets in zip(*lines):
if points is not None:
if TYPE_CHECKING:
assert all_offsets is not None
for i in range(len(all_offsets)-1):
all_lines.append(points[all_offsets[i]:all_offsets[i+1]])
else:
raise RuntimeError(f"Rendering LineType {line_type} not implemented")

return all_lines

def filled(
self,
filled: cpy.FillReturn,
Expand All @@ -487,11 +394,13 @@ def filled(
return

ax = self._get_ax(ax)
all_points, all_offsets = self._filled_to_lists_of_points_and_offsets(filled, fill_type)
filled = convert_fill_type(filled, fill_type, FillType.ChunkCombinedOffset)

# Lines.
if line_color is not None:
for points, offsets in zip(all_points, all_offsets):
for points, offsets in zip(*filled):
if points is None:
continue
for start, end in zip(offsets[:-1], offsets[1:]):
xys = points[start:end]
ax.plot(xys[:, 0], xys[:, 1], c=line_color, alpha=line_alpha)
Expand All @@ -503,7 +412,9 @@ def filled(

# Points.
if point_color is not None:
for points, offsets in zip(all_points, all_offsets):
for points, offsets in zip(*filled):
if points is None:
continue
mask = np.ones(offsets[-1], dtype=bool)
mask[offsets[1:]-1] = False # Exclude end points.
if start_point_color is not None:
Expand Down Expand Up @@ -535,15 +446,17 @@ def lines(
return

ax = self._get_ax(ax)
all_lines = self._lines_to_list_of_points(lines, line_type)
separate_lines = convert_line_type(lines, line_type, LineType.Separate)
if TYPE_CHECKING:
separate_lines = cast(cpy.LineReturn_Separate, separate_lines)

if arrow_size > 0.0:
for line in all_lines:
for line in separate_lines:
for i in range(len(line)-1):
self._arrow(ax, line[i], line[i+1], color, alpha, arrow_size)

if point_color is not None:
for line in all_lines:
for line in separate_lines:
start_index = 0
end_index = len(line)
if start_point_color is not None:
Expand Down
22 changes: 4 additions & 18 deletions lib/contourpy/util/mpl_util.py
Expand Up @@ -6,18 +6,17 @@
import numpy as np

from contourpy import FillType, LineType
from contourpy.convert import _codes_from_offsets

if TYPE_CHECKING:
from contourpy._contourpy import (
CodeArray, FillReturn, LineReturn, LineReturn_Separate, OffsetArray,
)
from contourpy._contourpy import FillReturn, LineReturn, LineReturn_Separate


def filled_to_mpl_paths(filled: FillReturn, fill_type: FillType) -> list[mpath.Path]:
if fill_type in (FillType.OuterCode, FillType.ChunkCombinedCode):
paths = [mpath.Path(points, codes) for points, codes in zip(*filled) if points is not None]
elif fill_type in (FillType.OuterOffset, FillType.ChunkCombinedOffset):
paths = [mpath.Path(points, offsets_to_mpl_codes(offsets))
paths = [mpath.Path(points, _codes_from_offsets(offsets))
for points, offsets in zip(*filled) if points is not None]
elif fill_type == FillType.ChunkCombinedCodeOffset:
paths = []
Expand All @@ -35,7 +34,7 @@ def filled_to_mpl_paths(filled: FillReturn, fill_type: FillType) -> list[mpath.P
for i in range(len(outer_offsets)-1):
offs = offsets[outer_offsets[i]:outer_offsets[i+1]+1]
pts = points[offs[0]:offs[-1]]
paths += [mpath.Path(pts, offsets_to_mpl_codes(offs - offs[0]))]
paths += [mpath.Path(pts, _codes_from_offsets(offs - offs[0]))]
else:
raise RuntimeError(f"Conversion of FillType {fill_type} to MPL Paths is not implemented")
return paths
Expand Down Expand Up @@ -64,16 +63,3 @@ def lines_to_mpl_paths(lines: LineReturn, line_type: LineType) -> list[mpath.Pat
else:
raise RuntimeError(f"Conversion of LineType {line_type} to MPL Paths is not implemented")
return paths


def mpl_codes_to_offsets(codes: CodeArray) -> OffsetArray:
offsets = np.nonzero(codes == 1)[0].astype(np.uint32)
offsets = np.append(offsets, len(codes))
return offsets


def offsets_to_mpl_codes(offsets: OffsetArray) -> CodeArray:
codes = np.full(offsets[-1]-offsets[0], 2, dtype=np.uint8) # LINETO = 2
codes[offsets[:-1]] = 1 # MOVETO = 1
codes[offsets[1:]-1] = 79 # CLOSEPOLY 79
return codes