2626from mathics .core .evaluation import Evaluation
2727from mathics .core .expression import Expression
2828from mathics .core .list import ListExpression
29+ from mathics .eval .nevaluator import eval_N
2930from mathics .core .symbols import Symbol , SymbolList
3031from mathics .core .systemsymbols import (
3132 SymbolAll ,
3839 SymbolLog10 ,
3940 SymbolNone ,
4041 SymbolRGBColor ,
42+ SymbolSequence ,
4143 SymbolStyle ,
4244)
4345from mathics .eval .drawing .charts import draw_bar_chart , eval_chart
6264# TODO: work out exactly how to deploy.
6365use_vectorized_plot = os .getenv ("MATHICS3_USE_VECTORIZED_PLOT" , False )
6466if use_vectorized_plot :
65- from mathics .eval .drawing .plot3d_vectorized import eval_DensityPlot , eval_Plot3D
67+ from mathics .eval .drawing .plot3d_vectorized import eval_DensityPlot , eval_Plot3D , eval_ComplexPlot , eval_ComplexPlot3D
6668else :
67- from mathics .eval .drawing .plot3d import eval_DensityPlot , eval_Plot3D
68-
69- from mathics .eval .nevaluator import eval_N
69+ from mathics .eval .drawing .plot3d import eval_DensityPlot , eval_Plot3D , eval_ComplexPlot , eval_ComplexPlot3D
7070
7171# This tells documentation how to sort this module
7272# Here we are also hiding "drawing" since this erroneously appears at the top level.
@@ -137,6 +137,7 @@ def eval(self, points, evaluation: Evaluation, options: dict):
137137 )
138138
139139 points = points .evaluate (evaluation )
140+ print ("xxx points" , points )
140141 if not isinstance (points , ListExpression ):
141142 evaluation .message (class_name , "lpn" , points )
142143 return
@@ -460,17 +461,20 @@ def __init__(self, expr, range_exprs, options, evaluation):
460461 # plot ranges
461462 self .ranges = []
462463 for range_expr in range_exprs :
464+ print ("xxx range_expr" , range_expr )
463465 if not range_expr .has_form ("List" , 3 ):
464466 self .error (expr , "invrange" , range_expr )
465467 if not isinstance (range_expr .elements [0 ], Symbol ):
466468 self .error (expr , "invrange" , range_expr )
467469 range = [range_expr .elements [0 ]]
468470 for limit_expr in range_expr .elements [1 :3 ]:
469- limit = limit_expr . round_to_float ( evaluation )
470- if limit is None :
471+ limit = eval_N ( limit_expr , evaluation ). to_python ( )
472+ if not isinstance ( limit , ( int , float , complex )) :
471473 self .error (expr , "plln" , limit_expr , range_expr )
472474 range .append (limit )
473- if range [2 ] <= range [1 ]:
475+ if isinstance (limit , (int ,float )) and range [2 ] <= range [1 ]:
476+ self .error (expr , "invrange" , range_expr )
477+ if isinstance (limit , complex ) and (range [2 ].real <= range [1 ].real or range [2 ].imag <= range [1 ].imag ):
474478 self .error (expr , "invrange" , range_expr )
475479 self .ranges .append (range )
476480
@@ -539,7 +543,9 @@ def check_plotpoints(steps):
539543
540544
541545class _Plot3D (Builtin ):
542- """Common base class for Plot3D and DensityPlot"""
546+ """Common base class for Plot3D, DensityPlot, ComplexPlot, ComplexPlot3D"""
547+
548+ attributes = A_HOLD_ALL | A_PROTECTED
543549
544550 # Check for correct number of args
545551 eval_error = Builtin .generic_argument_error
@@ -565,29 +571,61 @@ class _Plot3D(Builtin):
565571 ),
566572 }
567573
574+ # Plot3D, ComplexPlot3D
575+ options3d = Graphics3D .options | {
576+ "Axes" : "True" ,
577+ "AspectRatio" : "1" ,
578+ "Mesh" : "Full" ,
579+ "PlotPoints" : "None" ,
580+ "BoxRatios" : "{1, 1, 0.4}" ,
581+ "MaxRecursion" : "2" ,
582+ }
583+
584+ # DensityPlot, ComplexPlot
585+ options2d = Graphics .options | {
586+ "Axes" : "False" ,
587+ "AspectRatio" : "1" ,
588+ "Mesh" : "None" ,
589+ "Frame" : "True" ,
590+ "ColorFunction" : "Automatic" ,
591+ "ColorFunctionScaling" : "True" ,
592+ "PlotPoints" : "None" ,
593+ "MaxRecursion" : "0" ,
594+ # 'MaxRecursion': '2', # FIXME causes bugs in svg output see #303
595+ }
596+
568597 def eval (
569598 self ,
570599 functions ,
571- xrange ,
572- yrange ,
600+ ranges ,
573601 evaluation : Evaluation ,
574602 options : dict ,
575603 ):
576- """%(name)s[functions_, xrange_, yrange_ , OptionsPattern[%(name)s]]"""
604+ """%(name)s[functions_, ranges__ , OptionsPattern[%(name)s]]"""
577605
578606 # TODO: test error for too many, too few, no args
579607
580608 # parse options, bailing out if anything is wrong
581609 try :
582- plot_options = PlotOptions (self , [xrange , yrange ], options , evaluation )
610+ ranges = ranges .elements if ranges .head is SymbolSequence else [ranges ]
611+ plot_options = PlotOptions (self , ranges , options , evaluation )
583612 except ValueError :
584613 return None
585614
586- # ask the subclass to get one or more functions as appropriate
587- plot_options .functions = self .get_functions_param (functions )
615+ # TODO: consult many_functions variable set by subclass and error
616+ # if many_functions is False but multiple are supplied
617+ if functions .has_form ("List" , None ):
618+ plot_options .functions = functions .elements
619+ else :
620+ plot_options .functions = [functions ]
621+
622+ # subclass must set eval_function and graphics_class
623+ graphics = self .eval_function (plot_options , evaluation )
624+ if not graphics :
625+ return
626+ graphics_expr = graphics .generate (options_to_rules (options , self .graphics_class .options ))
627+ return graphics_expr
588628
589- # delegate to subclass, which will call the appropriate eval_* function
590- return self .do_eval (plot_options , evaluation , options )
591629
592630
593631class BarChart (_Chart ):
@@ -712,6 +750,27 @@ class ColorDataFunction(Builtin):
712750 summary_text = "color scheme object"
713751
714752
753+ class ComplexPlot3D (_Plot3D ):
754+
755+ summary_text = "plots one or more complex functions as a surface"
756+ expected_args = 2
757+ options = _Plot3D .options3d
758+
759+ many_functions = True
760+ eval_function = staticmethod (eval_ComplexPlot3D )
761+ graphics_class = Graphics3D
762+
763+ class ComplexPlot (_Plot3D ):
764+
765+ summary_text = "plots a complex function showing amplitude and phase using colors"
766+ expected_args = 2
767+ options = _Plot3D .options2d
768+
769+ many_functions = False
770+ eval_function = staticmethod (eval_ComplexPlot )
771+ graphics_class = Graphics
772+
773+
715774class DensityPlot (_Plot3D ):
716775 """
717776 <url>:WMA link: https://reference.wolfram.com/language/ref/DensityPlot.html</url>
@@ -736,35 +795,13 @@ class DensityPlot(_Plot3D):
736795 = -Graphics-
737796 """
738797
739- attributes = A_HOLD_ALL | A_PROTECTED
740-
741- options = Graphics .options .copy ()
742- options .update (
743- {
744- "Axes" : "False" ,
745- "AspectRatio" : "1" ,
746- "Mesh" : "None" ,
747- "Frame" : "True" ,
748- "ColorFunction" : "Automatic" ,
749- "ColorFunctionScaling" : "True" ,
750- "PlotPoints" : "None" ,
751- "MaxRecursion" : "0" ,
752- # 'MaxRecursion': '2', # FIXME causes bugs in svg output see #303
753- }
754- )
755798 summary_text = "density plot for a function"
799+ expected_args = 3
800+ options = _Plot3D .options2d
756801
757- # TODO: error if more than one function here
758- def get_functions_param (self , functions ):
759- """can only have one function"""
760- return [functions ]
761-
762- # called by superclass
763- def do_eval (self , plot_options , evaluation , options ):
764- """called by superclass to call appropriate eval_* function"""
765- graphics = eval_DensityPlot (plot_options , evaluation )
766- graphics_expr = graphics .generate (options_to_rules (options , Graphics .options ))
767- return graphics_expr
802+ many_functions = False
803+ eval_function = staticmethod (eval_DensityPlot )
804+ graphics_class = Graphics
768805
769806
770807class DiscretePlot (_Plot ):
@@ -1878,30 +1915,10 @@ class Plot3D(_Plot3D):
18781915 = -Graphics3D-
18791916 """
18801917
1881- attributes = A_HOLD_ALL | A_PROTECTED
1882-
1883- options = Graphics .options .copy ()
1884- options .update (
1885- {
1886- "Axes" : "True" ,
1887- "AspectRatio" : "1" ,
1888- "Mesh" : "Full" ,
1889- "PlotPoints" : "None" ,
1890- "BoxRatios" : "{1, 1, 0.4}" ,
1891- "MaxRecursion" : "2" ,
1892- }
1893- )
18941918 summary_text = "plots 3D surfaces of one or more functions"
1919+ expected_args = 3
1920+ options = _Plot3D .options3d
18951921
1896- def get_functions_param (self , functions ):
1897- """May have a function or a list of functions"""
1898- if functions .has_form ("List" , None ):
1899- return functions .elements
1900- else :
1901- return [functions ]
1902-
1903- def do_eval (self , plot_options , evaluation , options ):
1904- """called by superclass to call appropriate eval_* function"""
1905- graphics = eval_Plot3D (plot_options , evaluation )
1906- graphics_expr = graphics .generate (options_to_rules (options , Graphics3D .options ))
1907- return graphics_expr
1922+ many_functions = True
1923+ eval_function = staticmethod (eval_Plot3D )
1924+ graphics_class = Graphics3D
0 commit comments