Esempio n. 1
0
 def _writeAlternateText(self, theSvg, thePoint, theId, theText, theAltS, yOffs=Coord.Dim(0, 'pt')):
     """Composes and writes the (pop-up) alternate text.
     thePoint is the physical point to locate both texts."""
     # 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,
                 # Edge the plot point up and left by a bit
                 Coord.newPt(
                     thePoint,
                     incX=Coord.Dim(-1 * altFontSize * (1 + len(theText) * altFontLenFactor / 2.0), 'pt'),
                     incY=Coord.Dim(-1*altFontHeightFactor * altFontSize, 'pt') + yOffs,
                 ),
                 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(
             thePoint,
             incX=Coord.Dim(-1 * altFontSize * len(theText) * altFontLenFactor / 2.0, 'pt'),
             incY=yOffs,
         )
         with SVGWriter.SVGText(theSvg, myAltTextPt, 'Courier', altFontSize,
                     {
                         'font-weight'       : "normal",
                     }
                 ):
             self._writeStringListToTspan(theSvg, myAltTextPt, theAltS)
Esempio n. 2
0
 def test_pcTo(self):
     """TestPlotNodeBboxBoxyChildren.test_pcTo() - test pcTo()."""
     self.assertEqual(self._pnbcObj.pcTo(self._logicalDatum, 0),
                      Coord.Pt(
                          Coord.Dim(23, 'mm'),
                          Coord.Dim(195, 'mm'),
                      ))
     self.assertEqual(self._pnbcObj.pcTo(self._logicalDatum, 1),
                      Coord.Pt(
                          Coord.Dim(27, 'mm'),
                          Coord.Dim(195, 'mm'),
                      ))
     self.assertEqual(self._pnbcObj.pcTo(self._logicalDatum, 2),
                      Coord.Pt(
                          Coord.Dim(31, 'mm'),
                          Coord.Dim(195, 'mm'),
                      ))
Esempio n. 3
0
 def testBbChildren(self):
     """Tests PlotNode() set/get bbChildren."""
     myObj = PlotNode.PlotNodeBbox()
     myObj.bbChildren = Coord.Box(
         Coord.Dim(1, 'px'),
         Coord.Dim(2, 'px'),
     )
     self.assertEqual(myObj.bbChildren,
                      Coord.Box(
                          Coord.Dim(1, 'px'),
                          Coord.Dim(2, 'px'),
                      ))
     self.assertEqual(
         myObj.bbChildrenWidth,
         Coord.Dim(1, 'px'),
     )
     self.assertEqual(
         myObj.bbChildrenDepth,
         Coord.Dim(2, 'px'),
     )
Esempio n. 4
0
 def test_pcRoll(self):
     """TestPlotNodeBboxBoxyChildren.test_pcRoll() - test pcRoll()."""
     #parentChildTakeoffPoint
     #print
     #print 'myObj.parentChildTakeoffPoint[0]:', myObj.parentChildTakeoffPoint(myD, 0)
     self.assertEqual(self._pnbcObj.pcRoll(self._logicalDatum, 0),
                      Coord.Pt(
                          Coord.Dim(23, 'mm'),
                          Coord.Dim(188, 'mm'),
                      ))
     #print
     #print 'myObj.parentChildTakeoffPoint[1]:', myObj.parentChildTakeoffPoint(myD, 1)
     self.assertEqual(self._pnbcObj.pcRoll(self._logicalDatum, 1),
                      Coord.Pt(
                          Coord.Dim(27, 'mm'),
                          Coord.Dim(188, 'mm'),
                      ))
     #print
     #print 'myObj.parentChildTakeoffPoint[2]:', myObj.parentChildTakeoffPoint(myD, 2)
     self.assertEqual(self._pnbcObj.pcRoll(self._logicalDatum, 2),
                      Coord.Pt(
                          Coord.Dim(31, 'mm'),
                          Coord.Dim(188, 'mm'),
                      ))
Esempio n. 5
0
 def testScale(self):
     """Dim() scale()."""
     myObj = Coord.Dim(12, 'px')
     myObj = myObj.scale(2.0)
     self.assertEqual(myObj.value, 24)
     self.assertEqual(myObj.units, 'px')
Esempio n. 6
0
    def test_01(self):
        """TestSVGlWriter.test_01(): <desc> and four rectangles.
        From second example in http://www.w3.org/TR/2003/REC-SVG11-20030114/struct.html#NewDocumentOverview"""
        myF = io.StringIO()
        myViewPort = Coord.Box(
            Coord.Dim(5, 'cm'),
            Coord.Dim(4, 'cm'),
        )
        with SVGWriter.SVGWriter(myF, myViewPort) as xS:
            with XmlWrite.Element(xS, 'desc'):
                xS.characters('Four separate rectangles')
            myPt = Coord.Pt(Coord.Dim(0.5, 'cm'), Coord.Dim(0.5, 'cm'))
            myBx = Coord.Box(Coord.Dim(2.0, 'cm'), Coord.Dim(1.0, 'cm'))
            with SVGWriter.SVGRect(xS, myPt, myBx):
                pass
            myPt = Coord.Pt(Coord.Dim(0.5, 'cm'), Coord.Dim(2.0, 'cm'))
            myBx = Coord.Box(Coord.Dim(1.0, 'cm'), Coord.Dim(1.5, 'cm'))
            with SVGWriter.SVGRect(xS, myPt, myBx):
                pass
            myPt = Coord.Pt(Coord.Dim(3.0, 'cm'), Coord.Dim(0.5, 'cm'))
            myBx = Coord.Box(Coord.Dim(1.5, 'cm'), Coord.Dim(2.0, 'cm'))
            with SVGWriter.SVGRect(xS, myPt, myBx):
                pass
            myPt = Coord.Pt(Coord.Dim(3.5, 'cm'), Coord.Dim(3.0, 'cm'))
            myBx = Coord.Box(Coord.Dim(1.0, 'cm'), Coord.Dim(0.5, 'cm'))
            with SVGWriter.SVGRect(xS, myPt, myBx):
                pass
            myPt = Coord.Pt(Coord.Dim(0.01, 'cm'), Coord.Dim(0.01, 'cm'))
            myBx = Coord.Box(Coord.Dim(4.98, 'cm'), Coord.Dim(3.98, 'cm'))
            with SVGWriter.SVGRect(xS,
                                   myPt,
                                   myBx,
                                   attrs={
                                       'fill': "none",
                                       'stroke': "blue",
                                       'stroke-width': ".02cm",
                                   }):
                pass
        #print
        #print myF.getvalue()
        self.assertEqual(
            myF.getvalue(), """<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg height="4.00cm" version="1.1" width="5.00cm" xmlns="http://www.w3.org/2000/svg">
  <desc>Four separate rectangles</desc>
  <rect height="1.00cm" width="2.00cm" x="0.50cm" y="0.50cm" />
  <rect height="1.50cm" width="1.00cm" x="0.50cm" y="2.00cm" />
  <rect height="2.00cm" width="1.50cm" x="3.00cm" y="0.50cm" />
  <rect height="0.50cm" width="1.00cm" x="3.50cm" y="3.00cm" />
  <rect fill="none" height="3.98cm" stroke="blue" stroke-width=".02cm" width="4.98cm" x="0.01cm" y="0.01cm" />
</svg>
""")
Esempio n. 7
0
    def test_04(self):
        """TestSVGlWriter.test_04(): a line.
        Based on http://www.w3.org/TR/2003/REC-SVG11-20030114/shapes.html#LineElement"""
        myF = io.StringIO()
        myViewPort = Coord.Box(
            Coord.Dim(12, 'cm'),
            Coord.Dim(4, 'cm'),
        )
        with SVGWriter.SVGWriter(myF, myViewPort) as xS:
            with XmlWrite.Element(xS, 'desc'):
                xS.characters(
                    'Example line01 - lines expressed in user coordinates')
            #xS.comment(" Show outline of canvas using 'rect' element ")
            myPt = Coord.Pt(Coord.baseUnitsDim(1), Coord.baseUnitsDim(1))
            myBx = Coord.Box(Coord.baseUnitsDim(1198), Coord.baseUnitsDim(398))
            with SVGWriter.SVGRect(xS, myPt, myBx, {
                    'fill': "none",
                    'stroke': "blue",
                    'stroke-width': "2"
            }):
                pass
            # Make a group
            with SVGWriter.SVGGroup(xS, {'stroke': 'green'}):
                with SVGWriter.SVGLine(
                        xS,
                        Coord.Pt(Coord.baseUnitsDim(100),
                                 Coord.baseUnitsDim(300)),
                        Coord.Pt(Coord.baseUnitsDim(300),
                                 Coord.baseUnitsDim(100)),
                    {'stroke-width': "5"}):
                    pass
                with SVGWriter.SVGLine(
                        xS,
                        Coord.Pt(Coord.baseUnitsDim(300),
                                 Coord.baseUnitsDim(300)),
                        Coord.Pt(Coord.baseUnitsDim(500),
                                 Coord.baseUnitsDim(100)),
                    {'stroke-width': "10"}):
                    pass
                with SVGWriter.SVGLine(
                        xS,
                        Coord.Pt(Coord.baseUnitsDim(500),
                                 Coord.baseUnitsDim(300)),
                        Coord.Pt(Coord.baseUnitsDim(700),
                                 Coord.baseUnitsDim(100)),
                    {'stroke-width': "15"}):
                    pass
                with SVGWriter.SVGLine(
                        xS,
                        Coord.Pt(Coord.baseUnitsDim(700),
                                 Coord.baseUnitsDim(300)),
                        Coord.Pt(Coord.baseUnitsDim(900),
                                 Coord.baseUnitsDim(100)),
                    {'stroke-width': "20"}):
                    pass
                with SVGWriter.SVGLine(
                        xS,
                        Coord.Pt(Coord.baseUnitsDim(900),
                                 Coord.baseUnitsDim(300)),
                        Coord.Pt(Coord.baseUnitsDim(1100),
                                 Coord.baseUnitsDim(100)),
                    {'stroke-width': "25"}):
                    pass
        #print
        #print myF.getvalue()
        self.assertEqual(
            myF.getvalue(), """<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg height="4.00cm" version="1.1" width="12.00cm" xmlns="http://www.w3.org/2000/svg">
  <desc>Example line01 - lines expressed in user coordinates</desc>
  <rect fill="none" height="398px" stroke="blue" stroke-width="2" width="1198px" x="1px" y="1px" />
  <g stroke="green">
    <line stroke-width="5" x1="100px" x2="300px" y1="300px" y2="100px" />
    <line stroke-width="10" x1="300px" x2="500px" y1="300px" y2="100px" />
    <line stroke-width="15" x1="500px" x2="700px" y1="300px" y2="100px" />
    <line stroke-width="20" x1="700px" x2="900px" y1="300px" y2="100px" />
    <line stroke-width="25" x1="900px" x2="1100px" y1="300px" y2="100px" />
  </g>
</svg>
""")
Esempio n. 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))
Esempio n. 9
0
    def plotToFileObj(self, theFileObj, theTpt):
        """Root level call to plot to a file object. The SVG stream is
        created here."""
        if self._numPassesToPlotSelf < 1:
            raise ValueError('No self._numPassesToPlotSelf set!')
        # Make viewBox user coordinates * self.VIEWBOX_SCALE
        myRootAttrs = {
#             'viewBox' : '0 0 %d %d' \
#                 % (
#                     theTpt.canvasP().width.value * self.VIEWBOX_SCALE,
#                     theTpt.canvasP().depth.value * self.VIEWBOX_SCALE,
#                     ),
            'xmlns:xlink'   : self.NAMESPACE_XLINK,
        }
        # Bit of a hacky way to add enough margin for the pop-ups or rather
        # drop downs. This adds space for the bottom most boxes.
        canvasY = theTpt.canvasP().depth + Coord.Dim(60, 'mm') + Coord.Dim(8, 'mm')
        myCanvas = Coord.Box(
                    theTpt.canvasP().width + Coord.Dim(60, 'mm'),
                    canvasY,
        )
        # Shrink canvas if it is a large plot
        yOffsetForScalingText =  Coord.Dim(10, 'mm')
        scaleIdx = self.SCALE_FACTORS.index(1)
        assert scaleIdx >= 0
        while scaleIdx > 0 and canvasY > self.SCALE_MAX_Y:
            canvasY = canvasY.scale(0.5)
            scaleIdx -= 1
        self._scale = self.SCALE_FACTORS[scaleIdx]
        with SVGWriter.SVGWriter(theFileObj, myCanvas, myRootAttrs, mustIndent=cpip.INDENT_ML) as myS:
            # yOffsetForScalingText is applied wrong, should respect theTpt
            myDatum = Coord.Pt(
                         CANVAS_PADDING.prev - yOffsetForScalingText,
                         CANVAS_PADDING.parent,
                    )
            self.writePreamble(myS)
            myS.comment(' Root position: %s, Sweep direction: %s canvas=%s datum=%s' \
                         % (theTpt.rootPos, theTpt.sweepDir, theTpt.canvasP(), myDatum),
                         newLine=True)
            # Shift the drawing down a bit to make way for the scale buttons.
            with SVGWriter.SVGGroup(myS, {'transform' : "translate(0, 24)"}):
                with SVGWriter.SVGGroup(myS,
                        {
                            'id' : 'graphic',
                            'transform' : "scale(%s)" % self.SCALE_FACTORS[scaleIdx]
                        }):
                    # Apply a group element for scaling the plot
                    # More hackery: yOffsetForScalingText is applied wrong, should respect theTpt
                    with SVGWriter.SVGRect(
                            myS,
                            Coord.newPt(
                                        Coord.zeroBaseUnitsPt(),
                                        incX=None,
                                        incY=yOffsetForScalingText),
                            theTpt.canvasP(),
                            {
                                'fill'         : 'none',
                                'stroke'       : 'grey',
                                'stroke-width' : '2',
                            },
                        ):
                        pass
                    # Start the plot
                    self.plotInitialise(myS, myDatum, theTpt)
                    # Now plot all contents
                    for p in range(self._numPassesToPlotSelf):
                        self.plotToSVGStream(myS, myDatum, theTpt, p, [])
                    # Finish the plot
                    self.plotFinalise(myS, myDatum, theTpt)
Esempio n. 10
0
 def testIsub_00(self):
     """Dim() -=."""
     myObj_0 = Coord.Dim(1, 'in')
     myObj_0 -= Coord.Dim(18, 'px')
     self.assertEqual(myObj_0.value, 0.75)
     self.assertEqual(myObj_0.units, 'in')
Esempio n. 11
0
 def testIadd_01(self):
     """Dim() += when initial units are None."""
     myObj_0 = Coord.Dim(12, None)
     myObj_0 += Coord.Dim(18, 'px')
     self.assertEqual(myObj_0.value, 30)
     self.assertEqual(myObj_0.units, 'px')
Esempio n. 12
0
 def testStr(self):
     """Dim() __str__()."""
     myObj = Coord.Dim(12, 'px')
     self.assertEqual(str(myObj), 'Dim(12px)')
Esempio n. 13
0
 def testConstructor(self):
     """Dim() constructor."""
     myObj = Coord.Dim(12, 'px')
     #print myObj
     self.assertEqual(myObj.value, 12)
     self.assertEqual(myObj.units, 'px')
Esempio n. 14
0
 def testMin_00(self):
     """Dim() min(...) [00]."""
     myObj_0 = Coord.Dim(71, 'px')
     myObj_1 = Coord.Dim(72, 'px')
     self.assertEqual(myObj_0, min(myObj_0, myObj_1))
     self.assertNotEqual(myObj_1, min(myObj_0, myObj_1))
Esempio n. 15
0
 def testCmp_01(self):
     """Dim() cmp() [01]."""
     myObj_0 = Coord.Dim(1, 'in')
     myObj_1 = Coord.Dim(1, 'in')
     self.assertEqual(myObj_0, myObj_1)
     self.assertTrue(myObj_0 == myObj_1)
Esempio n. 16
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
Esempio n. 17
0
class SVGTreeNodeBase(FileIncludeGraph.FigVisitorTreeNodeBase):
    COMMON_UNITS            = 'mm'
    # User units for viewBox and ploygon
    UNNAMED_UNITS           = 'px'
    VIEWBOX_SCALE           = 8.0
#===============================================================================
#    CANVAS_PADDING          = Coord.Pad(
#              Coord.Dim(4.0, COMMON_UNITS), # prev i.e. left
#              Coord.Dim(4.0, COMMON_UNITS), # next i.e. right
#              Coord.Dim(4.0, COMMON_UNITS), # parent i.e. top
#              Coord.Dim(4.0, COMMON_UNITS), # child i.e. bottom
#        )
#===============================================================================
    # Attributes for alternate text
    ALT_RECT_FILL = 'khaki'#'yellow'
    ALT_ID_SUFFIX = '.alt'
    ALT_FONT_FAMILY = 'monospace'
    ALT_FONT_PROPERTIES = {
        'Courier' : {
                        'size'          : 10,
                        'lenFactor'     : 0.5,
                        'heightFactor'  : 1.2,
            },
        'monospace' : {
                        'size'          : 10,
                        'lenFactor'     : 0.5,
                        'heightFactor'  : 1.2,
            },
        }
    NAMESPACE_XLINK = 'http://www.w3.org/1999/xlink'
    # Used to rescale SVG rather than zooming in the browser as the latter is
    # slow with Chrome and Safari (both WebKit) and pretty much everything else.
    # Initial, presentational, scale is chose depending on the size of the diagram. 
    SCALE_FACTORS = (0.05, 0.1, 0.25, 0.5, 1.0, 1.5, 2.0,)
    # Used to decide initial scale
    SCALE_MAX_Y = Coord.Dim(1000, 'mm')
#    POPUP_COORD_Y_OFFSET = Coord.Dim(20, 'pt')
    def __init__(self, theFig, theLineNum):
        super(SVGTreeNodeBase, self).__init__(theLineNum)
        self._isRoot = theFig is None
        if theFig is None:
            # Root node, children only
            self._fileName = None
            # This does not include children
            self._numTokens = -1
            self._condCompState = None
        else:
            self._fileName = theFig.fileName
            # This is significant tokens only and does not include children
            self._numTokens = theFig.numTokensSig
            self._condCompState = theFig.condCompState
        # The bounding box, to be (re)set by derived classes
        self._bb = None
        self.TRACE = cpip.SVG_COMMENT_FUNCTIONS
        # Number of passes to call plotSelf()
        self._numPassesToPlotSelf = 0

    #============================================
    # Section: Trace/Debug
    #============================================
    def dumpToStream(self, theS=sys.stdout, p=''):
        """Debug/trace."""
        theS.write('%sFile: %s\n' % (p, self.nodeName))
        for aLine in str(self.bb).splitlines():
            theS.write('%s%s\n' % (p, aLine))
        for aChild in self._children:
            aChild.dumpToStream(theS, p+'  ')
    
    def commentFunctionBegin(self, theSvg, **kwargs):
        """Injects a comment into the SVG with the start of the
        executing function name."""
        if self.TRACE:
            theSvg.comment(' %s(): %s %s'
                           % (inspect.stack()[1][3], 'BEGIN',
                              pprint.pformat(kwargs)), newLine=True)
          
    def commentFunctionEnd(self, theSvg, **kwargs):
        """Injects a comment into the SVG with the completion of the
        executing function name."""
        if self.TRACE:
            theSvg.comment(' %s(): %s %s'
                           % (inspect.stack()[1][3], 'END',
                              pprint.pformat(kwargs)), newLine=True)
          
    #============================================
    # End: Trace/Debug
    #============================================

    #============================================
    # Section: Accessor methods used by ancestors
    #============================================
    @property
    def isRoot(self):
        return self._isRoot
    
    @property
    def numTokens(self):
        """The number of significant tokens for me only (not including children)."""
        return self._numTokens
        
    @property
    def nodeName(self):
        """This is the file name or 'Root'."""
        if self.isRoot:
            return 'Root'
        return self._fileName
    
    @property
    def condCompState(self):
        """True/False if conditionally compiled node."""
        assert(not self.isRoot)
        return self._condCompState
    
    @property
    def bb(self):
        """Returns a PlotNode.PlotNodeBboxBoxy() object for this node."""
        assert(self._bb is not None)
        return self._bb
    
    @property
    def plotCanvas(self):
        """The logical size of the plot canvas as a Coord.Box()."""
        return self.bb.bbSigma
    #========================================
    # End: Accessor methods used by ancestors
    #========================================
    
    #===================================
    # Section: Finalisation and plotting
    #===================================
    def finalise(self):
        """This will be called on finalisation.
        For depth first finalisation the child class should call finalise
        on each child first."""
        raise NotImplementedError('finalise() not implemented')
                
    def plotToFilePath(self, theFileName, theTpt):
        """Root level call to plot to a SVG file, theTpt is an
        TreePlotTransform object and is used to transform the internal logical
        coordinates to physical plot positions."""
        self.plotToFileObj(open(theFileName, 'w'), theTpt)
        
    def plotToFileObj(self, theFileObj, theTpt):
        """Root level call to plot to a file object. The SVG stream is
        created here."""
        if self._numPassesToPlotSelf < 1:
            raise ValueError('No self._numPassesToPlotSelf set!')
        # Make viewBox user coordinates * self.VIEWBOX_SCALE
        myRootAttrs = {
#             'viewBox' : '0 0 %d %d' \
#                 % (
#                     theTpt.canvasP().width.value * self.VIEWBOX_SCALE,
#                     theTpt.canvasP().depth.value * self.VIEWBOX_SCALE,
#                     ),
            'xmlns:xlink'   : self.NAMESPACE_XLINK,
        }
        # Bit of a hacky way to add enough margin for the pop-ups or rather
        # drop downs. This adds space for the bottom most boxes.
        canvasY = theTpt.canvasP().depth + Coord.Dim(60, 'mm') + Coord.Dim(8, 'mm')
        myCanvas = Coord.Box(
                    theTpt.canvasP().width + Coord.Dim(60, 'mm'),
                    canvasY,
        )
        # Shrink canvas if it is a large plot
        yOffsetForScalingText =  Coord.Dim(10, 'mm')
        scaleIdx = self.SCALE_FACTORS.index(1)
        assert scaleIdx >= 0
        while scaleIdx > 0 and canvasY > self.SCALE_MAX_Y:
            canvasY = canvasY.scale(0.5)
            scaleIdx -= 1
        self._scale = self.SCALE_FACTORS[scaleIdx]
        with SVGWriter.SVGWriter(theFileObj, myCanvas, myRootAttrs, mustIndent=cpip.INDENT_ML) as myS:
            # yOffsetForScalingText is applied wrong, should respect theTpt
            myDatum = Coord.Pt(
                         CANVAS_PADDING.prev - yOffsetForScalingText,
                         CANVAS_PADDING.parent,
                    )
            self.writePreamble(myS)
            myS.comment(' Root position: %s, Sweep direction: %s canvas=%s datum=%s' \
                         % (theTpt.rootPos, theTpt.sweepDir, theTpt.canvasP(), myDatum),
                         newLine=True)
            # Shift the drawing down a bit to make way for the scale buttons.
            with SVGWriter.SVGGroup(myS, {'transform' : "translate(0, 24)"}):
                with SVGWriter.SVGGroup(myS,
                        {
                            'id' : 'graphic',
                            'transform' : "scale(%s)" % self.SCALE_FACTORS[scaleIdx]
                        }):
                    # Apply a group element for scaling the plot
                    # More hackery: yOffsetForScalingText is applied wrong, should respect theTpt
                    with SVGWriter.SVGRect(
                            myS,
                            Coord.newPt(
                                        Coord.zeroBaseUnitsPt(),
                                        incX=None,
                                        incY=yOffsetForScalingText),
                            theTpt.canvasP(),
                            {
                                'fill'         : 'none',
                                'stroke'       : 'grey',
                                'stroke-width' : '2',
                            },
                        ):
                        pass
                    # Start the plot
                    self.plotInitialise(myS, myDatum, theTpt)
                    # Now plot all contents
                    for p in range(self._numPassesToPlotSelf):
                        self.plotToSVGStream(myS, myDatum, theTpt, p, [])
                    # Finish the plot
                    self.plotFinalise(myS, myDatum, theTpt)

    def writePreamble(self, theS):
        """Write any preamble such as CSS or JavaScript.
        To be implemented by child classes."""
        raise NotImplementedError

    def plotInitialise(self, theSvg, theDatumL, theTpt):
        """Called once immediately before the recursive plotToSVGStream().
        Can be overridden in child classes for specific use.""" 
        pass

    def plotFinalise(self, theSvg, theDatumL, theTpt):
        """Called once immediately before the plot is closed.
        Can be overridden in child classes for specific use.""" 
        pass

    def plotToSVGStream(self, theSvg, theDatumL, theTpt, passNum, idStack):
        """Plot me to a stream and my children at the logical datum point,
        this is a recursive call."""
        self.commentFunctionBegin(theSvg, File=self._fileName, Pass=passNum)
        if not self.isRoot:
            if self.lineNum != -1:
                idStack.append(self.lineNum)
            idStack.append(self.nodeName)
        if len(self._children) > 0:
            if passNum == 0:
                if self.isRoot:
                    self._plotRootChildToChild(theSvg, theDatumL, theTpt)
                else:
                    self._plotSelfToChildren(theSvg, theDatumL, theTpt)
            # Recursive call
            # TODO: Consider reversing this so that drop-downs appear over
            # children.
#             for i, datumChildL in self._enumerateChildren(theDatumL, theTpt):
            for i, datumChildL in reversed(list(self._enumerateChildren(theDatumL, theTpt))):
                self._children[i].plotToSVGStream(theSvg, datumChildL, theTpt, passNum, idStack)
        # Plot me last so I sit over any me-to-child lines
        if not self.isRoot:
            self._plotSelf(theSvg, theDatumL, theTpt, passNum, idStack)
        else:
            self.plotRoot(theSvg, theDatumL, theTpt, passNum)
        if not self.isRoot:
            if self.lineNum != -1:
                idStack.pop()
            idStack.pop()
        self.commentFunctionEnd(theSvg, File=self._fileName, Pass=passNum)
        
    def plotRoot(self, theSvg, theDatumL, theTpt, passNum):
        """Call to plot any root node, for example our child class uses this
        to plot the histogram legend before starting on the text."""
        pass

    def _plotSelf(self, theSvg, theDatumL, theTpt, idStack):
        """Plot me to a stream at the logical datum point.
        Must be provided by child classes."""
        raise NotImplementedError('_plotSelf() not implemented')

    def _plotRootChildToChild(self, theSvg, theDatumL, theTpt):
        """In the case of me being root this plots child to child."""
        assert(self.isRoot)
        raise NotImplementedError('_plotRootChildToChild() not implemented')
        
    def _plotSelfToChildren(self, theSvg, theDatumL, theTpt):
        """In the case of me being not root this plots me to my children."""
        assert(self.isRoot)
        raise NotImplementedError('_plotSelfToChildren() not implemented')
    
    def _enumerateChildren(self, theDatumL, theTpt):
        """Generates a tuple of (index, logical_datum_point) for my children."""
        assert(len(self._children) > 0)
        datumChildL = theTpt.startChildrenLogicalPos(
                            self._bb.childBboxDatum(theDatumL),
                            self._bb.bbChildren,
                        )
        for i, aChild in enumerate(self._children):
            datumChildL = theTpt.preIncChildLogicalPos(datumChildL, aChild.bb.bbSigma)
            yield i, datumChildL
            datumChildL = theTpt.postIncChildLogicalPos(datumChildL, aChild.bb.bbSigma)

    #=============================================
    # Section: Writing SVG code to do pop-up text.
    #=============================================
    def _writeECMAScript(self, theSvg):
        """Writes the ECMA script for pop-up text switching."""
        myScriptS = []
        myScriptS.append("""
function swapOpacity(idFrom, idTo) {
    var svgFrom = document.getElementById(idFrom);
    var svgTo = document.getElementById(idTo);
    var attrFrom = svgFrom.getAttribute("opacity");
    var attrTo = svgTo.getAttribute("opacity");
    svgTo.setAttributeNS(null, "opacity", attrFrom);
    svgFrom.setAttributeNS(null, "opacity", attrTo);
}

function setOpacity(id, value) {
    var svgElem = document.getElementById(id);
    svgElem.setAttributeNS(null, "opacity", value);
}

""")
        # Pop-up for histogram that uses different technique
        myScriptS.append("""
function showHistogram(x, y) {
    var histElem = document.getElementById("HistogramLegend");
    // Use the ID to compute the y offset. The x offset is 8.0mm for text,
    // 2.0mm or 0mm for rect
    for (var i = 0; i < 38; i += 2) {
        var elem = histElem.children[i / 2]
        if (i == 0) {
            var xOffset = 0;
        } else if (elem.nodeName == "text") {
            var xOffset = 8;
        } else {
            var xOffset = 2;
        }
        elem.setAttributeNS(null, "x", x + xOffset + "mm");
        elem.setAttributeNS(null, "y", y + i + "mm");
    }
    histElem.setAttributeNS(null, "opacity", 1.0);
}
 
function hideHistogram() {
    setOpacity("HistogramLegend", 0.0)
}
 
""")
        # Scaling controls
        myScriptS.append("""
function scaleGraphic(scale, theId) {
    var graphicGroup = document.getElementById("graphic");
    graphicGroup.setAttributeNS(null, "transform", "scale(" + scale + ")");
    // Un-bold all then bold the txtId.
    var scaleGroup = document.getElementById("scaleGroup");
    for (var i = 0; i < scaleGroup.children.length; ++i) {
        var elem = scaleGroup.children[i];
        if (elem.id == theId) {
            elem.setAttributeNS(null, "font-weight", "bold");
        } else {
            elem.setAttributeNS(null, "font-weight", "normal");
        }
    }
}
""")
        theSvg.writeECMAScript(u''.join(myScriptS))

    def _writeAlternateText(self, theSvg, thePoint, theId, theText, theAltS, yOffs=Coord.Dim(0, 'pt')):
        """Composes and writes the (pop-up) alternate text.
        thePoint is the physical point to locate both texts."""
        # 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,
                    # Edge the plot point up and left by a bit
                    Coord.newPt(
                        thePoint,
                        incX=Coord.Dim(-1 * altFontSize * (1 + len(theText) * altFontLenFactor / 2.0), 'pt'),
                        incY=Coord.Dim(-1*altFontHeightFactor * altFontSize, 'pt') + yOffs,
                    ),
                    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(
                thePoint,
                incX=Coord.Dim(-1 * altFontSize * len(theText) * altFontLenFactor / 2.0, 'pt'),
                incY=yOffs,
            )
            with SVGWriter.SVGText(theSvg, myAltTextPt, 'Courier', altFontSize,
                        {
                            'font-weight'       : "normal",
                        }
                    ):
                self._writeStringListToTspan(theSvg, myAltTextPt, theAltS)


    def _writeStringListToTspan(self, theSvg, thePointX, theList):
        """Converts a multi-line string to tspan elements in monospaced format.
        theSvg is the SVG stream.
        theAttrs is the attributes of the enclosing <text> element.
        theStr is the string to write.
        
        This writes the tspan elements within an existing text element, thus:
        <text id="original.alt" font-family="Courier" font-size="12" text-anchor="middle" x="250" y="250">
            <tspan xml:space="preserve"> One</tspan>
            <tspan x="250" dy="1em" xml:space="preserve"> Two</tspan>
            <tspan x="250" dy="1em" xml:space="preserve">Three</tspan>
        </text>
        """
        #theSvg.xmlSpacePreserve()
        for i, aLine in enumerate(theList):
            elemAttrs = {}#'xml:space' : "preserve"}
            if i > 0:
                elemAttrs['x'] = SVGWriter.dimToTxt(thePointX.x) 
                elemAttrs['dy'] = "1.5em"
            with XmlWrite.Element(theSvg, 'tspan', elemAttrs):
                theSvg.characters(aLine)
                theSvg.characters(' ')
Esempio n. 18
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'),
         ))
Esempio n. 19
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'),
         ),
     )
Esempio n. 20
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'),
                      ))
Esempio n. 21
0
 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)
Esempio n. 22
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'),
     )
Esempio n. 23
0
    def test_05(self):
        """TestSVGlWriter.test_05(): a polyline.
        Based on http://www.w3.org/TR/2003/REC-SVG11-20030114/shapes.html#PolylineElement"""
        myF = io.StringIO()
        myViewPort = Coord.Box(
            Coord.Dim(12, 'cm'),
            Coord.Dim(4, 'cm'),
        )
        with SVGWriter.SVGWriter(myF, myViewPort,
                                 {'viewBox': "0 0 1200 400"}) as xS:
            with XmlWrite.Element(xS, 'desc'):
                xS.characters(
                    'Example line01 - lines expressed in user coordinates')
            #xS.comment(" Show outline of canvas using 'rect' element ")
            myPt = Coord.Pt(Coord.baseUnitsDim(1), Coord.baseUnitsDim(1))
            myBx = Coord.Box(Coord.baseUnitsDim(1198), Coord.baseUnitsDim(398))
            with SVGWriter.SVGRect(xS, myPt, myBx, {
                    'fill': "none",
                    'stroke': "blue",
                    'stroke-width': "2"
            }):
                pass
            # Make a group
            with SVGWriter.SVGPolyline(xS, [
                    Coord.Pt(Coord.baseUnitsDim(50), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(150), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(150), Coord.baseUnitsDim(325)),
                    Coord.Pt(Coord.baseUnitsDim(250), Coord.baseUnitsDim(325)),
                    Coord.Pt(Coord.baseUnitsDim(250), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(350), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(350), Coord.baseUnitsDim(250)),
                    Coord.Pt(Coord.baseUnitsDim(450), Coord.baseUnitsDim(250)),
                    Coord.Pt(Coord.baseUnitsDim(450), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(550), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(550), Coord.baseUnitsDim(175)),
                    Coord.Pt(Coord.baseUnitsDim(650), Coord.baseUnitsDim(175)),
                    Coord.Pt(Coord.baseUnitsDim(650), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(750), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(750), Coord.baseUnitsDim(100)),
                    Coord.Pt(Coord.baseUnitsDim(850), Coord.baseUnitsDim(100)),
                    Coord.Pt(Coord.baseUnitsDim(850), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(950), Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(950), Coord.baseUnitsDim(25)),
                    Coord.Pt(Coord.baseUnitsDim(1050), Coord.baseUnitsDim(25)),
                    Coord.Pt(Coord.baseUnitsDim(1050),
                             Coord.baseUnitsDim(375)),
                    Coord.Pt(Coord.baseUnitsDim(1150),
                             Coord.baseUnitsDim(375)),
            ], {
                    'fill': 'none',
                    'stroke': 'blue',
                    'stroke-width': "5"
            }):
                pass
        #print
        #print myF.getvalue()
        self.assertEqual(
            myF.getvalue(), """<?xml version='1.0' encoding="utf-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg height="4.00cm" version="1.1" viewBox="0 0 1200 400" width="12.00cm" xmlns="http://www.w3.org/2000/svg">
  <desc>Example line01 - lines expressed in user coordinates</desc>
  <rect fill="none" height="398px" stroke="blue" stroke-width="2" width="1198px" x="1px" y="1px" />
  <polyline fill="none" points="50,375 150,375 150,325 250,325 250,375 350,375 350,250 450,250 450,375 550,375 550,175 650,175 650,375 750,375 750,100 850,100 850,375 950,375 950,25 1050,25 1050,375 1150,375" stroke="blue" stroke-width="5" />
</svg>
""")
Esempio n. 24
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))""",
        )
Esempio n. 25
0
 def setUp(self):
     self._boxDefault = Coord.Box(
         Coord.Dim(300, None),
         Coord.Dim(500, None),
     )
Esempio n. 26
0
 def testIsub_01(self):
     """Dim() -= when initial units are None."""
     myObj_0 = Coord.Dim(36, None)
     myObj_0 -= Coord.Dim(12, 'px')
     self.assertEqual(myObj_0.value, 24)
     self.assertEqual(myObj_0.units, 'px')