first commit
This commit is contained in:
172
helix/presenters/image_presenter.py
Normal file
172
helix/presenters/image_presenter.py
Normal file
@@ -0,0 +1,172 @@
|
||||
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
|
||||
Reference in New Issue
Block a user