예제 #1
0
    def get_bottom_center_element(self):
        rect_svg_group, rect_svg_width, rect_svg_height = a_cross_in_a_rectangle(
            width=self.theme['bottom-center-rectangle']['width'],
            height=self.theme['bottom-center-rectangle']['height'],
            rect_spec=self.theme['bottom-center-rectangle'],
            cross_spec=self.theme['bottom-center-inner-shape'])
        rect_svg_element = SvgElement(svg=rect_svg_group,
                                      width=rect_svg_width,
                                      height=rect_svg_height)

        tilde_svg_group, tilde_svg_width, tilde_svg_height = a_tilde_in_a_rectangular_shape(
            width=self.theme['bottom-center-rectangle']['width'],
            height=self.theme['bottom-center-rectangle']['height'],
            rect_spec=self.theme['bottom-center-rectangle'],
            tilde_spec=self.theme['bottom-center-inner-shape'])
        tilde_svg_element = SvgElement(svg=tilde_svg_group,
                                       width=tilde_svg_width,
                                       height=tilde_svg_height)

        svg_group, group_width, group_height = align_and_combine_horizontally(
            [rect_svg_element, tilde_svg_element])

        return SvgElement(svg=svg_group,
                          width=group_width,
                          height=group_height)
 def get_inside_element(self):
     svg_group, group_width, group_height = a_cross_inside_a_circular_shape(
         radius=self.theme['inner-circle']['radius'],
         inner_shape_spec=self.theme['inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
예제 #3
0
    def assemble_labels(self):
        if 'hide_label' in self.lane_data['styles'] and self.lane_data['styles']['hide_label'] == 'true':
            return None

        group_id = '{0}:{1}-label'.format(self.bpmn_id, self.lane_id)

        # get the lane label, its min_width and max_width is the pool collection's height + all
        label_group, group_width, group_height = text_inside_a_rectangle(
                                                    text=self.lane_data['label'],
                                                    min_width=self.svg_element.height,
                                                    max_width=self.svg_element.height,
                                                    rect_spec=self.theme['rectangle'],
                                                    text_spec=self.theme['text'],
                                                    debug_enabled=False)

        label_group.set_id(id=group_id)

        # now we need to add the pool-collection's label just right to it
        child_label_element = self.pool_collection_instance.assemble_labels()

        transformer = TransformBuilder()
        child_label_xy = Point(group_width, 0)
        transformer.setTranslation(child_label_xy)
        child_label_element.svg.set_transform(transformer.getTransform())
        label_group.addElement(child_label_element.svg)

        group_width = group_width + child_label_element.width

        # wrap it in a svg element
        self.label_element = SvgElement(svg=label_group, width=group_width, height=group_height)
        # pprint(self.label_element.svg.getXML())
        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 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 get_top_left_element(self):
     radius = self.theme['top-left-inner-shape']['height'] / 2
     svg_group, group_width, group_height = two_gears_inside_a_circular_shape(
         radius=radius, inner_shape_spec=self.theme['top-left-inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
예제 #7
0
    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 data store svg
        data_store_group, data_store_group_width, data_store_group_height = include_and_scale_svg(
            spec=self.theme['shape-spec'])

        # 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
        d_x = max((label_group_width - data_store_group_width) / 2, 0)
        data_store_group_xy = '{0},{1}'.format(
            (label_group_width - data_store_group_width) / 2,
            label_group_height + self.snap_point_offset)
        transformer = TransformBuilder()
        transformer.setTranslation(data_store_group_xy)
        data_store_group.set_transform(transformer.getTransform())

        # the label is vertically below the folded rectangle
        label_group_xy = '{0},{1}'.format(
            0, label_group_height + self.snap_point_offset +
            data_store_group_height)
        transformer = TransformBuilder()
        transformer.setTranslation(label_group_xy)
        label_group.set_transform(transformer.getTransform())

        # place the elements
        svg_group.addElement(data_store_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 + data_store_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 - data_store_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='bottom')
        return self.svg_element
 def get_inside_element(self):
     radius = min(self.theme['diamond']['diagonal-x'],
                  self.theme['diamond']['diagonal-y']) * 0.25
     svg_group, group_width, group_height = a_circle(
         radius=radius, spec=self.theme['inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
예제 #9
0
 def get_top_left_element(self):
     svg_group, group_width, group_height = an_envelop(
         width=self.theme['top-left-inner-shape']['width'],
         height=self.theme['top-left-inner-shape']['height'],
         spec=self.theme['top-left-inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
 def get_inside_element(self):
     svg_group, group_width, group_height = a_star(
         width=self.theme['diamond']['diagonal-x'],
         height=self.theme['diamond']['diagonal-y'],
         spec=self.theme['inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
 def get_inside_element(self):
     radius = radius_of_the_circle_inside_the_diamond(
         width=self.theme['diamond']['diagonal-x'],
         height=self.theme['diamond']['diagonal-y'])
     svg_group, group_width, group_height = an_x_inside_a_circular_shape(
         radius=radius, inner_shape_spec=self.theme['inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
예제 #12
0
 def get_inside_element(self):
     radius = min(self.theme['diamond']['diagonal-x'],
                  self.theme['diamond']['diagonal-y']) * 0.27
     svg_group, group_width, group_height = an_equilateral_pentagon_in_a_circle(
         radius=radius,
         circle_spec=self.theme['inner-circle'],
         pentagon_spec=self.theme['inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
 def get_top_left_element(self):
     width = self.theme['top-left-inner-shape']['width']
     height = self.theme['top-left-inner-shape']['height']
     svg_group, group_width, group_height = a_right_arrow(
         width=width,
         height=height,
         spec=self.theme['top-left-inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
 def get_bottom_center_element(self):
     width = self.theme['bottom-center-inner-shape']['width']
     height = self.theme['bottom-center-inner-shape']['height']
     svg_group, group_width, group_height = three_bars(
         width=width,
         height=height,
         spec=self.theme['bottom-center-inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
예제 #15
0
    def get_bottom_center_element(self):
        svg_group, group_width, group_height = a_cross_in_a_rectangle(
            width=self.theme['bottom-center-rectangle']['width'],
            height=self.theme['bottom-center-rectangle']['height'],
            rect_spec=self.theme['bottom-center-rectangle'],
            cross_spec=self.theme['bottom-center-inner-shape'])

        return SvgElement(svg=svg_group,
                          width=group_width,
                          height=group_height)
 def get_inside_element(self):
     radius = radius_of_the_circle_inside_the_diamond(
         self.theme['diamond']['diagonal-x'],
         self.theme['diamond']['diagonal-y']) - 4
     svg_group, group_width, group_height = a_cross_in_a_circle(
         radius=radius,
         circle_spec=self.theme['inner-circle'],
         cross_spec=self.theme['inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
 def get_top_left_element(self):
     inner_svg, inner_width, inner_height = an_equilateral_pentagon_inside_a_circular_shape(
         radius=self.theme['top-left-circle']['radius'],
         inner_shape_spec=self.theme['top-left-inner-shape'])
     svg_group, group_width, group_height = envelop_and_center_in_a_circle(
         circle_spec=self.theme['top-left-circle'],
         svg=inner_svg,
         svg_width=inner_width,
         svg_height=inner_height)
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
 def get_inside_element(self):
     pad = 3
     outer_radius = min(self.theme['diamond']['diagonal-x'],
                        self.theme['diamond']['diagonal-y']) * 0.27
     inner_radius = outer_radius - pad
     svg_group, group_width, group_height = an_equilateral_pentagon_in_two_concentric_circles(
         outer_radius=outer_radius,
         inner_radius=inner_radius,
         outer_circle_spec=self.theme['inner-circle'],
         inner_circle_spec=self.theme['inner-circle'],
         pad=pad,
         pentagon_spec=self.theme['inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
    def assemble_elements(self):
        # channels are vertically stacked
        # root channels start at x=0
        # channels that are branch of some parent channel start horizontally after the node to which the first node is the to-node
        # TODO: how to horizontally shift channels based on to-node's relationship with from-node from other lane/pool
        # TODO: how and where to place the island channel?

        # wrap it in a svg group
        group_id = '{0}:{1}:{2}'.format(self.bpmn_id, self.lane_id,
                                        self.pool_id)
        svg_group = G(id=group_id)

        # lay the channels, channels are vertically stacked, but only the root channels start at left, branches ar horizontally positioned so that they fall to the right of their parent node's position
        group_width = 0
        current_y = self.theme['pad-spec']['top']
        transformer = TransformBuilder()
        for channel_list in self.channel_collection.channel_lists:
            for channel in channel_list:
                # if it is a root channel it, starts at left (0) x position
                if channel.is_root == True:
                    current_x = self.theme['pad-spec']['left']
                else:
                    # we find the parent node from which this channel is branched and position accordingly
                    if channel.parent_channel is not None:
                        parent_channel_object = self.channel_collection.channel_by_name(
                            channel.parent_channel)
                        x_pos = parent_channel_object.element.xy.x + parent_channel_object.x_of_node(
                            node_id=channel.name
                        ) + self.theme['dx-between-elements']
                    else:
                        x_pos = 0

                    if x_pos != 0:
                        current_x = self.theme['pad-spec'][
                            'left'] + x_pos + self.theme['dx-between-elements']
                    else:
                        current_x = self.theme['pad-spec']['left']

                # TODO: the channel may be moved up to just below of a previous channel if there is no part of any in between channels in the middle

                channel_element = channel.element
                channel_element_svg = channel_element.svg
                channel_element.xy = Point(current_x + channel_element.move_x,
                                           current_y)

                transformer.setTranslation(channel_element.xy)
                channel_element_svg.set_transform(transformer.getTransform())
                svg_group.addElement(channel_element_svg)

                group_width = max(
                    group_width, channel_element.xy.x + channel_element.width +
                    self.theme['pad-spec']['right'])
                current_y = current_y + channel_element.height + self.theme[
                    'dy-between-channels']

        group_height = current_y - self.theme[
            'dy-between-channels'] + self.theme['pad-spec']['bottom']

        # add the ractangle
        channel_collection_rect_svg = Rect(width=group_width,
                                           height=group_height)
        channel_collection_rect_svg.set_style(
            StyleBuilder(self.theme['style']).getStyle())
        svg_group.addElement(channel_collection_rect_svg)

        # wrap it in a svg element
        self.svg_element = SvgElement(svg=svg_group,
                                      width=group_width,
                                      height=group_height)

        # store the svg and dimensions for future reference
        self.channel_collection.element = self.svg_element

        return self.svg_element
 def get_top_left_element(self):
     svg_group, group_width, group_height = include_and_scale_svg(
         spec=self.theme['top-left-inner-shape'])
     return SvgElement(svg=svg_group,
                       width=group_width,
                       height=group_height)
    def create_flow(self, from_node, to_node, label, label_style):
        # first we need the diection from from_node to to_node
        if from_node.element.xy.west_of(to_node.element.xy):
            direction = 'east'
            # are they adjacent or apart
            if (self.channel.node_ordinal(to_node) - self.channel.node_ordinal(from_node)) == 1:
                distance = 'adjacent'
                flow_route = None
            else:
                distance = 'apart'
                flow_route = self.channel.get_a_channel_flow_route(boundary=direction, node=from_node, peer=to_node)

        elif from_node.element.xy.east_of(to_node.element.xy):
            direction = 'west'
            distance = '*'
            flow_route = self.channel.get_a_channel_flow_route(boundary=direction, node=from_node, peer=to_node)
        else:
            warn('from-node [{0}] and to-node [{1}] starts at same x position, they can not be connected inside a channel which is supposed to have all nodes on different x position on same y')
            return None



        # now we know the rule to chose for snapping and routing for the *from* and *to* node
        from_node_spec = self.snap_rules[direction][distance]['from-node'][from_node.category]
        to_node_spec = self.snap_rules[direction][distance]['to-node'][to_node.category]

        from_node_points_in_channel = self.channel.points_to_channel_flow_area(
                                        boundary=from_node_spec['cross-through-boundary'],
                                        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,
                                        flow_route=flow_route)

        if from_node_points_in_channel is None:
            warn('could not calculate snap points for from-node [{0}]'.format(from_node.id))
            return None

        if from_node_spec['cross-through-boundary']:
            # self.mark_points([from_node_points_in_channel[-1]], self.channel.element.svg, 'red')
            pass

        to_node_points_in_channel = self.channel.points_to_channel_flow_area(
                                        boundary=to_node_spec['cross-through-boundary'],
                                        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=to_node,
                                        edge_type=self.edge_type,
                                        flow_route=flow_route)

        if to_node_points_in_channel is None:
            warn('could not calculate snap points for to-node [{0}]'.format(to_node.id))
            return None

        if to_node_spec['cross-through-boundary']:
            # self.mark_points([to_node_points_in_channel[0]], self.channel.element.svg, 'green')
            pass


        # we now have two segments we connect the last point of *from-segment* to the first point of *to-segment* through a north-ward path
        flow_points = from_node_points_in_channel + to_node_points_in_channel
        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 longest horizontal line segment
            point_from, point_to = longest_horizontal_line_segment(flow_points)
            # the main connecting line for ChannelFlow is a horizontal line
            label_data['line-points'] = {'from': point_from, 'to': point_to}
            label_data['line-direction'] = 'east-west'
            # the text should be placed on top of the line
            label_data['placement'] = label_style.get('placement', 'north')
            label_data['move-x'] = float(label_style.get('move_x', 0))
            label_data['move-y'] = float(label_style.get('move_y', 0))

        # we have the points, now create and return the flow
        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)
예제 #22
0
    def create_flow(self, from_node, to_node, label, label_style):
        from_node_lane_number, from_node_lane_id, from_node_pool_number, from_node_pool_id, _ = self.lane_collection.lane_and_pool_number_and_id(from_node)
        to_node_lane_number, to_node_lane_id, to_node_pool_number, to_node_pool_id, _ = self.lane_collection.lane_and_pool_number_and_id(to_node)

        if self.validate(from_node_lane_number, to_node_lane_number, from_node, to_node) == False: return None

        # debug('LANE [{0}] - [{1}:{2}:{3}] --> [{4}:{5}:{6}]'.format(self.lane_collection.lane_id, from_node_lane_number, from_node_lane_id, from_node.id, to_node_lane_number, to_node_lane_id, to_node.id))

        # first we need the diection from from_node to to_node
        if from_node_lane_number < to_node_lane_number:
            direction = 'south'
            from_node_lane_boundary = 'south'
            to_node_lane_boundary = 'north'
            from_node_pool_boundary = 'south'
            to_node_pool_boundary = 'north'
        else:
            direction = 'north'
            from_node_lane_boundary = 'north'
            to_node_lane_boundary = 'south'
            from_node_pool_boundary = 'north'
            to_node_pool_boundary = 'south'

        # node-positions
        from_node_channel, from_node_ordinal = self.lane_collection.channel_and_ordinal(from_node)
        if from_node_ordinal == len(from_node_channel.nodes) - 1:
            from_node_position = 'east-most'
        else:
            from_node_position = '*'

        to_node_channel, to_node_ordinal = self.lane_collection.channel_and_ordinal(to_node)
        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_lane_boundary = from_node_spec['channel_boundary']
        to_node_lane_boundary = to_node_spec['channel_boundary']

        from_node_points_in_bpmn_coordinate = self.lane_collection.outside_the_lane(
                                                lane_boundary=from_node_lane_boundary,
                                                lane_number=from_node_lane_number,
                                                pool_boundary=from_node_pool_boundary,
                                                pool_number=from_node_pool_number,
                                                channel_boundary=from_node_spec['channel_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_bpmn_coordinate is None:
            warn('could not calculate snap points for from-node [{0}]'.format(from_node.id))
            return None

        to_node_points_in_bpmn_coordinate = self.lane_collection.outside_the_lane(
                                                lane_boundary=to_node_lane_boundary,
                                                lane_number=to_node_lane_number,
                                                pool_boundary=to_node_pool_boundary,
                                                pool_number=to_node_pool_number,
                                                channel_boundary=to_node_spec['channel_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=to_node,
                                                edge_type=self.edge_type)

        if to_node_points_in_bpmn_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 from_node_points_in_bpmn_coordinate[-1].north_of(to_node_points_in_bpmn_coordinate[0]):
            north_point = from_node_points_in_bpmn_coordinate[-1]
            south_point = to_node_points_in_bpmn_coordinate[0]
            joining_points = self.lane_collection.connect_southward(from_lane_number=from_node_lane_number, point_from=north_point, to_lane_number=to_node_lane_number, point_to=south_point)
        else:
            north_point = to_node_points_in_bpmn_coordinate[0]
            south_point = from_node_points_in_bpmn_coordinate[-1]
            joining_points = self.lane_collection.connect_southward(from_lane_number=to_node_lane_number, point_from=north_point, to_lane_number=from_node_lane_number, point_to=south_point)
            joining_points.reverse()

        # self.mark_points(joining_points, self.lane_collection.element.svg, color='red')

        # we have the points, now create and return the flow
        flow_points = from_node_points_in_bpmn_coordinate + joining_points + to_node_points_in_bpmn_coordinate

        # 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)

        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 rectangle element
        if self.node_data['label'] == '':
            label = id_to_label(self.node_id)
        else:
            label = self.node_data['label']

        rectangle_group, rectangle_group_width, rectangle_group_height = text_inside_a_rectangle(
            text=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'])

        # get the inside bottom center element
        bottom_center_element = self.get_bottom_center_element()

        # if an inside bottom center element is to be placed, the element should have a gap from the rectangle bottom
        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(
                (rectangle_group_width - bottom_center_group_width) / 2,
                rectangle_group_height - bottom_center_group_height -
                self.theme['rectangle']['inner-shape-margin-spec']['bottom'])
            transformer = TransformBuilder()
            transformer.setTranslation(bottom_center_group_xy)
            bottom_center_group.set_transform(transformer.getTransform())
            rectangle_group.addElement(bottom_center_group)

        # get the inside top left element
        top_left_element = self.get_top_left_element()

        # if an inside bottom center element is to be placed, the element should have a gap from the rectangle bottom
        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 = '{0},{1}'.format(
                self.theme['rectangle']['inner-shape-margin-spec']['left'],
                self.theme['rectangle']['inner-shape-margin-spec']['top'])
            transformer = TransformBuilder()
            transformer.setTranslation(top_left_group_xy)
            top_left_group.set_transform(transformer.getTransform())
            rectangle_group.addElement(top_left_group)

        # if there is an outer rectangle process that
        if 'outer-rectangle' in self.theme:
            rectangle_group, rectangle_group_width, rectangle_group_height = envelop_and_center_in_a_rectangle(
                svg=rectangle_group,
                svg_width=rectangle_group_width,
                svg_height=rectangle_group_height,
                rect_spec=self.theme['outer-rectangle'])

        # snap points
        snap_points = self.snap_points(rectangle_group_width,
                                       rectangle_group_height)
        self.snap_offset_x = self.snap_point_offset
        self.snap_offset_y = self.snap_point_offset
        # self.draw_snaps(snap_points, rectangle_group, x_offset=self.snap_offset_x, y_offset=self.snap_offset_y)
        label_pos = 'middle'

        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=rectangle_group,
                                      width=rectangle_group_width,
                                      height=rectangle_group_height,
                                      snap_points=snap_points,
                                      label_pos=label_pos)
        return self.svg_element
    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
예제 #25
0
    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
예제 #26
0
    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)