Bug 13022 fixed: Vectorial export did not clip large segments
[scilab.git] / scilab / modules / scirenderer / src / org / scilab / forge / scirenderer / implementation / g2d / motor / PolyLine.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2013 - Scilab Enterprises - Calixte Denizet
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  */
11
12 package org.scilab.forge.scirenderer.implementation.g2d.motor;
13
14 import java.awt.Color;
15 import java.awt.Shape;
16 import java.awt.Stroke;
17 import java.awt.geom.Line2D;
18 import java.awt.geom.Path2D;
19 import java.awt.Graphics2D;
20 import java.util.ArrayList;
21 import java.util.List;
22
23 import org.scilab.forge.scirenderer.tranformations.Vector3d;
24 import org.scilab.forge.scirenderer.tranformations.Vector4d;
25
26 /**
27  * Class to represent a polyline object. This is used only in 2D (in 3D, a line could begin
28  * on front of a face and could end behind it).
29  * This allows to draw a polyline like a polyline ! and not as a set of segments.
30  * Notice that a PolyLine is NOT a convex object... but in 2D it does not matter, algorithms
31  * which use the convexity are not applyed.
32  *
33  * @author Calixte DENIZET
34  */
35 public class PolyLine extends ConvexObject {
36
37     private boolean monochromatic;
38     private G2DStroke stroke;
39
40     /**
41      * Default constructor
42      * @param vertices the polyline vertices
43      * @param colors the vertices color
44      * @param stroke the stroke to used
45      */
46     public PolyLine(Vector3d[] vertices, Color[] colors, G2DStroke stroke) throws InvalidPolygonException {
47         super(vertices, colors);
48         if (vertices.length <= 1) {
49             throw new InvalidPolygonException("A polyline cannot have one or zero point");
50         }
51
52         this.monochromatic = isMonochromatic(colors);
53         this.stroke = stroke;
54     }
55
56     /**
57      * Get a set of polylines. The Nan of Inf vectors are removed and so the polyline is splitted.
58      * @param vertices the polyline vertices
59      * @param colors the vertices color
60      * @param stroke the stroke to used
61      * @param loop if true a looping polyline is created
62      * @return a set of polylines
63      */
64     public static List<PolyLine> getPolyLines(Vector3d[] vertices, Color[] colors, G2DStroke stroke, boolean loop) {
65         if (loop) {
66             Vector3d[] v = new Vector3d[vertices.length + 1];
67             Color[] c = new Color[vertices.length + 1];
68             for (int i = 0; i < vertices.length; i++) {
69                 v[i] = vertices[i];
70                 c[i] = colors[i];
71             }
72             v[vertices.length] = v[0];
73             c[vertices.length] = c[0];
74             vertices = v;
75             colors = c;
76         }
77
78         int pos = 0;
79         List<PolyLine> list = new ArrayList<PolyLine>(1);
80         while ((pos = trimLeft(vertices, pos)) != -1) {
81             final int second = findNanOrInf(vertices, pos + 1);
82             final int len = second - pos;
83             final Vector3d[] newVertices = new Vector3d[len];
84             final Color[] newColors = new Color[len];
85             for (int i = 0; i < len; i++) {
86                 newVertices[i] = vertices[pos + i];
87                 newColors[i] = colors[pos + i];
88             }
89             pos = second + 1;
90             try {
91                 list.add(new PolyLine(newVertices, newColors, stroke));
92             } catch (InvalidPolygonException e) { }
93         }
94
95         return list;
96     }
97
98     /**
99      * {@inheritDoc}
100      */
101     public List<ConvexObject> breakObject(Vector4d v) {
102         final double[] vv = v.getData();
103         final List<ConvexObject> list = new ArrayList<ConvexObject>(1);
104
105         // Since PolyLine are only used in 2D it is useless to check when z != 0
106         if (vv[2] == 0) {
107             final Vector3d np = new Vector3d(vv);
108             makeClip(vv);
109
110             int pos = 0;
111             boolean prev = false;
112
113             for (int i = 0; i < vertices.length; i++) {
114                 final boolean b = isBehind(vertices[i], np, vv[3]);
115                 if (b && !prev) {
116                     pos = i;
117                     prev = true;
118                 } else if (!b && prev) {
119                     prev = false;
120                     try {
121                         list.add(cut(pos, i, np, vv[3]));
122                     } catch (InvalidPolygonException e) { }
123                 }
124             }
125
126             if (prev) {
127                 try {
128                     list.add(cut(pos, vertices.length, np, vv[3]));
129                 } catch (InvalidPolygonException e) { }
130             }
131         } else {
132             list.add(this);
133         }
134
135         return list;
136     }
137
138     /**
139      * {@inheritDoc}
140      */
141     public List<ConvexObject> breakObject(ConvexObject o) {
142         return null;
143     }
144
145     /**
146      * {@inheritDoc}
147      */
148     protected boolean isDegenerate() {
149         return false;
150     }
151
152     /**
153      * {@inheritDoc}
154      */
155     protected boolean isNanOrInf() {
156         return false;
157     }
158
159     /**
160      * {@inheritDoc}
161      */
162     public Vector3d getNormal() {
163         // Never used
164         return null;
165     }
166
167     /**
168      * {@inheritDoc}
169      */
170     protected boolean isPlanar() {
171         // Never used
172         return true;
173     }
174
175     /**
176      * Cut a polyline on a clipping plane
177      * @param first the first vertex position
178      * @param second the second vertex position
179      * @param np the normal vector of the clipping plane
180      * @param C the constant part of the hyperplane
181      * @return a cutted PolyLine
182      */
183     private PolyLine cut(int first, int second, Vector3d np, double C) throws InvalidPolygonException {
184         final boolean cutAtBegin = first != 0;
185         final boolean cutAtEnd = second != vertices.length;
186
187         if (!cutAtBegin && !cutAtEnd) {
188             return this;
189         }
190
191         if (cutAtBegin && cutAtEnd) {
192             final int len = second - first + 2;
193
194             Vector3d[] newVertices = new Vector3d[len];
195             Color[] newColors = new Color[len];
196
197             for (int i = 1; i < len - 1; i++) {
198                 newVertices[i] = vertices[first + i - 1];
199                 newColors[i] = colors[first + i - 1];
200             }
201
202             double c = (C + vertices[first].scalar(np)) / vertices[first].minus(vertices[first - 1]).scalar(np);
203             newVertices[0] = Vector3d.getBarycenter(vertices[first - 1], vertices[first], c, 1 - c);
204             newColors[0] = getColorsBarycenter(colors[first - 1], colors[first], c, 1 - c);
205
206             c = (C + vertices[second].scalar(np)) / vertices[second].minus(vertices[second - 1]).scalar(np);
207             newVertices[len - 1] = Vector3d.getBarycenter(vertices[second - 1], vertices[second], c, 1 - c);
208             newColors[len - 1] = getColorsBarycenter(colors[second - 1], colors[second], c, 1 - c);
209
210             return new PolyLine(newVertices, newColors, this.stroke);
211         }
212
213         if (cutAtBegin) {
214             final double c = (C + vertices[first].scalar(np)) / vertices[first].minus(vertices[first - 1]).scalar(np);
215             final int len = second - first + 1;
216
217             Vector3d[] newVertices = new Vector3d[len];
218             Color[] newColors = new Color[len];
219
220             for (int i = 1; i < len; i++) {
221                 newVertices[i] = vertices[first + i - 1];
222                 newColors[i] = colors[first + i - 1];
223             }
224
225             newVertices[0] = Vector3d.getBarycenter(vertices[first - 1], vertices[first], c, 1 - c);
226             newColors[0] = getColorsBarycenter(colors[first - 1], colors[first], c, 1 - c);
227
228             return new PolyLine(newVertices, newColors, this.stroke);
229         } else {
230             final double c = (C + vertices[second].scalar(np)) / vertices[second].minus(vertices[second - 1]).scalar(np);
231             final int len = second - first + 1;
232
233             Vector3d[] newVertices = new Vector3d[len];
234             Color[] newColors = new Color[len];
235
236             for (int i = 0; i < len - 1; i++) {
237                 newVertices[i] = vertices[first + i];
238                 newColors[i] = colors[first + i];
239             }
240
241             newVertices[len - 1] = Vector3d.getBarycenter(vertices[second - 1], vertices[second], c, 1 - c);
242             newColors[len - 1] = getColorsBarycenter(colors[second - 1], colors[second], c, 1 - c);
243             return new PolyLine(newVertices, newColors, this.stroke);
244         }
245     }
246
247     @Override
248     public void draw(Graphics2D g2d) {
249         Stroke oldStroke = g2d.getStroke();
250         Shape oldClip = g2d.getClip();
251
252         Shape newClip = getClip();
253         if (newClip != null) {
254             g2d.clip(newClip);
255         }
256
257         if (monochromatic) {
258             g2d.setColor(colors[0]);
259             g2d.setStroke(stroke);
260             g2d.draw(getProjectedPolyLine());
261         } else {
262             // on peut surement faire mieux ici
263             // avec un LinearGradientPaint
264             Vector3d start = vertices[0];
265             Color color = colors[0];
266             double cumLen = 0;
267             float[] dashArray = stroke.getDashArray();
268             float lwidth = stroke.getLineWidth();
269             for (int i = 1; i < vertices.length; i++) {
270                 Stroke nstroke = new G2DStroke(lwidth, dashArray, (float) cumLen);
271                 g2d.setStroke(nstroke);
272                 g2d.setColor(color);
273                 g2d.draw(new Line2D.Double(start.getX(), start.getY(), vertices[i].getX(), vertices[i].getY()));
274                 cumLen += Math.hypot(start.getX() - vertices[i].getX(), start.getY() - vertices[i].getY());
275             }
276         }
277
278         if (newClip != null) {
279             g2d.setClip(oldClip);
280         }
281
282         g2d.setStroke(oldStroke);
283     }
284
285     /**
286      * Get first non Nan (or Inf) vector position
287      * @param v the array to trim
288      * @param start the starting position
289      * @return index of the first non Nan vector or -1 if not found
290      */
291     private static int trimLeft(Vector3d[] v, int start) {
292         for (int i = start; i < v.length; i++) {
293             if (!isNanOrInf(v[i])) {
294                 return i;
295             }
296         }
297
298         return -1;
299     }
300
301     /**
302      * Get first Nan (or Inf) vector position
303      * @param v the array to trim
304      * @param start the starting position
305      * @return index of the first Nan vector
306      */
307     private static int findNanOrInf(Vector3d[] v, int start) {
308         for (int i = start; i < v.length; i++) {
309             if (isNanOrInf(v[i])) {
310                 return i;
311             }
312         }
313
314         return v.length;
315     }
316 }