001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.demos.hardware;
031
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.List;
035
036import org.openimaj.image.MBFImage;
037import org.openimaj.image.typography.hershey.HersheyFont;
038import org.openimaj.math.geometry.point.Point2dImpl;
039
040import Jama.Matrix;
041
042/**
043 * Very crude orthographic wireframe renderer
044 *
045 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
046 *
047 */
048public class Simple3D {
049        /**
050         * Simple interface to describe a primative
051         *
052         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
053         */
054        public static interface Primative {
055                /**
056                 * Render the primative
057                 *
058                 * @param transform
059                 * @param tx
060                 * @param ty
061                 * @param image
062                 */
063                public void renderOrtho(Matrix transform, int tx, int ty, MBFImage image);
064
065                /**
066                 * Translate the primative
067                 *
068                 * @param x
069                 * @param y
070                 * @param z
071                 */
072                public void translate(int x, int y, int z);
073        }
074
075        /**
076         * A 3D point
077         *
078         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
079         *
080         */
081        public static class Point3D implements Primative {
082                Matrix pt;
083                private Float[] colour;
084                private int size;
085
086                /**
087                 * Construct
088                 *
089                 * @param x
090                 * @param y
091                 * @param z
092                 * @param colour
093                 * @param size
094                 */
095                public Point3D(double x, double y, double z, Float[] colour, int size) {
096                        pt = new Matrix(3, 1);
097                        pt.set(0, 0, x);
098                        pt.set(1, 0, y);
099                        pt.set(2, 0, z);
100                        this.colour = colour;
101                        this.size = size;
102                }
103
104                @Override
105                public void renderOrtho(Matrix transform, int tx, int ty, MBFImage image) {
106                        final Point2dImpl pt1 = projectOrtho(transform.times(pt));
107                        pt1.x += tx;
108                        pt1.y += ty;
109                        pt1.y = image.getHeight() - pt1.y;
110                        image.drawPoint(pt1, colour, size);
111                }
112
113                @Override
114                public void translate(int x, int y, int z) {
115                        pt.set(0, 0, pt.get(0, 0) + x);
116                        pt.set(1, 0, pt.get(1, 0) + y);
117                        pt.set(2, 0, pt.get(2, 0) + z);
118                }
119        }
120
121        /**
122         * 3D Text
123         *
124         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
125         *
126         */
127        public static class Text3D implements Primative {
128                Matrix pt;
129                private Float[] colour;
130                private int size;
131                private String text;
132
133                /**
134                 * Construct
135                 *
136                 * @param x
137                 * @param y
138                 * @param z
139                 * @param colour
140                 * @param size
141                 * @param text
142                 */
143                public Text3D(double x, double y, double z, Float[] colour, int size, String text) {
144                        pt = new Matrix(3, 1);
145                        pt.set(0, 0, x);
146                        pt.set(1, 0, y);
147                        pt.set(2, 0, z);
148                        this.colour = colour;
149                        this.size = size;
150                        this.text = text;
151                }
152
153                @Override
154                public void renderOrtho(Matrix transform, int tx, int ty, MBFImage image) {
155                        final Point2dImpl pt1 = projectOrtho(transform.times(pt));
156                        pt1.x += tx;
157                        pt1.y += ty;
158                        pt1.y = image.getHeight() - pt1.y;
159                        image.drawText(text, pt1, HersheyFont.ROMAN_DUPLEX, size, colour);
160                }
161
162                @Override
163                public void translate(int x, int y, int z) {
164                        pt.set(0, 0, pt.get(0, 0) + x);
165                        pt.set(1, 0, pt.get(1, 0) + y);
166                        pt.set(2, 0, pt.get(2, 0) + z);
167                }
168        }
169
170        /**
171         * 3D line
172         *
173         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
174         *
175         */
176        public static class Line3D implements Primative {
177                Matrix pt1;
178                Matrix pt2;
179                private Float[] colour;
180                private int thickness;
181
182                /**
183                 * Construct
184                 *
185                 * @param x1
186                 * @param y1
187                 * @param z1
188                 * @param x2
189                 * @param y2
190                 * @param z2
191                 * @param colour
192                 * @param size
193                 */
194                public Line3D(double x1, double y1, double z1, double x2, double y2, double z2, Float[] colour, int size) {
195                        pt1 = new Matrix(3, 1);
196                        pt1.set(0, 0, x1);
197                        pt1.set(1, 0, y1);
198                        pt1.set(2, 0, z1);
199                        pt2 = new Matrix(3, 1);
200                        pt2.set(0, 0, x2);
201                        pt2.set(1, 0, y2);
202                        pt2.set(2, 0, z2);
203                        this.colour = colour;
204                        this.thickness = size;
205                }
206
207                @Override
208                public void renderOrtho(Matrix transform, int tx, int ty, MBFImage image) {
209                        final Point2dImpl p1 = projectOrtho(transform.times(pt1));
210                        p1.translate(tx, ty);
211                        p1.y = image.getHeight() - p1.y;
212
213                        final Point2dImpl p2 = projectOrtho(transform.times(pt2));
214                        p2.translate(tx, ty);
215                        p2.y = image.getHeight() - p2.y;
216
217                        image.drawLine(p1, p2, thickness, colour);
218                }
219
220                @Override
221                public void translate(int x, int y, int z) {
222                        pt1.set(0, 0, pt1.get(0, 0) + x);
223                        pt1.set(1, 0, pt1.get(1, 0) + y);
224                        pt1.set(2, 0, pt1.get(2, 0) + z);
225                        pt2.set(0, 0, pt2.get(0, 0) + x);
226                        pt2.set(1, 0, pt2.get(1, 0) + y);
227                        pt2.set(2, 0, pt2.get(2, 0) + z);
228                }
229        }
230
231        /**
232         * A scene consisting of primatives
233         *
234         * @author Jonathon Hare (jsh2@ecs.soton.ac.uk)
235         *
236         */
237        public static class Scene {
238                List<Primative> primatives = new ArrayList<Primative>();
239
240                /**
241                 * Construct
242                 */
243                public Scene() {
244                }
245
246                /**
247                 * Construct
248                 *
249                 * @param primatives
250                 */
251                public Scene(List<Primative> primatives) {
252                        this.primatives.addAll(primatives);
253                }
254
255                /**
256                 * Construct
257                 *
258                 * @param primatives
259                 */
260                public Scene(Primative... primatives) {
261                        this.primatives.addAll(Arrays.asList(primatives));
262                }
263
264                /**
265                 * Add a primative to the scene
266                 *
267                 * @param p
268                 * @return the scene
269                 */
270                public Scene addPrimative(Primative p) {
271                        primatives.add(p);
272                        return this;
273                }
274
275                /**
276                 * Render the scene
277                 *
278                 * @param transform
279                 * @param image
280                 */
281                public void renderOrtho(Matrix transform, MBFImage image) {
282                        for (final Primative p : primatives)
283                                p.renderOrtho(transform, image.getWidth() / 2, image.getHeight() / 2, image);
284                }
285
286                /**
287                 * Translate the scene
288                 * 
289                 * @param x
290                 * @param y
291                 * @param z
292                 */
293                public void translate(int x, int y, int z) {
294                        for (final Primative p : primatives) {
295                                p.translate(x, y, z);
296                        }
297                }
298        }
299
300        /**
301         * @param pt
302         * @return
303         */
304        static Point2dImpl projectOrtho(Matrix pt) {
305                final Point2dImpl po = new Point2dImpl();
306
307                po.x = (float) pt.get(0, 0);
308                po.y = (float) pt.get(1, 0);
309
310                return po;
311        }
312
313        /**
314         * @param pitch
315         * @param yaw
316         * @param roll
317         * @return
318         */
319        static Matrix euler2Rot(final double pitch, final double yaw, final double roll)
320        {
321                Matrix R;
322                R = new Matrix(3, 3);
323
324                final double sina = Math.sin(pitch), sinb = Math.sin(yaw), sinc = Math
325                                .sin(roll);
326                final double cosa = Math.cos(pitch), cosb = Math.cos(yaw), cosc = Math
327                                .cos(roll);
328                R.set(0, 0, cosb * cosc);
329                R.set(0, 1, -cosb * sinc);
330                R.set(0, 2, sinb);
331                R.set(1, 0, cosa * sinc + sina * sinb * cosc);
332                R.set(1, 1, cosa * cosc - sina * sinb * sinc);
333                R.set(1, 2, -sina * cosb);
334                R.set(2, 0, R.get(0, 1) * R.get(1, 2) - R.get(0, 2) * R.get(1, 1));
335                R.set(2, 1, R.get(0, 2) * R.get(1, 0) - R.get(0, 0) * R.get(1, 2));
336                R.set(2, 2, R.get(0, 0) * R.get(1, 1) - R.get(0, 1) * R.get(1, 0));
337
338                return R;
339        }
340}