Пример #1
0
 def testBbSpacePaddingNullNode(self):
     """Tests PlotNode() set/get bbSpacePadding on a null node."""
     myObj = PlotNode.PlotNodeBbox()
     myObj.width = None
     myObj.depth = None
     myPad = Coord.Pad(
         Coord.Dim(1, 'px'),  # prev
         Coord.Dim(2, 'px'),  # next
         Coord.Dim(3, 'px'),  # parent
         Coord.Dim(4, 'px'),  # child
     )
     myObj.bbSelfPadding = myPad
     self.assertEqual(
         myObj.bbSelfPadding,
         Coord.Pad(
             Coord.Dim(1, 'px'),
             Coord.Dim(2, 'px'),
             Coord.Dim(3, 'px'),
             Coord.Dim(4, 'px'),
         ))
     self.assertEqual(
         myObj.bbSelfWidth,
         Coord.Dim(0, None),
     )
     self.assertEqual(
         myObj.bbSelfDepth,
         Coord.Dim(0, None),
     )
Пример #2
0
 def testCentre_02(self):
     """Tests PlotNode() get the centre point."""
     myObj = PlotNode.PlotNodeBbox()
     myObj.width = Coord.Dim(3, 'in')
     self.assertEqual(myObj.width, Coord.Dim(3, 'in'))
     myObj.depth = Coord.Dim(1, 'in')
     self.assertEqual(myObj.depth, Coord.Dim(1, 'in'))
     myPad = Coord.Pad(
         Coord.Dim(0.1, 'in'),  # prev
         Coord.Dim(0.3, 'in'),  # next
         Coord.Dim(0.5, 'in'),  # parent
         Coord.Dim(0.7, 'in'),  # child
     )
     myObj.bbSelfPadding = myPad
     myObj.bbSpaceChildren = Coord.Dim(0.9, 'in')
     self.assertEqual(
         myObj.plotPointCentre(
             Coord.Pt(
                 Coord.Dim(0.0, 'in'),
                 Coord.Dim(0.0, 'in'),
             ), ),
         Coord.Pt(
             Coord.Dim(1.6, 'in'),
             Coord.Dim(1.0, 'in'),
         ),
     )
Пример #3
0
    def testBbSpacePadding(self):
        """Tests PlotNode() set/get bbSpacePadding."""
        myObj = PlotNode.PlotNodeBbox()
        myObj.width = Coord.Dim(12, 'px')
        myObj.depth = Coord.Dim(6, 'px')
        #print '\nTRACE myObj', myObj
        myPad = Coord.Pad(
            Coord.Dim(1, 'px'),  # prev
            Coord.Dim(2, 'px'),  # next
            Coord.Dim(3, 'px'),  # parent
            Coord.Dim(4, 'px'),  # child
        )
        #print '\nTRACE myPad setting', myPad
        myObj.bbSelfPadding = myPad
        #print '\nTRACE myObj setting done', myObj
        a = myObj.bbSelfPadding
        self.assertEqual(
            myObj.bbSelfPadding,
            Coord.Pad(
                Coord.Dim(1, 'px'),
                Coord.Dim(2, 'px'),
                Coord.Dim(3, 'px'),
                Coord.Dim(4, 'px'),
            ))
        #print '\nTRACE myObj', myObj
        self.assertEqual(
            myObj.bbSelfWidth,
            # 1 + 12 + 2 = 15
            Coord.Dim(15, 'px'),
        )
        self.assertEqual(
            myObj.bbSelfDepth,
            # 3 + 6 + 4
            Coord.Dim(13, 'px'),
        )
        #print
        #print str(myObj)
        self.assertEqual(
            str(myObj),
            """|.......PlotNode: w=Dim(12px), d=Dim(6px)
|bbSpaceChildren: None
|..bbSelfPadding: Pad(prev=Dim(1px), next=Dim(2px), parent=Dim(3px), child=Dim(4px))
|.....bbChildren: None
|........bbSigma: Box(width=Dim(15px), depth=Dim(13px))""",
        )
Пример #4
0
 def test_zeroBaseUnitsPad(self):
     myPad = Coord.zeroBaseUnitsPad()
     # print()
     # print(myPad)
     self.assertEqual(
         myPad,
         Coord.Pad(prev=Coord.Dim(0.0, 'px'),
                   next=Coord.Dim(0.0, 'px'),
                   parent=Coord.Dim(0.0, 'px'),
                   child=Coord.Dim(0.0, 'px')))
Пример #5
0
 def setUp(self):
     self._pnbcObj = PlotNode.PlotNodeBboxBoxy()
     self._pnbcObj.width = Coord.Dim(12, 'mm')
     self._pnbcObj.depth = Coord.Dim(8, 'mm')
     #print '\nTRACE self._pnbcObj', self._pnbcObj
     self._pnbcObj.bbSelfPadding = Coord.Pad(
         Coord.Dim(1, 'mm'),  # prev
         Coord.Dim(3, 'mm'),  # next
         Coord.Dim(5, 'mm'),  # parent
         Coord.Dim(7, 'mm'),  # child
     )
     self._pnbcObj.bbSpaceChildren = Coord.Dim(16, 'mm')
     self.assertEqual(True, self._pnbcObj.bbChildrenWidth is None)
     self.assertEqual(True, self._pnbcObj.bbChildrenDepth is None)
     self.assertEqual(0, self._pnbcObj.numChildren)
     self._pnbcObj.extendChildBbox(
         Coord.Box(
             Coord.Dim(15, 'mm'),
             Coord.Dim(7, 'mm'),
         ))
     # Child box now w:15, d:7
     self.assertEqual(1, self._pnbcObj.numChildren)
     self.assertEqual(self._pnbcObj.bbChildrenWidth, Coord.Dim(15, 'mm'))
     self.assertEqual(self._pnbcObj.bbChildrenDepth, Coord.Dim(7, 'mm'))
     self._pnbcObj.extendChildBbox(
         Coord.Box(
             Coord.Dim(31, 'mm'),
             Coord.Dim(29, 'mm'),
         ))
     # Child box now w:15+31=46, d:max(7,29)=29
     self.assertEqual(2, self._pnbcObj.numChildren)
     self.assertEqual(self._pnbcObj.bbChildrenWidth, Coord.Dim(46, 'mm'))
     self.assertEqual(self._pnbcObj.bbChildrenDepth, Coord.Dim(29, 'mm'))
     self._pnbcObj.extendChildBbox(
         Coord.Box(
             Coord.Dim(11, 'mm'),
             Coord.Dim(9, 'mm'),
         ))
     # Child box now w:46+11=57, d:max(29,9)=29
     self.assertEqual(3, self._pnbcObj.numChildren)
     self.assertEqual(self._pnbcObj.bbChildrenWidth, Coord.Dim(57, 'mm'))
     self.assertEqual(self._pnbcObj.bbChildrenDepth, Coord.Dim(29, 'mm'))
     # Logical datum
     self._logicalDatum = Coord.Pt(
         Coord.Dim(21, 'mm'),
         Coord.Dim(180, 'mm'),
     )
Пример #6
0
 def testSigma_03(self):
     """Tests PlotNode() get sigma width and depth (parent is null node)."""
     myObj = PlotNode.PlotNodeBbox()
     myObj.width = None
     self.assertEqual(myObj.width, None)
     myObj.depth = None
     self.assertEqual(myObj.depth, None)
     myPad = Coord.Pad(
         Coord.Dim(0.5, 'in'),  # prev
         Coord.Dim(0.5, 'in'),  # next
         Coord.Dim(0.5, 'in'),  # parent
         Coord.Dim(0.5, 'in'),  # child
     )
     myObj.bbSelfPadding = myPad
     myObj.bbSpaceChildren = Coord.Dim(0.5, 'in')
     self.assertEqual(myObj.bbSpaceChildren, Coord.Dim(0.5, 'in'))
     myObj.bbChildren = Coord.Box(
         Coord.Dim(4, 'in'),
         Coord.Dim(2.5, 'in'),
     )
     self.assertEqual(
         myObj.bbChildrenWidth,
         Coord.Dim(4, 'in'),
     )
     self.assertEqual(
         myObj.bbChildrenDepth,
         Coord.Dim(2.5, 'in'),
     )
     self.assertEqual(myObj.bbChildren,
                      Coord.Box(
                          Coord.Dim(4, 'in'),
                          Coord.Dim(2.5, 'in'),
                      ))
     self.assertEqual(
         myObj.bbSigmaWidth,
         Coord.Dim(4.0, 'in'),
     )
     self.assertEqual(
         myObj.bbSigmaDepth,
         Coord.Dim(2.5, 'in'),
     )
     self.assertEqual(
         myObj.bbSigma,
         Coord.Box(
             Coord.Dim(4.0, 'in'),
             Coord.Dim(2.5, 'in'),
         ))
Пример #7
0
 def setUp(self):
     self._pnbcObj = PlotNode.PlotNodeBboxRoundy()
     self._pnbcObj.width = Coord.Dim(12, 'mm')
     self._pnbcObj.depth = Coord.Dim(8, 'mm')
     #print '\nTRACE self._pnbcObj', self._pnbcObj
     self._pnbcObj.bbSelfPadding = Coord.Pad(
         Coord.Dim(1, 'mm'),  # prev
         Coord.Dim(3, 'mm'),  # next
         Coord.Dim(5, 'mm'),  # parent
         Coord.Dim(7, 'mm'),  # child
     )
     self._pnbcObj.bbSpaceChildren = Coord.Dim(16, 'mm')
     self.assertEqual(True, self._pnbcObj.bbChildrenWidth is None)
     self.assertEqual(True, self._pnbcObj.bbChildrenDepth is None)
     self.assertEqual(0, self._pnbcObj.numChildren)
     # Logical datum
     self._logicalDatum = Coord.Pt(
         Coord.Dim(21, 'mm'),
         Coord.Dim(180, 'mm'),
     )
Пример #8
0
class SVGTreeNodeMain(IncGraphSVGBase.SVGTreeNodeBase):
    """This does most of the heavy lifting of plotting the include graph SVG.
    Challenges are plotting things in the 'right' order and with the 'right'
    JavaScript so that the DHTML does not look too hideous.
    Basic principle here is that `plotInitialise()` writes static data. In
    our case just the pretty histogram pop-up (Ed. is this right???).
    Then `plotToSVGStream()` is called - this is implemented in the base class.
    FInally `plotFinalise()` is called - this overlays the DHTML text. This is
    a little tricky as our way of DHTML is to switch opacity on underlying
    objects the switching boundary being the overlying object (e.g. '?').
    So _all_ the underlying objects need to be written first so that the
    overlying objects are always 'visible' to trigger onmouseover/onmouseout on
    the underlying object."""
    COMMON_UNITS = 'mm'
    WIDTH_PER_TOKEN = Coord.Dim(1.0 / 1000.0, COMMON_UNITS)
    WIDTH_MINIMUM = Coord.Dim(5, COMMON_UNITS)
    FILE_DEPTH = Coord.Dim(32.0, COMMON_UNITS)
    SPACE_PARENT_CHILD = Coord.Dim(16.0, COMMON_UNITS)
    FILE_PADDING = Coord.Pad(
        Coord.Dim(4.0, COMMON_UNITS),  # prev
        Coord.Dim(2.0, COMMON_UNITS),  # next
        Coord.Dim(16.0, COMMON_UNITS),  # parent
        Coord.Dim(16.0, COMMON_UNITS),  # child
    )
    # Lines joining root level children
    ATTRS_LINE_ROOT_CHILDREN_JOIN = {
        'stroke-width': "8",
        'stroke': "lightgrey",
    }
    # Node attributes (e.e. for rectangles)
    # Normal nodes
    ATTRS_NODE_NORMAL = {
        'fill': "mistyrose",
        'stroke': "black",
        'stroke-width': "1",
    }
    # Nodes that are empty
    ATTRS_NODE_MT = {
        'fill': "aqua",
        'stroke': "black",
        'stroke-width': "1",
    }
    # Conditionally compiled stuff
    ATTRS_NODE_CONDITIONAL = {
        'fill': "salmon",  #"orangered",
        'stroke': "black",
        #'stroke-dasharray'      : "4,4",
        'stroke-width': "1",
    }
    ATTRS_LINE_NORMAL_TO = {
        'stroke-width': "2",
        'stroke': "black",
    }
    ATTRS_LINE_NORMAL_FROM = {
        'stroke-width': "0.5",
        'stroke': "black",
    }
    ATTRS_LINE_MT_TO = {
        'stroke-width': "2",
        'stroke': "aqua",
        'stroke-dasharray': "8,8",
    }
    ATTRS_LINE_MT_FROM = {
        'stroke-width': "0.5",
        'stroke': "aqua",
        'stroke-dasharray': "8,8",
    }
    ATTRS_LINE_CONDITIONAL_TO = {
        'stroke-width': "0.5",
        'stroke': "black",
        'stroke-dasharray': "8,2,2,2",
    }
    ATTRS_LINE_CONDITIONAL_FROM = {
        'stroke-width': "0.25",
        'stroke': "black",
        'stroke-dasharray': "8,2,2,2",
    }
    # CSS entries
    STYLE_VERDANA_12 = 'text.V12'
    CLASS_VERDANA_12 = 'V12'
    ATTRS_VERDANA_12 = {
        'dominant-baseline': 'middle',
        'font-family': 'Verdana',
        'font-size': '12',
        'text-anchor': 'middle',
        'text-decoration': 'underline',
    }
    # Used for histogram
    STYLE_VERDANA_9 = 'text.V9'
    CLASS_VERDANA_9 = 'V9'
    ATTRS_VERDANA_9 = {
        'dominant-baseline': 'middle',
        'font-family': 'Verdana',
        'font-size': '9',
        #       'opacity'             : '1.0',
        #       'text-anchor'         : 'middle',
        #       'text-decoration'     : 'underline',
    }
    STYLE_TEXT_SCALE = 'text.scale'
    CLASS_TEXT_SCALE = 'scale'
    ATTRS_TEXT_SCALE = {
        'dominant-baseline': 'middle',
        'font-family': 'Verdana',
        'font-size': '12',
        'text-anchor': 'left',
    }
    # font-family="Courier" font-size="10" font-weight="normal"
    STYLE_COURIER_10 = 'text.C10'
    CLASS_COURIER_10 = 'C10'
    ATTRS_COURIER_10 = {
        'font-family': 'Courier',
        'font-size': '10',
        'font-weight': '10',
    }
    # Invisible rectangle
    STYLE_RECT_INVIS = 'rect.invis'
    CLASS_RECT_INVIS = 'invis'
    ATTRS_RECT_INVIS = {
        'fill': 'red',
        'opacity': '0',
        'stroke': 'black',
        'stroke-width': '1',
    }
    #
    # Chevron attributes
    CHEVRON_COLOUR_FILL = "palegreen"  #"cornflowerblue"
    CHEVRON_COLOUR_STROKE = "black"
    CHEVRON_STROKE_WIDTH = ".5"
    # Histogram plotting
    HIST_DEPTH = Coord.Dim(4.0, COMMON_UNITS)
    # This controls plot order as well as colour
    # Note: Unusually they are in sweep='-' i.e logical left-to-right order
    HIST_PP_TOKEN_TYPES_COLOURS = (
        (
            'header-name',
            'orange',
        ),  # Not used as 'derived' token
        (
            'identifier',
            'blue',
        ),  # Top 3!
        (
            'string-literal',
            'cyan',
        ),
        (
            'pp-number',
            'green',
        ),  # Next most popular after top 3
        (
            'character-literal',
            'magenta',
        ),
        (
            'preprocessing-op-or-punc',
            'red',
        ),  # Top 3!
        (
            'non-whitespace',
            'black',
        ),
        (
            'concat',
            'yellow',
        ),
        (
            'whitespace',
            'white',
        ),  # Top 3!
    )
    HIST_RECT_COLOUR_STROKE = "black"
    HIST_RECT_STROKE_WIDTH = ".5"
    HIST_LEGEND_ID = "HistogramLegend"
    # The placeholder text for JavaScript rollover
    POPUP_TEXT = ' ? '

    def __init__(self, theFig, theLineNum):
        super(SVGTreeNodeMain, self).__init__(theFig, theLineNum)
        # PpTokenCount object for my children only, set on finalise
        self._tokenCounterChildren = PpTokenCount.PpTokenCount()
        ## PpTokenCount object for me and my my children , set on finalise
        #self._tokenCounterTotal = PpTokenCount.PpTokenCount()
        # Total number of significant tokens in all children
        self._numChildSigTokens = 0
        # Mandatory override of the bounding box object
        self._bb = PlotNode.PlotNodeBboxBoxy()
        if theFig is None:
            # Root node, children only
            self._dataMap = None
        else:
            self._dataMap = {}
            # Take a copy of the include graph data
            self._dataMap['numToks'] = theFig.numTokens
            self._dataMap['condComp'] = theFig.condComp
            # A PpTokenCount.PpTokenCount() object for this node only.
            self._dataMap['tokenCntr'] = theFig.tokenCounter
            self._dataMap['findLogic'] = theFig.findLogic
        # A list of tuples of (Coord.Pt, Cooord.Box, attributes) that are to be
        # written last as <rect class="invis" ...
        self._triggerS = []
        # We have two passes
        self._numPassesToPlotSelf = 2

    #============================================
    # Section: Accessor methods used by ancestors
    #============================================
    @property
    def tokenCounter(self):
        """This is the PpTokenCount.PpTokenCount() for me only."""
        if self.isRoot:
            return PpTokenCount.PpTokenCount()
        return self._dataMap['tokenCntr']

    @property
    def tokenCounterChildren(self):
        """This is the computed PpTokenCount.PpTokenCount() for all my descendents."""
        ##"""This is the PpTokenCount.PpTokenCount() for my children."""
        return self._tokenCounterChildren

    @property
    def tokenCounterTotal(self):
        """This is the computed PpTokenCount.PpTokenCount() me plus my descendents."""
        retVal = PpTokenCount.PpTokenCount()
        retVal += self.tokenCounter
        retVal += self.tokenCounterChildren
        return retVal

    @property
    def condComp(self):
        """A string of conditional tests."""
        assert (not self.isRoot)
        return self._dataMap['condComp']

    @property
    def findLogic(self):
        """The find logic as a string."""
        assert (not self.isRoot)
        return self._dataMap['findLogic']

    #========================================
    # End: Accessor methods used by ancestors
    #========================================

    #===================================
    # Section: Finalisation and plotting
    #===================================
    def finalise(self):
        """Finalisation this sets up all the bounding boxes of me and my children."""
        for aChild in self._children:
            aChild.finalise()
        # Now accumulate my children's bounding boxes and token counts
        self._tokenCounterChildren = PpTokenCount.PpTokenCount()
        #self._tokenCounterTotal = PpTokenCount.PpTokenCount()
        #if not self.isRoot:
        #    self._tokenCounterTotal += self.tokenCounter
        self._numChildSigTokens = 0
        for aChild in self._children:
            self._bb.extendChildBbox(aChild.bb.bbSigma)
            self._tokenCounterChildren += aChild.tokenCounterTotal
            #self._tokenCounterTotal += aChild.tokenCounter
            self._numChildSigTokens += aChild.tokenCounterTotal.tokenCountNonWs(
                isAll=False)
        # Set up my bounding box only if non-root node
        if not self.isRoot:
            #self._bb.width = max(self.WIDTH_MINIMUM, self.WIDTH_PER_TOKEN.scale(self._tokenCount))
            self._bb.width = self.WIDTH_MINIMUM \
                + self.WIDTH_PER_TOKEN.scale(self.tokenCounterTotal.tokenCountNonWs(isAll=False))
            self._bb.depth = self.FILE_DEPTH
            self._bb.bbSelfPadding = self.FILE_PADDING
            if len(self._children) > 0:
                self._bb.bbSpaceChildren = self.SPACE_PARENT_CHILD
        # Bounding boxes now set up

    def writePreamble(self, theS):
        """Write any preamble such as CSS or JavaScript.
        To be implemented by child classes."""
        cssDict = {
            'tspan': {
                'white-space': 'pre'
            },
            self.STYLE_VERDANA_12: self.ATTRS_VERDANA_12,
            self.STYLE_VERDANA_9: self.ATTRS_VERDANA_9,
            self.STYLE_COURIER_10: self.ATTRS_COURIER_10,
            self.STYLE_TEXT_SCALE: self.ATTRS_TEXT_SCALE,
            self.STYLE_RECT_INVIS: self.ATTRS_RECT_INVIS,
        }
        with XmlWrite.Element(theS, 'defs', {}):
            theS.writeCSS(cssDict)
        self._writeECMAScript(theS)
        self._writeScaleControls(theS)

    def _writeScaleControls(self, theSvg):
        """Write the text elements that control re-scaling."""
        myAttrs = {
            'class': self.CLASS_TEXT_SCALE,
        }
        myPointP = Coord.Pt(
            Coord.Dim(8.0, self.COMMON_UNITS),
            Coord.Dim(4.0, self.COMMON_UNITS),
        )
        with SVGWriter.SVGGroup(theSvg, {'id': 'scaleGroup'}):
            with SVGWriter.SVGText(theSvg, myPointP, None, None, myAttrs):
                theSvg.characters('Select scale (bold selected):')
            myAttrs['text-decoration'] = "underline"
            myPointP = Coord.newPt(myPointP,
                                   incX=Coord.Dim(64, 'mm'),
                                   incY=None)
            for scale in self.SCALE_FACTORS:
                myAttrs['onclick'] = "scaleGraphic(%s, '%s')" % (scale, scale)
                myAttrs['id'] = str(scale)
                if scale == self._scale:
                    myAttrs['font-weight'] = 'bold'
                else:
                    myAttrs['font-weight'] = 'normal'
                text = '%d%%' % int(scale * 100)
                with SVGWriter.SVGText(theSvg, myPointP, None, None, myAttrs):
                    theSvg.characters(text)
                myPointP = Coord.newPt(myPointP,
                                       incX=Coord.Dim(5 * len(text), 'mm'),
                                       incY=None)

    def plotInitialise(self, theSvg, theDatumL, theTpt):
        """Plot the histogram legend once only."""
        self.commentFunctionBegin(theSvg)
        #         self._plotHistogramLegend(theSvg, theTpt)
        self.commentFunctionEnd(theSvg)

    def plotFinalise(self, theSvg, theDatumL, theTpt):
        """Finish the plot. In this case we write the text overlays."""
        # Plot all text elements so that they are on top
        self.commentFunctionBegin(theSvg, File=self._fileName)
        self._writeTriggers(theSvg)
        self.commentFunctionEnd(theSvg, File=self._fileName)

    def _writeTriggers(self, theSvg):
        """Write the rectangles that trigger pop-up text last so that they are on top."""
        for aChild in self._children:
            aChild._writeTriggers(theSvg)
        for _pt, _box, _attrs in self._triggerS:
            with SVGWriter.SVGRect(theSvg, _pt, _box, _attrs):
                pass

    def _plotSelf(self, theSvg, theDatumL, theTpt, thePassNum, idStack):
        """Plot me to a stream at the logical datum point"""
        assert (not self.isRoot)
        if thePassNum == 0:
            self.commentFunctionBegin(theSvg,
                                      File=self._fileName,
                                      Node=self.nodeName,
                                      Pass=thePassNum)
            # Plot self
            if self.condCompState:
                if self.numTokens > 0:
                    myAttrs = self.ATTRS_NODE_NORMAL
                else:
                    myAttrs = self.ATTRS_NODE_MT
            else:
                myAttrs = self.ATTRS_NODE_CONDITIONAL
            if self._bb.hasSetArea:
                # Plot my box at:
                myBoxDatumP = theTpt.boxDatumP(
                    self._bb.plotPointSelf(theDatumL), self._bb.box)
                with SVGWriter.SVGRect(
                        theSvg,
                        myBoxDatumP,
                        theTpt.boxP(self._bb.box),
                        myAttrs,
                ):
                    pass
            self._plotSelfInternals(theSvg, theDatumL, theTpt)
        elif thePassNum == 1:
            self._plotTextOverlay(theSvg, theDatumL, theTpt, idStack)
        self.commentFunctionEnd(theSvg,
                                File=self._fileName,
                                Node=self.nodeName,
                                Pass=thePassNum)

    def plotRoot(self, theSvg, theDatumL, theTpt, passNum):
        if passNum == 1:
            self._plotHistogramLegend(theSvg, theTpt)

    def _plotSelfToChildren(self, theSvg, theDatumL, theTpt):
        """Plot links from me to my children to a stream at the
        (self) logical datum point."""
        assert (len(self._children) > 0)
        assert (not self.isRoot)
        #print 'TRACE: plotSelfToChildren()', theDatumL
        myDatumL = self._bb.plotPointSelf(theDatumL)
        #nameP = self.nodeName
        for i, datumChildL in self._enumerateChildren(theDatumL, theTpt):
            if self._children[i].condCompState:
                if self._children[i].numTokens > 0:
                    myAttrsTo = self.ATTRS_LINE_NORMAL_TO
                    myAttrsFrom = self.ATTRS_LINE_NORMAL_FROM
                else:
                    myAttrsTo = self.ATTRS_LINE_MT_TO
                    myAttrsFrom = self.ATTRS_LINE_MT_FROM
            else:
                myAttrsTo = self.ATTRS_LINE_CONDITIONAL_TO
                myAttrsFrom = self.ATTRS_LINE_CONDITIONAL_FROM
            #nameC = self._children[i]._dataMap['name']
            if theTpt.positiveSweepDir:
                childOrd = len(self._children) - i - 1
            else:
                childOrd = i
            # Parent to child
            linePtsFirst = [
                theTpt.pt(l) for l in (
                    self._bb.pcRoll(myDatumL, childOrd),
                    self._bb.pcTo(myDatumL, childOrd),
                    self._children[i].bb.pcLand(datumChildL),
                    self._children[i].bb.pcStop(datumChildL),
                )
            ]
            # Now child to parent
            linePtsSecond = [
                theTpt.pt(l) for l in (
                    self._children[i].bb.cpRoll(datumChildL),
                    self._children[i].bb.cpTo(datumChildL),
                    self._bb.cpLand(myDatumL, childOrd),
                    self._bb.cpStop(myDatumL, childOrd),
                )
            ]
            if theTpt.positiveSweepDir:
                linePtsSecond, linePtsFirst = linePtsFirst, linePtsSecond
            j = 1
            #theSvg.comment(' %s to %s ' % (nameP, nameC))
            while j < len(linePtsFirst):
                with SVGWriter.SVGLine(
                        theSvg,
                        linePtsFirst[j - 1],
                        linePtsFirst[j],
                        myAttrsTo,
                ):
                    pass
                j += 1
            j = 1
            #theSvg.comment(' %s to %s ' % (nameC, nameP))
            while j < len(linePtsSecond):
                with SVGWriter.SVGLine(
                        theSvg,
                        linePtsSecond[j - 1],
                        linePtsSecond[j],
                        myAttrsFrom,
                ):
                    pass
                j += 1

    def _plotRootChildToChild(self, theSvg, theDatumL, theTpt):
        """Join up children of root node with vertical lines."""
        assert (len(self._children) > 0)
        assert (self.isRoot)
        self.commentFunctionBegin(theSvg)
        ptNextL = None
        for i, datumChildL in self._enumerateChildren(theDatumL, theTpt):
            if i > 0:
                ptPrevL = theTpt.prevdcL(
                    self._children[i].bb.plotPointSelf(datumChildL),
                    self._children[i].bb.box,
                )
                with SVGWriter.SVGLine(
                        theSvg,
                        theTpt.pt(ptNextL),
                        theTpt.pt(ptPrevL),
                        self.ATTRS_LINE_ROOT_CHILDREN_JOIN,
                ):
                    pass
            ptNextL = theTpt.nextdcL(
                self._children[i].bb.plotPointSelf(datumChildL),
                self._children[i].bb.box,
            )
        self.commentFunctionEnd(theSvg)

    def _plotSelfInternals(self, theSvg, theDl, theTpt):
        """Plot structures inside the box and the static text that is
        the file name."""
        # Histograms of token types
        if self.__mustPlotSelfHistogram():
            # First the histogram of just me
            myHistDl = self._bb.plotPointSelf(theDl)
            self._plotHistogram(theSvg, myHistDl, theTpt, self.tokenCounter)
            # Now the histogram of my children
            if self.__mustPlotChildHistogram():
                # Shuffle down a bit
                myHistDl = Coord.newPt(myHistDl, None, self.HIST_DEPTH)
                self._plotHistogram(theSvg, myHistDl, theTpt,
                                    self._tokenCounterChildren)
        # Now the Chevron
        self._plotChevron(theSvg, theDl, theTpt)
        # The filename as display text (no animation)
        if not self.isRoot:
            self._plotFileName(theSvg, theDl, theTpt)

    def _plotTextOverlay(self, theSvg, theDatumL, theTpt, idStack):
        """Plots all the text associated with the parent and child.
        We write the hidden objects first then the visible objects. This is
        because the hidden objects are controlled onmouseover/onmouseout on
        the visible objects and they have to be later in the SVG file for this
        to work."""
        self.commentFunctionBegin(theSvg,
                                  File=self._fileName,
                                  Node=self.nodeName)
        # TODO: Re-think this as it is non-intuitive. Essentially it should not
        # matter whether the child or the parent node is plotted first but it is
        # essential that all the 'hidden' DHTML text for any node is written
        # first, _then_ the 'visible' text that controls that 'hidden' text.
        #
        # Child information first
        if len(self._children) > 0 and not self.isRoot:
            self._plotTextOverlayChildren(theSvg, theDatumL, theTpt)
        # Histogram
        if self.__mustPlotSelfHistogram():
            # Write a single '?' in the middle
            myHistDl = self._bb.plotPointSelf(theDatumL)
            self._plotTextOverlayHistogram(theSvg, myHistDl, theTpt)
            if self.__mustPlotChildHistogram():
                # Shuffle down a bit
                myHistDl = Coord.newPt(myHistDl, None, self.HIST_DEPTH)
                self._plotTextOverlayHistogram(theSvg, myHistDl, theTpt)
        if not self.isRoot:
            self._plotTextOverlayTokenCountTable(theSvg, theDatumL, theTpt)
            self._plotFileNameStackPopup(theSvg, theDatumL, theTpt, idStack)
        self.commentFunctionEnd(theSvg,
                                File=self._fileName,
                                Node=self.nodeName)

    def _plotTextOverlayChildren(self, theSvg, theDatumL, theTpt):
        """Plot text associated with my children to a stream at the
        (self) logical datum point."""
        assert (len(self._children) > 0)
        assert (not self.isRoot)
        self.commentFunctionBegin(theSvg, File=self._fileName)
        for i, datumChildL in self._enumerateChildren(theDatumL, theTpt):
            self._plotWhereWhyHow(theSvg, i, datumChildL, theTpt)
        self.commentFunctionEnd(theSvg, File=self._fileName)

    def _plotWhereWhyHow(self, theSvg, iChild, theDatumL, theTpt):
        """Plot description of Where/Why/How inclusion of a single child to a
        stream at the (self) logical datum point."""
        assert (not self.isRoot)
        assert (len(self._children) > 0)
        assert (iChild >= 0 and iChild < len(self._children))
        self.commentFunctionBegin(theSvg, File=self._fileName)
        #myDatumL = theDatumL
        #myDatumL = Coord.newPt(theDatumL, incX=self.FILE_PADDING.prev, incY=None)#self.FILE_PADDING.parent.scale(-1.0))
        myDatumL = self._children[iChild].bb.plotPointSelf(theDatumL)
        # Move logical datum logically 'up' and 'right' by half
        #         myDatumL = Coord.newPt(
        #                 myDatumL,
        #                 incX=self._children[iChild].bb.width.scale(0.5),
        #                 incY=self.FILE_PADDING.parent.scale(-0.5),
        #         )
        myAltTxtPointP = theTpt.pt(
            Coord.newPt(
                myDatumL,
                incX=self._children[iChild].bb.width.scale(0.5),
                incY=self.FILE_PADDING.parent.scale(-0.5),
            ))
        altTextS = []
        altTextS.append('Where: %s#%d ' \
                        % (self.nodeName, self._children[iChild].lineNum))
        if len(self._children[iChild].condComp) > 0:
            #             altTextS.append(
            #                 '  Why: %s since: %s ' \
            #                 % (
            #                     str(self._children[iChild].condCompState),
            #                     self._children[iChild].condComp
            #                     )
            #             )
            assert self._children[iChild].condCompState
            altTextS.append(
                '  Why: %s ' \
                % (
                    self._children[iChild].condComp
                    )
            )
        else:
            altTextS.append(
                '  Why: %s ' \
                    % (str(self._children[iChild].condCompState)
                    )
            )
        altTextS.append('  How: #include %s' %
                        ', '.join(self._children[iChild].findLogic))
        #         self.writeAltTextAndMouseOverText(
        #                 theSvg, myPointP, theSvg.id,
        #                 self.POPUP_TEXT, altTextS, Coord.Dim(20, 'pt'))
        triggerBoxP = theTpt.boxP(
            Coord.Box(self._children[iChild].bb.width,
                      self.FILE_PADDING.parent))
        #         triggerPointP = theTpt.pt(myDatumL)
        triggerPointP = theTpt.pt(
            Coord.newPt(
                myDatumL,
                incX=self._children[iChild].bb.width,
                incY=self.FILE_PADDING.parent.scale(-1.0),
            ))
        self.writeAltTextAndMouseOverRect(
            theSvg,
            theSvg.id,
            myAltTxtPointP,
            altTextS,
            triggerPointP,
            triggerBoxP,
        )

        self.commentFunctionEnd(theSvg, File=self._fileName)

    def _plotTextOverlayHistogram(self, theSvg, theHistDl, theTpt):
        """Plot the text associated with a histogram."""
        myCentreL = Coord.newPt(theHistDl, self._bb.width.scale(0.5),
                                self.HIST_DEPTH.scale(0.5))
        myPointP = theTpt.pt(myCentreL)
        # TODO: The myPointP.x.value + 2, myPointP.y.value - 2
        # looks wrong. It is not using theTpt.
        myAttrs = {
              'class'       : self.CLASS_RECT_INVIS,
            'onmouseover'       : "showHistogram(%s, %s)" \
                % (myPointP.x.value + 3, myPointP.y.value + 2),
            'onmouseout'    : "hideHistogram()",
        }
        myWidth = self._bb.width
        myBox = Coord.Box(myWidth, self.HIST_DEPTH)
        with SVGWriter.SVGRect(theSvg, theTpt.boxDatumP(theHistDl, myBox),
                               theTpt.boxP(myBox), myAttrs):
            pass

    def _fileNamePoint(self, theDatumL, theTpt):
        """Returns the point to plot the file name or None."""
        if self._bb.hasSetArea:
            myDatumL = self._bb.plotPointSelf(theDatumL)
            textPointL = theTpt.tdcL(myDatumL, self._bb.box)
            # Logical increment to previous child is 'logical left' i.e. -x
            textPointL = Coord.newPt(textPointL,
                                     incX=self.FILE_PADDING.prev.scale(0.5),
                                     incY=None)
            return theTpt.pt(textPointL)

    def _fileIdStackToListOfStr(self, theIdStack):
        """Given a list of alternating file names and line numbers such as:
        ['root', 3, foo.h, 7, bar.h] this returns a list of strings thus:
        ['root#3, 'foo.h#7, 'bar.h']"""
        # Create file stack
        myAltS = []
        i = 0
        while i < len(theIdStack):
            if i + 1 < len(theIdStack):
                myAltS.append('%s#%d' % (theIdStack[i], theIdStack[i + 1]))
            else:
                myAltS.append(theIdStack[i])
            i += 2
        return myAltS

    def _plotFileName(self, theSvg, theDatumL, theTpt):
        """Writes out the file name adjacent to the file box as static text."""
        self.commentFunctionBegin(theSvg, File=self._fileName)
        if self._bb.hasSetArea:
            textPointP = self._fileNamePoint(theDatumL, theTpt)
            assert textPointP is not None
            myAttrs = {
                'class': self.CLASS_VERDANA_12,
                'opacity': '1.0',
            }
            with SVGWriter.SVGText(theSvg, textPointP, None, None, myAttrs):
                theSvg.characters(os.path.basename(self.nodeName))
        self.commentFunctionEnd(theSvg, File=self._fileName)

    def _plotFileNameStackPopup(self, theSvg, theDatumL, theTpt, idStack):
        """Writes out the file name at the top with a pop-up with the
        absolute path."""
        self.commentFunctionBegin(theSvg, File=self._fileName)
        if self._bb.hasSetArea:
            textPointP = self._fileNamePoint(theDatumL, theTpt)
            assert textPointP is not None
            # Make the trigger box 14 points high to cover the 12 pt text and
            # 12pts per char in the
            triggerBoxP = Coord.Box(
                Coord.baseUnitsDim(12 * len(os.path.basename(self.nodeName))),
                Coord.baseUnitsDim(14))
            triggerPointP = Coord.newPt(
                textPointP,
                triggerBoxP.width.scale(-0.5),
                triggerBoxP.depth.scale(-0.5),
            )
            self.writeAltTextAndMouseOverRect(
                theSvg,
                theSvg.id,
                textPointP,
                self._fileIdStackToListOfStr(idStack),
                triggerPointP,
                triggerBoxP,
            )
        self.commentFunctionEnd(theSvg, File=self._fileName)

    def _plotTextOverlayTokenCountTable(self, theSvg, theDatumL, theTpt):
        """Plots the token count table as text+alternate text."""
        assert (not self.isRoot)
        self.commentFunctionBegin(theSvg, File=self._fileName)
        myDatumL = self._bb.plotPointSelf(theDatumL)
        # Move to center
        myDatumL = Coord.newPt(
            myDatumL,
            incX=self._bb.width,  #.scale(0.5),
            incY=None)
        #                 incY=self._bb.depth.scale(0.5))
        triggerBoxL = self._bb.box
        if self.__mustPlotSelfHistogram():
            myDatumL = Coord.newPt(myDatumL, None, self.HIST_DEPTH)
            triggerBoxL = Coord.Box(triggerBoxL.width,
                                    triggerBoxL.depth - self.HIST_DEPTH)
            if self.__mustPlotChildHistogram():
                myDatumL = Coord.newPt(myDatumL, None, self.HIST_DEPTH)
                triggerBoxL = Coord.Box(triggerBoxL.width,
                                        triggerBoxL.depth - self.HIST_DEPTH)
        myDatumP = theTpt.pt(myDatumL)
        altTextS = self._altTextsForTokenCount()
        self.writeAltTextAndMouseOverRect(
            theSvg,
            theSvg.id,
            myDatumP,
            altTextS,
            myDatumP,
            theTpt.boxP(triggerBoxL),
        )
        self.commentFunctionEnd(theSvg, File=self._fileName)

    def _altTextsForTokenCount(self):
        """Returns a list of strings that are the alternate text for token counts."""
        assert (not self.isRoot)
        if len(self._children) > 0:
            myCounterTotal = PpTokenCount.PpTokenCount()
            myCounterTotal += self.tokenCounter
            myCounterTotal += self.tokenCounterChildren
        FIELD_WIDTH = 7
        myTokTypeS = [t[0] for t in self.HIST_PP_TOKEN_TYPES_COLOURS]
        typeLen = max([len(t) for t in myTokTypeS])
        altTextS = []
        if len(self._children) > 0:
            altTextS.append('%*s %*s [%*s] %*s [%*s] %*s [%*s]' \
                            % (typeLen,
                               'Type',
                               FIELD_WIDTH,
                               'Me',
                               FIELD_WIDTH,
                               'Me',
                               FIELD_WIDTH,
                               'Child',
                               FIELD_WIDTH,
                               'Child',
                               FIELD_WIDTH,
                               'All',
                               FIELD_WIDTH,
                               'All',
                               )
                            )
        else:
            altTextS.append('%*s %*s [%*s]' \
                            % (typeLen,
                               'Type',
                               FIELD_WIDTH,
                               'Me',
                               FIELD_WIDTH,
                               'Me',
                               )
                            )
#         cntrAll = cntrSig = 0
        cntrTotalS = [
            0,
        ] * 6
        for t in myTokTypeS:
            cntrTotalS[0] += self.tokenCounter.tokenCount(t, isAll=True)
            cntrTotalS[1] += self.tokenCounter.tokenCount(t, isAll=False)
            line = '%*s %*d [%*d]' \
                                % (typeLen,
                                   t,
                                   FIELD_WIDTH,
                                   self.tokenCounter.tokenCount(t, isAll=True),
                                   FIELD_WIDTH,
                                   self.tokenCounter.tokenCount(t, isAll=False),
                                   )
            if len(self._children) > 0:
                line += ' %*d [%*d] %*d [%*d]' \
                                % (FIELD_WIDTH,
                                   self.tokenCounterChildren.tokenCount(t, isAll=True),
                                   FIELD_WIDTH,
                                   self.tokenCounterChildren.tokenCount(t, isAll=False),
                                   FIELD_WIDTH,
                                   myCounterTotal.tokenCount(t, isAll=True),
                                   FIELD_WIDTH,
                                   myCounterTotal.tokenCount(t, isAll=False),
                                   )
                cntrTotalS[2] += self.tokenCounterChildren.tokenCount(
                    t, isAll=True)
                cntrTotalS[3] += self.tokenCounterChildren.tokenCount(
                    t, isAll=False)
                cntrTotalS[4] += myCounterTotal.tokenCount(t, isAll=True)
                cntrTotalS[5] += myCounterTotal.tokenCount(t, isAll=False)
            altTextS.append(line)
        line = '%*s %*d [%*d]' \
            % (typeLen,
               'Total',
               FIELD_WIDTH,
               cntrTotalS[0],
               FIELD_WIDTH,
               cntrTotalS[1],
               )
        if len(self._children) > 0:
            line += ' %*d [%*d] %*d [%*d]' \
                            % (FIELD_WIDTH,
                               cntrTotalS[2],
                               FIELD_WIDTH,
                               cntrTotalS[3],
                               FIELD_WIDTH,
                               cntrTotalS[4],
                               FIELD_WIDTH,
                               cntrTotalS[5],
                               )
        altTextS.append(line)
        return altTextS

    def __mustPlotSelfHistogram(self):
        return self.tokenCounter.tokenCountNonWs(isAll=False) > 0

    def __mustPlotChildHistogram(self):
        return self.__mustPlotSelfHistogram() \
                and len(self._children) > 0 \
                and self._numChildSigTokens > 0

    def _plotHistogram(self, theSvg, theHistDl, theTpt, theTokCounter):
        myTokCountTotal = theTokCounter.totalAllUnconditional
        # Avoid divide by zero errors
        assert (theTokCounter.tokenCountNonWs(isAll=False) > 0)
        assert (myTokCountTotal > 0)
        #myPos = Coord.Dim(0, self.COMMON_UNITS)
        myHistDl = theHistDl  #self._bb.plotPointSelf(theDl)
        for k, myFill in self.HIST_PP_TOKEN_TYPES_COLOURS:
            tCount = theTokCounter.tokenCount(k, isAll=False)
            if tCount > 0:
                myWidth = self._bb.width.scale(tCount /
                                               (1.0 * myTokCountTotal))
                myBox = Coord.Box(myWidth, self.HIST_DEPTH)
                # Convert to physical and plot
                with SVGWriter.SVGRect(
                        theSvg,
                        theTpt.boxDatumP(myHistDl, myBox),
                        theTpt.boxP(myBox),
                    {
                        'fill': myFill,
                        'stroke': self.HIST_RECT_COLOUR_STROKE,
                        'stroke-width': self.HIST_RECT_STROKE_WIDTH,
                    },
                ):
                    pass
                # Increment the datum
                myHistDl = Coord.newPt(myHistDl, incX=myWidth, incY=None)

    def _plotHistogramLegend(self, theSvg, theTpt):
        """Plot a standardised legend. This is plotted as a group within a defs."""
        myDatumP = Coord.Pt(
            Coord.Dim(0.0, self.COMMON_UNITS),
            Coord.Dim(0.0, self.COMMON_UNITS),
        )
        with SVGWriter.SVGGroup(theSvg, {
                'id': self.HIST_LEGEND_ID,
                'opacity': '0.0'
        }):
            idVal = 0
            # Outline rectangle
            with SVGWriter.SVGRect(
                    theSvg,
                    myDatumP,
                    Coord.Box(
                        Coord.Dim(48.0, self.COMMON_UNITS),
                        Coord.Dim(40.0, self.COMMON_UNITS),
                    ),
                {
                    'fill': self.ALT_RECT_FILL,
                    'id': '%d' % idVal,
                },
            ):
                idVal += 2
            myDatumP = Coord.newPt(
                myDatumP,
                incX=Coord.Dim(2.0, self.COMMON_UNITS),
                incY=Coord.Dim(2.0, self.COMMON_UNITS),
            )
            myTokIdxS = list(range(len(self.HIST_PP_TOKEN_TYPES_COLOURS)))
            if theTpt.positiveSweepDir:
                myTokIdxS.reverse()
            for iC in myTokIdxS:
                myBox = Coord.Box(self.HIST_DEPTH, self.HIST_DEPTH)
                # Convert to physical and plot
                with SVGWriter.SVGRect(
                        theSvg,
                        myDatumP,
                        myBox,
                    {
                        'fill': self.HIST_PP_TOKEN_TYPES_COLOURS[iC][1],
                        'stroke': self.HIST_RECT_COLOUR_STROKE,
                        'stroke-width': self.HIST_RECT_STROKE_WIDTH,
                        'id': '%d' % idVal
                    },
                ):
                    idVal += 2
                myTextDatumP = Coord.newPt(
                    myDatumP,
                    incX=self.HIST_DEPTH + Coord.Dim(2.0, self.COMMON_UNITS),
                    incY=self.HIST_DEPTH.scale(0.5),
                )
                with SVGWriter.SVGText(
                        theSvg, myTextDatumP, None, None, {
                            'font-family': 'Verdana',
                            'font-size': '10',
                            'dominant-baseline': 'middle',
                            'id': '%d' % idVal,
                        }):
                    theSvg.characters(self.HIST_PP_TOKEN_TYPES_COLOURS[iC][0])
                    idVal += 2
                # Increment the datum
                myDatumP = Coord.newPt(myDatumP,
                                       incX=None,
                                       incY=self.HIST_DEPTH)

    def _plotChevron(self, theSvg, theDl, theTpt):
        """Plots a wedge to represent the relative number of tokens in me and
        my children.
        D------------------.------------------|
        |                                     |
        |------------------.------------------|
        |                                     |
        A-----------B------.------D-----------|
        |            \     .     /            |
        |             \    .    /             |
        |              \   .   /              |
        |               \  .  /               |
        |                \ . /                |
        ------------------\C/------------------
        We plot in the order D moveto A moveto B lineto C lineto D lineto B
        """
        mySelfTokCount = self.tokenCounter.tokenCountNonWs(isAll=False)
        if mySelfTokCount == 0 and self._numChildSigTokens == 0:
            return
        # Find D
        myDl = self._bb.plotPointSelf(theDl)
        # Point C, all use this
        myPtC = Coord.newPt(myDl, self._bb.width.scale(0.5), self._bb.depth)
        # Increment by one or two histogram depths to point A
        if self.__mustPlotSelfHistogram():
            myDl = Coord.newPt(myDl, None, self.HIST_DEPTH)
        if self.__mustPlotChildHistogram():
            myDl = Coord.newPt(myDl, None, self.HIST_DEPTH)
        # Figure out move to B
        if self._numChildSigTokens == 0:
            # Chevron takes full width
            polyLogicalPtS = [
                myDl,
                myPtC,
                Coord.newPt(myDl, self._bb.width, None),
            ]
        else:
            ratioChevron = 1.0 * mySelfTokCount / (self._numChildSigTokens +
                                                   mySelfTokCount)
            myChevronOffset = self._bb.width.scale(0.5 * ratioChevron)
            #theSvg.comment(' Chevron offset: %s ' % str(myChevronOffset))
            # Offset to B
            myDl = Coord.newPt(myDl,
                               self._bb.width.scale(0.5) - myChevronOffset,
                               None)
            polyLogicalPtS = [
                myDl,
                myPtC,
                Coord.newPt(myDl, myChevronOffset.scale(2.0), None),
            ]

        polyPhysicalPtS = [theTpt.pt(p) for p in polyLogicalPtS]
        #theSvg.comment(' \npolyPhysicalPtS: %s \n' % str([str(p) for p in polyPhysicalPtS]))
        j = 1
        while j < len(polyPhysicalPtS):
            with SVGWriter.SVGLine(
                    theSvg,
                    polyPhysicalPtS[j - 1],
                    polyPhysicalPtS[j],
                {
                    'stroke-width': "2",
                    'stroke': "black",
                },
            ):
                pass
            j += 1

#     def writeAltTextAndMouseOverText(
#                         self, theSvg, thePoint, theId, theText, theAltS, yOffs):
#         """Writes out the alternate text to the SVG stream immediately and returns
#         a tuple of (thePoint, 'Verdana', 12, myAttrs, theText) to write out later
#         (so it is on top)."""
#         self._writeAlternateText(theSvg, thePoint, theId, theText, theAltS, yOffs)
#         # <text id="original" font-family="Verdana" font-size="12" text-anchor="middle" x="250" y="250">Original text.</text>
#         myAttrs = {
#             'id'                : 't%s' % theId,
#             'class'             : self.CLASS_VERDANA_12,
#             'opacity'           : '1.0',
# #             #'font-weight'       : "bold",
# #             'text-decoration'   : "underline",
# #             'text-anchor'       : "middle",
# #             'dominant-baseline' : "middle",
# #             #'dominant-baseline' : "text-after-edge",
#             'onmouseover'       : "swapOpacity('t%s', 't%s')" \
#                         % (theId, theId+self.ALT_ID_SUFFIX),
#             'onmouseout'        : "swapOpacity('t%s', 't%s')" \
#                         % (theId, theId+self.ALT_ID_SUFFIX),
#         }
# #         self._addECMAScriptMouseOverAttrs(myAttrs, theId, theId+self.ALT_ID_SUFFIX)
#         self._triggerText.append((thePoint, None, None, myAttrs, theText))

    def writeAltTextAndMouseOverRect(self, theSvg, theId, theAltPt, theAltS,
                                     theTrigPt, theTrigRect):
        """Composes and writes the (pop-up) alternate text.
        Also writes a trigger rectangle."""
        # Write a grouping element and give it the alternate ID
        with SVGWriter.SVGGroup(theSvg, {
                'id': 't%s%s' % (theId, self.ALT_ID_SUFFIX),
                'opacity': '0.0'
        }):
            altFontSize = self.ALT_FONT_PROPERTIES[
                self.ALT_FONT_FAMILY]['size']
            altFontLenFactor = self.ALT_FONT_PROPERTIES[
                self.ALT_FONT_FAMILY]['lenFactor']
            altFontHeightFactor = self.ALT_FONT_PROPERTIES[
                self.ALT_FONT_FAMILY]['heightFactor']
            # Compute masking box for alternate
            maxChars = max([len(s) for s in theAltS])
            # Take around 80% of character length
            boxWidth = Coord.Dim(altFontSize * maxChars * altFontLenFactor,
                                 'pt')
            if len(theAltS) < 2:
                boxHeight = Coord.Dim(altFontSize * 2, 'pt')
            else:
                boxHeight = Coord.Dim(
                    altFontSize * len(theAltS) * altFontHeightFactor, 'pt')

            boxAttrs = {'fill': self.ALT_RECT_FILL}
            with SVGWriter.SVGRect(
                    theSvg,
                    theAltPt,
                    Coord.Box(boxWidth, boxHeight),
                    boxAttrs,
            ):
                pass
            # As the main text is centered and the alt text is left
            # justified we need to move the text plot point left by a bit.
            myAltTextPt = Coord.newPt(
                theAltPt,
                incX=Coord.Dim(1 * altFontSize * 3 * altFontLenFactor / 2.0,
                               'pt'),
                incY=Coord.Dim(12, 'pt'),
            )
            with SVGWriter.SVGText(theSvg, myAltTextPt, 'Courier', altFontSize,
                                   {
                                       'font-weight': "normal",
                                   }):
                self._writeStringListToTspan(theSvg, myAltTextPt, theAltS)
        # Add the trigger rectangle for writing on finalise
        boxAttrs = {
            'class' : self.CLASS_RECT_INVIS,
            'id'                : 't%s' % theId,
            'onmouseover'       : "swapOpacity('t%s', 't%s')" \
                        % (theId, theId+self.ALT_ID_SUFFIX),
            'onmouseout'        : "swapOpacity('t%s', 't%s')" \
                        % (theId, theId+self.ALT_ID_SUFFIX),
        }
        self._triggerS.append((theTrigPt, theTrigRect, boxAttrs))
Пример #9
0
import sys
import inspect
import pprint

import cpip
from cpip.core import FileIncludeGraph
#from cpip.core import PpTokenCount
from cpip.util import XmlWrite
from cpip.plot import Coord
#from cpip.plot import PlotNode
from cpip.plot import SVGWriter
from cpip.plot import TreePlotTransform

CANVAS_PADDING = Coord.Pad(
          Coord.Dim(4.0, 'mm'), # prev i.e. left
          Coord.Dim(4.0, 'mm'), # next i.e. right
          Coord.Dim(4.0, 'mm'), # parent i.e. top
          Coord.Dim(4.0, 'mm'), # child i.e. bottom
    )

def processIncGraphToSvg(theLex, theFilePath, theClass, tptPos, tptSweep):
    """Convert a Include graph from a PpLexer to SVG in theFilePath."""
    myVis = FileIncludeGraph.FigVisitorTree(theClass)
    theLex.fileIncludeGraphRoot.acceptVisitor(myVis)
    # Tree is now a graph of: theClass
    myIgs = myVis.tree()
    # Pad the canvass
    myWidth = CANVAS_PADDING.prev \
                + myIgs.plotCanvas.width \
                + CANVAS_PADDING.next
    myDepth = CANVAS_PADDING.parent \
                + myIgs.plotCanvas.depth \
Пример #10
0
 def testBbChildren_00(self):
     """TestPlotNodeBboxWithChildren.testBbChildren_00() - three children."""
     myObj = PlotNode.PlotNodeBbox()
     myObj.width = Coord.Dim(12, 'mm')
     myObj.depth = Coord.Dim(8, 'mm')
     #print '\nTRACE myObj', myObj
     myObj.bbSelfPadding = Coord.Pad(
         Coord.Dim(1, 'mm'),  # prev
         Coord.Dim(3, 'mm'),  # next
         Coord.Dim(5, 'mm'),  # parent
         Coord.Dim(7, 'mm'),  # child
     )
     myObj.bbSpaceChildren = Coord.Dim(16, 'mm')
     self.assertEqual(True, myObj.bbChildrenWidth is None)
     self.assertEqual(True, myObj.bbChildrenDepth is None)
     self.assertEqual(0, myObj.numChildren)
     myObj.extendChildBbox(
         Coord.Box(
             Coord.Dim(15, 'mm'),
             Coord.Dim(7, 'mm'),
         ))
     # Child box now w:15, d:7
     self.assertEqual(1, myObj.numChildren)
     self.assertEqual(myObj.bbChildrenWidth, Coord.Dim(15, 'mm'))
     self.assertEqual(myObj.bbChildrenDepth, Coord.Dim(7, 'mm'))
     myObj.extendChildBbox(
         Coord.Box(
             Coord.Dim(31, 'mm'),
             Coord.Dim(29, 'mm'),
         ))
     # Child box now w:15+31=46, d:max(7,29)=29
     self.assertEqual(2, myObj.numChildren)
     self.assertEqual(myObj.bbChildrenWidth, Coord.Dim(46, 'mm'))
     self.assertEqual(myObj.bbChildrenDepth, Coord.Dim(29, 'mm'))
     myObj.extendChildBbox(
         Coord.Box(
             Coord.Dim(11, 'mm'),
             Coord.Dim(9, 'mm'),
         ))
     # Child box now w:46+11=57, d:max(29,9)=29
     self.assertEqual(3, myObj.numChildren)
     self.assertEqual(myObj.bbChildrenWidth, Coord.Dim(57, 'mm'))
     self.assertEqual(myObj.bbChildrenDepth, Coord.Dim(29, 'mm'))
     # bbSigma:
     # Width is 57mm as children are wider than me
     # Depth is 5 + 8 + 7 + 16 + 29 = 65mm
     #print
     #print 'myObj.bbSigma:', myObj.bbSigma
     self.assertEqual(myObj.bbSigma,
                      Coord.Box(
                          Coord.Dim(57, 'mm'),
                          Coord.Dim(65, 'mm'),
                      ))
     # Set my datum up
     myD = Coord.Pt(
         Coord.Dim(135, 'mm'),
         Coord.Dim(19, 'mm'),
     )
     # x should be 135 + 0.5 * (57-(1+12+3) + 1) = 135 + 0.5 * 41 + 1 = 156.5
     #print
     #print 'myObj.plotPointSelf:', myObj.plotPointSelf(myD)
     self.assertEqual(
         myObj.plotPointSelf(myD),
         Coord.Pt(
             Coord.Dim(156.5, 'mm'),
             Coord.Dim(24, 'mm'),
         ))
     #childBboxDatum
     #print
     #print 'myObj.childBboxDatum:', myObj.childBboxDatum(myD)
     self.assertEqual(myObj.childBboxDatum(myD),
                      Coord.Pt(
                          Coord.Dim(135, 'mm'),
                          Coord.Dim(55, 'mm'),
                      ))