Merge "Small improvements to statement collision reporting."
[simantics/platform.git] / bundles / org.simantics.utils.ui / src / org / simantics / utils / ui / color / ColorGradient.java
1 /*******************************************************************************\r
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
3  * in Industry THTH ry.\r
4  * All rights reserved. This program and the accompanying materials\r
5  * are made available under the terms of the Eclipse Public License v1.0\r
6  * which accompanies this distribution, and is available at\r
7  * http://www.eclipse.org/legal/epl-v10.html\r
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.utils.ui.color;\r
13 \r
14 import java.util.ArrayList;\r
15 import java.util.Collection;\r
16 import java.util.Collections;\r
17 \r
18 import org.eclipse.swt.SWT;\r
19 import org.eclipse.swt.graphics.Color;\r
20 import org.eclipse.swt.graphics.GC;\r
21 import org.eclipse.swt.graphics.Image;\r
22 import org.eclipse.swt.widgets.Display;\r
23 \r
24 /**\r
25  * \r
26  * <p>\r
27  * Color gradient between multiple colors\r
28  * </p>\r
29  * <p>\r
30  * Supports RGB and HSV linear interpolation between colors\r
31  * </p>\r
32  * \r
33  * @author Marko Luukkainen\r
34  *\r
35  */\r
36 public class ColorGradient {\r
37     public static final int RGB = 0;\r
38 \r
39     public static final int HSV = 1;\r
40 \r
41     private ArrayList<ColorValue> values;\r
42 \r
43     private int type;\r
44     \r
45     public ColorGradient() {\r
46         this.values = new ArrayList<ColorValue>();\r
47         this.type = RGB;\r
48     }\r
49 \r
50     public ColorGradient(ColorGradient copyFrom) {\r
51         this.values = new ArrayList<ColorValue>(copyFrom.values);\r
52         this.type = copyFrom.type;\r
53     }\r
54 \r
55     public ColorGradient(Collection<ColorValue> values) {\r
56         this.values = new ArrayList<ColorValue>(values);\r
57         Collections.sort(this.values, new ColorValueComparator());\r
58         this.type = RGB;\r
59     }\r
60 \r
61     public ColorGradient(ColorValue array[]) {\r
62         this.values = new ArrayList<ColorValue>();\r
63         for (ColorValue c : array) {\r
64             values.add(c);\r
65         }\r
66         Collections.sort(this.values, new ColorValueComparator());\r
67         this.type = RGB;\r
68     }\r
69 \r
70     public ColorGradient(Collection<ColorValue> values, int type) {\r
71         this.values = new ArrayList<ColorValue>(values);\r
72         Collections.sort(this.values, new ColorValueComparator());\r
73         this.type = type;\r
74     }\r
75 \r
76     public ColorGradient(ColorValue array[], int type) {\r
77         this.values = new ArrayList<ColorValue>();\r
78         for (ColorValue c : array) {\r
79             values.add(c);\r
80         }\r
81         Collections.sort(this.values, new ColorValueComparator());\r
82         this.type = type;\r
83     }\r
84 \r
85     /**\r
86      * Interpolates color in RGB space\r
87      * \r
88      * @param value\r
89      * @return\r
90      */\r
91     private byte[] getRGBColor(double value) {\r
92         int index = 1;\r
93         while (values.get(index).getValue() <= value && index < values.size()-1)\r
94             index++;\r
95 \r
96         value -= values.get(index - 1).getValue();\r
97         value /= (values.get(index).getValue() - values.get(index - 1).getValue());\r
98         double valuei = 1.0 - value;\r
99         byte color[] = new byte[] {\r
100                 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getR() + valuei * values.get(index - 1).getColor().getR())),\r
101                 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getG() + valuei * values.get(index - 1).getColor().getG())),\r
102                 (byte) Math.min(255.0, Math.floor(value * values.get(index).getColor().getB() + valuei * values.get(index - 1).getColor().getB())) };\r
103         return color;\r
104 \r
105     }\r
106 \r
107     /**\r
108      * Interpolates color in HSV space\r
109      * \r
110      * @param value\r
111      * @return\r
112      */\r
113     private byte[] getHSVColor(double value) {\r
114         int index = 1;\r
115         while (values.get(index).getValue() <= value && index < values.size()-1)\r
116             index++;\r
117 \r
118         value -= values.get(index - 1).getValue();\r
119         value /= (values.get(index).getValue() - values.get(index - 1).getValue());\r
120         double valuei = 1.0 - value;\r
121         double h;\r
122         if (Float.isNaN(values.get(index).getColor().getH())) {\r
123             h = values.get(index-1).getColor().getH();\r
124         } else if (Float.isNaN(values.get(index-1).getColor().getH())) {\r
125             h = values.get(index).getColor().getH();\r
126         } else {\r
127             // selecting shortest direction between hues\r
128             float angle = values.get(index).getColor().getH() - values.get(index - 1).getColor().getH();\r
129             if (angle > 180.f)\r
130                 angle -= 360.f;\r
131             else if (angle < -180.f)\r
132                 angle += 360.f;\r
133             h = values.get(index - 1).getColor().getH() + value * angle;\r
134             if (h > 360.f)\r
135                 h -= 360.f;\r
136             else if (h < 0.f)\r
137                 h+= 360.f;\r
138         }\r
139         org.simantics.utils.ui.color.Color interpolated = new org.simantics.utils.ui.color.Color(h, value * values.get(index).getColor().getS() + valuei * values.get(index - 1).getColor().getS(),\r
140                 value * values.get(index).getColor().getV() + valuei * values.get(index - 1).getColor().getV());\r
141         byte color[] = new byte[] { (byte) interpolated.getR(), (byte) interpolated.getG(), (byte) interpolated.getB() };\r
142 \r
143         return color;\r
144 \r
145     }\r
146 \r
147     /**\r
148      * <p>\r
149      * Returns gradient in array of bytes. Array is RGB order and int contains 3 * requested size of bytes.\r
150      * </p>\r
151      * <p>\r
152      * If gradient contains only one color array is filled with that color\r
153      * </p>\r
154      * <p>\r
155      * if gradient has no colors array is filled with white\r
156      * </p>\r
157      * @param size number of pixels\r
158      * @return gradient in array of bytes\r
159      */\r
160     public byte[] getGradientArray(int size) {\r
161         byte array[] = new byte[size * 3];\r
162         if (values.size() > 1) {\r
163             for (int i = 0; i < size; i++) {\r
164                 int index = i * 3;\r
165                 double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) i / (double) size;\r
166                 byte color[];\r
167                 if (type == RGB)\r
168                     color = getRGBColor(value);\r
169                 else\r
170                     color = getHSVColor(value);\r
171                 array[index] = color[0];\r
172                 array[index + 1] = color[1];\r
173                 array[index + 2] = color[2];\r
174             }\r
175         } else if (values.size() == 1) {\r
176             byte color[] = new byte[3];\r
177             color[0] = (byte)values.get(0).getColor().getR();\r
178             color[1] = (byte)values.get(0).getColor().getG();\r
179             color[2] = (byte)values.get(0).getColor().getB();\r
180             for (int i = 0; i < size; i++) {\r
181                 int index = i * 3;\r
182                 array[index] = color[0];\r
183                 array[index + 1] = color[1];\r
184                 array[index + 2] = color[2];\r
185             }\r
186         } else {\r
187             for (int i = 0; i < size; i++) {\r
188                 int index = i * 3;\r
189                 array[index] = (byte)255;\r
190                 array[index + 1] = (byte)255;\r
191                 array[index + 2] = (byte)255;\r
192             }\r
193         }\r
194         return array;\r
195     }\r
196 \r
197     /**\r
198      * <p>\r
199      * Returns gradient in image.\r
200      * </p>\r
201      * <p>\r
202      * If gradient contains only one color image is filled with that color\r
203      * </p>\r
204      * <p>\r
205      * if gradient has no colors image is filled with white\r
206      * </p>\r
207      * <p>\r
208      * Style must be set to  <code>SWT.HORIZONTAL</code> or <code>SWT.VERTICAL</code>\r
209      * </p>\r
210      * @param size number of pixels\r
211      * @return gradient in array of bytes\r
212      */\r
213     \r
214     public Image getGradientImage(int width, int height, int style) {\r
215         Image image = new Image(Display.getCurrent(), width, height);\r
216         GC gc = new GC(image);\r
217         gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_WHITE));\r
218         gc.fillRectangle(0, 0, width, height);\r
219         if (values.size() > 1) {\r
220             if (SWT.HORIZONTAL == (style | SWT.HORIZONTAL)) {\r
221                 for (int x = 0; x < width; x++) {\r
222                     double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) x\r
223                             / (double) (width - 1);\r
224                     byte byteColor[];\r
225                     if (type == RGB)\r
226                         byteColor = getRGBColor(value);\r
227                     else\r
228                         byteColor = getHSVColor(value);\r
229                     Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);\r
230                     gc.setForeground(color);\r
231                     gc.drawLine(x, 0, x, height);\r
232                     color.dispose();\r
233                 }\r
234             } else if (SWT.VERTICAL == (style | SWT.VERTICAL)){\r
235                 for (int y = 0; y < height; y++) {\r
236                     double value = values.get(0).getValue() + (values.get(values.size() - 1).getValue() - values.get(0).getValue()) * (double) y\r
237                             / (double) (height - 1);\r
238                     byte byteColor[];\r
239                     if (type == RGB)\r
240                         byteColor = getRGBColor(value);\r
241                     else\r
242                         byteColor = getHSVColor(value);\r
243                     Color color = new Color(Display.getCurrent(), byteColor[0] & 0xff, byteColor[1] & 0xff, byteColor[2] & 0xff);\r
244                     gc.setForeground(color);\r
245                     gc.drawLine(0, y, width, y);\r
246                     color.dispose();\r
247                 }\r
248             } else {\r
249                 gc.dispose();\r
250                 image.dispose();\r
251                 SWT.error(SWT.ERROR_INVALID_ARGUMENT);\r
252             }\r
253         } else if (values.size() == 1) {\r
254             Color color = new Color(Display.getCurrent(), values.get(0).getColor().getR(), values.get(0).getColor().getG(), values.get(0).getColor().getB());      \r
255             gc.setBackground(color);\r
256             gc.fillRectangle(0, 0, width, height);\r
257             color.dispose();\r
258         } else {\r
259             gc.fillRectangle(0, 0, width, height);\r
260         }\r
261         gc.dispose();\r
262         return image;\r
263     }\r
264     \r
265     public int getType() {\r
266         return type;\r
267     }\r
268     \r
269     public Collection<ColorValue> getColorValues() {\r
270         return values;\r
271     }\r
272     \r
273     public ColorValue[] getColorValueArray() {\r
274         return values.toArray(new ColorValue[values.size()]);\r
275     }\r
276     \r
277     @Deprecated\r
278     public void addCastorColorValue(ColorValue value) {\r
279         values.add(value);\r
280         Collections.sort(this.values, new ColorValueComparator());\r
281     }\r
282     \r
283     @Deprecated\r
284     public void setCastorType(int type) {\r
285         this.type = type;\r
286     }\r
287     \r
288     @Override\r
289     public int hashCode() {\r
290         int hash = 0x58fb3;\r
291         for (ColorValue cv : values)\r
292             hash ^= cv.hashCode();\r
293         return hash;\r
294     }\r
295     \r
296     @Override\r
297     public boolean equals(Object obj) {\r
298         if (!(obj instanceof ColorGradient)) return false;\r
299         ColorGradient cg = (ColorGradient) obj;\r
300         if (cg.type != type) return false;\r
301         if (values.size()!=cg.values.size()) return false;\r
302         if (!values.containsAll(cg.values)) return false;\r
303         return true;\r
304     }\r
305 \r
306 }\r