import sys from math import sqrt, degrees, acos from helix.models.corner import Corner from helix.constants.global_constants import max_corner_angle import flask_featureflags as feature class ProjectPresenter(object): def __init__(self, system_type, module_type): self.offset = None self.system_type = system_type self.module_type = module_type '''"This function expects coordinates to be in unit values, processed in coordinates_calculator.py" ''' def get_panel_data(self, panels, subarrays, max_y = None): if self.offset is not None: raise RuntimeError("ProjectPresenter panels must be computed before buildings") table_data = [] system_constants = self.system_type.system_constants() module_constants = self.system_type.module_constants(self.module_type) spacing_x, spacing_y = module_constants.presenter_spacing wind_zones = system_constants.wind_zones for panel in panels: subarray = [x for x in subarrays if x.subarray_number == panel.subarray][0] origin = subarray.origin table_data.append({ 'x': (panel.coordinate.x + origin.x) * spacing_x, 'y': (panel.coordinate.y + origin.y) * spacing_y, 'width': spacing_x, 'height': spacing_y, 'data': { 'panel_id': panel.id, 'panel_type': panel.panel_type.number(), 'ballast': panel.ballast, 'wind_anchors': panel.wind_anchors, 'seismic_anchors': panel.seismic_anchors, 'wind_zones': wind_zones[panel.wind_zone], 'link_trays': panel.presented_link_tray, 'cross_trays': panel.cross_tray, 'psf': round(panel.pressure, 2), 'subarray': panel.subarray } }) # Move coordinates to reflect origin being at top-left # (as per canvas) instead of bottom-left height = max(map(lambda row: row['y'], table_data)) first_cell = min(map(lambda row: row['y'], table_data)) self.offset = height for panel in table_data: panel['y'] = height - panel['y'] + first_cell return table_data def get_buildings(self, buildings, max_y): if self.offset is None: self.offset = 0 module_constants = self.system_type.module_constants(self.module_type) spacing_x, spacing_y = module_constants.presenter_spacing # max_y = -sys.maxsize - 1 # for flipping the y coordinate result = [] for building in buildings: presentable_building = [] result.append(presentable_building) # origin = self.find_origin(building) for point in building: point.x = point.x * spacing_x point.y = abs((point.y * spacing_y) - max_y) presentable_building.append(point.__dict__) return result def get_corners(self, buildings): def angle(x1, y1, x2, y2): # Use dotproduct to find angle between vectors # This always returns an angle between 0, pi numer = (x1 * x2 + y1 * y2) denom = sqrt((x1 ** 2 + y1 ** 2) * (x2 ** 2 + y2 ** 2)) return acos(numer / denom) def cross_sign(x1, y1, x2, y2): # True if cross is positive # False if negative or zero return x1 * y2 > x2 * y1 result = [] if feature.is_active('ff_cpp'): for building in buildings: presentable_building = [] result.append(presentable_building) for i in range(len(building)): p1 = building[i] ref = building[i - 1] p2 = building[i - 2] x1, y1 = p1[0] - ref[0], p1[1] - ref[1] x2, y2 = p2[0] - ref[0], p2[1] - ref[1] corner_length_cw = sqrt(x2**2 + y2**2) corner_length_ccw = sqrt(x1**2 + y1**2) theta = degrees(angle(x1, y1, x2, y2)) #print('Points', p1, ref, p2) #print('Angle', theta) if (cross_sign(x1, y1, x2, y2)) and (theta < max_corner_angle) : #print('Inner Angle') presentable_building.append(Corner(ref[0], ref[1], corner_length_ccw,corner_length_cw, theta).__dict__) return result def get_max_y(self,buildings, panels): module_constants = self.system_type.module_constants(self.module_type) _, spacing_y = module_constants.presenter_spacing all_y = [] if buildings is None or buildings == []: # no buildings in the file, probably CSV coordinates loaded for panel in panels: all_y.append(panel.coordinate.y) for building in buildings: for point in building: all_y.append(point.y) return max(all_y) * spacing_y