/* * This file is part of LaTeXDraw. * Copyright (c) 2005-2017 Arnaud BLOUIN * LaTeXDraw is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later version. * LaTeXDraw is distributed without any warranty; without even the implied * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. */ package net.sf.latexdraw.view.svg; import java.awt.geom.Point2D; import java.util.List; import net.sf.latexdraw.badaboom.BadaboomCollector; import net.sf.latexdraw.models.ShapeFactory; import net.sf.latexdraw.models.interfaces.shape.FreeHandStyle; import net.sf.latexdraw.models.interfaces.shape.IFreehand; import net.sf.latexdraw.models.interfaces.shape.IPoint; import net.sf.latexdraw.parsers.svg.SVGAttributes; import net.sf.latexdraw.parsers.svg.SVGDocument; import net.sf.latexdraw.parsers.svg.SVGElement; import net.sf.latexdraw.parsers.svg.SVGGElement; import net.sf.latexdraw.parsers.svg.SVGPathElement; import net.sf.latexdraw.parsers.svg.parsers.SVGPointsParser; import net.sf.latexdraw.parsers.svg.path.SVGPathSegClosePath; import net.sf.latexdraw.parsers.svg.path.SVGPathSegCurvetoCubic; import net.sf.latexdraw.parsers.svg.path.SVGPathSegLineto; import net.sf.latexdraw.parsers.svg.path.SVGPathSegList; import net.sf.latexdraw.parsers.svg.path.SVGPathSegMoveto; import net.sf.latexdraw.util.LNamespace; /** * An SVG generator for a free hand drawing. * @author Arnaud BLOUIN */ class LFreeHandSVGGenerator extends LShapeSVGGenerator<IFreehand> { protected LFreeHandSVGGenerator(final IFreehand fh) { super(fh); } protected LFreeHandSVGGenerator(final SVGGElement elt, final boolean withTransformation) { this(ShapeFactory.INST.createFreeHand()); final SVGElement elt2 = getLaTeXDrawElement(elt, null); if(elt==null || elt2==null || !(elt2 instanceof SVGPathElement)) throw new IllegalArgumentException(); final SVGPathElement main = (SVGPathElement)elt2; String v = elt.getAttribute(LNamespace.LATEXDRAW_NAMESPACE+':'+LNamespace.XML_INTERVAL); if(v!=null) try{ shape.setInterval(Double.valueOf(v).intValue()); } catch(final NumberFormatException ex) { BadaboomCollector.INSTANCE.add(ex); } final List<Point2D> pts = SVGPointsParser.getPoints(elt.getAttribute(LNamespace.LATEXDRAW_NAMESPACE+':'+LNamespace.XML_POINTS)); if(pts==null) throw new IllegalArgumentException(); for(final Point2D pt : pts) shape.addPoint(ShapeFactory.INST.createPoint(pt.getX(), pt.getY())); setSVGLatexdrawParameters(elt); setSVGParameters(main); setSVGShadowParameters(getLaTeXDrawElement(elt, LNamespace.XML_TYPE_SHADOW)); v = elt.getAttribute(LNamespace.LATEXDRAW_NAMESPACE+':'+LNamespace.XML_ROTATION); if(v!=null) try{ shape.setRotationAngle(Double.valueOf(v)); } catch(final NumberFormatException ex) { BadaboomCollector.INSTANCE.add(ex); } try{ FreeHandStyle type = FreeHandStyle.getType(elt.getAttribute(LNamespace.LATEXDRAW_NAMESPACE+':'+LNamespace.XML_PATH_TYPE)); if(type==null) { final int val = Double.valueOf(elt.getAttribute(LNamespace.LATEXDRAW_NAMESPACE+':'+LNamespace.XML_PATH_TYPE)).intValue(); type = val==0 ? FreeHandStyle.LINES : FreeHandStyle.CURVES; } shape.setType(type); } catch(final NumberFormatException ex) { BadaboomCollector.INSTANCE.add(ex); } if(withTransformation) applyTransformations(elt); } /** * Fills the given SVG path with elements corresponding to the Freehand curved path. * @since 3.0 */ protected void getPathCurves(final SVGPathSegList path) { double prevx = shape.getPtAt(-1).getX(); double prevy = shape.getPtAt(-1).getY(); double curx = shape.getPtAt(0).getX(); double cury = shape.getPtAt(0).getY(); double midx = (curx + prevx) / 2.; double midy = (cury + prevy) / 2.; int i; final int size = shape.getNbPoints(); final int interval = shape.getInterval(); path.add(new SVGPathSegMoveto(curx, cury, false)); if(size>interval) { prevx = curx; prevy = cury; curx = shape.getPtAt(interval).getX(); cury = shape.getPtAt(interval).getY(); midx = (curx + prevx) / 2.; midy = (cury + prevy) / 2.; path.add(new SVGPathSegLineto(midx, midy, false)); } for(i=interval*2; i<size; i+=interval) { final double x1 = (midx + curx) / 2.; final double y1 = (midy + cury) / 2.; prevx = curx; prevy = cury; curx = shape.getPtAt(i).getX(); cury = shape.getPtAt(i).getY(); midx = (curx + prevx) / 2.; midy = (cury + prevy) / 2.; final double x2 = (prevx + midx) / 2.; final double y2 = (prevy + midy) / 2.; path.add(new SVGPathSegCurvetoCubic(midx, midy, x1, y1, x2, y2, false)); } if(i-interval+1<size) { final double x1 = (midx + curx) / 2.; final double y1 = (midy + cury) / 2.; prevx = curx; prevy = cury; curx = shape.getPtAt(-1).getX(); cury = shape.getPtAt(-1).getY(); midx = (curx + prevx) / 2.; midy = (cury + prevy) / 2.; final double x2 = (prevx + midx) / 2.; final double y2 = (prevy + midy) / 2.; path.add(new SVGPathSegCurvetoCubic(shape.getPtAt(-1).getX(), shape.getPtAt(-1).getY(), x1, y1, x2, y2, false)); } } /** * Fills the given SVG path with elements corresponding to the Freehand lined path. * @since 3.0 */ protected void getPathLines(final SVGPathSegList path) { final IPoint p = shape.getPtAt(0); int i; final int size = shape.getNbPoints(); final int interval = shape.getInterval(); path.add(new SVGPathSegMoveto(p.getX(), p.getY(), false)); for(i=interval; i<size; i+=interval) path.add(new SVGPathSegLineto(shape.getPtAt(i).getX(), shape.getPtAt(i).getY(), false)); if(i-interval<size) path.add(new SVGPathSegLineto(shape.getPtAt(-1).getX(), shape.getPtAt(-1).getY(), false)); } /** * @return The path of the shape. * @since 2.0.0 */ public SVGPathSegList getPath() { final SVGPathSegList path = new SVGPathSegList(); switch(shape.getType()) { case CURVES: getPathCurves(path); break; case LINES: getPathLines(path); break; } if(!shape.isOpen()) path.add(new SVGPathSegClosePath()); return path; } @Override public SVGElement toSVG(final SVGDocument doc) { if(doc==null) return null; final SVGElement root = new SVGGElement(doc); SVGElement elt; root.setAttribute(LNamespace.LATEXDRAW_NAMESPACE + ':' + LNamespace.XML_TYPE, LNamespace.XML_TYPE_FREEHAND); root.setAttribute(SVGAttributes.SVG_ID, getSVGID()); root.setAttribute(LNamespace.LATEXDRAW_NAMESPACE + ':' + LNamespace.XML_PATH_TYPE, String.valueOf(shape.getType())); root.setAttribute(LNamespace.LATEXDRAW_NAMESPACE + ':' + LNamespace.XML_INTERVAL, String.valueOf(shape.getInterval())); final String path = getPath().toString(); final StringBuilder pts = new StringBuilder(); for(int i = 0, size = shape.getNbPoints(); i < size; i++) pts.append(shape.getPtAt(i).getX()).append(' ').append(shape.getPtAt(i).getY()).append(' '); root.setAttribute(LNamespace.LATEXDRAW_NAMESPACE + ':' + LNamespace.XML_POINTS, pts.toString()); if(shape.hasShadow()) { final SVGElement shad = new SVGPathElement(doc); shad.setAttribute(SVGAttributes.SVG_D, path); setSVGShadowAttributes(shad, false); root.appendChild(shad); } if(shape.hasShadow() && shape.isFilled())// && shape.getLineStyle()!=LineStyle.NONE ) {// The background of the borders must be filled is there is a shadow. elt = new SVGPathElement(doc); elt.setAttribute(SVGAttributes.SVG_D, path); setSVGBorderBackground(elt, root); } elt = new SVGPathElement(doc); elt.setAttribute(SVGAttributes.SVG_D, path); root.appendChild(elt); setSVGAttributes(doc, elt, false); elt.setAttribute(LNamespace.LATEXDRAW_NAMESPACE + ':' + LNamespace.XML_ROTATION, String.valueOf(shape.getRotationAngle())); return root; } }