173 lines
7.1 KiB
Python
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
|