Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

fix tightbbox to account for markeredgewidth#16607

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
Show all changes
33 commits
Select commitHold shift + click to select a range
90dce86
fix tightbbox to account for markeredgewidth
brunobeltranFeb 29, 2020
5ed49fd
cleanup definition of "point" marker
brunobeltranMar 1, 2020
9088cf4
document unit_regular_polygon
brunobeltranMar 1, 2020
fd6df9b
untested version of new code to get marker bbox
brunobeltranMar 3, 2020
ef2fefd
fix for marker bbox now works except for on miter
brunobeltranMar 3, 2020
db033e4
fixed mis-ordered PathEndAngles for ticks
brunobeltranMar 3, 2020
7e41bf5
flake8 for new markers code
brunobeltranMar 3, 2020
f1014b5
factor marker bbox code to be within MarkerStyles
brunobeltranMar 3, 2020
42cc5db
bugfix, forgot self in MarkerStyle.get_centered_bbox
brunobeltranMar 3, 2020
8fcb223
misc bugfixes after factoring get_centered_bbox
brunobeltranMar 3, 2020
d95e4d8
markers bbox code visually tested, now works
brunobeltranMar 3, 2020
4592cda
flake8 for new markers bbox code
brunobeltranMar 3, 2020
c08826e
fixed formula for miter marker bbox, bevel broke
brunobeltranMar 4, 2020
7f9db16
bugfix caret bbox calculation, incorrect angles
brunobeltranMar 4, 2020
1805dc7
fixed star tip angle in marker bbox calculation
brunobeltranMar 4, 2020
a13598d
test marker bbox. failing here, pass in jupyter
brunobeltranMar 4, 2020
0f7300a
bugfix so markers bbox api stays in pts units
brunobeltranMar 4, 2020
d6f1571
forgot to push new test references images up
brunobeltranMar 4, 2020
2bee048
cleanup variable name consistency
brunobeltranMar 6, 2020
66694a0
use conversion not magic nums for line get_extents
brunobeltranMar 9, 2020
c4a45de
iter_curves: iterate over path more conveniently
brunobeltranMar 9, 2020
c5bdd8d
helper functions for bezier curve zeros/tangents
brunobeltranMar 10, 2020
41f268e
CornerInfo in bezier.py, should be path.py
brunobeltranMar 10, 2020
637a7f2
update marker bbox to work for arbitrary paths
brunobeltranMar 10, 2020
5ac9114
bugfix, new marker bbox code now runs, untested
brunobeltranMar 10, 2020
ef36ec2
pyflake fixes for marker bbox code
brunobeltranMar 10, 2020
82e3c12
cleanup path/bezier to prevent import triangle
brunobeltranMar 12, 2020
8585eca
fix prev commit, make split_in_out method of Path
brunobeltranMar 12, 2020
54e3a3e
generalized path bbox code tested on some markers
brunobeltranMar 12, 2020
b390653
path bbox now works for all markers but "pixel"
brunobeltranMar 12, 2020
85a3050
reorg'd path/bezier code now builds docs no errors
brunobeltranMar 12, 2020
79aa3b7
cleanup docstrings of stroked path bbox code
brunobeltranMar 12, 2020
1ea37be
fixed sphinx warnings in path.py's docstrings
brunobeltranMar 12, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 63 additions & 91 deletionslib/matplotlib/bezier.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -7,7 +7,6 @@
import numpy as np

import matplotlib.cbook as cbook
from matplotlib.path import Path


class NonIntersectingPathException(ValueError):
Expand DownExpand Up@@ -177,18 +176,74 @@ class BezierSegment:
"""

def __init__(self, control_points):
n = len(control_points)
self._orders = np.arange(n)
coeff = [math.factorial(n - 1)
// (math.factorial(i) * math.factorial(n - 1 - i))
for i in range(n)]
self._px = np.asarray(control_points).T * coeff
self.cpoints = np.asarray(control_points)
self.n, self.d = self.cpoints.shape
self._orders = np.arange(self.n)
coeff = [math.factorial(self.n - 1)
// (math.factorial(i) * math.factorial(self.n - 1 - i))
for i in range(self.n)]
self._px = self.cpoints.T * coeff

def point_at_t(self, t):
"""Return the point on the Bezier curve for parameter *t*."""
return tuple(
self._px @ (((1 - t) ** self._orders)[::-1] * t ** self._orders))

@property
def tan_in(self):
if self.n < 2:
raise ValueError("Need at least two control points to get tangent "
"vector!")
return self.cpoints[1] - self.cpoints[0]

@property
def tan_out(self):
if self.n < 2:
raise ValueError("Need at least two control points to get tangent "
"vector!")
return self.cpoints[-1] - self.cpoints[-2]

@property
def interior_extrema(self):
if self.n <= 2: # a line's extrema are always its tips
return np.array([]), np.array([])
elif self.n == 3: # quadratic curve
# the bezier curve in standard form is
# cp[0] * (1 - t)^2 + cp[1] * 2t(1-t) + cp[2] * t^2
# can be re-written as
# cp[0] + 2 (cp[1] - cp[0]) t + (cp[2] - 2 cp[1] + cp[0]) t^2
# which has simple derivative
# 2*(cp[2] - 2*cp[1] + cp[0]) t + 2*(cp[1] - cp[0])
num = 2*(self.cpoints[2] - 2*self.cpoints[1] + self.cpoints[0])
denom = self.cpoints[1] - self.cpoints[0]
mask = ~np.isclose(denom, 0)
zeros = num[mask]/denom[mask]
dims = np.arange(self.d)[mask]
in_range = (0 <= zeros) & (zeros <= 1)
return dims[in_range], zeros[in_range]
elif self.n == 4: # cubic curve
P = self.cpoints
# derivative of cubic bezier curve has coefficients
a = 3*(P[3] - 3*P[2] + 3*P[1] - P[0])
b = 6*(P[2] - 2*P[1] + P[0])
c = 3*(P[1] - P[0])
discriminant = b**2 - 4*a*c
dims = []
zeros = []
for i in range(self.d):
if discriminant[i] < 0:
continue
roots = [(-b[i] + np.sqrt(discriminant[i]))/2/a[i],
(-b[i] - np.sqrt(discriminant[i]))/2/a[i]]
for root in roots:
if 0 <= root <= 1:
dims.append(i)
zeros.append(root)
return np.asarray(dims), np.asarray(zeros)
else: # self.n > 4:
raise NotImplementedError("Zero finding only implemented up to "
"cubic curves.")


def split_bezier_intersecting_with_closedpath(
bezier, inside_closedpath, tolerance=0.01):
Expand DownExpand Up@@ -225,68 +280,6 @@ def split_bezier_intersecting_with_closedpath(
# matplotlib specific


def split_path_inout(path, inside, tolerance=0.01, reorder_inout=False):
"""
Divide a path into two segments at the point where ``inside(x, y)`` becomes
False.
"""
path_iter = path.iter_segments()

ctl_points, command = next(path_iter)
begin_inside = inside(ctl_points[-2:]) # true if begin point is inside

ctl_points_old = ctl_points

concat = np.concatenate

iold = 0
i = 1

for ctl_points, command in path_iter:
iold = i
i += len(ctl_points) // 2
if inside(ctl_points[-2:]) != begin_inside:
bezier_path = concat([ctl_points_old[-2:], ctl_points])
break
ctl_points_old = ctl_points
else:
raise ValueError("The path does not intersect with the patch")

bp = bezier_path.reshape((-1, 2))
left, right = split_bezier_intersecting_with_closedpath(
bp, inside, tolerance)
if len(left) == 2:
codes_left = [Path.LINETO]
codes_right = [Path.MOVETO, Path.LINETO]
elif len(left) == 3:
codes_left = [Path.CURVE3, Path.CURVE3]
codes_right = [Path.MOVETO, Path.CURVE3, Path.CURVE3]
elif len(left) == 4:
codes_left = [Path.CURVE4, Path.CURVE4, Path.CURVE4]
codes_right = [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4]
else:
raise AssertionError("This should never be reached")

verts_left = left[1:]
verts_right = right[:]

if path.codes is None:
path_in = Path(concat([path.vertices[:i], verts_left]))
path_out = Path(concat([verts_right, path.vertices[i:]]))

else:
path_in = Path(concat([path.vertices[:iold], verts_left]),
concat([path.codes[:iold], codes_left]))

path_out = Path(concat([verts_right, path.vertices[i:]]),
concat([codes_right, path.codes[i:]]))

if reorder_inout and not begin_inside:
path_in, path_out = path_out, path_in

return path_in, path_out


def inside_circle(cx, cy, r):
"""
Return a function that checks whether a point is in a circle with center
Expand All@@ -306,6 +299,7 @@ def _f(xy):

# quadratic Bezier lines


def get_cos_sin(x0, y0, x1, y1):
dx, dy = x1 - x0, y1 - y0
d = (dx * dx + dy * dy) ** .5
Expand DownExpand Up@@ -478,25 +472,3 @@ def make_wedged_bezier2(bezier2, width, w1=1., wm=0.5, w2=0.):
c3x_right, c3y_right)

return path_left, path_right


def make_path_regular(p):
"""
If the ``codes`` attribute of `.Path` *p* is None, return a copy of *p*
with ``codes`` set to (MOVETO, LINETO, LINETO, ..., LINETO); otherwise
return *p* itself.
"""
c = p.codes
if c is None:
c = np.full(len(p.vertices), Path.LINETO, dtype=Path.code_type)
c[0] = Path.MOVETO
return Path(p.vertices, c)
else:
return p


def concatenate_paths(paths):
"""Concatenate a list of paths into a single path."""
vertices = np.concatenate([p.vertices for p in paths])
codes = np.concatenate([make_path_regular(p).codes for p in paths])
return Path(vertices, codes)
9 changes: 7 additions & 2 deletionslib/matplotlib/lines.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -617,8 +617,13 @@ def get_window_extent(self, renderer):
ignore=True)
# correct for marker size, if any
if self._marker:
ms = (self._markersize / 72.0 * self.figure.dpi) * 0.5
bbox = bbox.padded(ms)
m_bbox = self._marker.get_bbox(
self._markersize, self._markeredgewidth)
# markers use units of pts, not pixels
box_points_px = renderer.points_to_pixels(m_bbox.get_points())
# add correct padding to each side of bbox (note: get_points means
# the four points of the bbox, not units of "pt".
bbox = Bbox(bbox.get_points() + box_points_px)
return bbox

@Artist.axes.setter
Expand Down
48 changes: 43 additions & 5 deletionslib/matplotlib/markers.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -133,7 +133,7 @@

from . import cbook, rcParams
from .path import Path
from .transforms import IdentityTransform, Affine2D
from .transforms import IdentityTransform, Affine2D, Bbox

# special-purpose marker identifiers:
(TICKLEFT, TICKRIGHT, TICKUP, TICKDOWN,
Expand DownExpand Up@@ -198,9 +198,6 @@ class MarkerStyle:
fillstyles = ('full', 'left', 'right', 'bottom', 'top', 'none')
_half_fillstyles = ('left', 'right', 'bottom', 'top')

# TODO: Is this ever used as a non-constant?
_point_size_reduction = 0.5

def __init__(self, marker=None, fillstyle=None):
"""
Attributes
Expand DownExpand Up@@ -408,7 +405,8 @@ def _set_pixel(self):
self._snap_threshold = None

def _set_point(self):
self._set_circle(reduction=self._point_size_reduction)
# a "point" is defined to a circle with half the requested markersize
self._set_circle(reduction=0.5)

_triangle_path = Path(
[[0.0, 1.0], [-1.0, -1.0], [1.0, -1.0], [0.0, 1.0]],
Expand DownExpand Up@@ -898,3 +896,43 @@ def _set_x_filled(self):
self._alt_transform = Affine2D().translate(-0.5, -0.5)
self._transform.rotate_deg(rotate)
self._alt_transform.rotate_deg(rotate_alt)

def get_bbox(self, markersize, markeredgewidth=0, **kwargs):
"""Get size of bbox of marker directly from its path.

Parameters
----------
markersize : float
"Size" of the marker, in points.

markeredgewidth : float, optional, default: 0
Width, in points, of the stroke used to create the marker's edge.

kwargs : Dict[str, object]
forwarded to path's iter_curves and iter_corners

Returns
-------
bbox : matplotlib.transforms.Bbox
The extents of the marker including its edge (in points) if it were
centered at (0,0).

Note
----
The approach used is simply to notice that the bbox with no marker edge
must be defined by a corner (control point of the linear parts of path)
or a an extremal point on one of the curved parts of the path.

For a nonzero marker edge width, because the interior extrema will by
definition be parallel to the bounding box, we need only check if the
path location + width/2 extends the bbox at each interior extrema.
Then, for each join and cap, we check if that join extends the bbox.
"""
if np.isclose(markersize, 0):
return Bbox([[0, 0], [0, 0]])
unit_path = self._transform.transform_path(self._path)
scale = Affine2D().scale(markersize)
path = scale.transform_path(unit_path)
return Bbox.from_extents(path.get_stroked_extents(markeredgewidth,
self._joinstyle,
self._capstyle))
19 changes: 9 additions & 10 deletionslib/matplotlib/patches.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,10 +10,9 @@
import matplotlib as mpl
from . import artist, cbook, colors, docstring, lines as mlines, transforms
from .bezier import (
NonIntersectingPathException, concatenate_paths, get_cos_sin,
get_intersection, get_parallels, inside_circle, make_path_regular,
make_wedged_bezier2, split_bezier_intersecting_with_closedpath,
split_path_inout)
NonIntersectingPathException, get_cos_sin, get_intersection, get_parallels,
inside_circle, make_wedged_bezier2,
split_bezier_intersecting_with_closedpath)
from .path import Path


Expand DownExpand Up@@ -2724,7 +2723,7 @@ def insideA(xy_display):
return patchA.contains(xy_event)[0]

try:
left, right = split_path_inout(path,insideA)
left, right =path.split_path_inout(insideA)
except ValueError:
right = path

Expand All@@ -2736,7 +2735,7 @@ def insideB(xy_display):
return patchB.contains(xy_event)[0]

try:
left, right = split_path_inout(path,insideB)
left, right =path.split_path_inout(insideB)
except ValueError:
left = path

Expand All@@ -2751,13 +2750,13 @@ def _shrink(self, path, shrinkA, shrinkB):
if shrinkA:
insideA = inside_circle(*path.vertices[0], shrinkA)
try:
left, path = split_path_inout(path,insideA)
left, path =path.split_path_inout(insideA)
except ValueError:
pass
if shrinkB:
insideB = inside_circle(*path.vertices[-1], shrinkB)
try:
path, right = split_path_inout(path,insideB)
path, right =path.split_path_inout(insideB)
except ValueError:
pass
return path
Expand DownExpand Up@@ -3187,7 +3186,7 @@ def __call__(self, path, mutation_size, linewidth,
and takes care of the aspect ratio.
"""

path = make_path_regular(path)
path =path.make_path_regular()

if aspect_ratio is not None:
# Squeeze the given height by the aspect_ratio
Expand DownExpand Up@@ -4174,7 +4173,7 @@ def get_path(self):
"""
_path, fillable = self.get_path_in_displaycoord()
if np.iterable(fillable):
_path =concatenate_paths(_path)
_path =Path.make_compound_path(*_path)
return self.get_transform().inverted().transform_path(_path)

def get_path_in_displaycoord(self):
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp