]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.g2d/src/org/simantics/g2d/utils/PathUtils2.java
Two rendering glitch fixes for time series charts
[simantics/platform.git] / bundles / org.simantics.g2d / src / org / simantics / g2d / utils / PathUtils2.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.g2d.utils;
13
14 import java.awt.Shape;
15 import java.awt.geom.CubicCurve2D;
16 import java.awt.geom.Line2D;
17 import java.awt.geom.Path2D;
18 import java.awt.geom.PathIterator;
19 import java.awt.geom.Point2D;
20 import java.awt.geom.QuadCurve2D;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Iterator;
24 import java.util.List;
25
26 /**
27  * Path Utils.
28  * <p>
29  * A line segment (linear, quadratic or cubic bezier) is described
30  * with a double array. The length of the array describes its degree (4,6,8).
31  * The first 2 elements define start point and last 2 the end point.
32  * Points in the middle are bezier control points.
33  * 
34  * @See {@link GeometryUtils} for more geometry related utilities
35  * @author Toni Kalajainen
36  */
37 public class PathUtils2 {
38
39         /**
40          * Get tangent of an bezier
41          * @param lineSegment bezier of n degrees
42          * @param degree 1..3 
43          * @param t 0..1
44          * @return unit vector
45          */
46         public static Point2D getLineTangent(Shape line, double t, Point2D pt)
47         {
48                 double x=0, y=0;
49                 if (line instanceof Line2D)
50                 {
51                         Line2D l = (Line2D) line;
52                         x = l.getX1() - l.getX2();
53                         y = l.getY1() - l.getY2();
54                 } else if (line instanceof QuadCurve2D) {
55                         QuadCurve2D l = (QuadCurve2D) line;
56                         double p0x = l.getX1();
57                         double p0y = l.getY1();
58                         double p1x = l.getCtrlX();
59                         double p1y = l.getCtrlY();
60                         double p2x = l.getX2();
61                         double p2y = l.getY2();                 
62                         x = 2*t*(p0x - 2*p1x + p2x) + 2*(-p0x + p1x);
63                         y = 2*t*(p0y - 2*p1y + p2y) + 2*(-p0y + p1y);
64                         
65                 } else if (line instanceof CubicCurve2D) {
66                         CubicCurve2D l = (CubicCurve2D) line;
67                         double p0x = l.getX1();
68                         double p0y = l.getY1();
69                         double p1x = l.getCtrlX1();
70                         double p1y = l.getCtrlY1();
71                         double p2x = l.getCtrlX2();
72                         double p2y = l.getCtrlY2();
73                         double p3x = l.getX2();
74                         double p3y = l.getY2();                 
75                         x = 3*(1-t)*(1-t)*(p1x-p2x) + 3*(p2x-p1x)*2*t*(1-t) + 3*(p3x-p0x)*t*t;
76                         y = 3*(1-t)*(1-t)*(p1y-p0y) + 3*(p2y-p1y)*2*t*(1-t) + 3*(p3y-p2y)*t*t;
77                 } else throw new IllegalArgumentException();
78                 
79                 return new Point2D.Double(x, y);
80         }
81         
82         /**
83          * 
84          * @param lineSegment
85          * @param t 0..1
86          * @return
87          */
88         public static Point2D getLinePos(Shape line, double t, Point2D pt)
89         {               
90                 assert(line!=null);
91                 double x=0, y=0;
92                 if (pt==null) pt = new Point2D.Double();
93                 
94                 if (line instanceof Line2D) {
95                         Line2D l = (Line2D) line;
96                         double p0x = l.getX1();
97                         double p0y = l.getY1();
98                         double p1x = l.getX2();
99                         double p1y = l.getY2();
100                         
101                         x = p0x*(1-t) + t*p1x;
102                         y = p0y*(1-t) + t*p1y;
103                 } else 
104                 if (line instanceof QuadCurve2D) {
105                         QuadCurve2D l = (QuadCurve2D) line;
106                         double p0x = l.getX1();
107                         double p0y = l.getY1();
108                         double p1x = l.getCtrlX();
109                         double p1y = l.getCtrlY();
110                         double p2x = l.getX2();
111                         double p2y = l.getY2();                 
112                         
113                         double c2x = p0x-2*p1x+p2x;
114                         double c2y = p0y-2*p1y+p2y;
115                         
116                         double c1x = -2*p0x+2*p1x;
117                         double c1y = -2*p0y+2*p1y;
118                         
119                         double c0x = p0x;
120                         double c0y = p0y;
121                         
122                         x = t*t*c2x+t*c1x+c0x;
123                         y = t*t*c2y+t*c1y+c0y;
124                 } else if (line instanceof CubicCurve2D) {
125                         CubicCurve2D l = (CubicCurve2D) line;
126                         double p0x = l.getX1();
127                         double p0y = l.getY1();
128                         double p1x = l.getCtrlX1();
129                         double p1y = l.getCtrlY1();
130                         double p2x = l.getCtrlX2();
131                         double p2y = l.getCtrlY2();
132                         double p3x = l.getX2();
133                         double p3y = l.getY2();                 
134
135                         x = (1-t)*(1-t)*(1-t)*p0x + 3*t*(1-t)*(1-t)*p1x + 3*t*t*(1-t)*p2x + t*t*t*p3x;
136                         y = (1-t)*(1-t)*(1-t)*p0y + 3*t*(1-t)*(1-t)*p1y + 3*t*t*(1-t)*p2y + t*t*t*p3y;
137                 } else throw new IllegalArgumentException();
138                 pt.setLocation(x, y);
139                 
140                 return pt;
141         }
142         
143         public static double getLineLength(Shape line)
144         {
145                 if (line instanceof Line2D) {
146                         Line2D l = (Line2D) line;
147                         double dx = l.getX2() - l.getX1();
148                         double dy = l.getY2() - l.getY1();
149                         return Math.sqrt(dx*dx+dy*dy);
150                 }
151                 
152                 // Quick'n'dirty approximation 
153                 // TODO Replace with accurate value
154                 double result = 0;
155                 Point2D prevPos = getLinePos(line, 0.0, null); 
156                 for (int i=0; i<10; i++)
157                 {
158                         double t = (double)(i+1)/10;
159                         Point2D pos = getLinePos(line, t, null);
160                         result += pos.distance(prevPos);
161                         prevPos.setLocation(pos);
162                 }
163                 return result;
164         }
165         
166         public static int getLineDegree(Shape line)
167         {
168                 if (line instanceof Line2D) return 1;
169                 if (line instanceof QuadCurve2D) return 2;
170                 if (line instanceof CubicCurve2D) return 3;
171                 throw new IllegalArgumentException(line+" is not a shape");
172         }
173         
174         
175         public static double[] toArray(Shape line)
176         {
177                 if (line instanceof Line2D) {
178                         Line2D l = (Line2D) line;
179                         return new double[] {l.getX1(), l.getY1(), l.getX2(), l.getY2()};
180                 }
181                 if (line instanceof QuadCurve2D) {
182                         QuadCurve2D l = (QuadCurve2D) line;
183                         return new double[] {l.getX1(), l.getY1(), l.getCtrlX(), l.getCtrlY(), l.getX2(), l.getY2()};
184                 }
185                 if (line instanceof CubicCurve2D) {
186                         CubicCurve2D l = (CubicCurve2D) line;
187                         return new double[] {l.getX1(), l.getY1(), l.getCtrlX1(), l.getCtrlY1(), l.getCtrlX2(), l.getCtrlY2(), l.getX2(), l.getY2()};                   
188                 }
189                 throw new IllegalArgumentException(line+" is not a shape");             
190         }
191         
192         public static Shape toShape(double dada[])
193         {
194                 if (dada.length==4) 
195                         return new Line2D.Double(dada[0], dada[1], dada[2], dada[3]);
196                 if (dada.length==6) 
197                         return new QuadCurve2D.Double(dada[0], dada[1], dada[2], dada[3], dada[4], dada[5]);
198                 if (dada.length==8) 
199                         return new CubicCurve2D.Double(dada[0], dada[1], dada[2], dada[3], dada[4], dada[5], dada[6], dada[7]);
200                 throw new IllegalArgumentException();
201         }
202
203         public static void interpolatePaths(List<Shape> l1, List<Shape> l2, double t, Collection<Shape> result)
204         {
205                 if (l1.size()>l2.size()) {
206                         List<Shape> l_ = l1;
207                         l1 = l2;
208                         l2 = l_;
209                         t = 1-t;
210                 }
211                 
212                 int div = l2.size() / l1.size();
213                 int mod = l2.size() % l1.size();
214                 
215                 int rightIndex = 0;
216                 for (int i=0; i<l1.size(); i++) {
217                         int rightCounterParts = i<l1.size()-1 ? (i<mod?div+1:div) : div; 
218                         int leftIndex = i;
219                         Shape leftShape = l1.get(leftIndex); 
220                         for (int j=0; j<rightCounterParts; j++)
221                         {
222                                 Shape rightShape = l2.get(rightIndex++);
223                                 double inv = 1/(double)rightCounterParts;
224                                 double t0  = j*inv;
225                                 double t1  = (j+1)*inv;
226                                 Shape leftShapePart  = subdiv(leftShape, t0, t1);                               
227                                 Shape intpShape = interpolateLine(leftShapePart, rightShape, t);
228                                 result.add( intpShape );
229                         }
230                 }
231         }
232         
233         /**
234          * Interpolate two paths
235          * @param path1
236          * @param path2
237          * @param t phase 0..1, 0==path1, 1==path2
238          * @return
239          */
240         public static Path2D interpolatePaths(PathIterator path1, PathIterator path2, double t)
241         {
242                 ArrayList<Shape> l1 = new ArrayList<Shape>();
243                 ArrayList<Shape> l2 = new ArrayList<Shape>(); 
244                 toShapes(path1, l1);
245                 toShapes(path2, l2);
246                 ArrayList<Shape> result = new ArrayList<Shape>();
247                 interpolatePaths(l1, l2, t, result);
248                 Path2D p = new Path2D.Double();
249                 appedToPath(p, result);
250                 return p;
251         }
252
253         public static Path2D interpolatePaths(Path2D path1, Path2D path2, double t)
254         {
255                 return interpolatePaths(path1.getPathIterator(null), path2.getPathIterator(null), t);
256         }
257         
258         public static Shape interpolateLine(Shape l1, Shape l2, double t)
259         {
260                 assert(t>=0 && t<=1);
261                 if (t==0) return l1;
262                 if (t==1) return l2;
263                 
264                 double[] a1 = toArray(l1);
265                 double[] a2 = toArray(l2);
266                 double[] res = PathUtils.interpolateLineSegment(a1, a2, t);
267                 return toShape(res);
268         }
269         
270         public static void toShapes(PathIterator pi, Collection<Shape> result)
271         {
272                 Iterator<Shape> i = toShapeIterator(pi);
273                 while (i.hasNext()) {
274                         Shape segment = i.next();
275                         result.add(segment);
276                 }
277         }
278         
279         /**
280          * Returns an iterator that splits path into line shapes
281          * @param pi path iterator
282          * @return line segment iterator
283          */
284         public static Iterator<Shape> toShapeIterator(final PathIterator pi)
285         {
286                 return new PathIteratorToShapeIterator(pi);
287         }
288         
289         private static class PathIteratorToShapeIterator implements Iterator<Shape>
290         {
291                 final PathIterator pi;
292                 double  lineTo[] = new double[6];
293                 double  startPos[] = new double[2];
294                 double  from[] = new double[2];
295                 int             degree = 0;
296                 PathIteratorToShapeIterator(PathIterator pi) {
297                         this.pi = pi;
298                         while(!pi.isDone()) {
299                                 int type = pi.currentSegment(lineTo);
300                                 pi.next();
301                                 if (type == PathIterator.SEG_MOVETO) {
302                                         startPos[0] = from[0] = lineTo[0];
303                                         startPos[1] = from[1] = lineTo[1];
304                                 }
305                                 if (type == PathIterator.SEG_CLOSE) {
306                                         type = PathIterator.SEG_LINETO;
307                                         lineTo[0] = startPos[0];
308                                         lineTo[1] = startPos[1];
309                                 }
310                                 if (type>=PathIterator.SEG_LINETO && type<=PathIterator.SEG_CUBICTO)
311                                 {
312                                         degree = type;
313                                         // from == xx
314                                         break;
315                                 }
316                         }
317                 }               
318                 @Override
319                 public boolean hasNext() {
320                         return degree>0;
321                 }
322                 @Override
323                 public Shape next() {
324                         if (degree==0) return null;
325                         Shape res = null;
326                         if (degree==1) 
327                                 res = new Line2D.Double(from[0], from[1], lineTo[0], lineTo[1]);
328                         else if (degree==2) 
329                                 res = new QuadCurve2D.Double(from[0], from[1], lineTo[0], lineTo[1], lineTo[2], lineTo[3]);
330                         else if (degree==3) 
331                                 res = new CubicCurve2D.Double(from[0], from[1], lineTo[0], lineTo[1], lineTo[2], lineTo[3], lineTo[4], lineTo[5]);
332                         else throw new IllegalArgumentException();
333                         // traverse path iterator until end or until next segment is known
334                         degree = 0;
335                         from[0] = lineTo[0];
336                         from[1] = lineTo[1];
337                         while(!pi.isDone()) {
338                                 int type = pi.currentSegment(lineTo);
339                                 pi.next();
340                                 if (type == PathIterator.SEG_MOVETO) {
341                                         startPos[0] = from[0] = lineTo[0];
342                                         startPos[1] = from[1] = lineTo[1];
343                                 }
344                                 if (type == PathIterator.SEG_CLOSE) {
345                                         type = PathIterator.SEG_LINETO;
346                                         lineTo[0] = startPos[0];
347                                         lineTo[1] = startPos[1];
348                                 }
349                                 if (type>=PathIterator.SEG_LINETO && type<=PathIterator.SEG_CUBICTO)
350                                 {
351                                         degree = type;                                  
352                                         break;
353                                 }
354                         }
355                         return res;
356                 }
357                 @Override
358                 public void remove() {
359                         throw new UnsupportedOperationException();
360                 }
361         }
362
363         
364         
365         
366         public static Shape subdiv_takeLeft(Shape line, double t)
367         {
368                 if (t==1) return line;
369                 if (line instanceof Line2D) {
370                         Line2D l = (Line2D) line;
371                         double p0x = l.getX1();
372                         double p0y = l.getY1();
373                         double p1x = l.getX2();
374                         double p1y = l.getY2();                 
375                         double p1x_  = p0x*(1-t) + p1x*t;
376                         double p1y_  = p0y*(1-t) + p1y*t;
377                         
378                         return new Line2D.Double( p0x, p0y, p1x_, p1y_ );
379                 }
380                 
381                 if (line instanceof QuadCurve2D) {
382                         QuadCurve2D l = (QuadCurve2D) line;
383                         double p0x = l.getX1();
384                         double p0y = l.getY1();
385                         double p1x = l.getCtrlX();
386                         double p1y = l.getCtrlY();                      
387                         double p2x = l.getX2();
388                         double p2y = l.getY2();                 
389                         double p1x_  = p0x*(1-t) + p1x*t;
390                         double p1y_  = p0y*(1-t) + p1y*t;
391                 
392                         double q0x = p0x*(1-t) + p1x*t;
393                         double q0y = p0y*(1-t) + p1y*t;
394
395                         double q1x = p1x*(1-t) + p2x*t;
396                         double q1y = p1y*(1-t) + p2y*t;
397                         
398                         double p2x_ = q0x*(1-t) + q1x*t;
399                         double p2y_ = q0y*(1-t) + q1y*t;
400                                                                 
401                         return new QuadCurve2D.Double( p0x, p0y, p1x_, p1y_, p2x_, p2y_ );                      
402                 }
403
404                 if (line instanceof CubicCurve2D) {
405                         CubicCurve2D l = (CubicCurve2D) line;
406                         double p0x = l.getX1();
407                         double p0y = l.getY1();
408                         double p1x = l.getCtrlX1();
409                         double p1y = l.getCtrlY1();                     
410                         double p2x = l.getCtrlX2();
411                         double p2y = l.getCtrlY2();                     
412                         double p3x = l.getX2();
413                         double p3y = l.getY2();                 
414                         double p1x_  = p0x*(1-t) + p1x*t;
415                         double p1y_  = p0y*(1-t) + p1y*t;
416                 
417                         double q0x = p0x*(1-t) + p1x*t;
418                         double q0y = p0y*(1-t) + p1y*t;
419
420                         double q1x = p1x*(1-t) + p2x*t;
421                         double q1y = p1y*(1-t) + p2y*t;
422                         
423                         double p2x_ = q0x*(1-t) + q1x*t;
424                         double p2y_ = q0y*(1-t) + q1y*t;
425                         
426                         double q2x = p2x*(1-t) + p3x*t;
427                         double q2y = p2y*(1-t) + p3y*t;
428
429                         double r0x = q0x*(1-t) + q1x*t;
430                         double r0y = q0y*(1-t) + q1y*t;
431                                 
432                         double r1x = q1x*(1-t) + q2x*t;
433                         double r1y = q1y*(1-t) + q2y*t;
434                                 
435                         double p3x_  = r0x*(1-t) + r1x*t;
436                         double p3y_  = r0y*(1-t) + r1y*t;                       
437                         
438                         return new CubicCurve2D.Double(p0x, p0y, p1x_, p1y_, p2x_, p2y_, p3x_, p3y_);                   
439                 }
440
441                 throw new IllegalArgumentException();
442         }
443         
444         public static Shape subdiv_takeRight(Shape line, double t)
445         {
446                 if (t==0) return line;
447                 if (line instanceof Line2D) {
448                         Line2D l = (Line2D) line;
449                         double p0x = l.getX1();
450                         double p0y = l.getY1();
451                         double p1x = l.getX2();
452                         double p1y = l.getY2();                 
453                         double p0x_  = p0x*(1-t) + p1x*t;
454                         double p0y_  = p0y*(1-t) + p1y*t;
455                         
456                         return new Line2D.Double( p0x_, p0y_, p1x, p1y );
457                 }
458                 
459                 if (line instanceof QuadCurve2D) {
460                         QuadCurve2D l = (QuadCurve2D) line;             
461                         double p0x = l.getX1();
462                         double p0y = l.getY1();
463                         double p1x = l.getCtrlX();
464                         double p1y = l.getCtrlY();                      
465                         double p2x = l.getX2();
466                         double p2y = l.getY2();                 
467
468                         double q0x = p0x*(1-t) + p1x*t;
469                         double q0y = p0y*(1-t) + p1y*t;
470
471                         double q1x = p1x*(1-t) + p2x*t;
472                         double q1y = p1y*(1-t) + p2y*t;
473                         
474                         double p2x_ = q0x*(1-t) + q1x*t;
475                         double p2y_ = q0y*(1-t) + q1y*t;
476                         
477                         return new QuadCurve2D.Double( p2x_, p2y_, q1x, q1y, p2x, p2y );                        
478                 }               
479
480                 
481                 if (line instanceof CubicCurve2D) {
482                         CubicCurve2D l = (CubicCurve2D) line;
483                         
484                         double p0x = l.getX1();
485                         double p0y = l.getY1();
486                         double p1x = l.getCtrlX1();
487                         double p1y = l.getCtrlY1();                     
488                         double p2x = l.getCtrlX2();
489                         double p2y = l.getCtrlY2();                     
490                         double p3x = l.getX2();
491                         double p3y = l.getY2();
492                         
493                         double q0x = p0x*(1-t) + p1x*t;
494                         double q0y = p0y*(1-t) + p1y*t;
495
496                         double q1x = p1x*(1-t) + p2x*t;
497                         double q1y = p1y*(1-t) + p2y*t;
498                         
499                         double q2x = p2x*(1-t) + p3x*t;
500                         double q2y = p2y*(1-t) + p3y*t;
501
502                         double r0x = q0x*(1-t) + q1x*t;
503                         double r0y = q0y*(1-t) + q1y*t;
504                                 
505                         double r1x = q1x*(1-t) + q2x*t;
506                         double r1y = q1y*(1-t) + q2y*t;
507                                 
508                         double p3x_  = r0x*(1-t) + r1x*t;
509                         double p3y_  = r0y*(1-t) + r1y*t;                       
510                         
511                         return new CubicCurve2D.Double( p3x_, p3y_, r1x, r1y, q2x, q2y, p3x, p3y );                     
512                 }                               
513                 
514                 return null;
515         }
516         
517
518
519         /**
520          * Crops line segment into a smaller line segment
521          * @param line line segment
522          * @param t0 begin t
523          * @param t1 end t
524          * @return cropped line segment
525          */
526         public static Shape subdiv(Shape line, double t0, double t1)
527         {
528                 if (t0==0 && t1==1) return line;
529                 Shape temp = subdiv_takeLeft(line, t1);
530                 return subdiv_takeRight(temp, t0/t1);
531         }
532         
533         public static void appedToPath(Path2D p, Shape ... shapes)
534         {
535                 for (Shape s : shapes)
536                 {
537                         Point2D cur = p.getCurrentPoint();
538                         if (s instanceof Line2D) {
539                                 Line2D l = (Line2D) s;
540                                 if (cur.getX()!=l.getX1() || cur.getY()!=l.getY1())
541                                         p.moveTo(l.getX1(), l.getY1());
542                                 p.lineTo(l.getX2(), l.getY2());
543                         } else if (s instanceof QuadCurve2D) {
544                                 QuadCurve2D l = (QuadCurve2D) s;
545                                 if (cur.getX()!=l.getX1() || cur.getY()!=l.getY1())
546                                         p.moveTo(l.getX1(), l.getY1());
547                                 p.quadTo(l.getCtrlX(), l.getCtrlY(), l.getX2(), l.getY2());
548                         } else if (s instanceof CubicCurve2D) {
549                                 CubicCurve2D l = (CubicCurve2D) s;
550                                 if (cur.getX()!=l.getX1() || cur.getY()!=l.getY1())
551                                         p.moveTo(l.getX1(), l.getY1());
552                                 p.curveTo(l.getCtrlX1(), l.getCtrlY1(), l.getCtrlX2(), l.getCtrlY2(), l.getX2(), l.getY2());
553                         } throw new IllegalArgumentException();
554                 }
555         }
556         public static void appedToPath(Path2D p, Collection<Shape> shapes)
557         {
558                 for (Shape s : shapes)
559                 {
560                         Point2D cur = p.getCurrentPoint();
561                         if (s instanceof Line2D) {
562                                 Line2D l = (Line2D) s;
563                                 if (cur==null || cur.getX()!=l.getX1() || cur.getY()!=l.getY1())
564                                         p.moveTo(l.getX1(), l.getY1());
565                                 p.lineTo(l.getX2(), l.getY2());
566                         } else if (s instanceof QuadCurve2D) {
567                                 QuadCurve2D l = (QuadCurve2D) s;
568                                 if (cur==null || cur.getX()!=l.getX1() || cur.getY()!=l.getY1())
569                                         p.moveTo(l.getX1(), l.getY1());
570                                 p.quadTo(l.getCtrlX(), l.getCtrlY(), l.getX2(), l.getY2());
571                         } else if (s instanceof CubicCurve2D) {
572                                 CubicCurve2D l = (CubicCurve2D) s;
573                                 if (cur==null || cur.getX()!=l.getX1() || cur.getY()!=l.getY1())
574                                         p.moveTo(l.getX1(), l.getY1());
575                                 p.curveTo(l.getCtrlX1(), l.getCtrlY1(), l.getCtrlX2(), l.getCtrlY2(), l.getX2(), l.getY2());
576                         } else throw new IllegalArgumentException();
577                 }
578         }
579         
580
581         public static void main(String[] args) {
582                 
583         }
584                         
585         
586 }