Path: blob/master/src/sage/geometry/hyperplane_arrangement/plot.py
8817 views
"""1Plotting of Hyperplane Arrangements23PLOT OPTIONS::45Beside the usual plot options (enter ``plot?``), the plot command for6hyperplane arrangements includes the following:78- ``hyperplane_colors`` -- Color or list of colors, one for each9hyperplane (default: equally spread range of hues).1011- ``hyperplane_labels`` -- Boolean, ``'short'``, ``'long'`` (default:12``False``). If False, no labels are shown; if 'short' or 'long',13the hyperplanes are given short or long labels, respectively. If14``True``, the hyperplanes are given long labels.1516- ``label_colors`` -- Color or list of colors, one for each hyperplane17(default: black).1819- ``label_fontsize`` -- Size for hyperplane_label font (default:20``14``). This does not work for 3d plots.2122- ``label_offsets`` -- Amount be which labels are offset from23h.point() for each hyperplane h. The format is different for each24dimension: if the hyperplanes have dimension 0, the offset can be a25single number or a list of numbers, one for each hyperplane; if the26hyperplanes have dimension 1, the offset can be a single 2-tuple, or27a list of 2-tuples, one for each hyperplane; if the hyperplanes have28dimension 2, the offset can be a single 3-tuple or a list of293-tuples, one for each hyperplane. (Defaults: 0-dim: ``0.1``,301-dim: ``(0,1)``, 2-dim: ``(0,0,0.2)``).3132- ``hyperplane_legend`` -- Boolean, ``'short'``, ``'long'`` (default:33``'long'``; in 3-d: ``False``). If ``False``, no legend is shown;34if ``True``, ``'short'``, or ``'long'``, the legend is shown with35the default, long, or short labeling, respectively. (For36arrangements of lines or planes, only.)3738- ``hyperplane_opacities`` -- A number or list of numbers, one for each39hyperplane, between 0 and 1. Only applies to 3d plots.4041- ``point_sizes`` -- Number or list of numbers, one for each hyperplane42giving the sizes of points in a zero-dimensional arrangement43(default: ``50``).4445- ``ranges`` -- Range for the parameters or a list of ranges of46parameters, one for each hyperplane, for the parametric plots of the47hyperplanes. If a single positive number `r` is given for48``ranges``, then all parameters run from -r to r. Otherwise, for a49line in the plane, the range has the form ``[a,b]`` (default:50``[-3,3]``), and for a plane in 3-space, the range has the form51``[[a,b],[c,d]]`` (default: ``[[-3,3],[-3,3]]``). The ranges are52centered around ``hyperplane_arrangement.point()``.5354EXAMPLES::5556sage: H3.<x,y,z> = HyperplaneArrangements(QQ)57sage: A = H3([(1,0,0), 0], [(0,0,1), 5])58sage: A.plot(hyperplane_opacities=0.5, hyperplane_labels=True, hyperplane_legend=False)5960sage: c = H3([(1,0,0),0], [(0,0,1),5])61sage: c.plot(ranges=10)62sage: c.plot(ranges=[[9.5,10], [-3,3]])63sage: c.plot(ranges=[[[9.5,10], [-3,3]], [[-6,6], [-5,5]]])646566sage: H2.<s,t> = HyperplaneArrangements(QQ)67sage: h = H2([(1,1),0], [(1,-1),0], [(0,1),2])68sage: h.plot(ranges=20)69sage: h.plot(ranges=[-1, 10])70sage: h.plot(ranges=[[-1, 1], [-5, 5], [-1, 10]])7172sage: a = hyperplane_arrangements.coordinate(3)73sage: opts = {'hyperplane_colors':['yellow', 'green', 'blue']}74sage: opts['hyperplane_labels'] = True75sage: opts['label_offsets'] = [(0,2,2), (2,0,2), (2,2,0)]76sage: opts['hyperplane_legend'] = False77sage: opts['hyperplane_opacities'] = 0.778sage: a.plot(**opts)79sage: opts['hyperplane_labels'] = 'short'80sage: a.plot(**opts)8182sage: H.<u> = HyperplaneArrangements(QQ)83sage: pts = H(3*u+4, 2*u+5, 7*u+1)84sage: pts.plot(hyperplane_colors=['yellow','black','blue'])85sage: pts.plot(point_sizes=[50,100,200], hyperplane_colors='blue')8687sage: H.<x,y,z> = HyperplaneArrangements(QQ)88sage: a = H(x, y+1, y+2)89sage: a.plot(hyperplane_labels=True,label_colors='blue',label_fontsize=18)90sage: a.plot(hyperplane_labels=True,label_colors=['red','green','black'])91"""9293from copy import copy94from colorsys import hsv_to_rgb95from sage.plot.plot3d.parametric_plot3d import parametric_plot3d96from sage.plot.plot3d.shapes2 import text3d97from sage.plot.graphics import Graphics98from sage.plot.line import line99from sage.plot.text import text100from sage.plot.point import point101from sage.plot.plot import parametric_plot102from sage.symbolic.all import SR103104105def plot(hyperplane_arrangement, **kwds):106r"""107Return a plot of the hyperplane arrangement.108109If the arrangement is in 4 dimensions but inessential, a plot of110the essentialization is returned.111112.. NOTE::113114This function is available as the115:meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.plot`116method of hyperplane arrangements. You should not call this117function directly, only through the method.118119INPUT:120121- ``hyperplane_arrangement`` -- the hyperplane arrangement to plot122123- ``**kwds`` -- plot options: see124:mod:`sage.geometry.hyperplane_arrangement.plot`.125126OUTPUT:127128A graphics object of the plot.129130EXAMPLES::131132sage: B = hyperplane_arrangements.semiorder(4)133sage: B.plot()134Displaying the essentialization.135"""136N = len(hyperplane_arrangement)137dim = hyperplane_arrangement.dimension()138if hyperplane_arrangement.base_ring().characteristic() != 0:139raise NotImplementedError('must be a field of characteristic 0')140elif dim == 4:141if not hyperplane_arrangement.is_essential():142print 'Displaying the essentialization.'143hyperplane_arrangement = hyperplane_arrangement.essentialization()144elif dim not in [1,2,3]: # revise to handle 4d145return # silently146# handle extra keywords147if kwds.has_key('hyperplane_colors'):148hyp_colors = kwds.pop('hyperplane_colors')149if not type(hyp_colors) == list: # we assume its a single color then150hyp_colors = [hyp_colors] * N151else:152HSV_tuples = [(i*1.0/N, 0.8, 0.9) for i in range(N)]153hyp_colors = map(lambda x: hsv_to_rgb(*x), HSV_tuples)154if kwds.has_key('hyperplane_labels'):155hyp_labels = kwds.pop('hyperplane_labels')156has_hyp_label = True157if not type(hyp_labels) == list: # we assume its a boolean then158hyp_labels = [hyp_labels] * N159relabeled = []160for i in range(N):161if hyp_labels[i] in [True,'long']:162relabeled.append(True)163else:164relabeled.append(str(i))165hyp_labels = relabeled166else:167has_hyp_label = False168if kwds.has_key('label_colors'):169label_colors = kwds.pop('label_colors')170has_label_color = True171if not type(label_colors) == list: # we assume its a single color then172label_colors = [label_colors] * N173else:174has_label_color = False175if kwds.has_key('label_fontsize'):176label_fontsize = kwds.pop('label_fontsize')177has_label_fontsize = True178if not type(label_fontsize) == list: # we assume its a single size then179label_fontsize = [label_fontsize] * N180else:181has_label_fontsize = False182if kwds.has_key('label_offsets'):183has_offsets = True184offsets = kwds.pop('label_offsets')185else:186has_offsets = False # give default values below187hyperplane_legend = kwds.pop('hyperplane_legend', 'long' if dim < 3 else False)188if kwds.has_key('hyperplane_opacities'):189hyperplane_opacities = kwds.pop('hyperplane_opacities')190has_opacity = True191if not type(hyperplane_opacities) == list: # we assume a single number then192hyperplane_opacities = [hyperplane_opacities] * N193else:194has_opacity = False195point_sizes = kwds.pop('point_sizes', 50)196if not type(point_sizes) == list:197point_sizes = [point_sizes] * N198if kwds.has_key('ranges'):199ranges_set = True200ranges = kwds.pop('ranges')201if not type(ranges) in [list,tuple]: # ranges is a single number202ranges = [ranges] * N203# So ranges is some type of list.204elif dim == 2: # arrangement of lines in the plane205if not type(ranges[0]) in [list,tuple]: # a single interval206ranges = [ranges] * N207elif dim == 3: # arrangement of planes in 3-space208if not type(ranges[0][0]) in [list,tuple]:209ranges = [ranges] * N210elif dim not in [2,3]: # ranges is not an option unless dim is 2 or 3211ranges_set = False212else: # a list of intervals, one for each hyperplane is given213pass # ranges does not need to be modified214else:215ranges_set = False # give default values below216# the extra keywords have now been handled217# now handle the legend218if dim in [1,2]: # points on a line or lines in the plane219if hyperplane_legend in [True,'long']:220hyps = hyperplane_arrangement.hyperplanes()221legend_labels = [hyps[i]._latex_() for i in range(N)]222elif hyperplane_legend == 'short' :223legend_labels = [str(i) for i in range(N)]224else: # dim==3, arrangement of planes in 3-space225if hyperplane_legend in [True, 'long']:226legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'long')227elif hyperplane_legend == 'short':228legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'short')229## done handling the legend230## now create the plot231p = Graphics()232for i in range(N):233newk = copy(kwds)234if has_hyp_label:235newk['hyperplane_label'] = hyp_labels[i]236if has_offsets:237if type(offsets) != list:238newk['label_offset'] = offsets239else:240newk['label_offset'] = offsets[i]241else:242newk['hyperplane_label'] = False243if has_label_color:244newk['label_color'] = label_colors[i]245if has_label_fontsize:246newk['label_fontsize'] = label_fontsize[i]247if has_opacity:248newk['opacity'] = hyperplane_opacities[i]249if dim == 1:250newk['point_size'] = point_sizes[i]251if dim in [1,2] and hyperplane_legend != False: # more options than T/F252newk['legend_label'] = legend_labels[i]253if ranges_set:254newk['ranges'] = ranges[i]255p += plot_hyperplane(hyperplane_arrangement[i], rgbcolor=hyp_colors[i], **newk)256if dim == 1:257if hyperplane_legend != False: # there are more options than T/F258p.legend(True)259return p260elif dim == 2:261if hyperplane_legend != False: # there are more options than T/F262p.legend(True)263return p264else: # dim==3265if hyperplane_legend != False: # there are more options than T/F266return p, legend3d267else:268return p269270271272273274def plot_hyperplane(hyperplane, **kwds):275r"""276Return the plot of a single hyperplane.277278INPUT:279280- ``**kwds`` -- plot options: see below281282OUTPUT:283284A graphics object of the plot.285286.. RUBRIC:: Plot Options287288Beside the usual plot options (enter ``plot?``), the plot command for289hyperplanes includes the following:290291- ``hyperplane_label`` -- Boolean value or string (default: ``True``).292If ``True``, the hyperplane is labeled with its equation, if a293string, it is labeled by that string, otherwise it is not294labeled.295296- ``label_color`` -- (Default: ``'black'``) Color for hyperplane_label.297298- ``label_fontsize`` -- Size for ``hyperplane_label`` font (default: 14)299(does not work in 3d, yet).300301- ``label_offset`` -- (Default: 0-dim: 0.1, 1-dim: (0,1),3022-dim: (0,0,0.2)) Amount by which label is offset from303``hyperplane.point()``.304305- ``point_size`` -- (Default: 50) Size of points in a zero-dimensional306arrangement or of an arrangement over a finite field.307308- ``ranges`` -- Range for the parameters for the parametric plot of the309hyperplane. If a single positive number ``r`` is given for the310value of ``ranges``, then the ranges for all parameters are set to311`[-r, r]`. Otherwise, for a line in the plane, ``ranges`` has the312form ``[a, b]`` (default: [-3,3]), and for a plane in 3-space, the313``ranges`` has the form ``[[a, b], [c, d]]`` (default: [[-3,3],[-3,3]]).314(The ranges are centered around ``hyperplane.point()``.)315316EXAMPLES::317318sage: H1.<x> = HyperplaneArrangements(QQ)319sage: a = 3*x + 4320sage: a.plot() # indirect doctest321sage: a.plot(point_size=100,hyperplane_label='hello')322323324sage: H2.<x,y> = HyperplaneArrangements(QQ)325sage: b = 3*x + 4*y + 5326sage: b.plot()327sage: b.plot(ranges=(1,5),label_offset=(2,-1))328sage: opts = {'hyperplane_label':True, 'label_color':'green',329....: 'label_fontsize':24, 'label_offset':(0,1.5)}330sage: b.plot(**opts)331332sage: H3.<x,y,z> = HyperplaneArrangements(QQ)333sage: c = 2*x + 3*y + 4*z + 5334sage: c.plot()335sage: c.plot(label_offset=(1,0,1), color='green', label_color='red', frame=False)336sage: d = -3*x + 2*y + 2*z + 3337sage: d.plot(opacity=0.8)338sage: e = 4*x + 2*z + 3339sage: e.plot(ranges=[[-1,1],[0,8]], label_offset=(2,2,1), aspect_ratio=1)340"""341if hyperplane.base_ring().characteristic() != 0:342raise NotImplementedError('base field must have characteristic zero')343elif hyperplane.dimension() not in [0, 1, 2]: # dimension of hyperplane, not ambient space344raise ValueError('can only plot hyperplanes in dimensions 1, 2, 3')345# handle extra keywords346if kwds.has_key('hyperplane_label'):347hyp_label = kwds.pop('hyperplane_label')348if hyp_label == False:349has_hyp_label = False350else:351has_hyp_label = True352else: # default353hyp_label = True354has_hyp_label = True355if has_hyp_label:356if hyp_label == True: # then label hyperplane with its equation357if hyperplane.dimension() == 2: # jmol does not like latex358label = hyperplane._repr_linear(include_zero=False)359else:360label = hyperplane._latex_()361else:362label = hyp_label # a string363if kwds.has_key('label_color'):364label_color = kwds.pop('label_color')365else:366label_color = 'black'367if kwds.has_key('label_fontsize'):368label_fontsize = kwds.pop('label_fontsize')369else:370label_fontsize = 14371if kwds.has_key('label_offset'):372has_offset = True373label_offset = kwds.pop('label_offset')374else:375has_offset = False # give default values below376if kwds.has_key('point_size'):377pt_size = kwds.pop('point_size')378else:379pt_size = 50380if kwds.has_key('ranges'):381ranges_set = True382ranges = kwds.pop('ranges')383else:384ranges_set = False # give default values below385# the extra keywords have now been handled386# now create the plot387if hyperplane.dimension() == 0: # a point on a line388x, = hyperplane.A()389d = hyperplane.b()390p = point((d/x,0), size = pt_size, **kwds)391if has_hyp_label:392if not has_offset:393label_offset = 0.1394p += text(label, (d/x,label_offset),395color=label_color,fontsize=label_fontsize)396p += text('',(d/x,label_offset+0.4)) # add space at top397if not kwds.has_key('ymax'):398kwds['ymax'] = 0.5399elif hyperplane.dimension() == 1: # a line in the plane400pnt = hyperplane.point()401w = hyperplane.linear_part().matrix()402x, y = hyperplane.A()403d = hyperplane.b()404t = SR.var('t')405if ranges_set:406if type(ranges) in [list,tuple]:407t0, t1 = ranges408else: # ranges should be a single positive number409t0, t1 = -ranges, ranges410else: # default411t0, t1 = -3, 3412p = parametric_plot(pnt+t*w[0], (t,t0,t1), **kwds)413if has_hyp_label:414if has_offset:415b0, b1 = label_offset416else:417b0, b1 = 0, 0.2418label = text(label,(pnt[0]+b0,pnt[1]+b1),419color=label_color,fontsize=label_fontsize)420p += label421elif hyperplane.dimension() == 2: # a plane in 3-space422pnt = hyperplane.point()423w = hyperplane.linear_part().matrix()424a, b, c = hyperplane.A()425d = hyperplane.b()426s,t = SR.var('s t')427if ranges_set:428if type(ranges) in [list,tuple]:429s0, s1 = ranges[0]430t0, t1 = ranges[1]431else: # ranges should be a single positive integers432s0, s1 = -ranges, ranges433t0, t1 = -ranges, ranges434else: # default435s0, s1 = -3, 3436t0, t1 = -3, 3437p = parametric_plot3d(pnt+s*w[0]+t*w[1],(s,s0,s1),(t,t0,t1),**kwds)438if has_hyp_label:439if has_offset:440b0, b1, b2 = label_offset441else:442b0, b1, b2 = 0, 0, 0443label = text3d(label,(pnt[0]+b0,pnt[1]+b1,pnt[2]+b2),444color=label_color,fontsize=label_fontsize)445p += label446return p447448449450451452def legend_3d(hyperplane_arrangement, hyperplane_colors, length):453r"""454Create plot of a 3d legend for an arrangement of planes in 3-space. The455``length`` parameter determines whether short or long labels are used in456the legend.457458INPUT:459460- ``hyperplane_arrangement`` -- a hyperplane arrangement461462- ``hyperplane_colors`` -- list of colors463464- ``length`` -- either ``'short'`` or ``'long'``465466OUTPUT:467468- A graphics object.469470EXAMPLES::471472sage: a = hyperplane_arrangements.semiorder(3)473sage: from sage.geometry.hyperplane_arrangement.plot import legend_3d474sage: legend_3d(a, colors.values()[:6],length='long')475476sage: b = hyperplane_arrangements.semiorder(4)477sage: c = b.essentialization()478sage: legend_3d(c, colors.values()[:12], length='long')479480sage: legend_3d(c, colors.values()[:12], length='short')481482sage: p = legend_3d(c, colors.values()[:12], length='short')483sage: p.set_legend_options(ncol=4)484sage: type(p)485<class 'sage.plot.graphics.Graphics'>486"""487if hyperplane_arrangement.dimension() != 3:488raise ValueError('arrangements must be in 3-space')489hyps = hyperplane_arrangement.hyperplanes()490N = len(hyperplane_arrangement)491if length == 'short':492labels = [' ' + str(i) for i in range(N)]493else:494labels = [' ' + hyps[i]._repr_linear(include_zero=False) for i in495range(N)]496p = Graphics()497for i in range(N):498p += line([(0,0),(0,0)], color=hyperplane_colors[i], thickness=8,499legend_label=labels[i], axes=False)500p.set_legend_options(title='Hyperplanes', loc='center', labelspacing=0.4,501fancybox=True, font_size='x-large', ncol=2)502p.legend(True)503return p504505506507