Files
old-krovovi-kalkulator/helix/presenters/image_presenter.py
2017-11-07 09:23:57 +01:00

173 lines
7.1 KiB
Python

from svgwrite.container import Group
import svgwrite
from svgwrite.shapes import Rect, Polygon
from svgwrite.text import Text
import wand
from wand.api import library
from wand.image import Image
from helix.calculators.subarray_helper import extract_subarray
from helix.constants.color import Color
from helix.constants.system_type import SystemType
class ImagePresenter(object):
def __init__(self, system_type, module_type):
self.system_type = system_type
self.module_type = module_type
def generate_image(self, panels, subarrays):
image_dimensions = (620, 710)
if len(panels) < 3000:
svg_string = self.generate_array_svg(image_dimensions, panels, subarrays)
else:
svg_string = ImagePresenter.svg_too_large_string(image_dimensions)
with Image(blob=svg_string.encode('utf-8'), format='svg') as img:
with wand.color.Color('transparent') as background_color:
library.MagickSetBackgroundColor(img.wand, background_color.resource)
png_image = img.make_blob('png')
return png_image
@staticmethod
def svg_too_large_string(image_dimensions):
dwg = svgwrite.Drawing(size=image_dimensions)
dwg.add(Text("Array may be too large, please try again or break up array into smaller components",
x=[image_dimensions[0] / 2],
y=[image_dimensions[1] / 2],
text_anchor="middle"))
return dwg.tostring()
def generate_array_svg(self, image_dimensions, panels, subarrays):
dwg = svgwrite.Drawing(size=image_dimensions)
array_dimensions = self.calculate_max_array_dimensions(panels, subarrays)
module_constants = self.system_type.module_constants(self.module_type)
array_width, array_height = array_dimensions
spacing_x, spacing_y = module_constants.presenter_spacing
spacing_x *= 10
spacing_y *= 10
scaling_x = image_dimensions[0] / (array_width * spacing_x)
scaling_y = image_dimensions[1] / (array_height * spacing_y)
scaling_factor = min(scaling_x, scaling_y)
for subarray in subarrays:
dwg.add(self.draw_subarray(panels, subarray, array_dimensions, scaling_factor))
return dwg.tostring()
def draw_subarray(self, all_panels, subarray, array_dimensions, scaling_factor):
module_constants = self.system_type.module_constants(self.module_type)
array_width, array_height = array_dimensions
spacing_x, spacing_y = module_constants.presenter_spacing
spacing_x *= 10
spacing_y *= 10
panels = extract_subarray(all_panels, subarray.subarray_number)
subarray_group = Group(transform="scale(%f, %f)" % (scaling_factor, scaling_factor))
for panel in panels:
group = self.draw_panel(panel, subarray, array_height - 1, (spacing_x, spacing_y))
subarray_group.add(group)
return subarray_group
def draw_panel(self, panel, subarray, max_height, panel_dimensions):
origin = subarray.origin
width, height = panel_dimensions
border_width = 0.5
inner_width = width - border_width
inner_height = height - border_width
inner_dimensions = (inner_width - border_width, inner_height - border_width)
x = (panel.coordinate.x + origin.x) * width
y = (max_height - (panel.coordinate.y + origin.y)) * height
group = Group(stroke_width=border_width, transform="translate(%f, %f)" % (x, y)) # FIXME: look into not having to do this.
fill_color, secondary_fill_color, border_color, text_color = self.colors(panel)
# draw the background, with a white border around everything
background_rect = Rect(insert=(0, 0), size=panel_dimensions, fill=fill_color.value, stroke=Color.array_background.value)
group.add(background_rect)
# IFF we have both seismic and wind anchors, draw the panel as the "half wind, half seismic" thing.
# This means we add a triangle that divides the panel.
if secondary_fill_color is not None:
points = [
(border_width, border_width),
(inner_width, inner_height),
(inner_width, border_width)
]
dual_anchor_indicator = Polygon(points, fill=secondary_fill_color.value, stroke=secondary_fill_color.value)
group.add(dual_anchor_indicator)
# draw a border rect.
# This doesn't need to be drawn if we don't have any wind or seismic anchors.
if border_color != fill_color:
border_rect = Rect(insert=(border_width, border_width), size=inner_dimensions, stroke=border_color.value, fill_opacity=0)
group.add(border_rect)
# If dual-tilt, draw the little triangle on the right side of the panel
# Use a hardcoded border value, because this won't change.
if self.system_type == SystemType.dualTilt:
points = [
(width / 2, border_width),
(width / 2, inner_height),
(inner_width, height / 2),
]
dual_anchor_indicator = Polygon(points, fill_opacity=0, stroke=Color.border.value, stroke_width=border_width / 2)
group.add(dual_anchor_indicator)
# add all the text!
ballast_text = self.text_overlay(str(panel.ballast), panel_dimensions, 0.3, 0.55, "2.5px", text_color)
group.add(ballast_text)
anchors_text = self.text_overlay(str(panel.wind_anchors + panel.seismic_anchors), panel_dimensions, 0.7, 0.55, "2.5px", text_color)
group.add(anchors_text)
return group
@staticmethod
def text_overlay(text, dimensions, x, y, font_size, color):
return Text(
text,
x=[dimensions[0] * x],
y=[dimensions[1] * y],
font_size=font_size,
font_family='sans-serif',
text_anchor="middle",
fill=color.value
)
@staticmethod
def calculate_max_array_dimensions(all_panels, subarrays):
actual_y_coordinates = []
actual_x_coordinates = []
for subarray in subarrays:
panels = extract_subarray(all_panels, subarray.subarray_number)
actual_y_coordinates += [panel.coordinate.y + subarray.origin.y for panel in panels]
actual_x_coordinates += [panel.coordinate.x + subarray.origin.x for panel in panels]
return max(actual_x_coordinates) + 1, max(actual_y_coordinates) + 1
@staticmethod
def colors(panel):
fill = Color.default_panel_background
secondary = None
border = Color.border
text = Color.dark_text
if panel.seismic_anchors > 0 and panel.wind_anchors > 0:
fill = Color.seismic_background
secondary = Color.wind_background
elif panel.seismic_anchors > 0:
fill = Color.seismic_background
elif panel.wind_anchors > 0:
fill = Color.wind_background
else:
border = Color.default_panel_background
text = Color.light_text
return fill, secondary, border, text