@Override
  public void redraw() {
    setupCSS(svgp);
    final StyleLibrary style = context.getStyleResult().getStyleLibrary();
    double dotsize = style.getLineWidth(StyleLibrary.PLOT);

    for (DBIDIter id = sample.getSample().iter(); id.valid(); id.advance()) {
      double[] v = proj.fastProjectDataToRenderSpace(rel.get(id));
      if (v[0] != v[0] || v[1] != v[1]) {
        continue; // NaN!
      }
      Element tooltip = makeTooltip(id, v[0], v[1], dotsize);
      SVGUtil.addCSSClass(tooltip, TOOLTIP_HIDDEN);

      // sensitive area.
      Element area = svgp.svgRect(v[0] - dotsize, v[1] - dotsize, 2 * dotsize, 2 * dotsize);
      SVGUtil.addCSSClass(area, TOOLTIP_AREA);

      EventTarget targ = (EventTarget) area;
      targ.addEventListener(SVGConstants.SVG_MOUSEOVER_EVENT_TYPE, hoverer, false);
      targ.addEventListener(SVGConstants.SVG_MOUSEOUT_EVENT_TYPE, hoverer, false);
      targ.addEventListener(SVGConstants.SVG_CLICK_EVENT_TYPE, hoverer, false);

      // NOTE: do not change the sequence in which these are inserted!
      layer.appendChild(area);
      layer.appendChild(tooltip);
    }
  }
 /**
  * Toggle the Tooltip of an element.
  *
  * @param elem Element
  * @param type Event type
  */
 protected void toggleTooltip(Element elem, String type) {
   String csscls = elem.getAttribute(SVGConstants.SVG_CLASS_ATTRIBUTE);
   if (SVGConstants.SVG_MOUSEOVER_EVENT_TYPE.equals(type)) {
     if (TOOLTIP_HIDDEN.equals(csscls)) {
       SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_VISIBLE);
     }
   } else if (SVGConstants.SVG_MOUSEOUT_EVENT_TYPE.equals(type)) {
     if (TOOLTIP_VISIBLE.equals(csscls)) {
       SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_HIDDEN);
     }
   } else if (SVGConstants.SVG_CLICK_EVENT_TYPE.equals(type)) {
     if (TOOLTIP_STICKY.equals(csscls)) {
       SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_HIDDEN);
     }
     if (TOOLTIP_HIDDEN.equals(csscls) || TOOLTIP_VISIBLE.equals(csscls)) {
       SVGUtil.setAtt(elem, SVGConstants.SVG_CLASS_ATTRIBUTE, TOOLTIP_STICKY);
     }
   }
 }
    @Override
    public void incrementalRedraw() {
      if (layer == null) {
        makeLayerElement();
        addCSSClasses();
      }

      // TODO make the number of digits configurable
      final String label = (epsilon > 0.0) ? FormatUtil.NF4.format(epsilon) : "";
      // compute absolute y-value of bar
      final double yAct = getYFromEpsilon(epsilon);

      if (elemText == null) {
        elemText = svgp.svgText(StyleLibrary.SCALE * 1.05, yAct, label);
        SVGUtil.setAtt(elemText, SVGConstants.SVG_CLASS_ATTRIBUTE, CSS_EPSILON);
        layer.appendChild(elemText);
      } else {
        elemText.setTextContent(label);
        SVGUtil.setAtt(elemText, SVGConstants.SVG_Y_ATTRIBUTE, yAct);
      }

      // line and handle
      if (elementLine == null) {
        elementLine = svgp.svgLine(0, yAct, StyleLibrary.SCALE * 1.04, yAct);
        SVGUtil.addCSSClass(elementLine, CSS_LINE);
        layer.appendChild(elementLine);
      } else {
        SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y1_ATTRIBUTE, yAct);
        SVGUtil.setAtt(elementLine, SVG12Constants.SVG_Y2_ATTRIBUTE, yAct);
      }
      if (elementPoint == null) {
        elementPoint = svgp.svgCircle(StyleLibrary.SCALE * 1.04, yAct, StyleLibrary.SCALE * 0.004);
        SVGUtil.addCSSClass(elementPoint, CSS_LINE);
        layer.appendChild(elementPoint);
      } else {
        SVGUtil.setAtt(elementPoint, SVG12Constants.SVG_CY_ATTRIBUTE, yAct);
      }

      if (eventarea == null) {
        eventarea =
            new DragableArea(
                svgp,
                StyleLibrary.SCALE,
                -StyleLibrary.SCALE * 0.01, //
                StyleLibrary.SCALE * 0.1,
                plotheight + StyleLibrary.SCALE * 0.02,
                this);
        layer.appendChild(eventarea.getElement());
      }
    }
    @Override
    public void fullRedraw() {
      setupCanvas();
      final StyleLibrary style = context.getStyleLibrary();
      CSSClass css = new CSSClass(svgp, POLYS);
      // TODO: separate fill and line colors?
      css.setStatement(
          SVGConstants.CSS_STROKE_WIDTH_PROPERTY, style.getLineWidth(StyleLibrary.POLYGONS));
      css.setStatement(SVGConstants.CSS_STROKE_PROPERTY, style.getColor(StyleLibrary.POLYGONS));
      css.setStatement(SVGConstants.CSS_FILL_PROPERTY, SVGConstants.CSS_NONE_VALUE);
      svgp.addCSSClassOrLogError(css);
      svgp.updateStyleElement();

      // draw data
      for (DBIDIter iditer = rep.iterDBIDs(); iditer.valid(); iditer.advance()) {
        try {
          PolygonsObject poly = rep.get(iditer);
          if (poly == null) {
            continue;
          }
          SVGPath path = new SVGPath();
          for (Polygon ppoly : poly.getPolygons()) {
            Vector first = ppoly.get(0);
            double[] f = proj.fastProjectDataToRenderSpace(first.getArrayRef());
            path.moveTo(f[0], f[1]);
            for (ArrayListIter<Vector> it = ppoly.iter(); it.valid(); it.advance()) {
              if (it.getOffset() == 0) {
                continue;
              }
              double[] p = proj.fastProjectDataToRenderSpace(it.get().getArrayRef());
              path.drawTo(p[0], p[1]);
            }
            // close path.
            path.drawTo(f[0], f[1]);
          }
          Element e = path.makeElement(svgp);
          SVGUtil.addCSSClass(e, POLYS);
          layer.appendChild(e);
        } catch (ObjectNotFoundException e) {
          // ignore.
        }
      }
    }