def process_option(self, name, value): if name == "System`VertexColors": if not value.has_form("List", None): raise BoxConstructError black = RGBColor(components=[0, 0, 0, 1]) self.vertex_colors = [[black] * len(line) for line in self.lines] colors = value.leaves if not self.multi_parts: colors = [Expression(SymbolList, *colors)] for line_index, line in enumerate(self.lines): if line_index >= len(colors): break line_colors = colors[line_index] if not line_colors.has_form("List", None): continue for index, color in enumerate(line_colors.leaves): if index >= len(self.vertex_colors[line_index]): break try: self.vertex_colors[line_index][index] = _Color.create( color) except ColorError: continue else: raise BoxConstructError
def apply(self, colors, u, evaluation): "Blend[{colors___}, u_]" colors_orig = colors try: colors = [_Color.create(color) for color in colors.get_sequence()] if not colors: raise ColorError except ColorError: evaluation.message("Blend", "arg", Expression(SymbolList, colors_orig)) return if u.has_form("List", None): values = [value.round_to_float(evaluation) for value in u.leaves] if None in values: values = None if len(u.leaves) != len(colors): values = None use_list = True else: values = u.round_to_float(evaluation) if values is None: pass elif values > 1: values = 1.0 elif values < 0: values = 0.0 use_list = False if values is None: return evaluation.message("Blend", "argl", u, Expression(SymbolList, colors_orig)) if use_list: return self.do_blend(colors, values).to_expr() else: x = values pos = int(floor(x * (len(colors) - 1))) x = (x - pos * 1.0 / (len(colors) - 1)) * (len(colors) - 1) if pos == len(colors) - 1: return colors[-1].to_expr() else: return self.do_blend(colors[pos:(pos + 2)], [1 - x, x]).to_expr()
def _prepare_elements(self, leaves, options, neg_y=False, max_width=None): if not leaves: raise BoxConstructError self.graphics_options = self.get_option_values(leaves[1:], **options) background = self.graphics_options["System`Background"] if (isinstance(background, Symbol) and background.get_name() == "System`Automatic"): self.background_color = None else: self.background_color = _Color.create(background) base_width, base_height, size_multiplier, size_aspect = self._get_image_size( options, self.graphics_options, max_width) plot_range = self.graphics_options["System`PlotRange"].to_python() if plot_range == "System`Automatic": plot_range = ["System`Automatic", "System`Automatic"] if not isinstance(plot_range, list) or len(plot_range) != 2: raise BoxConstructError evaluation = options.get("evaluation", None) if evaluation is None: evaluation = self.evaluation elements = GraphicsElements(leaves[0], evaluation, neg_y) axes = [] # to be filled further down def calc_dimensions(final_pass=True): """ calc_dimensions gets called twice: In the first run (final_pass = False, called inside _prepare_elements), the extent of all user-defined graphics is determined. Axes are created accordingly. In the second run (final_pass = True, called from outside), the dimensions of these axes are taken into account as well. This is also important to size absolutely sized objects correctly (e.g. values using AbsoluteThickness). """ # always need to compute extent if size aspect is automatic if "System`Automatic" in plot_range or size_aspect is None: xmin, xmax, ymin, ymax = elements.extent() else: xmin = xmax = ymin = ymax = None if (final_pass and any(x for x in axes) and plot_range != ["System`Automatic", "System`Automatic"]): # Take into account the dimensions of axes and axes labels # (they should be displayed completely even when a specific # PlotRange is given). exmin, exmax, eymin, eymax = elements.extent( completely_visible_only=True) else: exmin = exmax = eymin = eymax = None def get_range(min, max): if max < min: min, max = max, min elif min == max: if min < 0: min, max = 2 * min, 0 elif min > 0: min, max = 0, 2 * min else: min, max = -1, 1 return min, max try: if plot_range[0] == "System`Automatic": if xmin is None and xmax is None: xmin = 0 xmax = 1 elif xmin == xmax: xmin -= 1 xmax += 1 elif isinstance(plot_range[0], list) and len( plot_range[0]) == 2: xmin, xmax = list(map(float, plot_range[0])) xmin, xmax = get_range(xmin, xmax) xmin = elements.translate((xmin, 0))[0] xmax = elements.translate((xmax, 0))[0] if exmin is not None and exmin < xmin: xmin = exmin if exmax is not None and exmax > xmax: xmax = exmax else: raise BoxConstructError if plot_range[1] == "System`Automatic": if ymin is None and ymax is None: ymin = 0 ymax = 1 elif ymin == ymax: ymin -= 1 ymax += 1 elif isinstance(plot_range[1], list) and len( plot_range[1]) == 2: ymin, ymax = list(map(float, plot_range[1])) ymin, ymax = get_range(ymin, ymax) ymin = elements.translate((0, ymin))[1] ymax = elements.translate((0, ymax))[1] if ymin > ymax: ymin, ymax = ymax, ymin if eymin is not None and eymin < ymin: ymin = eymin if eymax is not None and eymax > ymax: ymax = eymax else: raise BoxConstructError except (ValueError, TypeError): raise BoxConstructError w = 0 if (xmin is None or xmax is None) else xmax - xmin h = 0 if (ymin is None or ymax is None) else ymax - ymin if size_aspect is None: aspect = h / w else: aspect = size_aspect height = base_height if height is None: height = base_width * aspect width = height / aspect if width > base_width: width = base_width height = width * aspect height = height width *= size_multiplier height *= size_multiplier return xmin, xmax, ymin, ymax, w, h, width, height xmin, xmax, ymin, ymax, w, h, width, height = calc_dimensions( final_pass=False) elements.set_size(xmin, ymin, w, h, width, height) xmin -= w * 0.02 xmax += w * 0.02 ymin -= h * 0.02 ymax += h * 0.02 axes.extend( self.create_axes(elements, self.graphics_options, xmin, xmax, ymin, ymax)) return elements, calc_dimensions
def _prepare_elements(self, leaves, options, max_width=None): if not leaves: raise BoxConstructError self.graphics_options = self.get_option_values(leaves[1:], **options) background = self.graphics_options["System`Background"] if (isinstance(background, Symbol) and background.get_name() == "System`Automatic"): self.background_color = None else: self.background_color = _Color.create(background) evaluation = options["evaluation"] base_width, base_height, size_multiplier, size_aspect = self._get_image_size( options, self.graphics_options, max_width) # TODO: Handle ImageScaled[], and Scaled[] lighting_option = self.graphics_options["System`Lighting"] lighting = lighting_option.to_python() # can take symbols or strings self.lighting = [] if lighting == "System`Automatic": self.lighting = [ { "type": "Ambient", "color": [0.3, 0.2, 0.4] }, { "type": "Directional", "color": [0.8, 0.0, 0.0], "position": [2, 0, 2], }, { "type": "Directional", "color": [0.0, 0.8, 0.0], "position": [2, 2, 2], }, { "type": "Directional", "color": [0.0, 0.0, 0.8], "position": [0, 2, 2], }, ] elif lighting == "Neutral": # Lighting->"Neutral" self.lighting = [ { "type": "Ambient", "color": [0.3, 0.3, 0.3] }, { "type": "Directional", "color": [0.3, 0.3, 0.3], "position": [2, 0, 2], }, { "type": "Directional", "color": [0.3, 0.3, 0.3], "position": [2, 2, 2], }, { "type": "Directional", "color": [0.3, 0.3, 0.3], "position": [0, 2, 2], }, ] elif lighting == "System`None": pass elif isinstance(lighting, list) and all( isinstance(light, list) for light in lighting): for light in lighting: if light[0] in [ '"Ambient"', '"Directional"', '"Point"', '"Spot"' ]: try: head = light[1].get_head_name() except AttributeError: break color = get_class(head)(light[1]) if light[0] == '"Ambient"': self.lighting.append({ "type": "Ambient", "color": color.to_rgba() }) elif light[0] == '"Directional"': position = [0, 0, 0] if isinstance(light[2], list): if len(light[2]) == 3: position = light[2] if len(light[2]) == 2 and all( # noqa isinstance(p, list) and len(p) == 3 for p in light[2]): position = [ light[2][0][i] - light[2][1][i] for i in range(3) ] self.lighting.append({ "type": "Directional", "color": color.to_rgba(), "position": position, }) elif light[0] == '"Point"': position = [0, 0, 0] if isinstance(light[2], list) and len(light[2]) == 3: position = light[2] self.lighting.append({ "type": "Point", "color": color.to_rgba(), "position": position, }) elif light[0] == '"Spot"': position = [0, 0, 1] target = [0, 0, 0] if isinstance(light[2], list): if len(light[2]) == 2: if (isinstance(light[2][0], list) and len(light[2][0]) == 3 # noqa ): position = light[2][0] if (isinstance(light[2][1], list) and len(light[2][1]) == 3 # noqa ): target = light[2][1] if len(light[2]) == 3: position = light[2] angle = light[3] self.lighting.append({ "type": "Spot", "color": color.to_rgba(), "position": position, "target": target, "angle": angle, }) else: evaluation.message("Graphics3D", "invlight", lighting_option) # ViewPoint Option viewpoint_option = self.graphics_options["System`ViewPoint"] viewpoint = viewpoint_option.to_python(n_evaluation=evaluation) if isinstance(viewpoint, list) and len(viewpoint) == 3: if all(isinstance(x, numbers.Real) for x in viewpoint): # TODO Infinite coordinates e.g. {0, 0, Infinity} pass else: try: viewpoint = { "Above": [0, 0, 2], "Below": [0, 0, -2], "Front": [0, -2, 0], "Back": [0, 2, 0], "Left": [-2, 0, 0], "Right": [2, 0, 0], }[viewpoint] except KeyError: # evaluation.message() # TODO viewpoint = [1.3, -2.4, 2] assert (isinstance(viewpoint, list) and len(viewpoint) == 3 and all(isinstance(x, numbers.Real) for x in viewpoint)) self.viewpoint = viewpoint # TODO Aspect Ratio # aspect_ratio = self.graphics_options['AspectRatio'].to_python() boxratios = self.graphics_options["System`BoxRatios"].to_python() if boxratios == "System`Automatic": boxratios = ["System`Automatic"] * 3 else: boxratios = boxratios if not isinstance(boxratios, list) or len(boxratios) != 3: raise BoxConstructError plot_range = self.graphics_options["System`PlotRange"].to_python() if plot_range == "System`Automatic": plot_range = ["System`Automatic"] * 3 if not isinstance(plot_range, list) or len(plot_range) != 3: raise BoxConstructError elements = Graphics3DElements(leaves[0], evaluation) def calc_dimensions(final_pass=True): if "System`Automatic" in plot_range: xmin, xmax, ymin, ymax, zmin, zmax = elements.extent() else: xmin = xmax = ymin = ymax = zmin = zmax = None try: if plot_range[0] == "System`Automatic": if xmin is None and xmax is None: xmin = 0 xmax = 1 elif xmin == xmax: xmin -= 1 xmax += 1 elif isinstance(plot_range[0], list) and len( plot_range[0]) == 2: xmin, xmax = list(map(float, plot_range[0])) xmin = elements.translate((xmin, 0, 0))[0] xmax = elements.translate((xmax, 0, 0))[0] else: raise BoxConstructError if plot_range[1] == "System`Automatic": if ymin is None and ymax is None: ymin = 0 ymax = 1 elif ymin == ymax: ymin -= 1 ymax += 1 elif isinstance(plot_range[1], list) and len( plot_range[1]) == 2: ymin, ymax = list(map(float, plot_range[1])) ymin = elements.translate((0, ymin, 0))[1] ymax = elements.translate((0, ymax, 0))[1] else: raise BoxConstructError if plot_range[2] == "System`Automatic": if zmin is None and zmax is None: zmin = 0 zmax = 1 elif zmin == zmax: zmin -= 1 zmax += 1 elif isinstance(plot_range[1], list) and len( plot_range[1]) == 2: zmin, zmax = list(map(float, plot_range[2])) zmin = elements.translate((0, 0, zmin))[2] zmax = elements.translate((0, 0, zmax))[2] else: raise BoxConstructError except (ValueError, TypeError): raise BoxConstructError boxscale = [1.0, 1.0, 1.0] if boxratios[0] != "System`Automatic": boxscale[0] = boxratios[0] / (xmax - xmin) if boxratios[1] != "System`Automatic": boxscale[1] = boxratios[1] / (ymax - ymin) if boxratios[2] != "System`Automatic": boxscale[2] = boxratios[2] / (zmax - zmin) if final_pass: xmin *= boxscale[0] xmax *= boxscale[0] ymin *= boxscale[1] ymax *= boxscale[1] zmin *= boxscale[2] zmax *= boxscale[2] # Rescale lighting for i, light in enumerate(self.lighting): if self.lighting[i]["type"] != "Ambient": self.lighting[i]["position"] = [ light["position"][j] * boxscale[j] for j in range(3) ] if self.lighting[i]["type"] == "Spot": self.lighting[i]["target"] = [ light["target"][j] * boxscale[j] for j in range(3) ] # Rescale viewpoint self.viewpoint = [ vp * max([xmax - xmin, ymax - ymin, zmax - zmin]) for vp in self.viewpoint ] w = 0 if (xmin is None or xmax is None) else xmax - xmin h = 0 if (ymin is None or ymax is None) else ymax - ymin return xmin, xmax, ymin, ymax, zmin, zmax, boxscale, w, h xmin, xmax, ymin, ymax, zmin, zmax, boxscale, w, h = calc_dimensions( final_pass=False) axes, ticks, ticks_style = self.create_axes( elements, self.graphics_options, xmin, xmax, ymin, ymax, zmin, zmax, boxscale, ) return elements, axes, ticks, ticks_style, calc_dimensions, boxscale