Esempio n. 1
0
def RenderAnimatedPart(Data):
    """ Renders list of strings that will be injected to frame """

    # -----------------------------------------------------------------------------

    def Field(Name, Default):
        return Data[Name] if Name in Data else Default

    # -----------------------------------------------------------------------------

    # TODO: string concatenation is slow
    robot_sprite = ""
    path = ""
    sparks = ""

    side = 0.5
    IT = tIndentTracker('  ')

    robot_sprite += SVGGroup(
        IT, {
            'transform':
            'rotate(%g %g %g)' %
            ((Data['ActualOrientation'] * 180 / pi) + 180.0,
             Data['ActualPosition'][0], Data['ActualPosition'][1])
        })

    # robot_sprite += SVGGroup(IT, {'transform': 'scale(2)'})
    robot_sprite += IT(
        '<polygon points="%g,%g %g,%g %g,%g" style="fill:lime;'
        ''
        'stroke:purple;stroke-width:0.06" />' %
        (Data['ActualPosition'][0] + side / 2.0, Data['ActualPosition'][1] -
         side / 2.0, Data['ActualPosition'][0] + side / 2.0,
         Data['ActualPosition'][1] + side / 2.0,
         Data['ActualPosition'][0] - side, Data['ActualPosition'][1]))
    robot_sprite += SVGGroupEnd(IT)
    # robot_sprite += SVGGroupEnd(IT)

    ActualPath = Field('ActualPath', None)
    if ActualPath is not None:
        path += IT('<!-- Actual path -->')
        path += SVGPath(IT, ShapeFromVertices(ActualPath, 1), {
            'stroke': '#40f',
            'stroke-width': '0.02'
        })

    # ??
    # Paths in rainbow colours
    Paths = Field('Paths', None)
    if Paths is not None and len(Paths) > 0:

        NumPaths = len(Paths)

        path += IT('<!-- Paths in rainbow colours -->')
        for PathIx, Path in enumerate(Paths):
            if NumPaths >= 2:
                Progress = float(PathIx) / float(NumPaths - 1)
            else:
                Progress = 1.0
            Opacity = 1.0 if Progress in [0.0, 1.0] else 0.60 + 0.00 * Progress
            ColourStr = ProgressColourStr(Progress, Opacity)
            path += IT('<!-- Path %d, (%.1f%%) -->' %
                       (PathIx, 100.0 * Progress))
            path += SVGPath(IT, Path, {"stroke": ColourStr})

    Sparks = Field('Sparks', None)
    if Sparks is not None and len(Sparks) > 0:
        B = (Pt_Break, None)
        sparks += IT('<!-- Points of interests (sparks) -->')

        for SparkPos in Sparks:
            SparkPos = (SparkPos[0], SparkPos[1])
            S = (GreekCross(SparkPos, 0.4) + [B] +
                 PiecewiseArc(SparkPos, 0.25, (0, 2.0 * pi), 8))
            sparks += IT('<!-- Goal position -->')
            sparks += SVGPath(IT, S, {
                'stroke': '#d00',
                'stroke-width': '0.1',
                'stroke-linecap': 'butt'
            })
    # End of plot

    return [robot_sprite, path, sparks]
Esempio n. 2
0
def RenderFrameTemplate(Data, draw_dynamic_elements=True):
    '''Return Data rendered to an SVG file in a string.

    Data is a dictionary with the keys:

      Title:
        This title is rendered and is embedded in the SVG header.
      Grid:
        See SVGGrid().
      Map:
        The map is a 2D array of integers for hazards (1) and clear spaces (0).
      StartPos:
        The car starts here. The coordinates are units of map squares and the
        origin is in the centre of Map[0][0].
      GoalPos:
        Hopefully the car ends within GoalThreshold of here.
      GoalThreshold:
        The radius of the goal zone is measured in grid units.
      Sparks:
        Each time a collision occurs, a coordinate pair is added to Sparks.
      Paths:
        For the puspose of visualising progressive path modifications, Paths
        is a list of Shapes to be rendered in red-to-indigo rainbow colours.

    '''

    # -----------------------------------------------------------------------------

    # Shorthand

    A = Pt_Anchor
    C = Pt_Control
    B = (Pt_Break, None)

    # -----------------------------------------------------------------------------

    def Field(Name, Default):
        return Data[Name] if Name in Data else Default

    # -----------------------------------------------------------------------------

    IT = tIndentTracker('  ')

    Result = ''
    Title = Field('Title', '(Untitled)')

    Result += SVGStart(IT, Title, {
        'width': '28cm',
        'height': '19cm',
        'viewBox': '0 0 28 19'
    })

    Result += IT('<defs>')
    IT.StepIn()
    Result += IT('<marker id="ArrowHead"',
                 '    viewBox="0 0 10 10" refX="0" refY="5"',
                 '    markerUnits="strokeWidth"',
                 '    markerWidth="8" markerHeight="6"', '    orient="auto">'
                 '  <path d="M 0,0  L 10,5  L 0,10"/>', '</marker>')
    # Sparks are not used
    # Result += SparkSymbolDef(IT, 'spark')  if Sparks is not None else ''

    # More marker, symbol and gradient definitions can go here.
    IT.StepOut()
    Result += IT('</defs>')

    # Background

    Result += IT(
        '<rect x="0" y="0" width="28" height="19" stroke="none" fill="white"/>'
    )

    # Outer group

    Result += IT('<!-- Outer group -->')
    Result += SVGGroup(IT, {'stroke': 'black', 'stroke-width': '0.025'})

    # Grid group
    # Substitute for the group created by SVGGrid that is now deleted

    Result += SVGGroup(
        IT, {
            'stroke': 'black',
            'stroke-width': 'butt',
            'transform': 'matrix(1.5, 0, 0, 1.5, 5.5, 1.5)',
            'fill': 'none'
        })

    Result += IT('<!-- Background -->')

    # Extract part of SVG with board
    board_data = open(Data['Map']['vector_graphics_file']).read().splitlines()
    board_data = board_data[board_data.index("<!-- Board -->") + 1:]
    board_data = board_data[0:board_data.index("<!-- Board -->")]
    Result += IT("\n".join(board_data))

    # Iniial position
    StartPos = Field('StartPos', None)
    if StartPos is not None:
        S = [(A, (-1.0, -1.0)), (A, (-1.0, +1.0)), (A, (+1.0, +1.0)),
             (A, (+1.0, -1.0)), (A, (-1.0, -1.0))]

        Result += IT('<!-- Initial position -->')
        Result += SVGPath(IT, TransformedShape(AffineMtxTS(StartPos, 0.3), S),
                          {
                              'stroke': '#09f',
                              'stroke-width': '0.1',
                              'stroke-linecap': 'square'
                          })

    # Robot
    if draw_dynamic_elements:
        Result += IT('{0}')
    else:
        Result += ''

    # Goal position
    GoalPos = Field('GoalPos', None)
    if GoalPos is not None:

        GoalThreshold = Field('GoalThreshold', None)
        if GoalThreshold is not None:
            Result += IT('<!-- Goal threshold -->')
            Result += IT('<circle cx="%g" cy="%g" r="%g"' %
                         (GoalPos[0], GoalPos[1], GoalThreshold))
            IT.StepIn(2)
            Result += IT(
                'fill="rgba(0, 255, 0, 0.1)" stroke="#0d0" stroke-width="0.02"/>'
            )
            IT.StepOut(2)

        S = (GreekCross(GoalPos, 0.4) + [B] +
             PiecewiseArc(GoalPos, 0.25, (0, 2.0 * pi), 8))
        Result += IT('<!-- Goal position -->')
        Result += SVGPath(IT, S, {
            'stroke': '#0d0',
            'stroke-width': '0.1',
            'stroke-linecap': 'butt'
        })

    # Sparks
    if draw_dynamic_elements:
        Result += IT("{2}")
    else:
        Result += ''
    # if Sparks is not None and len(Sparks) > 0:
    #     Result += IT('<!-- Points of interests (sparks) -->')
    #     Result += SVGGroup(IT, {'transform': 'translate(-0.5, -0.5)'})
    #     for SparkPos in Sparks:
    #         Result += IT(
    #             ('<use x="%g" y="%g"' % (SparkPos[0], SparkPos[1])) +
    #             ' width="1" height="1" xlink:href="#spark"/>'
    #         )
    #     Result += SVGGroupEnd(IT)

    # Actual path
    if draw_dynamic_elements:
        Result += IT("{1}")
    else:
        Result += ''

    # End board group
    # This ends the newly added group substituting the SVGGrid group.
    # Moved here the SVGGroupEnd which was for some reason previously appended
    # to the Actual path element making the code look convoluted

    Result += SVGGroupEnd(IT)

    # Title and legend

    Result += IT('<!-- Title background -->')
    Result += IT(
        '<rect x="0" y="0" width="28" height="1.1" stroke="none" fill="white"/>'
    )

    Result += IT('<!-- Title group -->')
    Result += SVGGroup(
        IT, {
            'font-family': 'sans-serif',
            'font-size': '0.36',
            'font-weight': 'normal',
            'fill': 'black',
            'stroke': 'none'
        })

    Result += IT('<!-- Title -->')
    Result += SVGText(IT, (0.5, 0.82), Title, {
        'font-size': '0.72',
        'font-weight': 'bold'
    })

    Result += IT('<!-- Legend line labels-->')
    Result += SVGText(IT, (19.5, 0.82), 'Actual path')

    Result += IT('<!-- Legend lines -->')
    Result += SVGGroup(IT, {
        'fill': 'none',
        'stroke-width': '0.12',
        'stroke-linecap': 'round'
    })

    Result += SVGPath(IT, [(Pt_Anchor, (18.5, 0.7)), (Pt_Anchor, (19.3, 0.7))],
                      {'stroke': '#40f'})

    Result += SVGGroupEnd(IT)

    # End of title group

    Result += SVGGroupEnd(IT)

    # End of outer group

    Result += SVGGroupEnd(IT)

    Result += SVGEnd(IT)

    return Result
def RenderFrameTemplate(Data, draw_dynamic_elements=True):
    '''Return Data rendered to an SVG file in a string.

    Data is a dictionary with the keys:

      Title:
        This title is rendered and is embedded in the SVG header.
      Grid:
        See SVGGrid().
      Map:
        The map is a 2D array of integers for hazards (1) and clear spaces (0).
      StartPos:
        The car starts here. The coordinates are units of map squares and the
        origin is in the centre of Map[0][0].
      GoalPos:
        Hopefully the car ends within GoalThreshold of here.
      GoalThreshold:
        The radius of the goal zone is measured in grid units.
      Sparks:
        Each time a collision occurs, a coordinate pair is added to Sparks.
      Paths:
        For the puspose of visualising progressive path modifications, Paths
        is a list of Shapes to be rendered in red-to-indigo rainbow colours.

    '''

    # -----------------------------------------------------------------------------

    # Shorthand

    A = Pt_Anchor
    C = Pt_Control
    B = (Pt_Break, None)

    # -----------------------------------------------------------------------------

    def Field(Name, Default):
        return Data[Name] if Name in Data else Default

    # -----------------------------------------------------------------------------

    IT = tIndentTracker('  ')

    Result = ''
    Title = Field('Title', '(Untitled)')

    Result += SVGStart(IT, Title, {
        'width': '28cm',
        'height': '19cm',
        'viewBox': '0 0 28 19'
    })

    Result += IT('<defs>')
    IT.StepIn()
    Result += IT(
        '<marker id="ArrowHead"',
        '    viewBox="0 0 10 10" refX="0" refY="5"',
        '    markerUnits="strokeWidth"',
        '    markerWidth="8" markerHeight="6"',
        '    orient="auto">'
        '  <path d="M 0,0  L 10,5  L 0,10"/>',
        '</marker>'
    )
    # Sparks are not used
    # Result += SparkSymbolDef(IT, 'spark')  if Sparks is not None else ''

    # More marker, symbol and gradient definitions can go here.
    IT.StepOut()
    Result += IT('</defs>')

    # Background

    Result += IT(
        '<rect x="0" y="0" width="28" height="19" stroke="none" fill="white"/>'
    )

    # Outer group

    Result += IT('<!-- Outer group -->')
    Result += SVGGroup(IT, {'stroke': 'black', 'stroke-width': '0.025'})

    # Grid group
    # Substitute for the group created by SVGGrid that is now deleted

    Result += SVGGroup(IT, {'stroke': 'black', 'stroke-width':'butt',
        'transform': 'matrix(1.5, 0, 0, 1.5, 5.5, 1.5)', 'fill': 'none'})

    Result += IT('<!-- Background -->')

    # Extract part of SVG with board
    board_data = open(Data['Map']['vector_graphics_file']).read().splitlines()
    board_data = board_data[board_data.index("<!-- Board -->")+1:]
    board_data = board_data[0:board_data.index("<!-- Board -->")]
    Result += IT("\n".join(board_data))

    # Iniial position
    StartPos = Field('StartPos', None)
    if StartPos is not None:
        S = [
            (A, (-1.0, -1.0)), (A, (-1.0, +1.0)),
            (A, (+1.0, +1.0)), (A, (+1.0, -1.0)), (A, (-1.0, -1.0))
        ]

        Result += IT('<!-- Initial position -->')
        Result += SVGPath(IT,
                          TransformedShape(AffineMtxTS(StartPos, 0.3), S),
                          {'stroke': '#09f', 'stroke-width': '0.1', 'stroke-linecap': 'square'}
                          )

    # Robot
    if draw_dynamic_elements:
        Result += IT('{0}')
    else:
        Result += ''

    # Goal position
    GoalPos = Field('GoalPos', None)
    if GoalPos is not None:

        GoalThreshold = Field('GoalThreshold', None)
        if GoalThreshold is not None:
            Result += IT('<!-- Goal threshold -->')
            Result += IT(
                '<circle cx="%g" cy="%g" r="%g"' %
                (GoalPos[0], GoalPos[1], GoalThreshold)
            )
            IT.StepIn(2)
            Result += IT(
                'fill="rgba(0, 255, 0, 0.1)" stroke="#0d0" stroke-width="0.02"/>'
            )
            IT.StepOut(2)

        S = (
            GreekCross(GoalPos, 0.4) + [B] +
            PiecewiseArc(GoalPos, 0.25, (0, 2.0 * pi), 8)
        )
        Result += IT('<!-- Goal position -->')
        Result += SVGPath(IT, S, {
            'stroke': '#0d0', 'stroke-width': '0.1', 'stroke-linecap': 'butt'
        })

    # Sparks
    if draw_dynamic_elements:
        Result += IT("{2}")
    else:
        Result += ''
    # if Sparks is not None and len(Sparks) > 0:
    #     Result += IT('<!-- Points of interests (sparks) -->')
    #     Result += SVGGroup(IT, {'transform': 'translate(-0.5, -0.5)'})
    #     for SparkPos in Sparks:
    #         Result += IT(
    #             ('<use x="%g" y="%g"' % (SparkPos[0], SparkPos[1])) +
    #             ' width="1" height="1" xlink:href="#spark"/>'
    #         )
    #     Result += SVGGroupEnd(IT)

    # Actual path
    if draw_dynamic_elements:
        Result += IT("{1}")
    else:
        Result += ''

    # End board group
    # This ends the newly added group substituting the SVGGrid group.
    # Moved here the SVGGroupEnd which was for some reason previously appended
    # to the Actual path element making the code look convoluted

    Result += SVGGroupEnd(IT)

    # Title and legend

    Result += IT('<!-- Title background -->')
    Result += IT(
        '<rect x="0" y="0" width="28" height="1.1" stroke="none" fill="white"/>'
    )

    Result += IT('<!-- Title group -->')
    Result += SVGGroup(IT, {
        'font-family': 'sans-serif',
        'font-size': '0.36',
        'font-weight': 'normal',
        'fill': 'black',
        'stroke': 'none'
    })

    Result += IT('<!-- Title -->')
    Result += SVGText(IT, (0.5, 0.82), Title, {
        'font-size': '0.72',
        'font-weight': 'bold'
    })

    Result += IT('<!-- Legend line labels-->')
    Result += SVGText(IT, (19.5, 0.82), 'Actual path')

    Result += IT('<!-- Legend lines -->')
    Result += SVGGroup(IT, {
        'fill': 'none',
        'stroke-width': '0.12',
        'stroke-linecap': 'round'
    })

    Result += SVGPath(IT,
                      [(Pt_Anchor, (18.5, 0.7)), (Pt_Anchor, (19.3, 0.7))],
                      {'stroke': '#40f'}
                      )

    Result += SVGGroupEnd(IT)

    # End of title group

    Result += SVGGroupEnd(IT)

    # End of outer group

    Result += SVGGroupEnd(IT)

    Result += SVGEnd(IT)

    return Result
def RenderAnimatedPart(Data):
    """ Renders list of strings that will be injected to frame """

    # -----------------------------------------------------------------------------

    def Field(Name, Default):
        return Data[Name] if Name in Data else Default

    # -----------------------------------------------------------------------------

    # TODO: string concatenation is slow
    robot_sprite = ""
    path = ""
    sparks = ""

    side = 0.5
    IT = tIndentTracker('  ')

    robot_sprite += SVGGroup(IT, {'transform': 'rotate(%g %g %g)' % (
        (Data['ActualOrientation'] * 180 / pi) + 180.0,
        Data['ActualPosition'][0], Data['ActualPosition'][1])
                                  })

    # robot_sprite += SVGGroup(IT, {'transform': 'scale(2)'})
    robot_sprite += IT('<polygon points="%g,%g %g,%g %g,%g" style="fill:lime;'
                       ''
                       'stroke:purple;stroke-width:0.06" />'
                       % (Data['ActualPosition'][0] + side / 2.0, Data['ActualPosition'][1] - side / 2.0,
                          Data['ActualPosition'][0] + side / 2.0, Data['ActualPosition'][1] + side / 2.0,
                          Data['ActualPosition'][0] - side, Data['ActualPosition'][1]
                          ))
    robot_sprite += SVGGroupEnd(IT)
    # robot_sprite += SVGGroupEnd(IT)


    ActualPath = Field('ActualPath', None)
    if ActualPath is not None:
        path += IT('<!-- Actual path -->')
        path += SVGPath(IT,
                        ShapeFromVertices(ActualPath, 1),
                        {'stroke': '#40f', 'stroke-width': '0.02'}
                        )


    # ??
    # Paths in rainbow colours
    Paths = Field('Paths', None)
    if Paths is not None and len(Paths) > 0:

        NumPaths = len(Paths)

        path += IT('<!-- Paths in rainbow colours -->')
        for PathIx, Path in enumerate(Paths):
            if NumPaths >= 2:
                Progress = float(PathIx) / float(NumPaths - 1)
            else:
                Progress = 1.0
            Opacity = 1.0 if Progress in [0.0, 1.0] else 0.60 + 0.00 * Progress
            ColourStr = ProgressColourStr(Progress, Opacity)
            path += IT('<!-- Path %d, (%.1f%%) -->' % (PathIx, 100.0 * Progress))
            path += SVGPath(IT, Path, {"stroke": ColourStr})

    Sparks = Field('Sparks', None)
    if Sparks is not None and len(Sparks) > 0:
        B = (Pt_Break, None)
        sparks += IT('<!-- Points of interests (sparks) -->')

        for SparkPos in Sparks:
            SparkPos = (SparkPos[0], SparkPos[1])
            S = (
                GreekCross(SparkPos, 0.4) + [B] +
                PiecewiseArc(SparkPos, 0.25, (0, 2.0 * pi), 8)
            )
            sparks += IT('<!-- Goal position -->')
            sparks += SVGPath(IT, S, {
                'stroke': '#d00', 'stroke-width': '0.1', 'stroke-linecap': 'butt'
            })
    # End of plot

    return [robot_sprite, path, sparks]
Esempio n. 5
0
def RenderToSVG(Data):

  '''Return data rendered to an SVG file in a string.

  Grid is a dictionary with the keys:

    Title: This title is rendered and is embedded in the SVG header.
    Grid: See SVGGrid().
    Paths: A list of Shapes to be rendered in red-to-indigo rainbow colours.
    BadPaths: A list of Shapes to be rendered in a faded colour.

  '''

  #-----------------------------------------------------------------------------

  def Field(Name, Default):
    return Data[Name] if Name in Data else Default

  #-----------------------------------------------------------------------------

  IT = tIndentTracker('  ')
  Result = ''

  Title = Field('Title', '(Untitled)')

  Result += SVGStart(IT, Title, {
    'width': '28cm',
    'height': '19cm',
    'viewBox': '0 0 28 19',
  })

  Result += IT('<defs>')
  IT.StepIn()
  Result += IT(
    '<marker id="ArrowHead"',
    '    viewBox="0 0 10 10" refX="0" refY="5"',
    '    markerUnits="strokeWidth"',
    '    markerWidth="16" markerHeight="12"',
    '    orient="auto">'
    '  <path d="M 0,0  L 10,5  L 0,10  z"/>',
    '</marker>'
  )
  # More marker, symbol and gradient definitions can go here.
  IT.StepOut()
  Result += IT('</defs>')

  # Background

  Result += IT(
    '<!-- Background -->',
    '<rect x="0" y="0" width="28" height="19" stroke="none" fill="white"/>'
  )

  # Outer group

  Result += IT('<!-- Outer group -->')
  Result += SVGGroup(IT, {'stroke': 'black', 'stroke-width': '0.025'})

  # Plots of both rejected and tentatively accepted paths

  Result += IT('<!-- Grid -->')
  Result += SVGGrid(IT, Data['Grid'])

  # Rejected paths

  BadPaths = Field('BadPaths', None)
  if BadPaths is not None:

    Result += IT('<!-- Rejected paths -->')
    Result += SVGGroup(IT, {
      'opacity': '0.10', 'stroke': '#ff0099'
    })

    NumBadPaths = len(BadPaths)

    for PathIx, Path in enumerate(BadPaths):
      Result += SVGPath(IT, Path)

    Result += SVGGroupEnd(IT)

  # Axes

  Result += IT('<!-- Axes -->')
  RangeMin = Data['Grid']['RangeMinima']
  RangeMax = Data['Grid']['RangeMaxima']
  Result += SVGGroup(IT, {
    'stroke': 'black',
    'stroke-width': '0.05',
    'stroke-linecap': 'square',
    'marker-end': 'url(#ArrowHead)'
  })
  Result += SVGPath(IT,
    [(Pt_Anchor, (RangeMin[0], 0.0)), (Pt_Anchor, (RangeMax[0] + 0.1, 0.0))]
  )
  Result += SVGPath(IT,
    [(Pt_Anchor, (0.0, RangeMin[1])), (Pt_Anchor, (0.0, RangeMax[1] + 0.1))]
  )
  Result += SVGGroupEnd(IT)

  # Paths in rainbow colours

  Paths = Field('Paths', None)
  if Paths is not None:

    NumPaths = len(Paths)

    Result += IT('<!-- Paths in rainbow colours -->')
    for PathIx, Path in enumerate(Paths):
      if NumPaths >= 2:
        Progress = float(PathIx) / float(NumPaths - 1)
      else:
        Progress = 1.0
      Opacity = 1.0 if Progress in [0.0, 1.0] else 0.60 + 0.00 * Progress
      ColourStr = ProgressColourStr(Progress, Opacity)
      Result += IT('<!-- Path %d, (%.1f%%) -->' % (PathIx, 100.0 * Progress))
      Result += SVGPath(IT, Path, {"stroke": ColourStr})

  # End of plot

  Result += SVGGroupEnd(IT)

  # Title and legend

  Result += IT('<!-- Title background -->')
  Result += IT(
    '<rect x="0" y="0" width="28" height="1.1" stroke="none" fill="white"/>'
  )

  Result += IT('<!-- Title group -->')
  Result += SVGGroup(IT, {
    'font-family': 'sans-serif',
    'font-size': '0.36',
    'font-weight': 'normal',
    'fill': 'black',
    'stroke': 'none'
  })

  Result += IT('<!-- Title -->')
  Result += SVGText(IT, (0.5, 0.82), Title, {
    'font-size': '0.72',
    'font-weight': 'bold'
  })

  Result += IT('<!-- Legend line labels-->')
  Result += SVGText(IT, (23.5, 0.82), 'Initial')
  Result += SVGText(IT, (26.0, 0.82), 'Final')

  Result += IT('<!-- Legend lines -->')
  Result += SVGGroup(IT, {
    'fill': 'none',
    'stroke-width': '0.1',
    'stroke-linecap': 'round'
  })

  Result += SVGPath(IT,
    [(Pt_Anchor, (22.5, 0.7)), (Pt_Anchor, (23.3, 0.7))],
    {'stroke': ProgressColourStr(0.0)}
  )

  Result += SVGPath(IT,
    [(Pt_Anchor, (25.0, 0.7)), (Pt_Anchor, (25.8, 0.7))],
    {'stroke': ProgressColourStr(1.0)}
  )

  Result += SVGGroupEnd(IT)

  # End of title group

  Result += SVGGroupEnd(IT)

  # End of outer group

  Result += SVGGroupEnd(IT)

  Result += SVGEnd(IT)

  return Result
def RenderFrameTemplate(Data, draw_dynamic_elements=True):

  '''Return Data rendered to an SVG file in a string.

  Data is a dictionary with the keys:

    Title:
      This title is rendered and is embedded in the SVG header.
    Grid:
      See SVGGrid().
    Map:
      The map is a 2D array of integers for hazards (1) and clear spaces (0).
    StartPos:
      The car starts here. The coordinates are units of map squares and the
      origin is in the centre of Map[0][0].
    GoalPos:
      Hopefully the car ends within GoalThreshold of here.
    GoalThreshold:
      The radius of the goal zone is measured in grid units.
    Sparks:
      Each time a collision occurs, a coordinate pair is added to Sparks.
    Paths:
      For the puspose of visualising progressive path modifications, Paths
      is a list of Shapes to be rendered in red-to-indigo rainbow colours.

  '''

  #-----------------------------------------------------------------------------

  # Shorthand

  A = Pt_Anchor
  C = Pt_Control
  B = (Pt_Break, None)

  #-----------------------------------------------------------------------------

  def Field(Name, Default):
    return Data[Name] if Name in Data else Default

  #-----------------------------------------------------------------------------



  IT = tIndentTracker('  ')

  Result = ''
  SparkSymbol = ''
  Sparks = Field('Sparks', None)
  Title = Field('Title', '(Untitled)')

  Result += SVGStart(IT, Title, {
    'width': '28cm',
    'height': '19cm',
    'viewBox': '0 0 28 19'
  })

  Result += IT('<defs>')
  IT.StepIn()
  Result += IT(
    '<marker id="ArrowHead"',
    '    viewBox="0 0 10 10" refX="0" refY="5"',
    '    markerUnits="strokeWidth"',
    '    markerWidth="8" markerHeight="6"',
    '    orient="auto">'
    '  <path d="M 0,0  L 10,5  L 0,10  z"/>',
    '</marker>'
  )
  Result += SparkSymbolDef(IT, 'spark') if Sparks is not None else ''
  # More marker, symbol and gradient definitions can go here.
  IT.StepOut()
  Result += IT('</defs>')

  # Background

  Result += IT(
    '<!-- Background -->',
    '<rect x="0" y="0" width="28" height="19" stroke="none" fill="white"/>'
  )

  # Outer group

  Result += IT('<!-- Outer group -->')
  Result += SVGGroup(IT, {'stroke': 'black', 'stroke-width': '0.025'})

  # Plot with grid

  Result += IT('<!-- Grid -->')
  Result += SVGGrid(IT, Data['Grid'])

  # Maze

  Map = Field('Map', None)
  if Map is not None:

    Result += IT('<!-- Hazards -->')
    Result += SVGGroup(IT, {
      'fill': '#a40',
      'stroke': 'black',
      'stroke-width': '0.01',
      'stroke-linecap': 'butt',
    })

    scale_color = 255.0/len(Map)

    arrow_colors = {MAP_SPECIAL_DIRECTION: '00F5F1', MAP_SPECIAL_OPTIMAL: 'F58F00'}
    for i in range(len(Map)):
      for j in range(len(Map[0])):
        if Map[i][j] == 1:
          #Result += IT('<circle cx="%g" cy="%g" r="0.495"/>\n' % (i, j))
          Result += IT('<rect x="%g" y="%g" width="1" height="1" stroke="none" fill="#B30000"/>\n'
                       %(i - 0.5, j - 0.5))
        elif type(Map[i][j]) is list:


            if Map[i][j][0] == MAP_SPECIAL_EUCLIDEAN_DISTANCE:
                Result += IT('<rect x="%g" y="%g" width="1" height="1" stroke="none" fill="rgb(%g,%g,%g)"/>\n'
                       %(i - 0.5, j - 0.5,
                    max(0, 255-int(scale_color*Map[i][j][1])),
                       max(0, 255-int(scale_color*Map[i][j][1])),
                        max(0, 255-int(scale_color*Map[i][j][1]))

                ))
            else:
                Result += SVGGroup(IT, {'transform': 'translate(%g, %g)' % (i, j)})
                Result += SVGGroup(IT, {'transform': 'scale(0.0005)'})
                Result += SVGGroup(IT, {'transform': 'rotate(%g, %g, %g)' % (45*Map[i][j][1] -270, i-0.5, i-0.5)})

                Result += IT('<polygon style="stroke:none; '
                             'fill:#%s;" points="100,600 100,-200  '
                             '500,200 500,-100  0,-600  -500,-100 -500,'
                             '200 -100,-200 -100,600 "/>'%(arrow_colors[Map[i][j][0]],))
                Result += SVGGroupEnd(IT)
                Result += SVGGroupEnd(IT)
                Result += SVGGroupEnd(IT)







    Result += SVGGroupEnd(IT)



  # Iniial position

  StartPos = Field('StartPos', None)
  if StartPos is not None:

    S = [
      (A, (-1.0, -1.0)), (A, (-1.0, +1.0)),
      (A, (+1.0, +1.0)), (A, (+1.0, -1.0)), (A, (-1.0, -1.0))
    ]

    Result += IT('<!-- Initial position -->')
    Result += SVGPath(IT,
      TransformedShape(AffineMtxTS(StartPos, 0.3), S),
      {'stroke': '#09f', 'stroke-width': '0.1', 'stroke-linecap': 'square'}
    )



  # Robot
  if draw_dynamic_elements:
    Result += '{0}'
  else:
    Result += ''

  #side = 0.2
  #Result += SVGGroup(IT, {'transform': 'rotate(%g %g %g)'%(
  #                                     (Data['ActualOrientation'] * 180 / pi) + 180.0,
  #    Data['ActualPosition'][0],  Data['ActualPosition'][1])
  #})
  #Result += IT('<polygon points="%g,%g %g,%g %g,%g" style="fill:lime;stroke:purple;stroke-width:0.01" />'
  #%(Data['ActualPosition'][0] + side/2.0, Data['ActualPosition'][1] - side/2.0,
  #Data['ActualPosition'][0] + side/2.0, Data['ActualPosition'][1] + side/2.0,
  #  Data['ActualPosition'][0] - side, Data['ActualPosition'][1]
  #))
  #Result += SVGGroupEnd(IT)



  # Goal position
  GoalPos = Field('GoalPos', None)
  if GoalPos is not None:

    GoalThreshold = Field('GoalThreshold', None)
    if GoalThreshold is not None:
      Result += IT('<!-- Goal threshold -->')
      Result += IT(
        '<circle cx="%g" cy="%g" r="%g"' %
          (GoalPos[0], GoalPos[1], GoalThreshold)
      )
      IT.StepIn(2)
      Result += IT(
        'fill="rgba(0, 255, 0, 0.1)" stroke="#0d0" stroke-width="0.02"/>'
      )
      IT.StepOut(2)

    S = (
      GreekCross(GoalPos, 0.4) + [B] +
      PiecewiseArc(GoalPos, 0.25, (0, 2.0 * pi), 8)
    )
    Result += IT('<!-- Goal position -->')
    Result += SVGPath(IT, S, {
      'stroke': '#0d0', 'stroke-width': '0.1', 'stroke-linecap': 'butt'
    })

  # Sparks

  if Sparks is not None and len(Sparks) > 0:
    Result += IT('<!-- Points of collision (sparks) -->')
    Result += SVGGroup(IT, {'transform': 'translate(-0.5, -0.5)'})
    for SparkPos in Sparks:
      Result += IT(
        ('<use x="%g" y="%g"' % (SparkPos[0], SparkPos[1])) +
        ' width="1" height="1" xlink:href="#spark"/>'
      )
    Result += SVGGroupEnd(IT)

  # Actual path
  if draw_dynamic_elements:
    Result += "{1}"
  else:
    Result += ''
  #
  #ActualPath = Field('ActualPath', None)
  #if ActualPath is not None:
  #  Result += IT('<!-- Actual path -->')
  #  Result += SVGPath(IT,
  #    ShapeFromVertices(ActualPath, 1),
  #    {'stroke': '#40f', 'stroke-width': '0.02'}
  #  )
  #
  #
  ## ??
  ## Paths in rainbow colours
  #Paths = Field('Paths', None)
  #if Paths is not None and len(Paths) > 0:
  #
  #  NumPaths = len(Paths)
  #
  #  Result += IT('<!-- Paths in rainbow colours -->')
  #  for PathIx, Path in enumerate(Paths):
  #    if NumPaths >= 2:
  #      Progress = float(PathIx) / float(NumPaths - 1)
  #    else:
  #      Progress = 1.0
  #    Opacity = 1.0 if Progress in [0.0, 1.0] else 0.60 + 0.00 * Progress
  #    ColourStr = ProgressColourStr(Progress, Opacity)
  #    Result += IT('<!-- Path %d, (%.1f%%) -->' % (PathIx, 100.0 * Progress))
  #    Result += SVGPath(IT, Path, {"stroke": ColourStr})
  #
  ## End of plot
  #
  #Result += SVGGroupEnd(IT)

  # Title and legend

  Result += IT('<!-- Title background -->')
  Result += IT(
    '<rect x="0" y="0" width="28" height="1.1" stroke="none" fill="white"/>'
  )

  Result += IT('<!-- Title group -->')
  Result += SVGGroup(IT, {
    'font-family': 'sans-serif',
    'font-size': '0.36',
    'font-weight': 'normal',
    'fill': 'black',
    'stroke': 'none'
  })

  Result += IT('<!-- Title -->')
  Result += SVGText(IT, (0.5, 0.82), Title, {
    'font-size': '0.72',
    'font-weight': 'bold'
  })

  Result += IT('<!-- Legend line labels-->')
  Result += SVGText(IT, (19.5, 0.82), 'Actual path')
  #Result += SVGText(IT, (22.6, 0.82), 'Estimated')
  #Result += SVGText(IT, (26.0, 0.82), 'Actual')

  Result += IT('<!-- Legend lines -->')
  Result += SVGGroup(IT, {
    'fill': 'none',
    'stroke-width': '0.12',
    'stroke-linecap': 'round'
  })

  Result += SVGPath(IT,
    [(Pt_Anchor, (18.5, 0.7)), (Pt_Anchor, (19.3, 0.7))],
    {'stroke': '#40f'}
  )
  #Result += SVGPath(IT,
  #  [(Pt_Anchor, (21.6, 0.7)), (Pt_Anchor, (22.4, 0.7))],
  #  {'stroke': '#f09', 'stroke-dasharray': '0.075 0.15'}
  #)
  #
  #Result += SVGPath(IT,
  #  [(Pt_Anchor, (25.0, 0.7)), (Pt_Anchor, (25.8, 0.7))],
  #  {'stroke': '#40f'}
  #)

  Result += SVGGroupEnd(IT)

  # End of title group

  Result += SVGGroupEnd(IT)

  # End of outer group

  Result += SVGGroupEnd(IT)

  Result += SVGEnd(IT)

  return Result
def RenderAnimatedPart(Data):
    """ Renders list of strings that will be injected to frame """

    #-----------------------------------------------------------------------------

    def Field(Name, Default):
        return Data[Name] if Name in Data else Default

    #-----------------------------------------------------------------------------


    robot_sprite = ""
    side = 0.5
    IT = tIndentTracker('  ')

    robot_sprite += SVGGroup(IT, {'transform': 'rotate(%g %g %g)'%(
                                         (Data['ActualOrientation'] * 180 / pi) + 180.0,
        Data['ActualPosition'][0],  Data['ActualPosition'][1])
    })
    #robot_sprite += SVGGroup(IT, {'transform': 'scale(2)'})
    robot_sprite += IT('<polygon points="%g,%g %g,%g %g,%g" style="fill:lime;'
                       ''
                       'stroke:purple;stroke-width:0.06" />'
    %(Data['ActualPosition'][0] + side/2.0, Data['ActualPosition'][1] - side/2.0,
    Data['ActualPosition'][0] + side/2.0, Data['ActualPosition'][1] + side/2.0,
      Data['ActualPosition'][0] - side, Data['ActualPosition'][1]
    ))
    robot_sprite += SVGGroupEnd(IT)
    #robot_sprite += SVGGroupEnd(IT)
    path = ""

    ActualPath = Field('ActualPath', None)
    if ActualPath is not None:
      path += IT('<!-- Actual path -->')
      path += SVGPath(IT,
        ShapeFromVertices(ActualPath, 1),
        {'stroke': '#40f', 'stroke-width': '0.02'}
      )


    # ??
    # Paths in rainbow colours
    Paths = Field('Paths', None)
    if Paths is not None and len(Paths) > 0:

      NumPaths = len(Paths)

      path += IT('<!-- Paths in rainbow colours -->')
      for PathIx, Path in enumerate(Paths):
        if NumPaths >= 2:
          Progress = float(PathIx) / float(NumPaths - 1)
        else:
          Progress = 1.0
        Opacity = 1.0 if Progress in [0.0, 1.0] else 0.60 + 0.00 * Progress
        ColourStr = ProgressColourStr(Progress, Opacity)
        path += IT('<!-- Path %d, (%.1f%%) -->' % (PathIx, 100.0 * Progress))
        path += SVGPath(IT, Path, {"stroke": ColourStr})

    # End of plot

    path += SVGGroupEnd(IT)

    return [robot_sprite, path]