Files
old-krovovi-kalkulator/helix/presenters/image_presenter.py

173 lines
7.1 KiB
Python
Raw Normal View History

2017-11-07 09:23:57 +01:00
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