Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 16c4aec

Browse files
committed
Fix a few bugs in ConnectionStyle classes. Add ConnectionPatch.
svn path=/trunk/matplotlib/; revision=7259
1 parent cc6fb08 commit 16c4aec

2 files changed

Lines changed: 315 additions & 6 deletions

File tree

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2009-07-14 Fix a few bugs in ConnectionStyle algorithms. Add
2+
ConnectionPatch class. -JJL
3+
14
2009-07-11 Added a fillstyle Line2D property for half filled markers
25
-- see examples/pylab_examples/fillstyle_demo.py JDH
36

lib/matplotlib/patches.py

Lines changed: 312 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,8 +1729,8 @@ class RArrow(LArrow):
17291729
"""
17301730

17311731
def __init__(self, pad=0.3):
1732-
self.pad = pad
1733-
super(BoxStyle.RArrow, self).__init__()
1732+
#self.pad = pad
1733+
super(BoxStyle.RArrow, self).__init__(pad)
17341734

17351735
def transmute(self, x0, y0, width, height, mutation_size):
17361736

@@ -2466,7 +2466,7 @@ def connect(self, posA, posB):
24662466
cosA, sinA = math.cos(self.angleA/180.*math.pi),\
24672467
math.sin(self.angleA/180.*math.pi),
24682468
cosB, sinB = math.cos(self.angleB/180.*math.pi),\
2469-
-math.sin(self.angleB/180.*math.pi),
2469+
math.sin(self.angleB/180.*math.pi),
24702470

24712471
cx, cy = get_intersection(x1, y1, cosA, sinA,
24722472
x2, y2, cosB, sinB)
@@ -2478,9 +2478,15 @@ def connect(self, posA, posB):
24782478
vertices.append((cx, cy))
24792479
codes.append(Path.LINETO)
24802480
else:
2481-
vertices.extend([(cx - self.rad * cosA, cy - self.rad * sinA),
2481+
dx1, dy1 = x1-cx, y1-cy
2482+
d1 = (dx1**2 + dy1**2)**.5
2483+
f1 = self.rad/d1
2484+
dx2, dy2 = x2-cx, y2-cy
2485+
d2 = (dx2**2 + dy2**2)**.5
2486+
f2 = self.rad/d2
2487+
vertices.extend([(cx + dx1*f1, cy + dy1*f1),
24822488
(cx, cy),
2483-
(cx + self.rad * cosB, cy + self.rad * sinB)])
2489+
(cx + dx2*f2, cy + dy2*f2)])
24842490
codes.extend([Path.LINETO, Path.CURVE3, Path.CURVE3])
24852491

24862492
vertices.append((x2, y2))
@@ -2623,7 +2629,8 @@ def connect(self, posA, posB):
26232629
#angle = self.angle % 180.
26242630
#if angle < 0. or angle > 180.:
26252631
# angle
2626-
theta0 = (self.angle%180.)/180.*math.pi
2632+
#theta0 = (self.angle%180.)/180.*math.pi
2633+
theta0 = self.angle/180.*math.pi
26272634
#theta0 = (((self.angle+90)%180.) - 90.)/180.*math.pi
26282635
dtheta = theta1 - theta0
26292636
dl = dd*math.sin(dtheta)
@@ -3744,3 +3751,302 @@ def draw(self, renderer):
37443751

37453752
gc.restore()
37463753
renderer.close_group('patch')
3754+
3755+
3756+
class ConnectionPatch(FancyArrowPatch):
3757+
"""
3758+
A :class:`~matplotlib.patches.ConnectionPatch` class is to make
3759+
connecting lines between two points (possibly in different axes).
3760+
"""
3761+
def __str__(self):
3762+
return "ConnectionPatch((%g,%g),(%g,%g))" % \
3763+
(self.xy1[0],self.xy1[1],self.xy2[0],self.xy2[1])
3764+
3765+
def __init__(self, xyA, xyB, coordsA, coordsB=None,
3766+
axesA=None, axesB=None,
3767+
arrowstyle="-",
3768+
arrow_transmuter=None,
3769+
connectionstyle="arc3",
3770+
connector=None,
3771+
patchA=None,
3772+
patchB=None,
3773+
shrinkA=0.,
3774+
shrinkB=0.,
3775+
mutation_scale=10.,
3776+
mutation_aspect=None,
3777+
clip_on=False,
3778+
**kwargs):
3779+
"""
3780+
Connect point *xyA* in *coordsA* with point *xyB* in *coordsB*
3781+
3782+
3783+
Valid keys are
3784+
3785+
3786+
=============== ======================================================
3787+
Key Description
3788+
=============== ======================================================
3789+
arrowstyle the arrow style
3790+
connectionstyle the connection style
3791+
relpos default is (0.5, 0.5)
3792+
patchA default is bounding box of the text
3793+
patchB default is None
3794+
shrinkA default is 2 points
3795+
shrinkB default is 2 points
3796+
mutation_scale default is text size (in points)
3797+
mutation_aspect default is 1.
3798+
? any key for :class:`matplotlib.patches.PathPatch`
3799+
=============== ======================================================
3800+
3801+
3802+
*coordsA* and *coordsB* are strings that indicate the
3803+
coordinates of *xyA* and *xyB*.
3804+
3805+
================= ===================================================
3806+
Property Description
3807+
================= ===================================================
3808+
'figure points' points from the lower left corner of the figure
3809+
'figure pixels' pixels from the lower left corner of the figure
3810+
'figure fraction' 0,0 is lower left of figure and 1,1 is upper, right
3811+
'axes points' points from lower left corner of axes
3812+
'axes pixels' pixels from lower left corner of axes
3813+
'axes fraction' 0,1 is lower left of axes and 1,1 is upper right
3814+
'data' use the coordinate system of the object being
3815+
annotated (default)
3816+
'offset points' Specify an offset (in points) from the *xy* value
3817+
3818+
'polar' you can specify *theta*, *r* for the annotation,
3819+
even in cartesian plots. Note that if you
3820+
are using a polar axes, you do not need
3821+
to specify polar for the coordinate
3822+
system since that is the native "data" coordinate
3823+
system.
3824+
================= ===================================================
3825+
3826+
"""
3827+
if coordsB is None:
3828+
coordsB = coordsA
3829+
# we'll draw ourself after the artist we annotate by default
3830+
self.xy1 = xyA
3831+
self.xy2 = xyB
3832+
self.coords1 = coordsA
3833+
self.coords2 = coordsB
3834+
3835+
self.axesA = axesA
3836+
self.axesB = axesB
3837+
3838+
FancyArrowPatch.__init__(self,
3839+
posA=(0,0), posB=(1,1),
3840+
arrowstyle=arrowstyle,
3841+
arrow_transmuter=arrow_transmuter,
3842+
connectionstyle=connectionstyle,
3843+
connector=connector,
3844+
patchA=patchA,
3845+
patchB=patchB,
3846+
shrinkA=shrinkA,
3847+
shrinkB=shrinkB,
3848+
mutation_scale=mutation_scale,
3849+
mutation_aspect=mutation_aspect,
3850+
clip_on=clip_on,
3851+
**kwargs)
3852+
3853+
# if True, draw annotation only if self.xy is inside the axes
3854+
self._annotation_clip = None
3855+
3856+
__init__.__doc__ = cbook.dedent(__init__.__doc__) % artist.kwdocd
3857+
3858+
3859+
def _get_xy(self, x, y, s, axes=None):
3860+
"""
3861+
caculate the pixel position of given point
3862+
"""
3863+
3864+
if axes is None:
3865+
axes = self.axes
3866+
3867+
if s=='data':
3868+
trans = axes.transData
3869+
x = float(self.convert_xunits(x))
3870+
y = float(self.convert_yunits(y))
3871+
return trans.transform_point((x, y))
3872+
elif s=='offset points':
3873+
# convert the data point
3874+
dx, dy = self.xy
3875+
3876+
# prevent recursion
3877+
if self.xycoords == 'offset points':
3878+
return self._get_xy(dx, dy, 'data')
3879+
3880+
dx, dy = self._get_xy(dx, dy, self.xycoords)
3881+
3882+
# convert the offset
3883+
dpi = self.figure.get_dpi()
3884+
x *= dpi/72.
3885+
y *= dpi/72.
3886+
3887+
# add the offset to the data point
3888+
x += dx
3889+
y += dy
3890+
3891+
return x, y
3892+
elif s=='polar':
3893+
theta, r = x, y
3894+
x = r*np.cos(theta)
3895+
y = r*np.sin(theta)
3896+
trans = axes.transData
3897+
return trans.transform_point((x,y))
3898+
elif s=='figure points':
3899+
#points from the lower left corner of the figure
3900+
dpi = self.figure.dpi
3901+
l,b,w,h = self.figure.bbox.bounds
3902+
r = l+w
3903+
t = b+h
3904+
3905+
x *= dpi/72.
3906+
y *= dpi/72.
3907+
if x<0:
3908+
x = r + x
3909+
if y<0:
3910+
y = t + y
3911+
return x,y
3912+
elif s=='figure pixels':
3913+
#pixels from the lower left corner of the figure
3914+
l,b,w,h = self.figure.bbox.bounds
3915+
r = l+w
3916+
t = b+h
3917+
if x<0:
3918+
x = r + x
3919+
if y<0:
3920+
y = t + y
3921+
return x, y
3922+
elif s=='figure fraction':
3923+
#(0,0) is lower left, (1,1) is upper right of figure
3924+
trans = self.figure.transFigure
3925+
return trans.transform_point((x,y))
3926+
elif s=='axes points':
3927+
#points from the lower left corner of the axes
3928+
dpi = self.figure.dpi
3929+
l,b,w,h = axes.bbox.bounds
3930+
r = l+w
3931+
t = b+h
3932+
if x<0:
3933+
x = r + x*dpi/72.
3934+
else:
3935+
x = l + x*dpi/72.
3936+
if y<0:
3937+
y = t + y*dpi/72.
3938+
else:
3939+
y = b + y*dpi/72.
3940+
return x, y
3941+
elif s=='axes pixels':
3942+
#pixels from the lower left corner of the axes
3943+
3944+
l,b,w,h = axes.bbox.bounds
3945+
r = l+w
3946+
t = b+h
3947+
if x<0:
3948+
x = r + x
3949+
else:
3950+
x = l + x
3951+
if y<0:
3952+
y = t + y
3953+
else:
3954+
y = b + y
3955+
return x, y
3956+
elif s=='axes fraction':
3957+
#(0,0) is lower left, (1,1) is upper right of axes
3958+
trans = axes.transAxes
3959+
return trans.transform_point((x, y))
3960+
3961+
def set_annotation_clip(self, b):
3962+
"""
3963+
set *annotation_clip* attribute.
3964+
3965+
* True : the annotation will only be drawn when self.xy is inside the axes.
3966+
* False : the annotation will always be drawn regardless of its position.
3967+
* None : the self.xy will be checked only if *xycoords* is "data"
3968+
"""
3969+
self._annotation_clip = b
3970+
3971+
def get_annotation_clip(self):
3972+
"""
3973+
Return *annotation_clip* attribute.
3974+
See :meth:`set_annotation_clip` for the meaning of return values.
3975+
"""
3976+
return self._annotation_clip
3977+
3978+
3979+
def get_path_in_displaycoord(self):
3980+
"""
3981+
Return the mutated path of the arrow in the display coord
3982+
"""
3983+
3984+
x, y = self.xy1
3985+
posA = self._get_xy(x, y, self.coords1, self.axesA)
3986+
3987+
x, y = self.xy2
3988+
posB = self._get_xy(x, y, self.coords1, self.axesB)
3989+
3990+
_path = self.get_connectionstyle()(posA, posB,
3991+
patchA=self.patchA,
3992+
patchB=self.patchB,
3993+
shrinkA=self.shrinkA,
3994+
shrinkB=self.shrinkB
3995+
)
3996+
3997+
3998+
3999+
_path, fillable = self.get_arrowstyle()(_path,
4000+
self.get_mutation_scale(),
4001+
self.get_linewidth(),
4002+
self.get_mutation_aspect()
4003+
)
4004+
4005+
return _path, fillable
4006+
4007+
4008+
4009+
def _check_xy(self, renderer):
4010+
"""
4011+
check if the annotation need to
4012+
be drawn.
4013+
"""
4014+
4015+
b = self.get_annotation_clip()
4016+
4017+
4018+
if b or (b is None and self.coords1 == "data"):
4019+
x, y = self.xy1
4020+
xy_pixel = self._get_xy(x, y, self.coords1, self.axesA)
4021+
if not self.axes.contains_point(xy_pixel):
4022+
return False
4023+
4024+
if b or (b is None and self.coords2 == "data"):
4025+
x, y = self.xy2
4026+
xy_pixel = self._get_xy(x, y, self.coords2, self.axesB)
4027+
if self.axesB is None:
4028+
axes = self.axes
4029+
else:
4030+
axes = self.axesB
4031+
if not axes.contains_point(xy_pixel):
4032+
return False
4033+
4034+
return True
4035+
4036+
4037+
def draw(self, renderer):
4038+
"""
4039+
Draw.
4040+
"""
4041+
4042+
if renderer is not None:
4043+
self._renderer = renderer
4044+
if not self.get_visible(): return
4045+
4046+
if not self._check_xy(renderer):
4047+
return
4048+
4049+
FancyArrowPatch.draw(self, renderer)
4050+
4051+
4052+

0 commit comments

Comments
 (0)