def plotstatsimg(cbf, ref_vol, plot_params=None, order=('z', 'x', 'y'), estimate_brightness=False, label=None, compress='auto'): """ plot zsts """ plot_params = {} if plot_params is None else plot_params image_nii = _3d_in_file(cbf) data = image_nii.get_fdata() from nilearn.image import threshold_img bbox_nii = threshold_img(nb.load(cbf), 1) cuts = cuts_from_bbox(bbox_nii, cuts=7) out_files = [] if estimate_brightness: plot_params = robust_set_limits(data, plot_params) # Plot each cut axis for i, mode in enumerate(list(order)): plot_params['display_mode'] = mode plot_params['cut_coords'] = cuts[mode] plot_params['draw_cross'] = False plot_params['symmetric_cbar'] = False plot_params['threshold'] = 0.05 plot_params['vmax'] = 90 plot_params['colorbar'] = False plot_params['cmap'] = 'gray' if i == 0: plot_params['title'] = label plot_params['colorbar'] = False else: plot_params['title'] = None # Generate nilearn figure from nilearn.plotting import plot_stat_map display = plot_stat_map(stat_map_img=cbf, bg_img=ref_vol, **plot_params) svg = extract_svg(display, compress=compress) display.close() from lxml import etree from .. import NIWORKFLOWS_LOG # Find and replace the figure_1 id. try: xml_data = etree.fromstring(svg) except etree.XMLSyntaxError as e: NIWORKFLOWS_LOG.info(e) return svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def make_symbol(container: sg.SVGFigure, name, filepath): icon = sg.fromfile(filepath) size = get_view_box(icon) symbol = sg.fromstring( symbol_tpl.format(size['x'], size['y'], size['width'], size['height'], name)) symbol.append(icon.getroot()) container.append(symbol)
def plot_segs(image_nii, seg_niis, out_file, bbox_nii=None, masked=False, colors=None, compress='auto', **plot_params): """ plot segmentation as contours over the image (e.g. anatomical). seg_niis should be a list of files. mask_nii helps determine the cut coordinates. plot_params will be passed on to nilearn plot_* functions. If seg_niis is a list of size one, it behaves as if it was plotting the mask. """ plot_params = {} if plot_params is None else plot_params image_nii = _3d_in_file(image_nii) data = image_nii.get_data() plot_params = robust_set_limits(data, plot_params) bbox_nii = nb.load(image_nii if bbox_nii is None else bbox_nii) if masked: bbox_nii = nlimage.threshold_img(bbox_nii, 1e-3) cuts = cuts_from_bbox(bbox_nii, cuts=7) plot_params['colors'] = colors or plot_params.get('colors', None) out_files = [] for d in plot_params.pop('dimensions', ('z', 'x', 'y')): plot_params['display_mode'] = d plot_params['cut_coords'] = cuts[d] svg = _plot_anat_with_contours(image_nii, segs=seg_niis, compress=compress, **plot_params) # Find and replace the figure_1 id. try: xml_data = etree.fromstring(svg) except etree.XMLSyntaxError as e: NIWORKFLOWS_LOG.info(e) return find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', 'segmentation-%s-%s' % (d, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def plot_acpc(acpc_registered_img, div_id, plot_params=None, order=('z', 'x', 'y'), cuts=None, estimate_brightness=False, label=None, compress='auto'): """ Plot the results of an AC-PC transformation. """ plot_params = plot_params or {} # Do the low-b image first out_files = [] if estimate_brightness: plot_params = robust_set_limits( acpc_registered_img.get_fdata(dtype='float32').reshape(-1), plot_params) # Plot each cut axis for low-b for i, mode in enumerate(list(order)): plot_params['display_mode'] = mode plot_params['cut_coords'] = [-20.0, 0.0, 20.0] if i == 0: plot_params['title'] = label else: plot_params['title'] = None # Generate nilearn figure display = plot_anat(acpc_registered_img, **plot_params) for coord, axis in display.axes.items(): axis.ax.axvline(0, lw=1) axis.ax.axhline(0, lw=1) svg = extract_svg(display, compress=compress) display.close() # Find and replace the figure_1 id. xml_data = etree.fromstring(svg) find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', '%s-%s-%s' % (div_id, mode, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def plot_segs(image_nii, seg_niis, out_file, bbox_nii=None, masked=False, colors=None, compress='auto', **plot_params): """ plot segmentation as contours over the image (e.g. anatomical). seg_niis should be a list of files. mask_nii helps determine the cut coordinates. plot_params will be passed on to nilearn plot_* functions. If seg_niis is a list of size one, it behaves as if it was plotting the mask. """ plot_params = {} if plot_params is None else plot_params image_nii = _3d_in_file(image_nii) data = image_nii.get_data() plot_params = robust_set_limits(data, plot_params) bbox_nii = nb.load(image_nii if bbox_nii is None else bbox_nii) if masked: bbox_nii = nlimage.threshold_img(bbox_nii, 1e-3) cuts = cuts_from_bbox(bbox_nii, cuts=7) plot_params['colors'] = colors or plot_params.get('colors', None) out_files = [] for d in plot_params.pop('dimensions', ('z', 'x', 'y')): plot_params['display_mode'] = d plot_params['cut_coords'] = cuts[d] svg = _plot_anat_with_contours(image_nii, segs=seg_niis, compress=compress, **plot_params) # Find and replace the figure_1 id. try: xml_data = etree.fromstring(svg) except etree.XMLSyntaxError as e: NIWORKFLOWS_LOG.info(e) return find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', 'segmentation-%s-%s' % (d, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def _pixel_viewbox_of_figure(figure: SVGFigure, resolution_dpi: float) -> Tuple[float, float]: # NOTE: Attribute "viewbox" (with lowercase "b") is ignored by Inkscape 1.0.1 # in practice so I'm following Inkscape here and ignore the lowercase # edition, too. try: view_box = figure.root.attrib['viewBox'] except KeyError: left_str, top_str = '0', '0' width_str, height_str = figure.get_size() else: left_str, top_str, width_str, height_str = view_box.split() return ( _length_string_to_pixel(left_str, resolution_dpi), _length_string_to_pixel(top_str, resolution_dpi), _length_string_to_pixel(width_str, resolution_dpi), _length_string_to_pixel(height_str, resolution_dpi), )
def plot_denoise(lowb_nii, highb_nii, div_id, plot_params=None, highb_plot_params=None, order=('z', 'x', 'y'), cuts=None, estimate_brightness=False, label=None, lowb_contour=None, highb_contour=None, compress='auto', overlay=None, overlay_params=None): """ Plot the foreground and background views. Default order is: axial, coronal, sagittal Updated version from sdcflows """ plot_params = plot_params or {} highb_plot_params = highb_plot_params or {} # Use default MNI cuts if none defined if cuts is None: raise NotImplementedError # Do the low-b image first out_files = [] if estimate_brightness: plot_params = robust_set_limits( lowb_nii.get_fdata(dtype='float32').reshape(-1), plot_params) # Plot each cut axis for low-b for i, mode in enumerate(list(order)): plot_params['display_mode'] = mode plot_params['cut_coords'] = cuts[mode] if i == 0: plot_params['title'] = label + ": low-b" else: plot_params['title'] = None # Generate nilearn figure display = plot_anat(lowb_nii, **plot_params) if lowb_contour is not None: display.add_contours(lowb_contour, linewidths=1) svg = extract_svg(display, compress=compress) display.close() # Find and replace the figure_1 id. xml_data = etree.fromstring(svg) find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', '%s-%s-%s' % (div_id, mode, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) # Plot each cut axis for high-b if estimate_brightness: highb_plot_params = robust_set_limits( highb_nii.get_fdata(dtype='float32').reshape(-1), highb_plot_params) for i, mode in enumerate(list(order)): highb_plot_params['display_mode'] = mode highb_plot_params['cut_coords'] = cuts[mode] if i == 0: highb_plot_params['title'] = label + ': high-b' else: highb_plot_params['title'] = None # Generate nilearn figure display = plot_anat(highb_nii, **highb_plot_params) if highb_contour is not None: display.add_contours(highb_contour, linewidths=1) svg = extract_svg(display, compress=compress) display.close() # Find and replace the figure_1 id. xml_data = etree.fromstring(svg) find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', '%s-%s-%s' % (div_id, mode, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def plot_registration( anat_nii, div_id, plot_params=None, order=("z", "x", "y"), cuts=None, estimate_brightness=False, label=None, contour=None, compress="auto", overlay=None, overlay_params=None, ): """ Plot the foreground and background views. Default order is: axial, coronal, sagittal """ from uuid import uuid4 from lxml import etree import matplotlib.pyplot as plt from nilearn.plotting import plot_anat from svgutils.transform import SVGFigure from niworkflows.viz.utils import robust_set_limits, extract_svg, SVGNS plot_params = plot_params or {} # Use default MNI cuts if none defined if cuts is None: raise NotImplementedError # TODO out_files = [] if estimate_brightness: plot_params = robust_set_limits( anat_nii.get_fdata(dtype="float32").reshape(-1), plot_params) # Plot each cut axis for i, mode in enumerate(list(order)): plot_params["display_mode"] = mode plot_params["cut_coords"] = cuts[mode] if i == 0: plot_params["title"] = label else: plot_params["title"] = None # Generate nilearn figure display = plot_anat(anat_nii, **plot_params) if overlay is not None: _overlay_params = { "vmin": overlay.get_fdata(dtype="float32").min(), "vmax": overlay.get_fdata(dtype="float32").max(), "cmap": plt.cm.gray, "interpolation": "nearest", } _overlay_params.update(overlay_params) display.add_overlay(overlay, **_overlay_params) if contour is not None: display.add_contours(contour, colors="g", levels=[0.5], linewidths=0.5) svg = extract_svg(display, compress=compress) display.close() # Find and replace the figure_1 id. xml_data = etree.fromstring(svg) find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set("id", "%s-%s-%s" % (div_id, mode, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def plot_registration(anat_nii, div_id, plot_params=None, order=('z', 'x', 'y'), cuts=None, estimate_brightness=False, label=None, contour=None, compress='auto'): """ Plots the foreground and background views Default order is: axial, coronal, sagittal """ plot_params = {} if plot_params is None else plot_params # Use default MNI cuts if none defined if cuts is None: raise NotImplementedError # TODO out_files = [] if estimate_brightness: plot_params = robust_set_limits(anat_nii.get_data().reshape(-1), plot_params) # FreeSurfer ribbon.mgz ribbon = contour is not None and np.array_equal( np.unique(contour.get_data()), [0, 2, 3, 41, 42]) if ribbon: contour_data = contour.get_data() % 39 white = nlimage.new_img_like(contour, contour_data == 2) pial = nlimage.new_img_like(contour, contour_data >= 2) # dual mask dual_mask = contour is not None and np.array_equal( np.unique(contour.get_data().astype(np.uint8)), [0, 1, 2]) if dual_mask: contour_data = contour.get_data() outer_mask = nlimage.new_img_like(contour, contour_data == 1) inner_mask = nlimage.new_img_like(contour, contour_data == 2) all_mask = nlimage.new_img_like(contour, contour_data > 0) # Plot each cut axis for i, mode in enumerate(list(order)): plot_params['display_mode'] = mode plot_params['cut_coords'] = cuts[mode] if i == 0: plot_params['title'] = label else: plot_params['title'] = None # Generate nilearn figure display = plot_anat(anat_nii, **plot_params) if ribbon: kwargs = {'levels': [0.5], 'linewidths': 0.5} display.add_contours(white, colors='b', **kwargs) display.add_contours(pial, colors='r', **kwargs) elif dual_mask: kwargs = {'levels': [0.5], 'linewidths': 0.75} display.add_contours(inner_mask, colors='b', **kwargs) display.add_contours(outer_mask, colors='r', **kwargs) display.add_contours(all_mask, colors='c', **kwargs) elif contour is not None: display.add_contours(contour, colors='b', levels=[0.5], linewidths=0.5) svg = extract_svg(display, compress=compress) display.close() # Find and replace the figure_1 id. try: xml_data = etree.fromstring(svg) except etree.XMLSyntaxError as e: NIWORKFLOWS_LOG.info(e) return find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', '%s-%s-%s' % (div_id, mode, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
Creates a SVG tile image from a Mapbox Vector Tile. """ from __future__ import print_function import os import math import argparse import mapbox_vector_tile from enum import Enum from lxml import etree from tempfile import NamedTemporaryFile from django.contrib.gis.geos import LineString, Polygon from svgutils.transform import SVG, SVGFigure, FigureElement, LineElement # `mapbox-vector-tile` has a hardcoded tile extent of 4096 units. MVT_EXTENT = 4096 TILE_SIZE = 256 fig = SVGFigure(width=TILE_SIZE, height=TILE_SIZE) class CustomLineElement(LineElement): """ Inherits from LineElement, gives access to style attribute. """ def __init__(self, points, width=1, color='black', style=''): linedata = "M{} {} ".format(*points[0]) linedata += " ".join(map(lambda x: "L{} {}".format(*x), points[1:])) line = etree.Element( SVG + "path", { "d": linedata, "stroke-width": str(width), "stroke": color, "style": style
def _compose_view(bg_svgs, fg_svgs, ref=0): if fg_svgs is None: fg_svgs = [] # Merge SVGs and get roots svgs = bg_svgs + fg_svgs roots = [f.getroot() for f in svgs] # Query the size of each sizes = [] for f in svgs: viewbox = [float(v) for v in f.root.get("viewBox").split(" ")] width = int(viewbox[2]) height = int(viewbox[3]) sizes.append((width, height)) nsvgs = len(bg_svgs) sizes = np.array(sizes) # Calculate the scale to fit all widths width = sizes[ref, 0] scales = width / sizes[:, 0] heights = sizes[:, 1] * scales # Compose the views panel: total size is the width of # any element (used the first here) and the sum of heights fig = SVGFigure(width, heights[:nsvgs].sum()) yoffset = 0 for i, r in enumerate(roots): r.moveto(0, yoffset, scale=scales[i]) if i == (nsvgs - 1): yoffset = 0 else: yoffset += heights[i] # Group background and foreground panels in two groups if fg_svgs: newroots = [ GroupElement(roots[:nsvgs], {"class": "background-svg"}), GroupElement(roots[nsvgs:], {"class": "foreground-svg"}), ] else: newroots = roots fig.append(newroots) fig.root.attrib.pop("width") fig.root.attrib.pop("height") fig.root.set("preserveAspectRatio", "xMidYMid meet") with TemporaryDirectory() as tmpdirname: out_file = Path(tmpdirname) / "tmp.svg" fig.save(str(out_file)) # Post processing svg = out_file.read_text().splitlines() # Remove <?xml... line if svg[0].startswith("<?xml"): svg = svg[1:] # Add styles for the flicker animation if fg_svgs: svg.insert( 2, """\ <style type="text/css"> @keyframes flickerAnimation%s { 0%% {opacity: 1;} 100%% { opacity: 0; }} .foreground-svg { animation: 1s ease-in-out 0s alternate none infinite paused flickerAnimation%s;} .foreground-svg:hover { animation-play-state: running;} </style>""" % tuple([uuid4()] * 2), ) return svg
def fromfile(fname): fig = SVGFigure() svg_file = etree.parse(fname) fig.root = svg_file.getroot() return fig
def write_svg(self, filename): WIDTH = self._outer_board_width_pixel() HEIGHT = self._outer_board_height_pixel() output_fig = SVGFigure(Unit(f'{WIDTH}px'), Unit(f'{HEIGHT}px')) output_fig.append(self._lines) output_fig.save(filename)
def plot_registration(anat_nii, div_id, plot_params=None, order=('z', 'x', 'y'), cuts=None, estimate_brightness=False, label=None, contour=None, compress='auto'): """ Plot the foreground and background views. Default order is: axial, coronal, sagittal """ from uuid import uuid4 from lxml import etree from nilearn.plotting import plot_anat from svgutils.transform import SVGFigure from niworkflows.viz.utils import robust_set_limits, extract_svg, SVGNS plot_params = plot_params or {} # Use default MNI cuts if none defined if cuts is None: raise NotImplementedError # TODO out_files = [] if estimate_brightness: plot_params = robust_set_limits(anat_nii.get_data().reshape(-1), plot_params) # Plot each cut axis for i, mode in enumerate(list(order)): plot_params['display_mode'] = mode plot_params['cut_coords'] = cuts[mode] if i == 0: plot_params['title'] = label else: plot_params['title'] = None # Generate nilearn figure display = plot_anat(anat_nii, **plot_params) if contour is not None: display.add_contours(contour, colors='g', levels=[0.5], linewidths=0.5) svg = extract_svg(display, compress=compress) display.close() # Find and replace the figure_1 id. xml_data = etree.fromstring(svg) find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', '%s-%s-%s' % (div_id, mode, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def plot_registration(anat_nii, div_id, plot_params=None, order=('z', 'x', 'y'), cuts=None, estimate_brightness=False, label=None, contour=None, compress='auto'): """ Plots the foreground and background views Default order is: axial, coronal, sagittal """ plot_params = {} if plot_params is None else plot_params # Use default MNI cuts if none defined if cuts is None: raise NotImplementedError # TODO out_files = [] if estimate_brightness: plot_params = robust_set_limits(anat_nii.get_data().reshape(-1), plot_params) # FreeSurfer ribbon.mgz ribbon = contour is not None and np.array_equal( np.unique(contour.get_data()), [0, 2, 3, 41, 42]) if ribbon: contour_data = contour.get_data() % 39 white = nlimage.new_img_like(contour, contour_data == 2) pial = nlimage.new_img_like(contour, contour_data >= 2) # Plot each cut axis for i, mode in enumerate(list(order)): plot_params['display_mode'] = mode plot_params['cut_coords'] = cuts[mode] if i == 0: plot_params['title'] = label else: plot_params['title'] = None # Generate nilearn figure display = plot_anat(anat_nii, **plot_params) if ribbon: kwargs = {'levels': [0.5], 'linewidths': 0.5} display.add_contours(white, colors='b', **kwargs) display.add_contours(pial, colors='r', **kwargs) elif contour is not None: display.add_contours(contour, colors='b', levels=[0.5], linewidths=0.5) svg = extract_svg(display, compress=compress) display.close() # Find and replace the figure_1 id. try: xml_data = etree.fromstring(svg) except etree.XMLSyntaxError as e: NIWORKFLOWS_LOG.info(e) return find_text = etree.ETXPath("//{%s}g[@id='figure_1']" % SVGNS) find_text(xml_data)[0].set('id', '%s-%s-%s' % (div_id, mode, uuid4())) svg_fig = SVGFigure() svg_fig.root = xml_data out_files.append(svg_fig) return out_files
def compose_svg(atoms_to_put, options): board_svg_filename = os.path.join(options.board_theme_dir, _BOARD_SVG_BASENAME) board_ini_filename = os.path.join(options.board_theme_dir, _BOARD_INI_BASENAME) # Check for existance ourselves since configparser would throw NoSectionError # at us for a missing file. if not os.path.exists(board_ini_filename): raise OSError(errno.ENOENT, "No such file or directory: '%s'" % board_ini_filename) config = configparser.RawConfigParser(defaults={'river': 0.0}) config.read(board_ini_filename) output_board_offset_left_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'left') output_board_offset_top_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'top') output_board_width_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'width') output_board_height_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'height') output_board_river_height_pixel = config.getfloat(_BOARD_CONFIG_SECTION, 'river') # Regular pieces are at level 0; level 1 and above is drawn on top of (i.e. after) # the pieces while -1 and below is drawn below (i.e. before) the pieces. jobs_at_z_index = defaultdict(list) annotation_theme_config_filename = os.path.join( options.annotation_theme_dir, 'config.yml') with open(annotation_theme_config_filename) as f: annotation_theme_config = yaml.safe_load(f) for put_atom in atoms_to_put: if isinstance(put_atom, PutAnnotation): put_annotation: PutAnnotation = put_atom x_rel = float(put_annotation.x) / _MAX_X y_rel = float(_MAX_Y - put_annotation.y) / _MAX_Y filename = os.path.join(options.annotation_theme_dir, f'{put_annotation.annotation_name}.svg') annotation_scale = options.annotation_scale if annotation_theme_config[ 'allow_scaling'][put_annotation.annotation_name] else 1.0 atom_z_index = int(annotation_theme_config['z_index'][ put_annotation.annotation_name]) jobs_at_z_index[atom_z_index].append( (x_rel, y_rel, filename, annotation_scale)) else: assert isinstance(put_atom, PutPiece) put_piece: PutPiece = put_atom x_rel = float(put_piece.x) / _MAX_X y_rel = float(_MAX_Y - put_piece.y) / _MAX_Y basename = _FILENAME_OF_PARTY_PIECE[put_piece.party][ put_piece.piece] filename = os.path.join(options.piece_theme_dir, basename) jobs_at_z_index[_Z_INDEX_PIECE_LEVEL].append( (x_rel, y_rel, filename, options.piece_scale)) if options.debug: for x_rel in (0.0, 1.0): for y_rel in (0.0, 1.0): jobs_at_z_index[_Z_INDEX_DEBUG_DIAMOND].append( (x_rel, y_rel, os.path.join(options.piece_theme_dir, _DIAMOND_FILE_NAME), options.piece_scale)) # Read board board_fig = fromfile(board_svg_filename) board_root = board_fig.getroot() # Scale board to output board_width_pixel, board_height_pixel = _pixel_viewbox_of_figure( board_fig, options.resolution_dpi)[2:] height_factor = board_height_pixel / float(board_width_pixel) board_scale = options.width_pixel / board_width_pixel board_root.moveto(0, 0, scale_x=board_scale, scale_y=board_scale) output_board_offset_left_pixel *= board_scale output_board_offset_top_pixel *= board_scale output_board_width_pixel *= board_scale output_board_height_pixel *= board_scale output_board_river_height_pixel *= board_scale # Initialize output figure output_fig = SVGFigure(Unit(f'{options.width_pixel}px'), Unit(f'{options.width_pixel * height_factor}px')) output_fig.append([ board_root, ]) for _z_index, jobs in sorted(jobs_at_z_index.items()): for (x_rel, y_rel, filename, element_scale) in jobs: piece_fig = fromfile(filename) piece_root = piece_fig.getroot() original_piece_width_pixel, original_piece_height_pixel = \ _pixel_viewbox_of_figure(piece_fig, options.resolution_dpi)[2:] # Scale and put piece onto board center_x_pixel = output_board_offset_left_pixel + output_board_width_pixel * x_rel center_y_pixel = output_board_offset_top_pixel + (output_board_height_pixel - output_board_river_height_pixel) * y_rel \ + (output_board_river_height_pixel if (y_rel >= 0.5) else 0.0) maximum_future_piece_width_pixel = output_board_width_pixel / _MAX_X * element_scale maximum_future_piece_height_pixel = output_board_height_pixel / _MAX_Y * element_scale scale = min( maximum_future_piece_width_pixel / original_piece_width_pixel, maximum_future_piece_height_pixel / original_piece_height_pixel) future_piece_width_pixel = original_piece_width_pixel * scale future_piece_height_pixel = original_piece_height_pixel * scale x_pixel = center_x_pixel - future_piece_width_pixel / 2.0 y_pixel = center_y_pixel - future_piece_height_pixel / 2.0 piece_root.moveto(x_pixel, y_pixel, scale_x=scale, scale_y=scale) output_fig.append([ piece_root, ]) output_fig.save(options.output_file)
def __init__(self): SVGFigure.__init__(self) self.figures = []
def save(self, fname): self._generate_layout() SVGFigure.save(self, fname)