/* * 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 net.sf.latexdraw.models.ShapeFactory; import net.sf.latexdraw.models.interfaces.shape.Color; import net.sf.latexdraw.models.interfaces.shape.IArrow; import net.sf.latexdraw.models.interfaces.shape.IBezierCurve; import net.sf.latexdraw.models.interfaces.shape.IPoint; import net.sf.latexdraw.models.interfaces.shape.IShape; import net.sf.latexdraw.parsers.svg.SVGAttributes; import net.sf.latexdraw.parsers.svg.SVGDefsElement; 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.path.CtrlPointsSeg; import net.sf.latexdraw.parsers.svg.path.SVGPathSegClosePath; import net.sf.latexdraw.parsers.svg.path.SVGPathSegCurvetoCubic; import net.sf.latexdraw.parsers.svg.path.SVGPathSegList; import net.sf.latexdraw.parsers.svg.path.SVGPathSegMoveto; import net.sf.latexdraw.util.LNamespace; import net.sf.latexdraw.view.pst.PSTricksConstants; /** * An SVG generator for a Bézier curve. * @author Arnaud BLOUIN */ class LBezierCurveSVGGenerator extends LShapeSVGGenerator<IBezierCurve> { /** * Creates a generator of SVG bezier curve. * @param bc The bezier curve used for the generation. * @throws IllegalArgumentException If bc is null. * @since 2.0 */ protected LBezierCurveSVGGenerator(final IBezierCurve bc){ super(bc); } /** * Creates a Bézier curve from a latexdraw-SVG element. * @param elt The source element. * @since 2.0.0 */ protected LBezierCurveSVGGenerator(final SVGGElement elt, final boolean withTransformation) { this(ShapeFactory.INST.createBezierCurve()); final SVGElement elt2 = getLaTeXDrawElement(elt, null); if(elt==null || !(elt2 instanceof SVGPathElement)) throw new IllegalArgumentException(); final SVGPathElement main = (SVGPathElement)elt2; setPath(main.getSegList()); setSVGParameters(main); setSVGShadowParameters(getLaTeXDrawElement(elt, LNamespace.XML_TYPE_SHADOW)); setSVGDbleBordersParameters(getLaTeXDrawElement(elt, LNamespace.XML_TYPE_DBLE_BORDERS)); final IArrow arrow1 = shape.getArrowAt(0); final IArrow arrow2 = shape.getArrowAt(-1); setSVGArrow(arrow1, main.getAttribute(main.getUsablePrefix()+SVGAttributes.SVG_MARKER_START), main, SVGAttributes.SVG_MARKER_START); setSVGArrow(arrow2, main.getAttribute(main.getUsablePrefix()+SVGAttributes.SVG_MARKER_END), main, SVGAttributes.SVG_MARKER_END); homogeniseArrows(arrow1, arrow2); shape.setShowPts(getLaTeXDrawElement(elt, LNamespace.XML_TYPE_SHOW_PTS)!=null); if(withTransformation) applyTransformations(elt); } protected LBezierCurveSVGGenerator(final SVGPathElement path) { this(ShapeFactory.INST.createBezierCurve()); setPath(path.getSegList()); setSVGParameters(path); applyTransformations(path); } /** * Sets the shape path according to the given SVG path segments. * @param list The SVG path segments list * @since 2.0.0 */ public void setPath(final SVGPathSegList list) { if(list==null || list.size()<2 || !(list.get(0) instanceof SVGPathSegMoveto)) throw new IllegalArgumentException(); final SVGPathSegMoveto m = (SVGPathSegMoveto)list.get(0); CtrlPointsSeg c; int i=1; final int size = list.size(); Point2D pt = new Point2D.Double();// Creating a point to support when the first path element is relative. pt = m.getPoint(pt); shape.addPoint(ShapeFactory.INST.createPoint(pt)); if(size>1 && list.get(1) instanceof CtrlPointsSeg) {// We set the control point of the first point. c = (CtrlPointsSeg)list.get(1); shape.getFirstCtrlPtAt(-1).setPoint(ShapeFactory.INST.createPoint(c.getCtrl1(pt))); } while(i<size && list.get(i) instanceof CtrlPointsSeg) { c = (CtrlPointsSeg)list.get(i); Point2D currPt = c.getPoint(pt); shape.addPoint(ShapeFactory.INST.createPoint(currPt)); shape.getFirstCtrlPtAt(-1).setPoint(ShapeFactory.INST.createPoint(c.getCtrl2(pt))); pt = currPt; i++; } if(shape.getNbPoints()>2 && shape.getPtAt(-1).equals(shape.getPtAt(0), 0.00001)) {// We set the shape as closed shape.removePoint(-1); shape.setIsClosed(true); } else if(i<size && list.get(i) instanceof SVGPathSegClosePath)// There is something else at the end of the path. shape.setIsClosed(true); else shape.setIsClosed(false); shape.updateSecondControlPoints(); } /** * @return The SVG segment path list of the current Bézier curve. * @since 2.0.0 */ protected SVGPathSegList getPathSegList() { if(shape.getNbPoints()<2) return null; final int size = shape.getNbPoints(); int i; final SVGPathSegList path = new SVGPathSegList(); path.add(new SVGPathSegMoveto(shape.getPtAt(0).getX(), shape.getPtAt(0).getY(), false)); path.add(new SVGPathSegCurvetoCubic(shape.getPtAt(1).getX(), shape.getPtAt(1).getY(), shape.getFirstCtrlPtAt(0).getX(), shape.getFirstCtrlPtAt(0).getY(), shape.getFirstCtrlPtAt(1).getX(), shape.getFirstCtrlPtAt(1).getY(), false)); for(i=2; i<size; i++) path.add(new SVGPathSegCurvetoCubic(shape.getPtAt(i).getX(), shape.getPtAt(i).getY(), shape.getSecondCtrlPtAt(i-1).getX(), shape.getSecondCtrlPtAt(i-1).getY(), shape.getFirstCtrlPtAt(i).getX(), shape.getFirstCtrlPtAt(i).getY(), false)); if(shape.isClosed()) { final IPoint ctrl1b = shape.getFirstCtrlPtAt(0).centralSymmetry(shape.getPtAt(0)); final IPoint ctrl2b = shape.getFirstCtrlPtAt(-1).centralSymmetry(shape.getPtAt(-1)); path.add(new SVGPathSegCurvetoCubic(shape.getPtAt(0).getX(), shape.getPtAt(0).getY(), ctrl2b.getX(), ctrl2b.getY(), ctrl1b.getX(), ctrl1b.getY(), false)); path.add(new SVGPathSegClosePath()); } return path; } @Override public SVGElement toSVG(final SVGDocument doc) { if(doc==null || doc.getFirstChild().getDefs()==null) return null; final SVGDefsElement defs = doc.getFirstChild().getDefs(); final SVGElement root = new SVGGElement(doc); SVGElement elt; final String path = getPathSegList().toString(); root.setAttribute(LNamespace.LATEXDRAW_NAMESPACE+':'+LNamespace.XML_TYPE, LNamespace.XML_TYPE_BEZIER_CURVE); root.setAttribute(SVGAttributes.SVG_ID, getSVGID()); if(shape.hasShadow()) { final SVGElement shad = new SVGPathElement(doc); shad.setAttribute(SVGAttributes.SVG_D, path); setSVGShadowAttributes(shad, false); root.appendChild(shad); if(!shape.isClosed()) { setSVGArrow(shape, shad, 0, true, doc, defs); setSVGArrow(shape, shad, 1, true, doc, defs); } } if(shape.hasShadow() && !shape.getLineStyle().getLatexToken().equals(PSTricksConstants.LINE_NONE_STYLE) && shape.isFilled()) { // 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); if(shape.hasDbleBord()) { final SVGElement dblBord = new SVGPathElement(doc); dblBord.setAttribute(SVGAttributes.SVG_D, path); setSVGDoubleBordersAttributes(dblBord); root.appendChild(dblBord); } setSVGAttributes(doc, elt, false); elt.setAttribute(LNamespace.LATEXDRAW_NAMESPACE +':'+ LNamespace.XML_ROTATION, String.valueOf(shape.getRotationAngle())); if(!shape.isClosed()) { setSVGArrow(shape, elt, 0, false, doc, defs); setSVGArrow(shape, elt, 1, false, doc, defs); } if(shape.isShowPts()) root.appendChild(getShowPointsElement(doc)); return root; } /** * Creates an SVG g element that contains the 'show points' plotting. * @param doc The owner document. * @return The created g element or null if the shape has not the 'show points' option activated. * @since 2.0.0 */ protected SVGGElement getShowPointsElement(final SVGDocument doc) { if(!shape.isShowPts() || doc==null) return null; final double blackDash = shape.getDashSepBlack(); final double whiteDash = shape.getDashSepWhite(); final boolean hasDble = shape.hasDbleBord(); final Color col = shape.getLineColour(); final boolean isClosed = shape.isClosed(); final SVGGElement showPts = new SVGGElement(doc); final IArrow arrow1 = shape.getArrowAt(0); final IArrow arrow2 = shape.getArrowAt(-1); final double doubleSep = shape.getDbleBordSep(); final double thick = (hasDble ? shape.getDbleBordSep()+shape.getThickness()*2. : shape.getThickness())/2.; final double rad = (PSTricksConstants.DEFAULT_ARROW_DOTSIZE_DIM*IShape.PPC + PSTricksConstants.DEFAULT_ARROW_DOTSIZE_NUM*thick*2.)/2.; int i; final int size = shape.getNbPoints(); showPts.setAttribute(LNamespace.LATEXDRAW_NAMESPACE + ':' + LNamespace.XML_TYPE, LNamespace.XML_TYPE_SHOW_PTS); /* Plotting the lines. */ for(i=3; i<size; i+=2) { showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getPtAt(i-1), shape.getSecondCtrlPtAt(i-1), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getSecondCtrlPtAt(i-1), shape.getFirstCtrlPtAt(i), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getFirstCtrlPtAt(i), shape.getPtAt(i), blackDash, whiteDash, hasDble, 1., doubleSep)); } for(i=2; i<size; i+=2) { showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getPtAt(i-1), shape.getSecondCtrlPtAt(i-1), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getSecondCtrlPtAt(i-1), shape.getFirstCtrlPtAt(i), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getFirstCtrlPtAt(i), shape.getPtAt(i), blackDash, whiteDash, hasDble, 1., doubleSep)); } if(isClosed) { showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getPtAt(-1), shape.getSecondCtrlPtAt(-1), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getSecondCtrlPtAt(-1), shape.getSecondCtrlPtAt(0), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getSecondCtrlPtAt(0), shape.getPtAt(0), blackDash, whiteDash, hasDble, 1., doubleSep)); } showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getPtAt(0), shape.getFirstCtrlPtAt(0), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getFirstCtrlPtAt(0), shape.getFirstCtrlPtAt(1), blackDash, whiteDash, hasDble, 1., doubleSep)); showPts.appendChild(getShowPointsLine(doc, thick, col, shape.getFirstCtrlPtAt(1), shape.getPtAt(1), blackDash, whiteDash, hasDble, 1., doubleSep)); // Plotting the dots. if(!arrow1.hasStyle() || isClosed) showPts.appendChild(LShapeSVGGenerator.getShowPointsDot(doc, rad, shape.getPtAt(0), col)); if(!arrow2.hasStyle() || isClosed) showPts.appendChild(LShapeSVGGenerator.getShowPointsDot(doc, rad, shape.getPtAt(-1), col)); for(i=1; i<size-1; i++) { showPts.appendChild(LShapeSVGGenerator.getShowPointsDot(doc, rad, shape.getPtAt(i), col)); showPts.appendChild(LShapeSVGGenerator.getShowPointsDot(doc, rad, shape.getSecondCtrlPtAt(i), col)); } for(i=0; i<size; i++) showPts.appendChild(LShapeSVGGenerator.getShowPointsDot(doc, rad, shape.getFirstCtrlPtAt(i), col)); if(isClosed) { showPts.appendChild(LShapeSVGGenerator.getShowPointsDot(doc, rad, shape.getSecondCtrlPtAt(-1), col)); showPts.appendChild(LShapeSVGGenerator.getShowPointsDot(doc, rad, shape.getSecondCtrlPtAt(0), col)); } return showPts; } }