/*******************************************************************************
* Copyright 2010 Simon Mieth
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package org.kabeja.svg.generators;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.kabeja.common.DraftEntity;
import org.kabeja.entities.Polyline;
import org.kabeja.entities.Vertex;
import org.kabeja.entities.util.PolylineSegment;
import org.kabeja.math.MathUtils;
import org.kabeja.math.Point3D;
import org.kabeja.math.TransformContext;
import org.kabeja.svg.SVGConstants;
import org.kabeja.svg.SVGPathBoundaryGenerator;
import org.kabeja.svg.SVGUtils;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
public class SVGPolylineGenerator extends AbstractSVGSAXGenerator
implements SVGPathBoundaryGenerator {
public void toSAX(ContentHandler handler, Map svgContext, DraftEntity entity,
TransformContext transformContext) throws SAXException {
// the polyline will emit as a svg:path
// note the dxf polyline has more
// option as the svg:polyline
Polyline pline = (Polyline) entity;
if (pline.getVertexCount() > 0) {
if (pline.is3DPolygonMesh()) {
meshToSAX(handler, svgContext, pline);
} else if (pline.isPolyfaceMesh()) {
polyfaceToSAX(handler, svgContext, pline);
} else if (pline.isCurveFitVerticesAdded()) {
// splineFitToSAX(handler, svgContext);
} else if (pline.isSplineFitVerticesAdded()) {
splineFitToSAX(handler, svgContext, pline);
} else if (pline.is3DPolygon()) {
splineFitToSAX(handler, svgContext, pline);
} else {
polylineToSAX(handler, svgContext, pline);
}
}
}
protected void polylineToSAX(ContentHandler handler, Map svgContext,
Polyline pline) throws SAXException {
AttributesImpl attr = new AttributesImpl();
if ((pline.getStartWidth() != pline.getEndWidth()) ||
!pline.isConstantWidth()) {
// handle the different width
polylinePartToSAX(handler, svgContext, pline);
} else {
StringBuffer d = new StringBuffer();
Vertex last;
Vertex first;
Iterator<Vertex> i = pline.getVertices().iterator();
first = last = i.next();
d.append("M ");
d.append(SVGUtils.formatNumberAttribute(last.getPoint().getX()));
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(SVGUtils.formatNumberAttribute(last.getPoint().getY()));
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
while (i.hasNext()) {
Vertex end = (Vertex) i.next();
d.append(getVertexPath(last, end, pline));
last = end;
}
// bit coded values
if (pline.isClosed()) {
if (last.getBulge() != 0) {
d.append(getVertexPath(last, first, pline));
}
d.append(" z");
}
SVGUtils.addAttribute(attr, "d", d.toString());
super.setCommonAttributes(attr, svgContext, pline);
if (pline.getStartWidth() > 0.0) {
SVGUtils.addAttribute(attr,
SVGConstants.SVG_ATTRIBUTE_STROKE_WITDH,
SVGUtils.formatNumberAttribute(pline.getStartWidth()));
}
SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr);
}
}
/*
* (non-Javadoc)
*
* @see de.miethxml.kabeja.dxf.helpers.HatchBoundaryElement#getSVGPath()
*/
public String getSVGPath(DraftEntity entity) {
// create the path
Polyline pline = (Polyline) entity;
StringBuffer d = new StringBuffer();
Vertex last;
Vertex first;
Iterator<Vertex> i = pline.getVertices().iterator();
first = last = i.next();
d.append("M ");
d.append(last.getPoint().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(last.getPoint().getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
while (i.hasNext()) {
Vertex end = (Vertex) i.next();
d.append(getVertexPath(last, end, pline));
last = end;
}
// bit coded values
if (pline.isClosed()) {
if (last.getBulge() != 0) {
d.append(getVertexPath(last, first, pline));
}
d.append(" z");
}
return d.toString();
}
protected void polylinePartToSAX(ContentHandler handler, Map svgContext,
Polyline pline) throws SAXException {
// output as group
AttributesImpl attr = new AttributesImpl();
super.setCommonAttributes(attr, svgContext, pline);
SVGUtils.startElement(handler, SVGConstants.SVG_GROUP, attr);
PolylineSegment segment = null;
// boolean bulged = false;
boolean process = true;
Vertex start = pline.getVertex(0);
Vertex end = pline.getVertex(1);
segment = new PolylineSegment(start, end, pline);
int i = 1;
while (i < pline.getVertexCount()) {
// we need the next vertex to get the right endpoints
PolylineSegment next = null;
if ((i + 1) < pline.getVertexCount()) {
process = false;
Vertex nextStart = end;
end = pline.getVertex(i + 1);
next = new PolylineSegment(nextStart, end, pline);
if (next.isBulged()) {
segment.setPoint3(next.getPoint2());
segment.setPoint4(next.getPoint1());
} else {
segment.connect(next);
}
}
StringBuffer d = new StringBuffer();
d.append("M ");
if (segment.isBulged()) {
// first the line from 1->2
d.append(segment.getPoint1().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint1().getY());
d.append(" L ");
d.append(segment.getPoint2().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint2().getY());
// next the arc from 2->3
double r = 0;
if (segment.getBulge() > 0) {
r = segment.getInnerRadius();
} else {
r = segment.getOuterRadius();
}
d.append(" A ");
d.append(SVGUtils.formatNumberAttribute(r));
d.append(' ');
d.append(SVGUtils.formatNumberAttribute(r));
//the x axis rotation
d.append(" 0 ");
if (Math.abs(segment.getBulgeHeight()) > Math.abs(
segment.getRadius())) {
// large Arc-flag
d.append(" 1 ");
} else {
d.append(" 0 ");
}
// if the bulge > 0 the center point is on the left side
// if the bulge < 0 the center point is ont the right side
if (segment.getBulge() > 0) {
// the sweep-flag
d.append(" 0 ");
} else {
d.append(" 1 ");
}
d.append(segment.getPoint3().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint3().getY());
// next the line from 3->4
d.append(" L ");
d.append(segment.getPoint4().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint4().getY());
// next the arc from 4->1
r = 0;
if (segment.getBulge() > 0) {
r = segment.getInnerRadius();
} else {
r = segment.getOuterRadius();
}
d.append(" A ");
d.append(SVGUtils.formatNumberAttribute(r));
d.append(' ');
d.append(SVGUtils.formatNumberAttribute(r));
//the x axis rotation
d.append(" 0 ");
if (Math.abs(segment.getBulgeHeight()) > Math.abs(
segment.getRadius())) {
// large Arc-flag
d.append(" 1 ");
} else {
d.append(" 0 ");
}
// if the bulge > 0 the center point is on the left side
// if the bulge < 0 the center point is ont the right side
if (segment.getBulge() > 0) {
// the sweep-flag
d.append(" 0 ");
} else {
d.append(" 1 ");
}
d.append(segment.getPoint1().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint1().getY());
// and finally close the path
d.append(" Z");
} else {
// ok output the trapezium
// from p1 -> p2 -> p3 -> p4 and close
d.append(segment.getPoint1().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint1().getY());
d.append(" L ");
d.append(segment.getPoint2().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint2().getY());
d.append(" L ");
d.append(segment.getPoint3().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint3().getY());
d.append(" L ");
d.append(segment.getPoint4().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(segment.getPoint4().getY());
d.append(" Z");
}
// output
attr = new AttributesImpl();
super.setCommonAttributes(attr, svgContext, pline);
if (pline.getDocument().getHeader().isFillMode()) {
SVGUtils.addAttribute(attr, "fill", "currentColor");
}
SVGUtils.addAttribute(attr, "d", d.toString());
// output now
SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr);
if (!process) {
segment = next;
}
i++;
}
SVGUtils.endElement(handler, SVGConstants.SVG_GROUP);
}
protected void splineFitToSAX(ContentHandler handler, Map svgContext,
Polyline pline) throws SAXException {
// TODO we will first use the approximation of the spline
// and later take e deeper look at SVG-bezier curves /DXF b-splines
StringBuffer d = new StringBuffer();
Iterator<Vertex> i = pline.getVertices().iterator();
Vertex last = i.next();
d.append("M " + last.getPoint().getX() + " " + last.getPoint().getY() + " ");
while (i.hasNext()) {
Vertex vertex = (Vertex) i.next();
if (vertex.is2DSplineApproximationVertex()) {
d.append("L ");
d.append(vertex.getPoint().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(vertex.getPoint().getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
}
AttributesImpl attr = new AttributesImpl();
super.setCommonAttributes(attr, svgContext, pline);
SVGUtils.addAttribute(attr, "d", d.toString());
// output now
SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr);
}
protected void singleEdgeToSAX(ContentHandler handler, Vertex start,
Vertex end, Map svgContext, Polyline pline)
throws SAXException {
AttributesImpl attr = new AttributesImpl();
super.setCommonAttributes(attr, svgContext, pline);
StringBuffer d = new StringBuffer();
d.append("M ");
d.append(start.getPoint().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(start.getPoint().getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(getVertexPath(start, end, pline));
SVGUtils.addAttribute(attr, "d", d.toString());
if (start.getStartWidth() > 0.0) {
SVGUtils.addAttribute(attr,
SVGConstants.SVG_ATTRIBUTE_STROKE_WITDH,
"" + start.getStartWidth());
}
SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr);
}
protected void polyfaceToSAX(ContentHandler handler, Map svgContext,
Polyline pline) throws SAXException {
StringBuffer buf = new StringBuffer();
for(Vertex v:pline.getVertices()){
if (v.isFaceRecord()) {
Vertex v1 = pline.getPolyFaceMeshVertex(v.getPolyFaceMeshVertex0());
Vertex v2 = pline.getPolyFaceMeshVertex(v.getPolyFaceMeshVertex1());
Vertex v3 = pline.getPolyFaceMeshVertex(v.getPolyFaceMeshVertex2());
Vertex v4 = pline.getPolyFaceMeshVertex(v.getPolyFaceMeshVertex3());
if (v.isPolyFaceEdge0Visible() &&
(v.getPolyFaceMeshVertex0() != 0)) {
addEdgeToPath(v1, v2, buf);
}
if (v.isPolyFaceEdge1Visible() &&
(v.getPolyFaceMeshVertex1() != 0)) {
addEdgeToPath(v2, v3, buf);
}
if (v.isPolyFaceEdge2Visible() &&
(v.getPolyFaceMeshVertex2() != 0)) {
addEdgeToPath(v3, v4, buf);
}
if (v.isPolyFaceEdge3Visible() &&
(v.getPolyFaceMeshVertex3() != 0)) {
addEdgeToPath(v4, v1, buf);
} else if ((v4 == null) && (v3 != null)) {
// triangle
addEdgeToPath(v3, v1, buf);
}
if (buf.length() > 0) {
AttributesImpl attr = new AttributesImpl();
SVGUtils.addAttribute(attr, "d", buf.toString());
super.setCommonAttributes(attr, svgContext,v);
// fillmode ????????
SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr);
buf.delete(0, buf.length());
}
}
}
// if (buf.length() > 0) {
// AttributesImpl attr = new AttributesImpl();
// SVGUtils.addAttribute(attr, "d", buf.toString());
// super.setCommonAttributes(attr, svgContext, pline);
// SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr);
// }
}
protected void addEdgeToPath(Vertex start, Vertex end,
StringBuffer buf) {
buf.append('M');
buf.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
buf.append(start.getPoint().getX());
buf.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
buf.append(start.getPoint().getY());
buf.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
if (end != null) {
buf.append('L');
buf.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
buf.append(end.getPoint().getX());
buf.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
buf.append(end.getPoint().getY());
buf.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
}
protected void meshToSAX(ContentHandler handler, Map svgContext,
Polyline pline) throws SAXException {
// TODO check first the points and put the output together
StringBuffer d = new StringBuffer();
if (pline.isSimpleMesh()) {
int rows = pline.getRows();
d = new StringBuffer();
Point3D[][] points = new Point3D[pline.getRows()][pline.getColumns()];
Iterator<Vertex> it = pline.getVertices().iterator();
// create a line for each row
for (int i = 0; i < pline.getRows(); i++) {
d.append("M ");
for (int x = 0; x < pline.getColumns(); x++) {
Vertex v = (Vertex) it.next();
points[i][x] = v.getPoint();
d.append(v.getPoint().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(v.getPoint().getY());
if (x < (pline.getColumns() - 1)) {
d.append(" L ");
}
}
if (pline.isClosedMeshNDirection()) {
d.append('L');
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[i][0].getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[i][0].getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
}
// create a line for each column
for (int i = 0; i < pline.getColumns(); i++) {
d.append(" M ");
for (int x = 0; x < pline.getRows(); x++) {
d.append(points[x][i].getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[x][i].getY());
if (x < (pline.getRows() - 1)) {
d.append(" L ");
}
}
if (pline.isClosedMeshMDirection()) {
d.append('L');
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[0][i].getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[0][i].getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
}
} else {
Point3D[][] points = new Point3D[pline.getSurefaceDensityRows()][pline.getSurefaceDensityColumns()];
List<Vertex> appVertices = new ArrayList<Vertex>();
for(Vertex v:pline.getVertices()){
if (v.isMeshApproximationVertex()) {
appVertices.add(v);
}
}
Iterator<Vertex> it = appVertices.iterator();
// create a line for each row
for (int i = 0; i < pline.getSurefaceDensityRows(); i++) {
d.append("M ");
for (int x = 0; x < pline.getSurefaceDensityColumns(); x++) {
Vertex v = (Vertex) it.next();
points[i][x] = v.getPoint();
d.append(v.getPoint().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(v.getPoint().getY());
if (x < (pline.getSurefaceDensityColumns() - 1)) {
d.append(" L ");
}
}
if (pline.isClosedMeshNDirection()) {
d.append('L');
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[i][0].getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[i][0].getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
}
// create a line for each column
for (int i = 0; i < pline.getSurefaceDensityColumns(); i++) {
d.append(" M ");
for (int x = 0; x < pline.getSurefaceDensityRows(); x++) {
d.append(points[x][i].getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[x][i].getY());
if (x < (pline.getSurefaceDensityRows() - 1)) {
d.append(" L ");
}
}
if (pline.isClosedMeshMDirection()) {
d.append('L');
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[0][i].getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(points[0][i].getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
}
}
AttributesImpl attr = new AttributesImpl();
SVGUtils.addAttribute(attr, "d", d.toString());
super.setCommonAttributes(attr, svgContext, pline);
SVGUtils.emptyElement(handler, SVGConstants.SVG_PATH, attr);
}
protected String getVertexPath(Vertex start, Vertex end,
Polyline pline) {
StringBuffer d = new StringBuffer();
if (start.getBulge() != 0) {
// from the DXF-Specs.
double l = MathUtils.distance(start.getPoint(), end.getPoint());
// do nothing if the points are the same
if (l > 0.0) {
double r = pline.getRadius(Math.abs(start.getBulge()), l);
double h = (start.getBulge() * l) / 2;
// converting to an elipse with the same rx=ry
d.append("A ");
d.append(SVGUtils.formatNumberAttribute(r));
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(SVGUtils.formatNumberAttribute(r));
d.append(" 0");
if (Math.abs(start.getBulge()) > 1.0) {
// large Arc-flag
d.append(" 1 ");
} else {
d.append(" 0 ");
}
// if the bulge > 0 the center point is on the left side
// if the bulge < 0 the center point is ont the right side
if (start.getBulge() < 0) {
// the sweep-flag
d.append(" 0 ");
} else {
d.append(" 1 ");
}
d.append(end.getPoint().getX());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(end.getPoint().getY());
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
} else {
d.append("L ");
d.append(SVGUtils.formatNumberAttribute(end.getPoint().getX()));
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
d.append(SVGUtils.formatNumberAttribute(end.getPoint().getY()));
d.append(SVGConstants.SVG_ATTRIBUTE_PATH_PLACEHOLDER);
}
return d.toString();
}
}