def an_envelop(width, height, spec): envelop_group, envelop_width, envelop_height = a_rectangle(width, height, spec=spec) # two lines from center to top left and top right to give it an envelop look center_point = Point(width / 2, height / 2) top_left_point = Point(0, 0) top_right_point = Point(width, 0) points = [top_left_point, center_point, top_right_point, top_left_point] # the lines' color is shapes stroke line_style = StyleBuilder(spec['style']).getStyle() line1_svg = Line(x1=0, y1=0, x2=width / 2, y2=height / 2, style=spec['style']) line2_svg = Line(x1=width / 2, y1=height / 2, x2=width, y2=0, style=spec['style']) line1_svg.set_style(line_style) line2_svg.set_style(line_style) envelop_group.addElement(line1_svg) envelop_group.addElement(line2_svg) return envelop_group, width, height
def a_lightning(width, height, spec): diagonal = math.sqrt(width * width + height * height) len_mb_mt = (diagonal / 2) * 0.3 len_mt_tl = (diagonal / 2) * 0.4 angle_bl_tr = math.atan(height / width) angle_mt_tl = math.pi / 2 bottom_left = Point(0, height) mid_top = bottom_left.to_point(math.degrees(angle_bl_tr), diagonal / 2 + len_mb_mt / 2) mid_bottom = bottom_left.to_point(math.degrees(angle_bl_tr), diagonal / 2 - len_mb_mt / 2) top_left = mid_top.to_point(math.degrees(angle_mt_tl + angle_bl_tr), len_mt_tl) top_right = Point(width, 0) bottom_right = mid_bottom.to_point( -math.degrees(angle_mt_tl - angle_bl_tr), len_mt_tl) lightning_points = [ bottom_left, top_left, mid_top, top_right, bottom_right, mid_bottom ] # lightning_points = [bottom_left, top_left, mid_top] lightning_svg = Polygon(points=points_to_str(lightning_points)) lightning_svg.set_style(StyleBuilder(spec['style']).getStyle()) return lightning_svg, width, height
def two_gears_inside_a_circular_shape(radius, inner_shape_spec): svg_group = G() gear_radius = radius * 0.7 gear1_svg_group, _, _ = a_gear(radius=gear_radius, spec=inner_shape_spec) gear2_svg_group, _, _ = a_gear(radius=gear_radius, spec=inner_shape_spec) # gear1 is towards top left north_west_point_on_circle = Point(radius, radius).to_point(135, radius) gear1_center_point = north_west_point_on_circle.to_point(-45, gear_radius) gear1_svg_group_xy = '{0},{1}'.format(gear1_center_point.x - gear_radius, gear1_center_point.y - gear_radius) transformer = TransformBuilder() transformer.setTranslation(gear1_svg_group_xy) gear1_svg_group.set_transform(transformer.getTransform()) svg_group.addElement(gear1_svg_group) # gear2 is towards bottom right south_east_point_on_circle = Point(radius, radius).to_point(-45, radius) gear2_center_point = south_east_point_on_circle.to_point(135, gear_radius) gear2_svg_group_xy = '{0},{1}'.format(gear2_center_point.x - gear_radius, gear2_center_point.y - gear_radius) transformer = TransformBuilder() transformer.setTranslation(gear2_svg_group_xy) gear2_svg_group.set_transform(transformer.getTransform()) svg_group.addElement(gear2_svg_group) return svg_group, radius * 2, radius * 2
def connect_southward(self, from_lane_number, point_from, to_lane_number, point_to): # see if there is one or more lanes between the from-lane and to-lane if to_lane_number > from_lane_number + 1: # yes we have lanes in between # we bypass the northmost (first) lane lane_number_to_bypass = from_lane_number + 1 lane_to_bypass = self.pool_collection_list[lane_number_to_bypass] margin_spec = self.margin_spec(lane_number_to_bypass) points_to_bypass_the_lane = lane_to_bypass.bypass_vertically(coming_from=point_from, going_to=point_to, margin_spec=margin_spec) return [point_from] + points_to_bypass_the_lane + self.connect_southward(lane_number_to_bypass, points_to_bypass_the_lane[-1], to_lane_number, point_to) else: # there is no lane in between if point_from.y == point_to.y: # they are on the same horizontal line - inside a lane routing area return [point_from, Point(point_from.x, point_to.y), point_to] else: # they are not on a line, we need a connecting path, looks like one or both of them are at eastern/western boundary # the north (point_from) needs to come down to southern boundary point_from_next = Point(point_from.x, self.pool_collection_list[from_lane_number].element.xy.y + self.pool_collection_list[from_lane_number].element.height + self.margin_spec(to_lane_number)['bottom']) # the south (point_to) needs to move up to northern boundary point_to_next = Point(point_to.x, self.pool_collection_list[to_lane_number].element.xy.y - self.margin_spec(to_lane_number)['top']) return [point_from, point_from_next, point_to_next, point_to]
def an_upword_arrowhead(width, height, spec): mid_point = Point(width / 2, height / 2) top_point = Point(width / 2, 0) left_point = Point(0, height) right_point = Point(width, height) arrowhead_points = [mid_point, left_point, top_point, right_point] arrowhead_svg = Polygon(points=points_to_str(arrowhead_points)) arrowhead_svg.set_style(StyleBuilder(spec['style']).getStyle()) return arrowhead_svg, width, height
def snap_points(self, width, height): snaps = super().snap_points(width, height) # add two more (slight left and slight right) for north-middle) snaps['north']['middle'].append( SnapPoint(point=Point(width * 0.40, self.snap_point_offset * -1))) snaps['north']['middle'].append( SnapPoint(point=Point(width * 0.60, self.snap_point_offset * -1))) # add two more (slight left and slight right) for south-middle) snaps['south']['middle'].append( SnapPoint(point=Point(width * 0.40, height + self.snap_point_offset * 1))) snaps['south']['middle'].append( SnapPoint(point=Point(width * 0.60, height + self.snap_point_offset * 1))) # add two more (slight up and slight down) for east-middle) snaps['east']['middle'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.40))) snaps['east']['middle'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.60))) # add two more (slight up and slight down) for west-middle) snaps['west']['middle'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.40))) snaps['west']['middle'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.60))) return snaps
def a_gear(radius, spec): mark_length = radius * 0.3 circle_radius = radius - mark_length gear_group_svg, _, _ = a_circle(radius=circle_radius, spec=spec) # the inner circle inner_circle_radius = radius * 0.1 inner_circle_svg = Circle(cx=circle_radius, cy=circle_radius, r=inner_circle_radius) inner_circle_svg.set_style(StyleBuilder(spec['style']).getStyle()) gear_group_svg.addElement(inner_circle_svg) # the hour marks center_point = Point(circle_radius, circle_radius) for mark in range(0, 9): point_on_circle = center_point.to_point(40 * mark, circle_radius) point_outside = center_point.to_point(40 * mark, circle_radius + mark_length) mark_svg = Line(x1=point_on_circle.x, y1=point_on_circle.y, x2=point_outside.x, y2=point_outside.y) mark_svg.set_style(StyleBuilder(spec['style']).getStyle()) gear_group_svg.addElement(mark_svg) # add to group return gear_group_svg, radius * 2, radius * 2
def assemble_labels(self): group_id = '{0}:{1}-pools-labels'.format(self.bpmn_id, self.lane_id) svg_group = G(id=group_id) group_width = 0 transformer = TransformBuilder() for child_pool_class in self.child_pool_classes: child_label_element = child_pool_class.assemble_labels() if child_label_element is None: continue # the y position of this pool label in the group will be its corresponding swim-pool's y position child_label_xy = Point(0, child_pool_class.svg_element.xy.y) transformer.setTranslation(child_label_xy) child_label_element.svg.set_transform(transformer.getTransform()) svg_group.addElement(child_label_element.svg) group_width = max(child_label_element.width, group_width) group_height = self.svg_element.height # wrap it in a svg element self.label_element = SvgElement(svg=svg_group, width=group_width, height=group_height) return self.label_element
def assemble_elements(self): info('assembling lanes for [{0}] DONE'.format(self.bpmn_id)) # wrap it in a svg group group_id = '{0}-lanes'.format(self.bpmn_id) svg_group = G(id=group_id) # height of the lane collection is sum of height of all lanes with gaps between lanes max_lane_width = self.theme['pad-spec']['left'] current_y = self.theme['pad-spec']['top'] transformer = TransformBuilder() for child_lane_class in self.child_lane_classes: swim_lane_element = child_lane_class.assemble_elements() current_x = self.theme['pad-spec']['left'] + float(child_lane_class.lane_data['styles'].get('move_x', 0)) swim_lane_element.xy = Point(current_x, current_y) transformer.setTranslation(swim_lane_element.xy) swim_lane_element.svg.set_transform(transformer.getTransform()) svg_group.addElement(swim_lane_element.svg) max_lane_width = max(max_lane_width, current_x + swim_lane_element.width) current_y = current_y + swim_lane_element.height + self.theme['dy-between-lanes'] group_width = self.theme['pad-spec']['left'] + max_lane_width + self.theme['pad-spec']['right'] group_height = current_y - self.theme['dy-between-lanes'] + self.theme['pad-spec']['bottom'] # add the ractangle lane_collection_rect_svg = Rect(width=group_width, height=group_height) lane_collection_rect_svg.set_style(StyleBuilder(self.theme['style']).getStyle()) svg_group.addElement(lane_collection_rect_svg) # wrap it in a svg element self.svg_element = SvgElement(svg=svg_group, width=group_width, height=group_height) self.lane_collection.element = self.svg_element info('assembling lanes for [{0}] DONE'.format(self.bpmn_id)) return self.svg_element
def a_diamond(diagonal_x, diagonal_y, spec): svg_group = G() points = [ Point(0, diagonal_y / 2), Point(diagonal_x / 2, 0), Point(diagonal_x, diagonal_y / 2), Point(diagonal_x / 2, diagonal_y), Point(0, diagonal_y / 2) ] diamond_svg = Polygon(points=points_to_str(points)) diamond_svg.set_style(StyleBuilder(spec['style']).getStyle()) # add to group svg_group.addElement(diamond_svg) return svg_group, diagonal_x, diagonal_y
def a_page(width, height, spec): rect_svg_group, _, _ = a_rectangle(width=width, height=height, spec=spec) left_edge = width * 0.1 right_edge = width * 0.1 num_lines = 4 y_gap_between_lines = height / (num_lines + 1) for line in range(1, num_lines + 1): y_len = line * y_gap_between_lines left_point = Point(left_edge, y_len) right_point = Point(width - right_edge, y_len) line_svg = Line(x1=left_point.x, y1=left_point.y, x2=right_point.x, y2=right_point.y) line_svg.set_style(StyleBuilder(spec['style']).getStyle()) rect_svg_group.addElement(line_svg) return rect_svg_group, width, height
def connect_southward(self, point_from, point_to): if point_from == point_to: return [point_from] # see if there is any channel (vertically or horizontally) between point_from and point_to channels_vertically_between, channels_horizontally_between = self.channels_blocking_southward( point_from, point_to) # first we bypass the channels which are obstructing the path vertically if len(channels_vertically_between) > 0: # we bypass the northmost (first) channel channel_to_bypass = channels_vertically_between[0] debug('bypassing vertically blocking channel [{0}:{1}]'.format( channel_to_bypass.number, channel_to_bypass.name)) # self.mark_points([point_from], self.element.svg, 'red') # self.mark_points([point_to], self.element.svg, 'green') points_to_bypass_the_channel = channel_to_bypass.bypass_vertically( coming_from=point_from, going_to=point_to) # debug('[{0}] -> [{1}] channel [{2}:{3}] bypass points [{4}]'.format(point_from, point_to, channel_to_bypass.number, channel_to_bypass.name, points_to_bypass_the_channel)) # self.mark_points(points_to_bypass_the_channel, self.element.svg, 'blue') points = [ point_from ] + points_to_bypass_the_channel + self.connect_southward( points_to_bypass_the_channel[-1], point_to) # debug('[{0}] -> [{1}] connection points [{2}]'.format(point_from, point_to, points)) return points # next we handle the channels which are obstructing the path horizontally only when we have no vertical obstruction elif len(channels_horizontally_between) > 0: # debug('...horizontally blocking channels') # for channel in channels_horizontally_between: # debug('......[{0}]:[{1}]'.format(channel.number, channel.name)) # there may be a number of such obstructing channels, our target is to, find the channel closest to point_to (the last channel) channel_to_bypass = channels_horizontally_between[-1] # debug('bypassing [{0}]:[{1}] from {2} towards {3}'.format(channel_to_bypass.number, channel_to_bypass.name, point_to, point_from)) points_to_bypass_the_channel = channel_to_bypass.bypass_vertically( coming_from=point_to, going_to=point_from) points_to_bypass_the_channel.reverse() points = self.connect_southward( point_from, points_to_bypass_the_channel[0] ) + points_to_bypass_the_channel + [point_to] return points else: # there is no channel in between, we can just return the intersection point if point_from.x == point_to.x or point_from.y == point_to.y: return [point_from, point_to] else: return [point_from, Point(point_from.x, point_to.y), point_to]
def a_triangular_rewind(width, height, spec): svg_group = G() left_arrow_point_left = Point(0, height / 2) left_arrow_point_top = Point(width / 2, 0) left_arrow_point_bottom = Point(width / 2, height) right_arrow_point_left = Point(width / 2, height / 2) right_arrow_point_top = Point(width, 0) right_arrow_point_bottom = Point(width, height) left_arrow_points = [ left_arrow_point_left, left_arrow_point_top, left_arrow_point_bottom ] right_arrow_points = [ right_arrow_point_left, right_arrow_point_top, right_arrow_point_bottom ] left_arrow_svg = Polygon(points=points_to_str(left_arrow_points)) left_arrow_svg.set_style(StyleBuilder(spec['style']).getStyle()) right_arrow_svg = Polygon(points=points_to_str(right_arrow_points)) right_arrow_svg.set_style(StyleBuilder(spec['style']).getStyle()) svg_group.addElement(left_arrow_svg) svg_group.addElement(right_arrow_svg) return svg_group, width, height
def an_equilateral_triangle_inside_a_circular_shape(radius, inner_shape_spec): # in a trangle ABC, A is alwys the top vertex svg_group = G() pad = radius * 0.4 center_to_vertex = radius - pad center_point = Point(radius, radius) point_a = center_point.to_point(90, center_to_vertex) point_b = center_point.to_point(210, center_to_vertex) point_c = center_point.to_point(330, center_to_vertex) points = [point_a, point_b, point_c, point_a] triangle_svg = Polygon(points=points_to_str(points)) triangle_svg.set_style(StyleBuilder(inner_shape_spec['style']).getStyle()) # add to group svg_group.addElement(triangle_svg) return svg_group, radius * 2, radius * 2
def an_equilateral_pentagon_inside_a_circular_shape(radius, inner_shape_spec): svg_group = G() pad = radius * 0.4 center_to_vertex = radius - pad center_point = Point(radius, radius) point_a = center_point.to_point(90 - (72 * 0), center_to_vertex) point_b = center_point.to_point(90 - (72 * 1), center_to_vertex) point_c = center_point.to_point(90 - (72 * 2), center_to_vertex) point_d = center_point.to_point(90 - (72 * 3), center_to_vertex) point_e = center_point.to_point(90 - (72 * 4), center_to_vertex) points = [point_a, point_b, point_c, point_d, point_e, point_a] pentagon_svg = Polygon(points=points_to_str(points)) pentagon_svg.set_style(StyleBuilder(inner_shape_spec['style']).getStyle()) # add to group svg_group.addElement(pentagon_svg) return svg_group, radius * 2, radius * 2
def a_right_arrow(width, height, spec): arrow_height = height / 3 top_left = Point(0, (height - arrow_height) / 2) mid_top = Point(width / 2, (height - arrow_height) / 2) top = Point(width / 2, 0) right = Point(width, height / 2) bottom = Point(width / 2, height) mid_bottom = Point(width / 2, (height + arrow_height) / 2) bottom_left = Point(0, (height + arrow_height) / 2) arrow_points = [ top_left, mid_top, top, right, bottom, mid_bottom, bottom_left ] arrow_svg = Polygon(points=points_to_str(arrow_points)) arrow_svg.set_style(StyleBuilder(spec['style']).getStyle()) return arrow_svg, width, height
def main(argv=None): pygame.init() if len(argv) == 0: sys.exit(1) data_filename = argv[0] data = load_sense_data(data_filename) windowSize = grid.GRID_SIZE * CELL_PIX window = pygame.display.set_mode((windowSize, windowSize)) board = grid.OccupancyGrid(grid.GRID_SIZE, grid.CELL_SIZE, Point(0, 0)) sonarModel = nao_sonar_model() send_sense(data, board, window, sonarModel) #generate_random_board(board) render_grid(window, board) pygame.display.flip() wait_for_exit()
def a_clock(radius, spec): clock_group_svg, _, _ = a_circle(radius, spec) hour_mark_length = radius * 0.3 minute_hand_length = radius * 0.6 hour_hand_length = radius * 0.4 minute_hand_svg = Line(x1=radius, y1=radius, x2=radius, y2=(radius - minute_hand_length)) minute_hand_svg.set_style(StyleBuilder(spec['style']).getStyle()) hour_hand_svg = Line(x1=radius, y1=radius, x2=radius + hour_hand_length, y2=radius) hour_hand_svg.set_style(StyleBuilder(spec['style']).getStyle()) clock_group_svg.addElement(minute_hand_svg) clock_group_svg.addElement(hour_hand_svg) # the hour marks center_point = Point(radius, radius) for hour in range(0, 12): point_on_circle = center_point.to_point(30 * hour, radius) point_inside = center_point.to_point(30 * hour, radius - hour_mark_length) hour_mark_svg = Line(x1=point_on_circle.x, y1=point_on_circle.y, x2=point_inside.x, y2=point_inside.y) hour_mark_svg.set_style(StyleBuilder(spec['style']).getStyle()) clock_group_svg.addElement(hour_mark_svg) # add to group return clock_group_svg, radius * 2, radius * 2
def a_folded_rectangle(width, height, spec): svg_group = G() top_left = Point(0, 0) mid_top = Point(width - spec['fold-length'], 0) mid_right = Point(width, spec['fold-length']) bottom_right = Point(width, height) bottom_left = Point(0, height) shape1_points = [ top_left, mid_top, mid_right, bottom_right, bottom_left, top_left ] shape1_svg = Polygon(points=points_to_str(shape1_points)) shape1_svg.set_style(StyleBuilder(spec['style']).getStyle()) mid = Point(width - spec['fold-length'], spec['fold-length']) shape2_points = [mid_top, mid, mid_right] shape2_svg = Polygon(points=points_to_str(shape2_points)) shape2_svg.set_style(StyleBuilder(spec['style']).getStyle()) # add to group svg_group.addElement(shape1_svg) svg_group.addElement(shape2_svg) return svg_group, width, height
def to_svg(self): info('......processing node [{0}:{1}:{2}:{3}]'.format( self.bpmn_id, self.lane_id, self.pool_id, self.node_id)) # the label element label_group, label_group_width, label_group_height = text_inside_a_rectangle( text=self.node_data['label'], min_width=self.theme['rectangle']['min-width'], max_width=self.theme['rectangle']['max-width'], rect_spec=self.theme['rectangle'], text_spec=self.theme['text']) # the circle element if 'inner-circle' in self.theme: circle_group, circle_group_width, circle_group_height = two_concentric_circles( outer_radius=self.theme['circle']['radius'], inner_radius=self.theme['inner-circle']['radius'], outer_circle_spec=self.theme['circle'], inner_circle_spec=self.theme['inner-circle']) else: circle_group, circle_group_width, circle_group_height = a_circle( radius=self.theme['circle']['radius'], spec=self.theme['circle']) # get the inside element inside_element = self.get_inside_element() # if an inside element is to be placed inside the circle group, place it so that the inside object's center and the circle's center is same if inside_element is not None: inside_group, inside_group_width, inside_group_height = inside_element.svg, inside_element.width, inside_element.height inside_group_xy = Point( (circle_group_width - inside_group_width) / 2, (circle_group_height - inside_group_height) / 2) transformer = TransformBuilder() transformer.setTranslation(inside_group_xy) inside_group.set_transform(transformer.getTransform()) circle_group.addElement(inside_group) # wrap it in a svg group svg_group = G(id=self.group_id) # the circle is vertically below the label, keep a gap of *snap_point_offset* circle_group_xy = Point((label_group_width - circle_group_width) / 2, label_group_height + self.snap_point_offset) transformer = TransformBuilder() transformer.setTranslation(circle_group_xy) circle_group.set_transform(transformer.getTransform()) # where the label will be positioned depends on the value of label_pos if self.label_pos == 'bottom': label_group_xy = Point( 0, label_group_height + self.snap_point_offset + circle_group_height + self.snap_point_offset) transformer = TransformBuilder() transformer.setTranslation(label_group_xy) label_group.set_transform(transformer.getTransform()) # place the label and circle svg_group.addElement(label_group) svg_group.addElement(circle_group) # extend the height so that a blank space of the same height as text is at the bottom so that the circle's left edge is at dead vertical center group_width = label_group_width group_height = label_group_height + self.snap_point_offset + circle_group_height + self.snap_point_offset + label_group_height # snap points snap_points = self.snap_points(group_width, group_height) self.snap_offset_x = (label_group_width - circle_group_width) / 2 + self.snap_point_offset self.snap_offset_y = label_group_height + self.snap_point_offset * 2 # self.draw_snaps(snap_points, svg_group, x_offset=self.snap_offset_x, y_offset=self.snap_offset_y) info('......processing node [{0}:{1}:{2}:{3}] DONE'.format( self.bpmn_id, self.lane_id, self.pool_id, self.node_id)) self.svg_element = SvgElement(svg=svg_group, width=group_width, height=group_height, snap_points=snap_points, label_pos=self.label_pos) return self.svg_element
def a_cross(width, height, x_bar_height, y_bar_width, spec): svg_group = G() # make the X inside the group cross_points = [ Point(0, (height + x_bar_height) / 2), Point(0, (height - x_bar_height) / 2), Point((width - y_bar_width) / 2, (height - x_bar_height) / 2), Point((width - y_bar_width) / 2, 0), Point((width + y_bar_width) / 2, 0), Point((width + y_bar_width) / 2, (height - x_bar_height) / 2), Point(width, (height - x_bar_height) / 2), Point(width, (height + x_bar_height) / 2), Point((width + y_bar_width) / 2, (height + x_bar_height) / 2), Point((width + y_bar_width) / 2, height), Point((width - y_bar_width) / 2, height), Point((width - y_bar_width) / 2, (height + x_bar_height) / 2) ] cross_svg = Polygon(points=points_to_str(cross_points)) cross_svg.set_style(StyleBuilder(spec['style']).getStyle()) # add to group svg_group.addElement(cross_svg) return svg_group, width, height
def create_flow(self, from_node, to_node, label, label_style): from_node_channel, from_node_ordinal = self.channel_collection.channel_and_ordinal( from_node) to_node_channel, to_node_ordinal = self.channel_collection.channel_and_ordinal( to_node) if self.validate(from_node_channel, to_node_channel, from_node, to_node) == False: return None # first we need the diection from from_node to to_node if from_node_channel.number < to_node_channel.number: direction = 'south' else: direction = 'north' # node-positions if from_node_ordinal == len(from_node_channel.nodes) - 1: from_node_position = 'east-most' else: from_node_position = '*' if to_node_ordinal == 0: to_node_position = 'west-most' else: to_node_position = '*' # now we know the rule to chose for snapping and routing for the *from* and *to* node from_node_spec = self.snap_rules[direction]['from-node'][ from_node_position][from_node.category] to_node_spec = self.snap_rules[direction]['to-node'][to_node_position][ to_node.category] from_node_points_in_pool_coordinate = self.channel_collection.points_to_pool_flow_area( boundary=from_node_spec['cross-through-boundary'], channel=from_node_channel, node=from_node, side=from_node_spec['side'], position=from_node_spec['position'], role='from', approach_snap_point_from=from_node_spec[ 'approach-snap-point-from'], peer=to_node, edge_type=self.edge_type) if from_node_points_in_pool_coordinate is None: warn('could not calculate snap points for from-node [{0}]'.format( from_node.id)) return None to_node_points_in_pool_coordinate = self.channel_collection.points_to_pool_flow_area( boundary=to_node_spec['cross-through-boundary'], channel=to_node_channel, node=to_node, side=to_node_spec['side'], position=to_node_spec['position'], role='to', approach_snap_point_from=to_node_spec['approach-snap-point-from'], peer=from_node, edge_type=self.edge_type) if to_node_points_in_pool_coordinate is None: warn('could not calculate snap points for to-node [{0}]'.format( to_node.id)) return None # we always connect from the north point to the south point # if the points are vertically close it means that they are inside the same pool-flow-area between same two channels, we just connect them by adjusting the to_point y positions from_node_end_point = from_node_points_in_pool_coordinate[-1] to_node_start_point = to_node_points_in_pool_coordinate[0] # TODO: this 48 is a hack, it shold be the sy-between-channels value if abs(from_node_end_point.y - to_node_start_point.y) <= 24: joining_points = [ Point(to_node_start_point.x, from_node_end_point.y) ] else: # TODO: the connect_southward is problematic, no if from_node_points_in_pool_coordinate[-1].north_of( to_node_points_in_pool_coordinate[0]): north_point = from_node_points_in_pool_coordinate[-1] south_point = to_node_points_in_pool_coordinate[0] joining_points = self.channel_collection.connect_southward( point_from=north_point, point_to=south_point) else: # self.mark_points([from_node_points_in_pool_coordinate[-1]], self.channel_collection.element.svg, 'red') # self.mark_points([to_node_points_in_pool_coordinate[0]], self.channel_collection.element.svg, 'green') north_point = to_node_points_in_pool_coordinate[0] south_point = from_node_points_in_pool_coordinate[-1] joining_points = self.channel_collection.connect_southward( point_from=north_point, point_to=south_point) joining_points.reverse() # self.mark_points(joining_points, self.channel_collection.element.svg, 'blue') # self.mark_points(from_node_points_in_pool_coordinate, self.channel_collection.element.svg, 'red') # self.mark_points(to_node_points_in_pool_coordinate, self.channel_collection.element.svg, 'green') # we have the points, now create and return the flow flow_points = from_node_points_in_pool_coordinate + joining_points + to_node_points_in_pool_coordinate # debug('[{0}] -> [{1}] point outside channel from node: {2}'.format(from_node.id, to_node.id, from_node_points_in_pool_coordinate[-1])) # debug('[{0}] -> [{1}] point outside channel to node: {2}'.format(from_node.id, to_node.id, to_node_points_in_pool_coordinate[0])) # debug('[{0}] -> [{1}] joining points : {2}'.format(from_node.id, to_node.id, joining_points)) flow_points = optimize_points(flow_points) # determine the placement of the label label_data = None if label is not None and label != '': label_data = {} label_data['text'] = label # get the first vertical line segment having a min-length point_from, point_to = first_vertical_line_segment_longer_than( flow_points, 30) # the main connecting line for ChannelFlow is a horizontal line label_data['line-points'] = {'from': point_from, 'to': point_to} label_data['line-direction'] = 'north-south' # the text should be placed on top of the line label_data['placement'] = label_style.get('placement', 'east') label_data['move-x'] = float(label_style.get('move_x', 0)) label_data['move-y'] = float(label_style.get('move_y', 20)) flow_svg, flow_width, flow_height = a_flow(flow_points, label_data, self.theme, self.flow_scope) # debug('[{0}] -> [{1}] : {2}'.format(from_node.id, to_node.id, flow_points)) return SvgElement(svg=flow_svg, width=flow_width, height=flow_height)
def to_svg(self): info('......processing node [{0}:{1}:{2}:{3}]'.format( self.bpmn_id, self.lane_id, self.pool_id, self.node_id)) # the label element label_group, label_group_width, label_group_height = text_inside_a_rectangle( text=self.node_data['label'], min_width=self.theme['rectangle']['min-width'], max_width=self.theme['rectangle']['max-width'], rect_spec=self.theme['rectangle'], text_spec=self.theme['text']) # the folded rectangle folded_rectangle_group, folded_rectangle_group_width, folded_rectangle_group_height = a_folded_rectangle( width=self.theme['folded-rectangle']['width'], height=self.theme['folded-rectangle']['height'], spec=self.theme['folded-rectangle']) # top left element inside ------------------------------------------------------- top_left_element = self.get_top_left_element() # position properly inside the folder rectangle at top left if top_left_element is not None: top_left_group, top_left_group_width, top_left_group_height = top_left_element.svg, top_left_element.width, top_left_element.height top_left_group_xy = Point( self.theme['folded-rectangle']['pad-spec']['left'], self.theme['folded-rectangle']['pad-spec']['top']) transformer = TransformBuilder() transformer.setTranslation(top_left_group_xy) top_left_group.set_transform(transformer.getTransform()) folded_rectangle_group.addElement(top_left_group) # bottom center element inside ------------------------------------------------------- bottom_center_element = self.get_bottom_center_element() # position properly inside the folder rectangle at bottom center if bottom_center_element is not None: bottom_center_group, bottom_center_group_width, bottom_center_group_height = bottom_center_element.svg, bottom_center_element.width, bottom_center_element.height bottom_center_group_xy = '{0},{1}'.format( (folded_rectangle_group_width - bottom_center_group_width) / 2, folded_rectangle_group_height - bottom_center_group_height - self.theme['folded-rectangle']['pad-spec']['bottom']) transformer = TransformBuilder() transformer.setTranslation(bottom_center_group_xy) bottom_center_group.set_transform(transformer.getTransform()) folded_rectangle_group.addElement(bottom_center_group) # wrap them in a svg group ----------------------------------------------------------------- svg_group = G(id=self.group_id) # the folded rectangle is below an empty space of the same height of the label folded_rectangle_group_xy = Point( (label_group_width - folded_rectangle_group_width) / 2, label_group_height + self.snap_point_offset) transformer = TransformBuilder() transformer.setTranslation(folded_rectangle_group_xy) folded_rectangle_group.set_transform(transformer.getTransform()) # where the label will be positioned depends on the value of label_pos if self.label_pos == 'bottom': label_group_xy = Point( 0, label_group_height + self.snap_point_offset + folded_rectangle_group_height + self.snap_point_offset) transformer = TransformBuilder() transformer.setTranslation(label_group_xy) label_group.set_transform(transformer.getTransform()) # place the elements svg_group.addElement(folded_rectangle_group) svg_group.addElement(label_group) # extend the height so that a blank space of the same height as text is at the bottom so that the circle's left edge is at dead vertical center group_width = label_group_width group_height = label_group_height + self.snap_point_offset + folded_rectangle_group_height + self.snap_point_offset + label_group_height # snap points snap_points = self.snap_points(group_width, group_height) self.snap_offset_x = (label_group_width - folded_rectangle_group_width ) / 2 + self.snap_point_offset self.snap_offset_y = label_group_height + self.snap_point_offset * 2 # self.draw_snaps(snap_points, svg_group, x_offset=self.snap_offset_x, y_offset=self.snap_offset_y) info('......processing node [{0}:{1}:{2}:{3}] DONE'.format( self.bpmn_id, self.lane_id, self.pool_id, self.node_id)) self.svg_element = SvgElement(svg=svg_group, width=group_width, height=group_height, snap_points=snap_points, label_pos=self.label_pos) return self.svg_element
def points_to_curved_path(points): new_points = optimize_points(points) # first we generate a new set of points for every three points so that if the three points make a turn we have actually 5 points path = 'M {0}'.format(new_points[0]) # if we have only two points if len(points) == 2: path = '{0} L {1}'.format(path, points[1]) return path default_offset = 5 i = 0 while i < (len(new_points) - 2): p1, p2, p3 = new_points[i], new_points[i+1], new_points[i+2] # we know that p1-p2 line is perpendicular to p2-p3 line if p1.x == p2.x: # line p1-p2 is vertical, so p2-p3 must be horizontal if p1.y < p2.y: # p1 is above p2 # we just add a new point p2a just vertically (q_offset) above p2. # The default_offset may be greater than the vertical diff betwwen the points p1 and p2, we adjust it q_offset = min(abs(p2.y - p1.y)/2, default_offset) p2a = Point(p2.x, p2.y - q_offset) # The default_offset may be greater than the horizontal diff betwwen the points p2 and p3, we adjust it q_offset = min(abs(p3.x - p2.x)/2, default_offset) if p2.x < p3.x: # p2 is left to p3 # and another new point p2b just horizontally (q_offset) after p2 p2b = Point(p2.x + q_offset, p2.y) else: # p2 is right to p3 # and another new point p2b just horizontally (q_offset) before p2 p2b = Point(p2.x - q_offset, p2.y) else: # p1 is below p2 # we just add a new point p2a just vertically (q_offset) below p2 # The default_offset may be greater than the vertical diff betwwen the points p1 and p2, we adjust it q_offset = min((p1.y - p2.y)/2, default_offset) p2a = Point(p2.x, p2.y + q_offset) # The default_offset may be greater than the horizontal diff betwwen the points p2 and p3, we adjust it q_offset = min(abs(p3.x - p2.x)/2, default_offset) if p2.x < p3.x: # p2 is left to p3 # and another new point p2b just horizontally (q_offset) after p2 p2b = Point(p2.x + q_offset, p2.y) else: # p2 is right to p3 # and another new point p2b just horizontally (q_offset) before p2 p2b = Point(p2.x - q_offset, p2.y) if p1.y == p2.y: # line p1-p2 is horizontal, so p2-p3 must be vertical if p1.x < p2.x: # p1 is left to p2 # we just add a new point p2a just horizontally (q_offset) before p2 # The default_offset may be greater than the horizontal diff betwwen the points p1 and p2, we adjust it q_offset = min(abs(p2.x - p1.x)/2, default_offset) p2a = Point(p2.x - q_offset, p2.y) # The default_offset may be greater than the vertical diff betwwen the points p2 and p3, we adjust it q_offset = min(abs(p3.y - p2.y)/2, default_offset) if p2.y < p3.y: # p2 is above to p3 # and another new point p2b just vertically (q_offset) after p2 p2b = Point(p2.x, p2.y + q_offset) else: # p2 is below p3 # and another new point p2b just vertically (q_offset) before p2 p2b = Point(p2.x, p2.y - q_offset) else: # p1 is right to p2 # we just add a new point p2a just horizontally (q_offset) after p2 # The default_offset may be greater than the horizontal diff betwwen the points p1 and p2, we adjust it q_offset = min(abs(p2.x - p1.x)/2, default_offset) p2a = Point(p2.x + q_offset, p2.y) # The default_offset may be greater than the vertical diff betwwen the points p2 and p3, we adjust it q_offset = min(abs(p3.y - p2.y)/2, default_offset) if p2.y < p3.y: # p2 is above p3 # and another new point p2b just vertically (q_offset) after p2 p2b = Point(p2.x, p2.y + q_offset) else: # p2 is below p3 # and another new point p2b just vertically (q_offset) before p2 p2b = Point(p2.x, p2.y - q_offset) # the path is 5 point path with p2 now as the Q point # path = '{0} L {1} L {2} Q {3} {4} L {5}'.format(path, p1, p2a, p2, p2b, p3) path = '{0} L {1} Q {2} {3}'.format(path, p2a, p2, p2b) i = i + 1 path = '{0} L {1}'.format(path, new_points[-1]) return path
def bypass_vertically(self, coming_from, going_to, margin_spec): # the coming_from point is north of going_to, so we have to reach a point south of the lane either through east or west or directly dpending on which direction we are going_to if coming_from.north_of(going_to): # if the coming_from point is already below the lane, we have no point if coming_from.y >= self.element.xy.y + self.element.height + margin_spec[ 'bottom']: return [] # if the coming_from point's x position is outside the lane, we can directly go south elif (self.element.xy.x - margin_spec['left']) >= coming_from.x or coming_from.x >= ( self.element.xy.x + self.element.width + margin_spec['right']): return [ Point( coming_from.x, self.element.xy.y + self.element.height + margin_spec['bottom']) ] # if the coming_from point is properly above the lane, we make a path else: # to the lane north vertically below coming_from p1 = Point(coming_from.x, self.element.xy.y - margin_spec['top']) if going_to.east_of(coming_from): # to the lane north-east p2 = Point( self.element.xy.x + self.element.width + margin_spec['right'], p1.y) # debug('southward from: {0} to {1} new {2}'.format(point_from, point_to, new_target_point)) else: # to the lane north-west p2 = Point(self.element.xy.x - margin_spec['left'], p1.y) # debug('northward from: {0} to {1} new {2}'.format(point_from, point_to, new_target_point)) # to the lane south vertically below p2 p3 = Point( p2.x, self.element.xy.y + self.element.height + margin_spec['bottom']) # the coming_from point is south of lane, so we have to reach a point north of the lane either through east or west dpending on which direction we are going_to else: # if the coming_from point is already above the pool, we have no point if coming_from.y <= self.element.xy.y - margin_spec['top']: return [] # if the coming_from point's x position is outside the lane, we can directly go north elif (self.element.xy.x - margin_spec['left']) >= coming_from.x or coming_from.x >= ( self.element.xy.x + self.element.width + margin_spec['right']): return [ Point(coming_from.x, self.element.xy.y - margin_spec['top']) ] # if the coming_from point is properly below the lane, we make a path else: # to the lane south vertically above coming_from p1 = Point( coming_from.x, self.element.xy.y + self.element.height + margin_spec['bottom']) if going_to.east_of(coming_from): # to the lane south-east p2 = Point( self.element.xy.x + self.element.width + margin_spec['right'], p1.y) # debug('southward from: {0} to {1} new {2}'.format(point_from, point_to, new_target_point)) else: # to the lane south-west p2 = Point(self.element.xy.x - margin_spec['left'], p1.y) # debug('northward from: {0} to {1} new {2}'.format(point_from, point_to, new_target_point)) # to the lane north vertically above p2 p3 = Point(p2.x, self.element.xy.y - margin_spec['top']) return [p1, p2, p3]
def outside_the_lane(self, lane_boundary, pool_boundary, pool_number, channel_boundary, channel, node, side, position, role, approach_snap_point_from, peer, edge_type, lane_margin_spec): # first get outside the lane points_in_lane_coordinate = self.outside_the_pool( pool_boundary, pool_number, channel_boundary, channel, node, side, position, role, approach_snap_point_from, peer, edge_type) # now get outside the lane (pool_collection) - to do so we we either go south or north depending on lane_boundary last_pool_number = len(self.channel_collection_list) - 1 if lane_boundary == 'south': # we have to get to the southern boundary of the lane if role == 'from': the_point = Point( points_in_lane_coordinate[-1].x, self.element.height + lane_margin_spec['bottom']) the_points = self.connect_southward( pool_number, points_in_lane_coordinate[-1], last_pool_number, the_point) else: the_point = Point( points_in_lane_coordinate[0].x, self.element.height + lane_margin_spec['bottom']) the_points = self.connect_southward( pool_number, points_in_lane_coordinate[0], last_pool_number, the_point) elif lane_boundary == 'north': # we have to get to the northern boundary of the lane if role == 'from': the_point = Point(points_in_lane_coordinate[-1].x, -lane_margin_spec['top']) the_points = self.connect_southward( 0, points_in_lane_coordinate[-1], pool_number, the_point) else: the_point = Point(points_in_lane_coordinate[0].x, -lane_margin_spec['top']) the_points = self.connect_southward( 0, points_in_lane_coordinate[0], pool_number, the_point) elif lane_boundary == 'west': # we have to get to the western boundary of the lane if role == 'from': the_points = [ Point(-lane_margin_spec['left'], points_in_lane_coordinate[-1].y) ] # the_points = self.connect_southward(points_in_lane_coordinate[-1], the_point) else: the_points = [ Point(-lane_margin_spec['left'], points_in_lane_coordinate[0].y) ] # the_points = self.connect_southward(points_in_lane_coordinate[0], the_point) elif lane_boundary == 'east': # we have to get to the eastern boundary of the lane if role == 'from': the_points = [ Point(self.element.width + lane_margin_spec['right'], points_in_lane_coordinate[-1].y) ] # the_points = self.connect_southward(points_in_lane_coordinate[-1], the_point) else: the_points = [ Point(self.element.width + lane_margin_spec['right'], points_in_lane_coordinate[0].y) ] # the_points = self.connect_southward(points_in_lane_coordinate[0], the_point) else: # should never happen warn('lane boundary is unknown') return points_in_lane_coordinate # debug('{0} outside point for pool [{1}] xy: {2} ({3} x {4}) is at {5}'.format(pool_boundary, self.pool_id, self.element.xy, self.element.width, self.element.height, the_point)) if role == 'to': return the_points + points_in_lane_coordinate else: return points_in_lane_coordinate + the_points
def tune_labels(self): # get the width of the lane label having the maximum width max_lane_label_width = 0 max_pool_label_width = 0 for lane_group in self.label_element.svg.getAllElements(): # the first child is a rect and the rect element's width is the width of lane-label lane_label_width = lane_group.getElementAt(0).getAttribute('width') # print('lane {0} width: {1}'.format(lane_group.getAttribute('id'), lane_label_width)) max_lane_label_width = max(max_lane_label_width, lane_label_width) # the pool labels are in a group which is the third child for pool_group in lane_group.getElementAt(2).getAllElements(): # the first child is a rect and the rect element's width is the width of pool-label pool_label_width = pool_group.getElementAt(0).getAttribute( 'width') # print('....pool {0} width: {1}'.format(pool_group.getAttribute('id'), pool_label_width)) max_pool_label_width = max(max_pool_label_width, pool_label_width) # now we know the max width for lane and pool labels, we adjust them accordingly for lane_group in self.label_element.svg.getAllElements(): # the first child is a rect and the rect element's width is the width of lane-label lane_label_width_diff = max_lane_label_width - lane_group.getElementAt( 0).getAttribute('width') if lane_label_width_diff == 0: # we do nothing, this is already at the max width pass # change the rect's (first child) width lane_group.getElementAt(0).setAttribute('width', max_lane_label_width) # second child is an svg whose width needs to be adjusted by diff lane_label_svg_width = lane_group.getElementAt(1).getAttribute( 'width') lane_group.getElementAt(1).setAttribute( 'width', lane_label_svg_width + lane_label_width_diff) # third child is the pool label group, whose transform's translation's x position needs to be adjusted by diff transform = lane_group.getElementAt(2).getAttribute('transform') m = re.match('translate\((?P<x>.+),(?P<y>.+)\)', transform, re.IGNORECASE) if m and m.group('x') is not None and m.group('y') is not None: point = Point( float(m.group('x')) + lane_label_width_diff, float(m.group('y'))) lane_group.getElementAt(2).setAttribute( 'transform', 'translate({0})'.format(point)) # the pool labels are in a group which is the third child for pool_group in lane_group.getElementAt(2).getAllElements(): # the first child is a rect and the rect element's width is the width of pool-label pool_label_width_diff = max_pool_label_width - pool_group.getElementAt( 0).getAttribute('width') if pool_label_width_diff == 0: # we do nothing, this is already at the max width pass # change the rect's (first child) width pool_group.getElementAt(0).setAttribute( 'width', max_pool_label_width) # second child is an svg whose width needs to be adjusted by diff pool_label_svg_width = pool_group.getElementAt(1).getAttribute( 'width') pool_group.getElementAt(1).setAttribute( 'width', pool_label_svg_width + pool_label_width_diff)
def create_svg(self): info('assembling BPMN [{0}]'.format(self.bpmn_id)) # wrap it in a svg group svg_group = G(id=self.bpmn_id) bpmn_width = self.theme['bpmn-rect']['pad-spec'][ 'left'] + self.label_element.width + self.body_element.width + self.theme[ 'bpmn-rect']['pad-spec']['right'] # get the svg element for the label on top bpmn_label_svg, label_width, label_height = text_inside_a_rectangle( text=self.bpmn_data['label'], min_width=bpmn_width, max_width=bpmn_width, rect_spec=self.theme['rectangle'], text_spec=self.theme['text'], debug_enabled=False) bpmn_height = label_height + self.theme['bpmn-rect']['pad-spec'][ 'top'] + self.body_element.height + self.theme['bpmn-rect'][ 'pad-spec']['bottom'] body_rect_svg = Rect(width=bpmn_width, height=bpmn_height) body_rect_svg.set_style( StyleBuilder(self.theme['bpmn-rect']['style']).getStyle()) svg_group.addElement(body_rect_svg) # assemble bpmn text and bpmn body. text stacked on top of body # bpmn has a margin, so the outer group needs a transformation svg_group_xy = Point(self.theme['margin-spec']['left'], self.theme['margin-spec']['top']) transformer = TransformBuilder() transformer.setTranslation(svg_group_xy) svg_group.set_transform(transformer.getTransform()) if 'hide_labels' in self.bpmn_data['styles'] and self.bpmn_data[ 'styles']['hide_labels'] == 'true': # place the bpmn body group just below the text group right to label body_element_xy = Point( self.theme['bpmn-rect']['pad-spec']['left'], label_height + self.theme['bpmn-rect']['pad-spec']['top']) transformer = TransformBuilder() transformer.setTranslation(body_element_xy) self.body_element.svg.set_transform(transformer.getTransform()) # place the bpmn label svg_group.addElement(bpmn_label_svg) svg_group.addElement(self.body_element.svg) else: # place the lane-pool label just below the text to the laft label_element_xy = Point( self.theme['bpmn-rect']['pad-spec']['left'], label_height + self.theme['bpmn-rect']['pad-spec']['top']) transformer = TransformBuilder() transformer.setTranslation(label_element_xy) self.label_element.svg.set_transform(transformer.getTransform()) # place the bpmn body group just below the text group right to label body_element_xy = Point( self.theme['bpmn-rect']['pad-spec']['left'] + self.label_element.width, label_height + self.theme['bpmn-rect']['pad-spec']['top']) transformer = TransformBuilder() transformer.setTranslation(body_element_xy) self.body_element.svg.set_transform(transformer.getTransform()) # place the bpmn label svg_group.addElement(bpmn_label_svg) svg_group.addElement(self.label_element.svg) svg_group.addElement(self.body_element.svg) # wrap in canvas canvas_width = self.theme['margin-spec'][ 'left'] + bpmn_width + self.theme['margin-spec']['right'] canvas_height = self.theme['margin-spec'][ 'top'] + bpmn_height + self.theme['margin-spec']['bottom'] svg = Svg(0, 0, width=canvas_width, height=canvas_height) svg.addElement(svg_group) info('assembling BPMN [{0}]'.format(self.bpmn_id)) return svg
def snap_points(self, width, height): snaps = super().snap_points(width, height) # add two more (slight left and slight right) for north-middle) snaps['north']['middle'].append( SnapPoint(point=Point(width * 0.45, self.snap_point_offset * -1))) snaps['north']['middle'].append( SnapPoint(point=Point(width * 0.55, self.snap_point_offset * -1))) # add two more (slight left and slight right) for south-middle) snaps['south']['middle'].append( SnapPoint(point=Point(width * 0.45, height + self.snap_point_offset * 1))) snaps['south']['middle'].append( SnapPoint(point=Point(width * 0.55, height + self.snap_point_offset * 1))) # add two more (slight up and slight down) for east-middle) snaps['east']['middle'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.40))) snaps['east']['middle'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.60))) # add two more (slight up and slight down) for west-middle) snaps['west']['middle'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.40))) snaps['west']['middle'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.60))) # activities have snap-points in north-left - one in mid-left and two more (slight left and slight right) snaps['north']['left'] = [ SnapPoint(point=Point(width * 0.25, self.snap_point_offset * -1)) ] snaps['north']['left'].append( SnapPoint(point=Point(width * 0.20, self.snap_point_offset * -1))) snaps['north']['left'].append( SnapPoint(point=Point(width * 0.30, self.snap_point_offset * -1))) # activities have snap-points in north-right - one in mid-right and two more (slight left and slight right) snaps['north']['right'] = [ SnapPoint(point=Point(width * 0.75, self.snap_point_offset * -1)) ] snaps['north']['right'].append( SnapPoint(point=Point(width * 0.70, self.snap_point_offset * -1))) snaps['north']['right'].append( SnapPoint(point=Point(width * 0.80, self.snap_point_offset * -1))) # activities have snap-points in south-left - one in mid-left and two more (slight left and slight right) snaps['south']['left'] = [ SnapPoint(point=Point(width * 0.25, height + self.snap_point_offset * 1)) ] snaps['south']['left'].append( SnapPoint(point=Point(width * 0.20, height + self.snap_point_offset * 1))) snaps['south']['left'].append( SnapPoint(point=Point(width * 0.30, height + self.snap_point_offset * 1))) # activities have snap-points in south-right - one in mid-right and two more (slight left and slight right) snaps['south']['right'] = [ SnapPoint(point=Point(width * 0.75, height + self.snap_point_offset * 1)) ] snaps['south']['right'].append( SnapPoint(point=Point(width * 0.70, height + self.snap_point_offset * 1))) snaps['south']['right'].append( SnapPoint(point=Point(width * 0.80, height + self.snap_point_offset * 1))) # activities have snap-points in east-top - one in mid-top and two more (slight up and slight down) snaps['east']['top'] = [ SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.25)) ] snaps['east']['top'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.15))) snaps['east']['top'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.35))) # activities have snap-points in east-bottom - one in mid-bottom and two more (slight up and slight down) snaps['east']['bottom'] = [ SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.75)) ] snaps['east']['bottom'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.65))) snaps['east']['bottom'].append( SnapPoint(point=Point(width + self.snap_point_offset * 1, height * 0.85))) # activities have snap-points in west-top - one in mid-top and two more (slight up and slight down) snaps['west']['top'] = [ SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.25)) ] snaps['west']['top'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.15))) snaps['west']['top'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.35))) # activities have snap-points in west-bottom - one in mid-bottom and two more (slight up and slight down) snaps['west']['bottom'] = [ SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.75)) ] snaps['west']['bottom'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.65))) snaps['west']['bottom'].append( SnapPoint(point=Point(self.snap_point_offset * -1, height * 0.85))) return snaps
def a_path_with_label(points, label_data, spec, flow_scope): svg_group = G() path_data = points_to_curved_path(points) # path_data = points_to_path(points) # print(path_data) svg = Path(pathData=path_data) svg.set_style(StyleBuilder(spec['style'][flow_scope]).getStyle()) if label_data is not None: # calculate max_width if line_direction is east-west # print(label_data) if label_data['line-direction'] == 'east-west': max_width = abs(label_data['line-points']['to'].x - label_data['line-points']['from'].x) else: max_width = spec['label']['rectangle']['max-width'] label_group, label_group_width, label_group_height = text_inside_a_rectangle( text=label_data['text'], min_width=min(spec['label']['rectangle']['min-width'], max_width), max_width=max_width, rect_spec=spec['label']['rectangle'], text_spec=spec['label']['text']) # for east-west (horizontal label line) if label_data['line-direction'] == 'east-west': if label_data['line-points']['to'].east_of( label_data['line-points']['from']): label_pos_x = label_data['line-points']['from'].x + label_data[ 'move-x'] else: label_pos_x = label_data['line-points'][ 'from'].x - label_group_width + label_data['move-x'] if label_data['placement'] == 'north': label_pos_y = min(label_data['line-points']['from'].y, label_data['line-points']['to'].y ) - label_group_height + label_data['move-y'] else: label_pos_y = min( label_data['line-points']['from'].y, label_data['line-points']['to'].y) + label_data['move-y'] # for north-south (vertical label line) else: # label should be nearer to the from point if label_data['line-points']['to'].north_of( label_data['line-points']['from']): label_pos_y = label_data['line-points'][ 'from'].y - label_group_height - label_data['move-y'] else: label_pos_y = label_data['line-points']['from'].y + label_data[ 'move-y'] if label_data['placement'] == 'east': label_pos_x = min( label_data['line-points']['from'].x, label_data['line-points']['to'].x) + label_data['move-x'] else: label_pos_x = min(label_data['line-points']['from'].x, label_data['line-points']['to'].x ) - label_group_width + label_data['move-x'] # print('x: {} y: {} w: {} h: {}'.format(label_pos_x, label_pos_y, label_group_width, label_group_height)) # add label to group label_pos_xy = Point(label_pos_x, label_pos_y) transformer = TransformBuilder() transformer.setTranslation(label_pos_xy) label_group.set_transform(transformer.getTransform()) svg_group.addElement(label_group) # add flow to group svg_group.addElement(svg) return svg_group, 0, 0