@@ -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