Example #1
0
    def __init__(self, conf):
        self.__ARROW_MARKER_ID = "arrowMarker";

        self.__conf = conf

        self.__containerHeights = dict()
        self.__framesPositions = dict()
        self.__framesPositionsById = dict();

        self.__nodeRenderer = NodeRenderer(conf);
        self.__arrowRenderer = ArrowRenderer(conf);

        self.__prepareSVGObject()
        self.__prepareShapeBuilder()
        self.__prepareDefsContainer();
Example #2
0
class SVGTreeCreator:
    def __init__(self, conf):
        self.__ARROW_MARKER_ID = "arrowMarker";

        self.__conf = conf

        self.__containerHeights = dict()
        self.__framesPositions = dict()
        self.__framesPositionsById = dict();

        self.__nodeRenderer = NodeRenderer(conf);
        self.__arrowRenderer = ArrowRenderer(conf);

        self.__prepareSVGObject()
        self.__prepareShapeBuilder()
        self.__prepareDefsContainer();

    def __prepareSVGObject(self):
        self.__SVGObject = svg()

    def __prepareShapeBuilder(self):
        self.__shapeBuilder = ShapeBuilder()

    def __prepareDefsContainer(self):
        self.__defs = defs();

        self.__defs.addElement(Marker(self.__ARROW_MARKER_ID))

        self.__SVGObject.addElement(self.__defs);

    def prepareXML(self):
        return self.__SVGObject.getXML()

    def createSVGFile(self, fileName):
        self.__SVGObject.save(fileName)

    def prepareTreeLevels(self, treeLevels):
        for level in treeLevels.keys():
            self.prepareTreeLevel(treeLevels[level], level)

    def prepareTreeLevel(self, nodes, level):
        i = 0
        
        for node in nodes.values():
            self.prepareNode(node, self.__framesPositions[level][node['id']].x, self.__framesPositions[level][node['id']].y)
            i += 1

    def prepareConnectionsLevels(self, treeLevels):
        for level in treeLevels.keys():
            self.prepareConnectionsLevel(treeLevels[level], level)

    def prepareConnectionsLevel(self, nodes, level):
        for node in nodes.values():
            self.prepareConnectionsForNode(level, node)

    def prepareConnectionsForNode(self, level, node):
        #if node['type'] == 'node':
        if node.has_key('children'):
            for child in node['children']:
                #if child['type'] == 'node':
                self.prepareConnection(level, node, child);

                if child['type'] == 'reference':
                    refNode = self.__findNode(child['value'], self.__rootNode);
                    self.prepareRefConnection(child, refNode);

    def prepareConnection(self, level, startNode, endNode):
        startPositionFrame = self.__framesPositions[level][startNode['id']]
        endPositionFrame = self.__framesPositions[level + 1][endNode['id']]

        startX = startPositionFrame.x + startPositionFrame.width / 2;
        startY = startPositionFrame.y + startPositionFrame.height;
        endX = endPositionFrame.x + startPositionFrame.width / 2;
        endY = endPositionFrame.y;

        self.drawConnection(startX, startY, endX, endY, self.__conf['connection'], False)

    def prepareRefConnection(self, startNode, endNode):
        startPositionFrame = self.__framesPositionsById[startNode['id']]
        endPositionFrame = self.__framesPositionsById[endNode['id']]

        horizontalOffset = math.fabs(endPositionFrame.x - startPositionFrame.x);
        verticalOffset = math.fabs(endPositionFrame.y - startPositionFrame.y);

        top = endPositionFrame.y < startPositionFrame.y;
        bottom = not top;
        left = endPositionFrame.x < startPositionFrame.x;
        right = not left;

        if left and horizontalOffset > verticalOffset:
            #connect left edges
            startX = startPositionFrame.x;
            startY = startPositionFrame.y + startPositionFrame.height / 2;
            endX = endPositionFrame.x + startPositionFrame.width;
            endY = endPositionFrame.y + endPositionFrame.height / 2;
        elif top and verticalOffset > horizontalOffset:
            startX = startPositionFrame.x + startPositionFrame.width / 2;
            startY = startPositionFrame.y;
            endX = endPositionFrame.x + endPositionFrame.width / 2;
            endY = endPositionFrame.y + endPositionFrame.height;
        elif right and horizontalOffset > verticalOffset:
            startX = startPositionFrame.x + startPositionFrame.width;
            startY = startPositionFrame.y + startPositionFrame.height / 2;
            endX = endPositionFrame.x;
            endY = endPositionFrame.y + endPositionFrame.height / 2;
        elif bottom and verticalOffset > horizontalOffset:
            startX = startPositionFrame.x + startPositionFrame.width / 2;
            startY = startPositionFrame.y + startPositionFrame.height;
            endX = endPositionFrame.x + endPositionFrame.width / 2;
            endY = endPositionFrame.y;

        self.drawConnection(startX, startY, endX, endY, self.__conf['connection'], True)

    def drawConnection(self, startX, startY, endX, endY, connectionConf, isReference):
        diff = 10;

        if startX != endX:
            const = math.fabs(startY - endY) / math.fabs(startX - endX);
            offsetX = diff / math.sqrt(1 + const * const)
            offsetY = offsetX * const;

            if startX < endX:
                endX = endX - offsetX;
            else:
                endX = endX + offsetX;
        else:
            offsetY = diff;

        if startY < endY:
            endY = endY - offsetY;
        else:
            endY = endY + offsetY;

        line = self.__arrowRenderer.render(startX, startY, endX, endY, self.__ARROW_MARKER_ID, isReference);

        self.__SVGObject.addElement(line);

    def prepareNode(self, node, startX, startY):
        isRef = False;

        if node['type'] == 'reference':
            node = self.__findNode(node['value'], self.__rootNode);
            isRef = True;

        container = self.__nodeRenderer.render(node, startX, startY, isRef);
        
        self.__SVGObject.addElement(container);

    def __findNode(self, id, node):
        if node['type'] == 'node':
            if node['id'] == id:
                return node;
            elif node.has_key('children'):
                for child in node['children']:
                    found = self.__findNode(id, child)
                    if found != None:
                        return found;
        
        return None;

    def __determineFramesPositions(self, node, level = 0):
        '''
        Determines the spatial position of frames in SVG.

        @param node: root node of tree or subtree
        @param level: depth level of node investigated currently
        '''
        
        if not self.__framesPositions.__contains__(level): # TODO has_key?
            self.__framesPositions[level] = dict()

        self.__containerHeights[node['id']] = self.__nodeRenderer.getNodeHeight(node)

        x = (self.__conf['frame']['width'] + self.__conf['frame']['verticalOffset']) * len(self.__framesPositions[level])
        y = self.__findLevelVerticalPosition(level)
        width = self.__conf['frame']['width']
        height = self.__containerHeights[node['id']]
        
        framePosition = FramePosition(x, y, width, height);
        self.__framesPositions[level][node['id']] = framePosition;
        self.__framesPositionsById[node['id']] = framePosition;

        if node.has_key('children'):
            for child in node['children']:
                self.__determineFramesPositions(child, level + 1)
                
    def __updateFramesOffsets(self, node, parent = None, level = 0):
        if node.has_key('children'):
            conf_width = self.__conf['frame']['width']
            conf_verticalOffset = self.__conf['frame']['verticalOffset']
            
            offsetValue = self.__calculateOffset(node)
            
            #nodeEncountered = False

            # add offset to each node on the specified level
            for node_id in self.__framesPositions[level]:
                if node['id'] == node_id:
                    self.__framesPositions[level][node_id].x += offsetValue

                    #nodeEncountered = True
                else:
                    #if nodeEncountered:
                    self.__framesPositions[level][node_id].x += offsetValue * 2

            childCounter = 0
            
            for child in node['children']:
                tmp = self.__framesPositions[level][node['id']].x - offsetValue + \
                        ((conf_width + conf_verticalOffset) * childCounter)
                
                self.__framesPositions[level + 1][child['id']].x = tmp
                childCounter += 1
            
            for child in node['children']:
                self.__updateFramesOffsets(child, node, level + 1)

    def __calculateOffset(self, node):
        result = 0
        if node.has_key('children'):
            result = (len(node['children']) - 1) * (self.__conf['frame']['width'] + self.__conf['frame']['verticalOffset']) / 2
            for child in node['children']:
                result += self.__calculateOffset(child)

        return result

    def determineTreeLevels(self, data, level = 0, result = None):
        '''
        Grouping nodes according to depth levels. Considering type = 'node', excluding type = 'reference'.

        @param data: root node of tree or subtree
        @param level: depth level of node investigated currently
        @param result: dictionary with with levels as keys and list of nodes on this level as values

        @return: updated result
        '''
        
        #if data['type'] == 'node':
        if result == None and level == 0:
            result = dict()

        if not result.__contains__(level):
            result[level] = dict()
        result[level][len(result[level].keys())] = data

        if data.has_key('children'):
            for child in data['children']:
                result = self.determineTreeLevels(child, level + 1, result)

        return result

    def prepareTree(self, rootNode):
        self.__rootNode = rootNode;
        treeLevels = self.determineTreeLevels(rootNode)

        # Determine the max size of picture and place nodes one by another.
        # Example:
        # N
        # N N
        # N N N
        # N N
        self.__determineFramesPositions(rootNode)
       
        # Now put nodes in the correct places.
        # Example:
        #    N
        #  N   N
        # N N  N
        # N    N
        self.__updateFramesOffsets(rootNode)
        
        # Prepare arrows
        self.prepareConnectionsLevels(treeLevels)
        self.prepareTreeLevels(treeLevels)

    def __findLevelVerticalPosition(self, level):
        maxLevelValue = 0
        
        if level <> 0:
            for nodeHeightKey, nodeHeightValue in self.__containerHeights.iteritems():
                for nodeKey in self.__framesPositions[level - 1]:
                    if nodeHeightKey == nodeKey and nodeHeightValue > maxLevelValue:
                        maxLevelValue = nodeHeightValue

            maxLevelValue += self.__findLevelVerticalPosition(level - 1) + self.__conf['frame']['horizontalOffset']

        return maxLevelValue

    def __nodeChildrenLen(self, node):
        result = 0
        if node.has_key('children'):
            for child in node['children']:
                if child['type'] == 'node' :
                    result += 1

        return result