def apply(self, functions, x, xstart, xstop, y, ystart, ystop, evaluation, options): "%(name)s[functions_, {x_Symbol, xstart_, xstop_}, {y_Symbol, ystart_, ystop_}, OptionsPattern[%(name)s]]" xexpr_limits = Expression("List", x, xstart, xstop) yexpr_limits = Expression("List", y, ystart, ystop) expr = Expression(self.get_name(), functions, xexpr_limits, yexpr_limits, *options_to_rules(options)) functions = self.get_functions_param(functions) plot_name = self.get_name() x_name = x.get_name() y_name = y.get_name() try: xstart, xstop, ystart, ystop = [ value.to_number(n_evaluation=evaluation) for value in (xstart, xstop, ystart, ystop) ] except NumberError, exc: expr = Expression( plot_name, functions, Expression("List", x, xstart, xstop), Expression("List", y, ystart, ystop), *options_to_rules(options) ) evaluation.message(plot_name, "plln", value, expr) return
def apply(self, functions, x, xstart, xstop, y, ystart, ystop, evaluation, options): '%(name)s[functions_, {x_Symbol, xstart_, xstop_}, {y_Symbol, ystart_, ystop_}, OptionsPattern[%(name)s]]' xexpr_limits = Expression('List', x, xstart, xstop) yexpr_limits = Expression('List', y, ystart, ystop) expr = Expression(self.get_name(), functions, xexpr_limits, yexpr_limits, *options_to_rules(options)) functions = self.get_functions_param(functions) plot_name = self.get_name() x_name = x.get_name() y_name = y.get_name() try: xstart, xstop, ystart, ystop = [ value.to_number(n_evaluation=evaluation) for value in (xstart, xstop, ystart, ystop) ] except NumberError, exc: expr = Expression(plot_name, functions, Expression('List', x, xstart, xstop), Expression('List', y, ystart, ystop), *options_to_rules(options)) evaluation.message(plot_name, 'plln', value, expr) return
def apply(self, functions, x, start, stop, evaluation, options): 'Plot[functions_, {x_Symbol, start_, stop_}, OptionsPattern[Plot]]' expr = Expression('Plot', functions, Expression('List', x, start, stop), *options_to_rules(options)) if functions.has_form('List', None): functions = functions.leaves else: functions = [functions] x = x.get_name() try: start = start.to_number(n_evaluation=evaluation) except NumberError: evaluation.message('Plot', 'plln', start, expr) return try: stop = stop.to_number(n_evaluation=evaluation) except NumberError: evaluation.message('Plot', 'plln', stop, expr) return def eval_f(f, x_value): value = dynamic_scoping(f.evaluate, {x: x_value}, evaluation) value = chop(value).get_real_value() return value hue = 0.67 hue_pos = 0.236068 hue_neg = -0.763932 graphics = [] for index, f in enumerate(functions): points = [] continuous = False steps = 50 d = (stop - start) / steps for index in range(steps + 1): x_value = start + index * d y = eval_f(f, Real(x_value)) if y is not None: point = (x_value, y) if continuous: points[-1].append(point) else: points.append([point]) continuous = True else: continuous = False graphics.append(Expression('Hue', hue, 0.6, 0.6)) graphics.append(Expression('Line', Expression('List', *(Expression('List', *(Expression('List', Real(x), Real(y)) for x, y in line)) for line in points) ))) if index % 4 == 0: hue += hue_pos else: hue += hue_neg if hue > 1: hue -= 1 if hue < 0: hue += 1 return Expression('Graphics', Expression('List', *graphics), *options_to_rules(options))
def apply_makeboxes(self, content, evaluation, options): """MakeBoxes[%(name)s[content_, OptionsPattern[%(name)s]], StandardForm|TraditionalForm|OutputForm]""" def convert(content): head = content.get_head_name() if head == "System`List": return Expression(SymbolList, *[convert(item) for item in content.leaves]) elif head == "System`Style": return Expression("StyleBox", *[convert(item) for item in content.leaves]) if head in element_heads: if head == "System`Text": head = "System`Inset" atoms = content.get_atoms(include_heads=False) if any(not isinstance(atom, (Integer, Real)) and not atom.get_name() in GRAPHICS_SYMBOLS for atom in atoms): if head == "System`Inset": inset = content.leaves[0] if inset.get_head_name() == "System`Graphics": opts = {} # opts = dict(opt._leaves[0].name:opt_leaves[1] for opt in inset._leaves[1:]) inset = self.apply_makeboxes( inset._leaves[0], evaluation, opts) n_leaves = [inset] + [ Expression(SymbolN, leaf).evaluate(evaluation) for leaf in content.leaves[1:] ] else: n_leaves = (Expression(SymbolN, leaf).evaluate(evaluation) for leaf in content.leaves) else: n_leaves = content.leaves return Expression(head + self.box_suffix, *n_leaves) return content for option in options: if option not in ("System`ImageSize", ): options[option] = Expression( SymbolN, options[option]).evaluate(evaluation) from mathics.builtin.box.graphics import GraphicsBox from mathics.builtin.box.graphics3d import Graphics3DBox from mathics.builtin.drawing.graphics3d import Graphics3D if type(self) is Graphics: return GraphicsBox(convert(content), evaluation=evaluation, *options_to_rules(options)) elif type(self) is Graphics3D: return Graphics3DBox(convert(content), evaluation=evaluation, *options_to_rules(options))
def apply_elements(self, filename, elements, evaluation, options={}): 'Import[filename_, elements_List?(AllTrue[#, NotOptionQ]&), OptionsPattern[]]' # Check filename path = filename.to_python() if not (isinstance(path, str) and path[0] == path[-1] == '"'): evaluation.message('Import', 'chtype', filename) return Symbol('$Failed') # Download via URL if isinstance(filename, String): if any(filename.get_string_value().startswith(prefix) for prefix in ('http://', 'https://', 'ftp://')): return Expression('FetchURL', filename, elements, *options_to_rules(options)) # Load local file findfile = Expression('FindFile', filename).evaluate(evaluation) if findfile == Symbol('$Failed'): evaluation.message('Import', 'nffil') return findfile def determine_filetype(): return Expression( 'FileFormat', findfile).evaluate(evaluation=evaluation).get_string_value() return self._import(findfile, determine_filetype, elements, evaluation, options)
def apply(self, f, x, xstart, xstop, y, ystart, ystop, evaluation, options): 'DensityPlot[f_, {x_Symbol, xstart_, xstop_}, {y_Symbol, ystart_, ystop_}, OptionsPattern[DensityPlot]]' x = x.get_name() y = y.get_name() color_function = self.get_option(options, 'ColorFunction', evaluation, pop=True) color_function_scaling = self.get_option(options, 'ColorFunctionScaling', evaluation, pop=True) color_function_min = color_function_max = None if color_function.get_name() == 'Automatic': color_function = String('LakeColors') if color_function.get_string_value(): func = Expression('ColorData', color_function.get_string_value()).evaluate(evaluation) if func.has_form('ColorDataFunction', 4): color_function_min = func.leaves[2].leaves[0].get_real_value() color_function_max = func.leaves[2].leaves[1].get_real_value() color_function = Expression('Function', Expression(func.leaves[3], Expression('Slot', 1))) else: evaluation.message('DensityPlot', 'color', func) return if color_function.has_form('ColorDataFunction', 4): color_function_min = color_function.leaves[2].leaves[0].get_real_value() color_function_max = color_function.leaves[2].leaves[1].get_real_value() color_function_scaling = color_function_scaling.is_true() try: xstart, xstop, ystart, ystop = [value.to_number(n_evaluation=evaluation) for value in (xstart, xstop, ystart, ystop)] except NumberError, exc: expr = Expression('DensityPlot', f, Expression('List', x, xstart, xstop), Expression('List', y, ystart, ystop), *options_to_rules(options)) evaluation.message('DensityPlot', 'plln', exc.value, expr) return
def apply_makeboxes(self, array, f, evaluation, options): 'MakeBoxes[Grid[array_?MatrixQ, OptionsPattern[Grid]], f:StandardForm|TraditionalForm|OutputForm]' array = Expression('List', *(Expression('List', *(Expression('MakeBoxes', item, f) for item in row.leaves)) for row in array.leaves)) return Expression('GridBox', array, *options_to_rules(options))
def apply_elements(self, filename, elements, evaluation, options={}): 'Import[filename_, elements_List?(AllTrue[#, NotOptionQ]&), OptionsPattern[]]' # Check filename path = filename.to_python() if not (isinstance(path, six.string_types) and path[0] == path[-1] == '"'): evaluation.message('Import', 'chtype', filename) return Symbol('$Failed') # Download via URL if isinstance(filename, String): if any(filename.get_string_value().startswith(prefix) for prefix in ('http://', 'https://', 'ftp://')): return Expression('FetchURL', filename, elements, *options_to_rules(options)) # Load local file findfile = Expression('FindFile', filename).evaluate(evaluation) if findfile == Symbol('$Failed'): evaluation.message('Import', 'nffil') return findfile def determine_filetype(): return Expression('FileFormat', findfile).evaluate( evaluation=evaluation).get_string_value() return self._import(findfile, determine_filetype, elements, evaluation, options)
def apply_makeboxes(self, content, evaluation, options): 'MakeBoxes[Graphics[content_, OptionsPattern[Graphics]], StandardForm|TraditionalForm]' def convert(content): if content.has_form('List', None): return Expression('List', *[convert(item) for item in content.leaves]) head = content.get_head_name() if head in element_heads: if head == 'Text': head = 'Inset' atoms = content.get_atoms(include_heads=False) #print atoms #for atom in atoms: # if not isinstance(atom, (Integer, Real)) and not atom.get_name() in GRAPHICS_SYMBOLS: # print atom if any(not isinstance(atom, (Integer, Real)) and not atom.get_name() in GRAPHICS_SYMBOLS for atom in atoms): if head == 'Inset': n_leaves = [content.leaves[0]] + [Expression('N', leaf).evaluate(evaluation) for leaf in content.leaves[1:]] else: n_leaves = (Expression('N', leaf).evaluate(evaluation) for leaf in content.leaves) else: n_leaves = content.leaves return Expression(head + 'Box', *n_leaves) return content for option in options: options[option] = Expression('N', options[option]).evaluate(evaluation) return Expression('GraphicsBox', convert(content), *options_to_rules(options))
def apply_makeboxes(self, array, f, evaluation, options): 'MakeBoxes[Grid[array_?MatrixQ, OptionsPattern[Grid]], f:StandardForm|TraditionalForm|OutputForm]' array = Expression( 'List', *(Expression( 'List', *(Expression('MakeBoxes', item, f) for item in row.leaves)) for row in array.leaves)) return Expression('GridBox', array, *options_to_rules(options))
def apply(self, f, x, xstart, xstop, y, ystart, ystop, evaluation, options): 'DensityPlot[f_, {x_Symbol, xstart_, xstop_}, {y_Symbol, ystart_, ystop_}, OptionsPattern[DensityPlot]]' x = x.get_name() y = y.get_name() color_function = self.get_option(options, 'ColorFunction', evaluation, pop=True) color_function_scaling = self.get_option(options, 'ColorFunctionScaling', evaluation, pop=True) color_function_min = color_function_max = None if color_function.get_name() == 'Automatic': color_function = String('LakeColors') if color_function.get_string_value(): func = Expression( 'ColorData', color_function.get_string_value()).evaluate(evaluation) if func.has_form('ColorDataFunction', 4): color_function_min = func.leaves[2].leaves[0].get_real_value() color_function_max = func.leaves[2].leaves[1].get_real_value() color_function = Expression( 'Function', Expression(func.leaves[3], Expression('Slot', 1))) else: evaluation.message('DensityPlot', 'color', func) return if color_function.has_form('ColorDataFunction', 4): color_function_min = color_function.leaves[2].leaves[ 0].get_real_value() color_function_max = color_function.leaves[2].leaves[ 1].get_real_value() color_function_scaling = color_function_scaling.is_true() try: xstart, xstop, ystart, ystop = [ value.to_number(n_evaluation=evaluation) for value in (xstart, xstop, ystart, ystop) ] except NumberError, exc: expr = Expression('DensityPlot', f, Expression('List', x, xstart, xstop), Expression('List', y, ystart, ystop), *options_to_rules(options)) evaluation.message('DensityPlot', 'plln', exc.value, expr) return
def apply_makeboxes(self, array, f, evaluation, options): """MakeBoxes[Grid[array_?MatrixQ, OptionsPattern[Grid]], f:StandardForm|TraditionalForm|OutputForm]""" return Expression( "GridBox", Expression( "List", *( Expression("List", *(Expression("MakeBoxes", item, f) for item in row.leaves)) for row in array.leaves ) ), *options_to_rules(options) )
def apply(self, graphics, evaluation, options): """Show[graphics_, OptionsPattern[%(name)s]]""" for option in options: if option not in ("System`ImageSize",): options[option] = Expression(SymbolN, options[option]).evaluate( evaluation ) # The below could probably be done with graphics.filter.. new_leaves = [] options_set = set(options.keys()) for leaf in graphics.leaves: leaf_name = leaf.get_head_name() if leaf_name == "System`Rule" and str(leaf.leaves[0]) in options_set: continue new_leaves.append(leaf) new_leaves += options_to_rules(options) graphics = graphics.restructure(graphics.head, new_leaves, evaluation) return graphics
def apply(self, functions, x, start, stop, evaluation, options): '''%(name)s[functions_, {x_Symbol, start_, stop_}, OptionsPattern[%(name)s]]''' expr_limits = Expression('List', x, start, stop) expr = Expression(self.get_name(), functions, expr_limits, *options_to_rules(options)) functions = self.get_functions_param(functions) x_name = x.get_name() try: start = start.to_number(n_evaluation=evaluation) except NumberError: evaluation.message(self.get_name(), 'plln', start, expr) return try: stop = stop.to_number(n_evaluation=evaluation) except NumberError: evaluation.message(self.get_name(), 'plln', stop, expr) return if start >= stop: evaluation.message(self.get_name(), 'plld', expr_limits) return # PlotRange Option def check_range(range): if range in ('Automatic', 'All'): return True if isinstance(range, list) and len(range) == 2: if (isinstance(range[0], numbers.Real) and # noqa isinstance(range[1], numbers.Real)): return True return False plotrange_option = self.get_option(options, 'PlotRange', evaluation) plotrange = plotrange_option.to_python(n_evaluation=evaluation) x_range, y_range = self.get_plotrange(plotrange, start, stop) if not check_range(x_range) or not check_range(y_range): evaluation.message(self.get_name(), 'prng', plotrange_option) x_range, y_range = [start, stop], 'Automatic' # x_range and y_range are now either Automatic, All, or of the form # [min, max] assert x_range in ('Automatic', 'All') or isinstance(x_range, list) assert y_range in ('Automatic', 'All') or isinstance(y_range, list) # Mesh Option mesh_option = self.get_option(options, 'Mesh', evaluation) mesh = mesh_option.to_python() if mesh not in ['None', 'Full', 'All']: evaluation.message('Mesh', 'ilevels', mesh_option) mesh = 'None' # PlotPoints Option plotpoints_option = self.get_option(options, 'PlotPoints', evaluation) plotpoints = plotpoints_option.to_python() if plotpoints == 'None': plotpoints = 57 if not (isinstance(plotpoints, int) and plotpoints >= 2): return evaluation.message(self.get_name(), 'ppts', plotpoints) # MaxRecursion Option max_recursion_limit = 15 maxrecursion_option = self.get_option( options, 'MaxRecursion', evaluation) maxrecursion = maxrecursion_option.to_python() try: if maxrecursion == 'Automatic': maxrecursion = 3 elif maxrecursion == float('inf'): maxrecursion = max_recursion_limit raise ValueError elif isinstance(maxrecursion, int): if maxrecursion > max_recursion_limit: maxrecursion = max_recursion_limit raise ValueError if maxrecursion < 0: maxrecursion = 0 raise ValueError else: maxrecursion = 0 raise ValueError except ValueError: evaluation.message(self.get_name(), 'invmaxrec', maxrecursion, max_recursion_limit) assert isinstance(maxrecursion, int) # Exclusions Option # TODO: Make exclusions option work properly with ParametricPlot def check_exclusion(excl): if isinstance(excl, list): return all(check_exclusion(e) for e in excl) if excl == 'Automatic': return True if not isinstance(excl, numbers.Real): return False return True exclusions_option = self.get_option(options, 'Exclusions', evaluation) exclusions = exclusions_option.to_python(n_evaluation=evaluation) # TODO Turn expressions into points E.g. Sin[x] == 0 becomes 0, 2 Pi... if exclusions in ['None', ['None']]: exclusions = 'None' elif not isinstance(exclusions, list): exclusions = [exclusions] if (isinstance(exclusions, list) and # noqa all(check_exclusion(excl) for excl in exclusions)): pass else: evaluation.message( self.get_name(), 'invexcl', exclusions_option) exclusions = ['Automatic'] # exclusions is now either 'None' or a list of reals and 'Automatic' assert (exclusions == 'None' or isinstance(exclusions, list)) # constants to generate colors hue = 0.67 hue_pos = 0.236068 hue_neg = -0.763932 def get_points_minmax(points): xmin = xmax = ymin = ymax = None for line in points: for x, y in line: if xmin is None or x < xmin: xmin = x if xmax is None or x > xmax: xmax = x if ymin is None or y < ymin: ymin = y if ymax is None or y > ymax: ymax = y return xmin, xmax, ymin, ymax def zero_to_one(value): if value == 0: return 1 return value def get_points_range(points): xmin, xmax, ymin, ymax = get_points_minmax(points) if xmin is None or xmax is None: xmin, xmax = 0, 1 if ymin is None or ymax is None: ymin, ymax = 0, 1 return zero_to_one(xmax - xmin), zero_to_one(ymax - ymin) function_hues = [] base_plot_points = [] # list of points in base subdivision plot_points = [] # list of all plotted points mesh_points = [] graphics = [] # list of resulting graphics primitives for index, f in enumerate(functions): points = [] xvalues = [] # x value for each point in points tmp_mesh_points = [] # For this function only continuous = False d = (stop - start) / (plotpoints - 1) for i in xrange(plotpoints): x_value = start + i * d point = self.eval_f(f, x_name, x_value, evaluation) if point is not None: if continuous: points[-1].append(point) xvalues[-1].append(x_value) else: points.append([point]) xvalues.append([x_value]) continuous = True else: continuous = False base_points = [] for line in points: base_points.extend(line) base_plot_points.extend(base_points) xmin, xmax = automatic_plot_range([xx for xx, yy in base_points]) xscale = 1. / zero_to_one(xmax - xmin) ymin, ymax = automatic_plot_range([yy for xx, yy in base_points]) yscale = 1. / zero_to_one(ymax - ymin) if mesh == 'Full': for line in points: tmp_mesh_points.extend(line) def find_excl(excl): # Find which line the exclusion is in for l in range(len(xvalues)): # TODO: Binary Search faster? if xvalues[l][0] <= excl and xvalues[l][-1] >= excl: break if (xvalues[l][-1] <= excl and # nopep8 xvalues[min(l + 1, len(xvalues) - 1)][0] >= excl): return min(l + 1, len(xvalues) - 1), 0, False xi = 0 for xi in range(len(xvalues[l]) - 1): if xvalues[l][xi] <= excl and xvalues[l][xi + 1] >= excl: return l, xi + 1, True return l, xi + 1, False if exclusions != 'None': for excl in exclusions: if excl != 'Automatic': l, xi, split_required = find_excl(excl) if split_required: xvalues.insert(l + 1, xvalues[l][xi:]) xvalues[l] = xvalues[l][:xi] points.insert(l + 1, points[l][xi:]) points[l] = points[l][:xi] # assert(xvalues[l][-1] <= excl <= xvalues[l+1][0]) # Adaptive Sampling - loop again and interpolate highly angled # sections # Cos of the maximum angle between successive line segments ang_thresh = cos(pi / 180) for line, line_xvalues in zip(points, xvalues): recursion_count = 0 smooth = False while not smooth and recursion_count < maxrecursion: recursion_count += 1 smooth = True i = 2 while i < len(line): vec1 = (xscale * (line[i - 1][0] - line[i - 2][0]), yscale * (line[i - 1][1] - line[i - 2][1])) vec2 = (xscale * (line[i][0] - line[i - 1][0]), yscale * (line[i][1] - line[i - 1][1])) try: angle = (vec1[0] * vec2[0] + vec1[1] * vec2[1]) \ / sqrt((vec1[0] ** 2 + vec1[1] ** 2) * (vec2[0] ** 2 + vec2[1] ** 2)) except ZeroDivisionError: angle = 0.0 if abs(angle) < ang_thresh: smooth = False incr = 0 x_value = 0.5 * (line_xvalues[i - 1] + line_xvalues[i]) point = self.eval_f(f, x_name, x_value, evaluation) if point is not None: line.insert(i, point) line_xvalues.insert(i, x_value) incr += 1 x_value = 0.5 * (line_xvalues[i - 2] + line_xvalues[i - 1]) point = self.eval_f(f, x_name, x_value, evaluation) if point is not None: line.insert(i - 1, point) line_xvalues.insert(i - 1, x_value) incr += 1 i += incr i += 1 if exclusions == 'None': # Join all the Lines points = [[(xx, yy) for line in points for xx, yy in line]] graphics.append(Expression('Hue', hue, 0.6, 0.6)) graphics.append(Expression('Line', from_python(points))) for line in points: plot_points.extend(line) if mesh == 'All': for line in points: tmp_mesh_points.extend(line) if mesh != 'None': mesh_points.append(tmp_mesh_points) function_hues.append(hue) if index % 4 == 0: hue += hue_pos else: hue += hue_neg if hue > 1: hue -= 1 if hue < 0: hue += 1 x_range = get_plot_range([xx for xx, yy in base_plot_points], [xx for xx, yy in plot_points], x_range) y_range = get_plot_range([yy for xx, yy in base_plot_points], [yy for xx, yy in plot_points], y_range) options['PlotRange'] = from_python([x_range, y_range]) if mesh != 'None': for hue, points in zip(function_hues, mesh_points): graphics.append(Expression('Hue', hue, 0.6, 0.6)) meshpoints = [Expression('List', xx, yy) for xx, yy in points] graphics.append(Expression( 'Point', Expression('List', *meshpoints))) return Expression('Graphics', Expression('List', *graphics), *options_to_rules(options))
def final_graphics(self, graphics, options): return Expression('Graphics', Expression('List', *graphics), *options_to_rules(options))
def apply(self, points, evaluation, options): '%(name)s[points_, OptionsPattern[%(name)s]]' plot_name = self.get_name() all_points = points.to_python(n_evaluation=evaluation) expr = Expression(self.get_name(), points, *options_to_rules(options)) # PlotRange Option def check_range(range): if range in ('Automatic', 'All'): return True if isinstance(range, list) and len(range) == 2: if (isinstance(range[0], numbers.Real) and # noqa isinstance(range[1], numbers.Real)): return True return False plotrange_option = self.get_option(options, 'PlotRange', evaluation) plotrange = plotrange_option.to_python(n_evaluation=evaluation) if plotrange == 'All': plotrange = ['All', 'All'] elif plotrange == 'Automatic': plotrange = ['Automatic', 'Automatic'] elif isinstance(plotrange, numbers.Real): plotrange = [[-plotrange, plotrange], [-plotrange, plotrange]] elif isinstance(plotrange, list) and len(plotrange) == 2: if all(isinstance(pr, numbers.Real) for pr in plotrange): plotrange = ['All', plotrange] elif all(check_range(pr) for pr in plotrange): pass else: evaluation.message(self.get_name(), 'prng', plotrange_option) plotrange = ['Automatic', 'Automatic'] x_range, y_range = plotrange[0], plotrange[1] assert x_range in ('Automatic', 'All') or isinstance(x_range, list) assert y_range in ('Automatic', 'All') or isinstance(y_range, list) # Filling option # TODO: Fill between corresponding points in two datasets: filling_option = self.get_option(options, 'Filling', evaluation) filling = filling_option.to_python(n_evaluation=evaluation) if (filling in ['Top', 'Bottom', 'Axis'] or # noqa isinstance(filling, numbers.Real)): pass else: # Mathematica does not even check that filling is sane filling = None # Joined Option joined_option = self.get_option(options, 'Joined', evaluation) joined = joined_option.to_python() if joined not in [True, False]: evaluation.message(plot_name, 'joind', joined_option, expr) joined = False if isinstance(all_points, list) and len(all_points) != 0: if all(not isinstance(point, list) for point in all_points): # Only y values given all_points = [[[float(i + 1), all_points[i]] for i in range(len(all_points))]] elif all(isinstance(line, list) and len(line) == 2 for line in all_points): # Single list of (x,y) pairs all_points = [all_points] elif all(isinstance(line, list) for line in all_points): # List of lines if all(isinstance(point, list) and len(point) == 2 for line in all_points for point in line): pass elif all(not isinstance(point, list) for line in all_points for point in line): all_points = [ [[float(i + 1), l] for i, l in enumerate(line)] for line in all_points] else: return else: return else: return # Split into segments at missing data all_points = [[line] for line in all_points] for l, line in enumerate(all_points): i = 0 while i < len(all_points[l]): seg = line[i] for j, point in enumerate(seg): if not ((isinstance(point[0], float) or isinstance(point[0], int)) and (isinstance(point[1], float) or isinstance(point[1], int))): all_points[l].insert(i, seg[:j]) all_points[l][i + 1] = seg[j + 1:] i -= 1 break i += 1 y_range = get_plot_range( [y for line in all_points for seg in line for x, y in seg], [y for line in all_points for seg in line for x, y in seg], y_range) x_range = get_plot_range( [x for line in all_points for seg in line for x, y in seg], [x for line in all_points for seg in line for x, y in seg], x_range) if filling == 'Axis': # TODO: Handle arbitary axis intercepts filling = 0.0 elif filling == 'Bottom': filling = y_range[0] elif filling == 'Top': filling = y_range[1] hue = 0.67 hue_pos = 0.236068 hue_neg = -0.763932 graphics = [] for indx, line in enumerate(all_points): graphics.append(Expression('Hue', hue, 0.6, 0.6)) for segment in line: if joined: graphics.append(Expression('Line', from_python(segment))) if filling is not None: graphics.append(Expression('Hue', hue, 0.6, 0.6, 0.2)) fill_area = list(segment) fill_area.append([segment[-1][0], filling]) fill_area.append([segment[0][0], filling]) graphics.append(Expression( 'Polygon', from_python(fill_area))) else: graphics.append(Expression('Point', from_python(segment))) if filling is not None: for point in segment: graphics.append(Expression( 'Line', from_python([[point[0], filling], [point[0], point[1]]]))) if indx % 4 == 0: hue += hue_pos else: hue += hue_neg if hue > 1: hue -= 1 if hue < 0: hue += 1 options['PlotRange'] = from_python([x_range, y_range]) return Expression('Graphics', Expression('List', *graphics), *options_to_rules(options))
def apply(self, points, evaluation, options): "%(name)s[points_, OptionsPattern[%(name)s]]" plot_name = self.get_name() all_points = points.to_python(n_evaluation=evaluation) expr = Expression(self.get_name(), points, *options_to_rules(options)) # PlotRange Option def check_range(range): if range in ("Automatic", "All"): return True if isinstance(range, list) and len(range) == 2: if isinstance(range[0], numbers.Real) and isinstance(range[1], numbers.Real): return True return False plotrange_option = self.get_option(options, "PlotRange", evaluation) plotrange = plotrange_option.to_python(n_evaluation=evaluation) if plotrange == "All": plotrange = ["All", "All"] elif plotrange == "Automatic": plotrange = ["Automatic", "Automatic"] elif isinstance(plotrange, numbers.Real): plotrange = [[-plotrange, plotrange], [-plotrange, plotrange]] elif isinstance(plotrange, list) and len(plotrange) == 2: if all(isinstance(pr, numbers.Real) for pr in plotrange): plotrange = ["All", plotrange] elif all(check_range(pr) for pr in plotrange): pass else: evaluation.message(self.get_name(), "prng", plotrange_option) plotrange = ["Automatic", "Automatic"] x_range, y_range = plotrange[0], plotrange[1] assert x_range in ("Automatic", "All") or isinstance(x_range, list) assert y_range in ("Automatic", "All") or isinstance(y_range, list) # Filling option # TODO: Fill between corresponding points in two datasets: filling_option = self.get_option(options, "Filling", evaluation) filling = filling_option.to_python(n_evaluation=evaluation) if filling in ["Top", "Bottom", "Axis"] or isinstance(filling, numbers.Real): pass else: filling = None # Mathematica does not even check that filling is sane # Joined Option joined_option = self.get_option(options, "Joined", evaluation) joined = joined_option.to_python() if joined not in [True, False]: evaluation.message(plot_name, "joind", joined_option, expr) joined = False if isinstance(all_points, list) and len(all_points) != 0: if all(not isinstance(point, list) for point in all_points): # Only y values given all_points = [[[float(i), all_points[i]] for i in range(len(all_points))]] elif all(isinstance(line, list) and len(line) == 2 for line in all_points): # Single list of (x,y) pairs all_points = [all_points] elif all(isinstance(line, list) for line in all_points): # List of lines if all(isinstance(point, list) and len(point) == 2 for line in all_points for point in line): pass elif all(not isinstance(point, list) for line in all_points for point in line): all_points = [[[float(i), line[i]] for i in range(len(line))] for line in all_points] else: return else: return else: return y_range = get_plot_range( [y for line in all_points for x, y in line], [y for line in all_points for x, y in line], y_range ) x_range = get_plot_range( [x for line in all_points for x, y in line], [x for line in all_points for x, y in line], x_range ) if filling == "Axis": # TODO: Handle arbitary axis intercepts filling = 0.0 elif filling == "Bottom": filling = y_range[0] elif filling == "Top": filling = y_range[1] hue = 0.67 hue_pos = 0.236068 hue_neg = -0.763932 graphics = [] for indx, line in enumerate(all_points): graphics.append(Expression("Hue", hue, 0.6, 0.6)) if joined: graphics.append(Expression("Line", from_python(line))) if filling is not None: graphics.append(Expression("Hue", hue, 0.6, 0.6, 0.2)) fill_area = list(line) fill_area.append([x_range[1], filling]) fill_area.append([x_range[0], filling]) graphics.append(Expression("Polygon", from_python(fill_area))) else: graphics.append(Expression("Point", from_python(line))) if filling is not None: for point in line: graphics.append(Expression("Line", from_python([[point[0], filling], [point[0], point[1]]]))) if indx % 4 == 0: hue += hue_pos else: hue += hue_neg if hue > 1: hue -= 1 if hue < 0: hue += 1 options["PlotRange"] = from_python([x_range, y_range]) return Expression("Graphics", Expression("List", *graphics), *options_to_rules(options))
def apply(self, functions, x, start, stop, evaluation, options): 'Plot[functions_, {x_Symbol, start_, stop_}, OptionsPattern[Plot]]' expr = Expression('Plot', functions, Expression('List', x, start, stop), *options_to_rules(options)) if functions.has_form('List', None): functions = functions.leaves else: functions = [functions] x = x.get_name() try: start = start.to_number(n_evaluation=evaluation) except NumberError: evaluation.message('Plot', 'plln', start, expr) return try: stop = stop.to_number(n_evaluation=evaluation) except NumberError: evaluation.message('Plot', 'plln', stop, expr) return def eval_f(f, x_value): value = dynamic_scoping(f.evaluate, {x: x_value}, evaluation) value = chop(value).get_real_value() return value hue = 0.67 hue_pos = 0.236068 hue_neg = -0.763932 graphics = [] for index, f in enumerate(functions): points = [] continuous = False steps = 50 d = (stop - start) / steps for index in range(steps + 1): x_value = start + index * d y = eval_f(f, Real(x_value)) if y is not None: point = (x_value, y) if continuous: points[-1].append(point) else: points.append([point]) continuous = True else: continuous = False graphics.append(Expression('Hue', hue, 0.6, 0.6)) graphics.append( Expression( 'Line', Expression( 'List', *(Expression( 'List', *(Expression('List', Real(x), Real(y)) for x, y in line)) for line in points)))) if index % 4 == 0: hue += hue_pos else: hue += hue_neg if hue > 1: hue -= 1 if hue < 0: hue += 1 return Expression('Graphics', Expression('List', *graphics), *options_to_rules(options))
class DensityPlot(Builtin): """ <dl> <dt>'DensityPlot[$f$, {$x$, $xmin$, $xmax$}, {$y$, $ymin$, $ymax$}]' <dd>plots a density plot of $f$ with $x$ ranging from $xmin$ to $xmax$ and $y$ ranging from $ymin$ to $ymax$. </dl> >> DensityPlot[x ^ 2 + 1 / y, {x, -1, 1}, {y, 1, 4}] = -Graphics- """ from graphics import Graphics attributes = ('HoldAll', ) options = Graphics.options.copy() options.update({ 'Axes': 'False', 'AspectRatio': '1', 'Frame': 'True', 'ColorFunction': 'Automatic', 'ColorFunctionScaling': 'True', }) def apply(self, f, x, xstart, xstop, y, ystart, ystop, evaluation, options): 'DensityPlot[f_, {x_Symbol, xstart_, xstop_}, {y_Symbol, ystart_, ystop_}, OptionsPattern[DensityPlot]]' x = x.get_name() y = y.get_name() color_function = self.get_option(options, 'ColorFunction', evaluation, pop=True) color_function_scaling = self.get_option(options, 'ColorFunctionScaling', evaluation, pop=True) color_function_min = color_function_max = None if color_function.get_name() == 'Automatic': color_function = String('LakeColors') if color_function.get_string_value(): func = Expression( 'ColorData', color_function.get_string_value()).evaluate(evaluation) if func.has_form('ColorDataFunction', 4): color_function_min = func.leaves[2].leaves[0].get_real_value() color_function_max = func.leaves[2].leaves[1].get_real_value() color_function = Expression( 'Function', Expression(func.leaves[3], Expression('Slot', 1))) else: evaluation.message('DensityPlot', 'color', func) return if color_function.has_form('ColorDataFunction', 4): color_function_min = color_function.leaves[2].leaves[ 0].get_real_value() color_function_max = color_function.leaves[2].leaves[ 1].get_real_value() color_function_scaling = color_function_scaling.is_true() try: xstart, xstop, ystart, ystop = [ value.to_number(n_evaluation=evaluation) for value in (xstart, xstop, ystart, ystop) ] except NumberError, exc: expr = Expression('DensityPlot', f, Expression('List', x, xstart, xstop), Expression('List', y, ystart, ystop), *options_to_rules(options)) evaluation.message('DensityPlot', 'plln', exc.value, expr) return #print "Initialized" stored = {} def eval_f(x_value, y_value): value = stored.get((x_value, y_value), False) if value == False: value = dynamic_scoping(f.evaluate, { x: Real(x_value), y: Real(y_value) }, evaluation) value = chop(value).get_real_value() value = float(value) stored[(x_value, y_value)] = value return value v_borders = [None, None] triangles = [] eps = 0.01 def triangle(x1, y1, x2, y2, x3, y3, depth=None): if depth is None: x1, x2, x3 = [ xstart + value * (xstop - xstart) for value in (x1, x2, x3) ] y1, y2, y3 = [ ystart + value * (ystop - ystart) for value in (y1, y2, y3) ] depth = 0 v1, v2, v3 = eval_f(x1, y1), eval_f(x2, y2), eval_f(x3, y3) for v in (v1, v2, v3): if v_borders[0] is None or v < v_borders[0]: v_borders[0] = v if v_borders[1] is None or v > v_borders[1]: v_borders[1] = v if v1 is None or v2 is None or v3 is None: return limit = (v_borders[1] - v_borders[0]) * eps if depth < 2: if abs(v1 - v2) > limit: triangle(x1, y1, x3, y3, (x1 + x2) / 2, (y1 + y2) / 2, depth + 1) triangle(x2, y2, x3, y3, (x1 + x2) / 2, (y1 + y2) / 2, depth + 1) return if abs(v2 - v3) > limit: triangle(x1, y1, x2, y2, (x2 + x3) / 2, (y2 + y3) / 2, depth + 1) triangle(x1, y1, x3, y3, (x2 + x3) / 2, (y2 + y3) / 2, depth + 1) return if abs(v1 - v3) > limit: triangle(x2, y2, x1, y1, (x1 + x3) / 2, (y1 + y3) / 2, depth + 1) triangle(x2, y2, x3, y3, (x1 + x3) / 2, (y1 + y3) / 2, depth + 1) return triangles.append([(x1, y1, v1), (x2, y2, v2), (x3, y3, v3)]) points = 7 num = points * 1.0 for xi in range(points): for yi in range(points): triangle(xi / num, yi / num, (xi + 1) / num, (yi + 1) / num, (xi + 1) / num, yi / num) triangle(xi / num, yi / num, (xi + 1) / num, (yi + 1) / num, xi / num, (yi + 1) / num) v_min = v_max = None #if color_function_scaling: for t in triangles: for tx, ty, v in t: if v_min is None or v < v_min: v_min = v if v_max is None or v > v_max: v_max = v v_range = v_max - v_min if v_range == 0: v_range = 1 if color_function.has_form('ColorDataFunction', 4): color_func = color_function.leaves[3] else: color_func = color_function if color_function_scaling and color_function_min is not None and color_function_max is not None: color_function_range = color_function_max - color_function_min colors = {} def eval_color(x, y, v): #v_lookup = int(v * 100) #if color_function_scaling: v_scaled = (v - v_min) / v_range if color_function_scaling and color_function_min is not None and color_function_max is not None: v_color_scaled = color_function_min + v_scaled * color_function_range else: v_color_scaled = v v_lookup = int( v_scaled * 100 + 0.5) # calculate and store 100 different shades max. value = colors.get(v_lookup) if value is None: #print "Calc" #print "Scale" #print "Expression" #print "Calc color for %f" % v_scaled value = Expression(color_func, Real(v_color_scaled)) #print "Evaluate %s" % value value = value.evaluate(evaluation) #value = Expression('RGBColor', Real(0.5), Real(0.5), Real(0.5)) #print "Set" colors[v_lookup] = value return value #print "Points" points = [] vertex_colors = [] for p1, p2, p3 in triangles: #print "Triangle %s,%s,%s" % (p1, p2, p3) c1, c2, c3 = eval_color(*p1), eval_color(*p2), eval_color(*p3) #print "Append" points.append( Expression('List', Expression('List', *p1[:2]), Expression('List', *p2[:2]), Expression('List', *p3[:2]))) vertex_colors.append(Expression('List', c1, c2, c3)) #print "Polygon" polygon = Expression( 'Polygon', Expression('List', *points), Expression('Rule', Symbol('VertexColors'), Expression('List', *vertex_colors))) #print "Result" result = Expression('Graphics', polygon, *options_to_rules(options)) #print "Return" return result
def apply(self, functions, x, xstart, xstop, y, ystart, ystop, evaluation, options): '''%(name)s[functions_, {x_Symbol, xstart_, xstop_}, {y_Symbol, ystart_, ystop_}, OptionsPattern[%(name)s]]''' xexpr_limits = Expression('List', x, xstart, xstop) yexpr_limits = Expression('List', y, ystart, ystop) expr = Expression(self.get_name(), functions, xexpr_limits, yexpr_limits, *options_to_rules(options)) functions = self.get_functions_param(functions) plot_name = self.get_name() try: xstart, xstop, ystart, ystop = \ [value.to_number(n_evaluation=evaluation) for value in (xstart, xstop, ystart, ystop)] except NumberError: expr = Expression( plot_name, functions, Expression('List', x, xstart, xstop), Expression('List', y, ystart, ystop), *options_to_rules(options)) evaluation.message(plot_name, 'plln', value, expr) return if ystart >= ystop: evaluation.message(plot_name, 'plln', ystop, expr) return if xstart >= xstop: evaluation.message(plot_name, 'plln', xstop, expr) return # Mesh Option mesh_option = self.get_option(options, 'Mesh', evaluation) mesh = mesh_option.to_python() if mesh not in ['None', 'Full', 'All']: evaluation.message('Mesh', 'ilevels', mesh_option) mesh = 'Full' # PlotPoints Option plotpoints_option = self.get_option(options, 'PlotPoints', evaluation) plotpoints = plotpoints_option.to_python() def check_plotpoints(steps): if isinstance(steps, int) and steps > 0: return True return False if plotpoints == 'None': plotpoints = [7, 7] elif check_plotpoints(plotpoints): plotpoints = [plotpoints, plotpoints] if not (isinstance(plotpoints, list) and len(plotpoints) == 2 and check_plotpoints(plotpoints[0]) and check_plotpoints(plotpoints[1])): evaluation.message(self.get_name(), 'invpltpts', plotpoints) plotpoints = [7, 7] graphics = [] for indx, f in enumerate(functions): stored = {} def eval_f(x_value, y_value): value = stored.get((x_value, y_value), False) if value is False: value = quiet_evaluate(f, {x: Real( x_value), y: Real(y_value)}, evaluation) # value = dynamic_scoping( # f.evaluate, {x: Real(x_value), y: Real(y_value)}, # evaluation) # value = chop(value).get_real_value() if value is not None: value = float(value) stored[(x_value, y_value)] = value return value v_borders = [None, None] triangles = [] eps = 0.01 def triangle(x1, y1, x2, y2, x3, y3, depth=None): if depth is None: x1, x2, x3 = [xstart + value * (xstop - xstart) for value in (x1, x2, x3)] y1, y2, y3 = [ystart + value * (ystop - ystart) for value in (y1, y2, y3)] depth = 0 v1, v2, v3 = eval_f(x1, y1), eval_f(x2, y2), eval_f(x3, y3) for v in (v1, v2, v3): if v is not None: if v_borders[0] is None or v < v_borders[0]: v_borders[0] = v if v_borders[1] is None or v > v_borders[1]: v_borders[1] = v if v1 is None or v2 is None or v3 is None: if depth < 2: triangle(x1, y1, (x1 + x2) / 2, (y1 + y2) / 2, (x1 + x3) / 2, (y1 + y3) / 2, depth + 1) triangle(x2, y2, (x1 + x2) / 2, (y1 + y2) / 2, (x2 + x3) / 2, (y2 + y3) / 2, depth + 1) triangle(x3, y3, (x1 + x3) / 2, (y1 + y3) / 2, (x2 + x3) / 2, (y2 + y3) / 2, depth + 1) triangle((x1 + x2) / 2, (y1 + y2) / 2, (x2 + x3) / 2, (y2 + y3) / 2, (x1 + x3) / 2, (y1 + y3) / 2, depth + 1) return limit = (v_borders[1] - v_borders[0]) * eps if depth < 2: if abs(v1 - v2) > limit: triangle(x1, y1, x3, y3, (x1 + x2) / 2, (y1 + y2) / 2, depth + 1) triangle(x2, y2, x3, y3, (x1 + x2) / 2, (y1 + y2) / 2, depth + 1) return if abs(v2 - v3) > limit: triangle(x1, y1, x2, y2, ( x2 + x3) / 2, (y2 + y3) / 2, depth + 1) triangle(x1, y1, x3, y3, ( x2 + x3) / 2, (y2 + y3) / 2, depth + 1) return if abs(v1 - v3) > limit: triangle(x2, y2, x1, y1, ( x1 + x3) / 2, (y1 + y3) / 2, depth + 1) triangle(x2, y2, x3, y3, ( x1 + x3) / 2, (y1 + y3) / 2, depth + 1) return triangles.append([(x1, y1, v1), (x2, y2, v2), (x3, y3, v3)]) numx = plotpoints[0] * 1.0 numy = plotpoints[1] * 1.0 for xi in range(plotpoints[0]): for yi in range(plotpoints[1]): triangle(xi / numx, yi / numy, (xi + 1) / numx, (yi + 1) / numy, (xi + 1) / numx, yi / numy) triangle(xi / numx, yi / numy, (xi + 1) / numx, (yi + 1) / numy, xi / numx, (yi + 1) / numy) # Mesh should just be looking up stored values mesh_points = [] for xi in range(plotpoints[0] + 1): xval = xstart + xi / numx * (xstop - xstart) mesh_row = [] for yi in range(plotpoints[1] + 1): yval = ystart + yi / numy * (ystop - ystart) z = eval_f(xval, yval) if z is not None: mesh_row.append((xval, yval, z)) mesh_points.append(mesh_row) for yi in range(plotpoints[1] + 1): yval = ystart + yi / numy * (ystop - ystart) mesh_col = [] for xi in range(plotpoints[0] + 1): xval = xstart + xi / numx * (xstop - xstart) z = eval_f(xval, yval) if z is not None: mesh_col.append((xval, yval, z)) mesh_points.append(mesh_col) # Fix the grid near recursions x_grids = [xstart + (xi / numx) * (xstop - xstart) for xi in range(plotpoints[0] + 1)] y_grids = [ystart + (yi / numy) * (ystop - ystart) for yi in range(plotpoints[1] + 1)] for (xval, yval) in stored.keys(): if xval in x_grids: x_index = int((xval - xstart) * numx / (xstop - xstart) + 0.5) z = eval_f(xval, yval) if z is not None: mesh_points[x_index].append((xval, yval, z)) if yval in y_grids: y_index = int((yval - ystart) * numy / (ystop - ystart) + plotpoints[0] + 1.5) z = eval_f(xval, yval) if z is not None: mesh_points[y_index].append((xval, yval, z)) for mesh_line in mesh_points: mesh_line.sort() v_min = v_max = None for t in triangles: for tx, ty, v in t: if v_min is None or v < v_min: v_min = v if v_max is None or v > v_max: v_max = v graphics.extend(self.construct_graphics( triangles, mesh_points, v_min, v_max, options, evaluation)) return self.final_graphics(graphics, options)
def final_graphics(self, graphics, options): return Expression("Graphics", Expression("List", *graphics), *options_to_rules(options))