Skip to content

Commit

Permalink
Merge pull request #43 from tacaswell/enh_patch
Browse files Browse the repository at this point in the history
ENH: add a basic Patch artist
  • Loading branch information
ksunden authored Aug 16, 2024
2 parents f68736b + f7d140d commit 7021e10
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 1 deletion.
10 changes: 10 additions & 0 deletions data_prototype/artist.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from bisect import insort
from typing import Sequence
from contextlib import contextmanager

import numpy as np

Expand Down Expand Up @@ -316,3 +317,12 @@ def set_xlim(self, min_=None, max_=None):

def set_ylim(self, min_=None, max_=None):
self.axes.set_ylim(min_, max_)


@contextmanager
def _renderer_group(renderer, group, gid):
renderer.open_group(group, gid)
try:
yield
finally:
renderer.close_group(group)
107 changes: 106 additions & 1 deletion data_prototype/patches.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,114 @@
from matplotlib.patches import Patch as _Patch, Rectangle as _Rectangle
import matplotlib.path as mpath
import matplotlib.transforms as mtransforms
import matplotlib.colors as mcolors
import numpy as np


from .wrappers import ProxyWrapper, _stale_wrapper

from .containers import DataContainer

from matplotlib.patches import Patch as _Patch, Rectangle as _Rectangle
from .artist import Artist, _renderer_group
from .description import Desc
from .conversion_edge import Graph, CoordinateEdge, DefaultEdge


class Patch(Artist):
def __init__(self, container, edges=None, **kwargs):
super().__init__(container, edges, **kwargs)

scalar = Desc((), "display") # ... this needs thinking...
edges = [
CoordinateEdge.from_coords("xycoords", {"x": "auto", "y": "auto"}, "data"),
CoordinateEdge.from_coords("codes", {"codes": "auto"}, "display"),
CoordinateEdge.from_coords("facecolor", {"color": Desc(())}, "display"),
CoordinateEdge.from_coords("edgecolor", {"color": Desc(())}, "display"),
CoordinateEdge.from_coords("linewidth", {"linewidth": Desc(())}, "display"),
CoordinateEdge.from_coords("hatch", {"hatch": Desc(())}, "display"),
CoordinateEdge.from_coords("alpha", {"alpha": Desc(())}, "display"),
DefaultEdge.from_default_value("facecolor_def", "facecolor", scalar, "C0"),
DefaultEdge.from_default_value("edgecolor_def", "edgecolor", scalar, "C0"),
DefaultEdge.from_default_value("linewidth_def", "linewidth", scalar, 1),
DefaultEdge.from_default_value("linestyle_def", "linestyle", scalar, "-"),
DefaultEdge.from_default_value("alpha_def", "alpha", scalar, 1),
DefaultEdge.from_default_value("hatch_def", "hatch", scalar, None),
]
self._graph = self._graph + Graph(edges)

def draw(self, renderer, graph: Graph) -> None:
if not self.get_visible():
return
g = graph + self._graph
desc = Desc(("N",), "display")
scalar = Desc((), "display") # ... this needs thinking...

require = {
"x": desc,
"y": desc,
"codes": desc,
"facecolor": scalar,
"edgecolor": scalar,
"linewidth": scalar,
"linestyle": scalar,
"hatch": scalar,
"alpha": scalar,
}

# copy from line
conv = g.evaluator(self._container.describe(), require)
query, _ = self._container.query(g)
evald = conv.evaluate(query)

clip_conv = g.evaluator(
self._clip_box.describe(),
{"x": Desc(("N",), "display"), "y": Desc(("N",), "display")},
)
clip_query, _ = self._clip_box.query(g)
clipx, clipy = clip_conv.evaluate(clip_query).values()
# copy from line

path = mpath.Path._fast_from_codes_and_verts(
verts=np.vstack([evald["x"], evald["y"]]).T, codes=evald["codes"]
)

with _renderer_group(renderer, "patch", None):
gc = renderer.new_gc()

gc.set_foreground(evald["facecolor"], isRGBA=False)
gc.set_clip_rectangle(
mtransforms.Bbox.from_extents(clipx[0], clipy[0], clipx[1], clipy[1])
)
gc.set_linewidth(evald["linewidth"])
# gc.set_dashes(*self._dash_pattern)
# gc.set_capstyle(self._capstyle)
# gc.set_joinstyle(self._joinstyle)

# gc.set_antialiased(self._antialiased)

# gc.set_url(self._url)
# gc.set_snap(self.get_snap())

gc.set_alpha(evald["alpha"])

if evald["hatch"] is not None:
gc.set_hatch(evald["hatch"])
gc.set_hatch_color(evald["hatch_color"])

# if self.get_sketch_params() is not None:
# gc.set_sketch_params(*self.get_sketch_params())

# if self.get_path_effects():
# from matplotlib.patheffects import PathEffectRenderer
# renderer = PathEffectRenderer(self.get_path_effects(), renderer)

renderer.draw_path(
gc,
path,
mtransforms.IdentityTransform(),
mcolors.to_rgba(evald["facecolor"]),
)
gc.restore()


class PatchWrapper(ProxyWrapper):
Expand Down
34 changes: 34 additions & 0 deletions examples/new_patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
======
Circle
======
Example of directly creating a Patch artist that is defined by a
x, y, and path codes.
"""

import matplotlib.pyplot as plt


from data_prototype.artist import CompatibilityAxes
from data_prototype.patches import Patch
from data_prototype.containers import ArrayContainer

from matplotlib.path import Path

c = Path.unit_circle()

sc = ArrayContainer(None, x=c.vertices[:, 0], y=c.vertices[:, 1], codes=c.codes)
lw2 = Patch(sc, linewidth=3, linestyle=":", edgecolor="C5", alpha=1, hatch=None)

fig, nax = plt.subplots()
nax.set_aspect("equal")
ax = CompatibilityAxes(nax)
nax.add_artist(ax)
ax.add_artist(lw2, 2)
ax.set_xlim(-1.1, 1.1)
ax.set_ylim(-1.1, 1.1)

plt.show()
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ exclude = '''
| tests/data
)/
'''

[tool.setuptools_scm]

0 comments on commit 7021e10

Please sign in to comment.