/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2016 Andreas Maschke This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jwildfire.base.mathlib; import java.io.Serializable; import org.jwildfire.image.SimpleImage; public final class VecMathLib { public static final double COLORSCL = 255.0; @SuppressWarnings("serial") public static final class RGBColorD implements Serializable { public double r, g, b; public RGBColorD() { } public RGBColorD(double r, double g, double b) { this.r = r; this.g = g; this.b = b; } public RGBColorD(double r, double g, double b, double scale) { this.r = r * scale; this.g = g * scale; this.b = b * scale; } public RGBColorD(RGBColorD src, double scale) { this.r = src.r * scale; this.g = src.g * scale; this.b = src.b * scale; } public RGBColorD(int argb) { setARGBValue(argb); } public void setARGBValue(int argb) { // int a = (argb >>> 24) & 0xff; r = ((argb >>> 16) & 0xff) / COLORSCL; g = ((argb >>> 8) & 0xff) / COLORSCL; b = (argb & 0xff) / COLORSCL; } public void addFrom(double r, double g, double b) { this.r += r; this.g += g; this.b += b; } public void addFrom(double r, double g, double b, double scale) { this.r += r * scale; this.g += g * scale; this.b += b * scale; } } public static final class UVPairD { public double u, v; public UVPairD(double u, double v) { this.u = u; this.v = v; } public static UVPairD sphericalOpenGlMapping(VectorD vec) { double m = 2.0 * MathLib.sqrt(vec.x * vec.x + vec.y * vec.y + MathLib.sqr(vec.z + 1.0)); return new UVPairD(GfxMathLib.clamp(vec.x / m + 0.5), GfxMathLib.clamp(vec.y / m + 0.5)); } public static UVPairD sphericalBlinnNewellLatitudeMapping(VectorD vec) { return new UVPairD(GfxMathLib.clamp((MathLib.atan(vec.x / vec.z) + MathLib.M_PI) / (MathLib.M_PI + MathLib.M_PI)), GfxMathLib.clamp((MathLib.asin(vec.y) + MathLib.M_PI * 0.5) / MathLib.M_PI)); } public RGBColorD getColorFromMap(SimpleImage map) { int uImg = (int) (map.getImageWidth() * u); int vImg = (int) (map.getImageHeight() * v); RGBColorD lu = new RGBColorD(map.getARGBValueIgnoreBounds(uImg, vImg)); RGBColorD ru = new RGBColorD(map.getARGBValueIgnoreBounds(uImg + 1, vImg)); RGBColorD lb = new RGBColorD(map.getARGBValueIgnoreBounds(uImg, vImg + 1)); RGBColorD rb = new RGBColorD(map.getARGBValueIgnoreBounds(uImg + 1, vImg + 1)); return new RGBColorD( GfxMathLib.blerp(lu.r, ru.r, lb.r, rb.r, u, v), GfxMathLib.blerp(lu.g, ru.g, lb.g, rb.g, u, v), GfxMathLib.blerp(lu.b, ru.b, lb.b, rb.b, u, v)); } } @SuppressWarnings("serial") public static final class VectorD implements Serializable, Cloneable { public double x, y, z; public VectorD() { } public VectorD(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public double length() { return MathLib.sqrt(x * x + y * y + z * z); } public void normalize() { double l = length() + 1.0e-16; x /= l; y /= l; z /= l; } public static double dot(VectorD a, VectorD b) { return a.x * b.x + a.y * b.y + a.z * b.z; } public static VectorD reflect(VectorD d, VectorD n) { // r=d - 2(d*n)n double dn = dot(d, n); return new VectorD(d.x - 2.0 * dn * n.x, d.y - 2.0 * dn * n.y, d.z - 2.0 * dn * n.z); } public void addFrom(VectorD src) { x += src.x; y += src.y; z += src.z; } public void addFrom(VectorD src, double scale) { x += src.x * scale; y += src.y * scale; z += src.z * scale; } @Override public VectorD clone() { return new VectorD(x, y, z); } public static VectorD cross(VectorD a, VectorD b) { return new VectorD(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x); } public static VectorD a(VectorD a, VectorD b) { return new VectorD(a.x + b.x, a.y + b.y, a.z + b.z); } public static VectorD subtract(VectorD a, VectorD b) { return new VectorD(a.x - b.x, a.y - b.y, a.z - b.z); } } @SuppressWarnings("serial") public static final class Matrix4D implements Serializable { public final double m[][] = new double[4][4]; public static Matrix4D identity() { Matrix4D res = new Matrix4D(); res.m[0][0] = 1.0; res.m[1][1] = 1.0; res.m[2][2] = 1.0; res.m[3][3] = 1.0; return res; } public static Matrix4D lookAt(VectorD eyePosition, VectorD lookAt, VectorD upVector) { VectorD e = eyePosition.clone(); // e.normalize(); VectorD l = lookAt.clone(); // l.normalize(); VectorD up = upVector.clone(); // up.normalize(); // forward vector VectorD forward = VectorD.subtract(l, e); // forward.normalize(); // side vector VectorD side = VectorD.cross(forward, up); // side.normalize(); Matrix4D rotmat = new Matrix4D(); rotmat.m[0][0] = side.x; rotmat.m[1][0] = side.y; rotmat.m[2][0] = side.z; rotmat.m[3][0] = 0.0; rotmat.m[0][1] = up.x; rotmat.m[1][1] = up.y; rotmat.m[2][1] = up.z; rotmat.m[3][1] = 0.0; rotmat.m[0][2] = -forward.x; rotmat.m[1][2] = -forward.y; rotmat.m[2][2] = -forward.z; rotmat.m[3][2] = 0.0; rotmat.m[3][3] = 1.0; Matrix4D transmat = Matrix4D.translateMatrix(-e.x, -e.y, -e.z); return Matrix4D.multiply(transmat, rotmat); } public static Matrix4D multiply(Matrix4D a, Matrix4D b) { Matrix4D res = new Matrix4D(); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { res.m[i][j] = 0.0; for (int s = 0; s < 4; s++) { res.m[i][j] += a.m[i][s] * b.m[s][j]; } } } return res; } public static Matrix4D translateMatrix(double tx, double ty, double tz) { Matrix4D res = Matrix4D.identity(); res.m[0][3] = tx; res.m[1][3] = ty; res.m[2][3] = tz; return res; } public static VectorD multiply(Matrix4D a, VectorD v) { VectorD res = new VectorD(); res.x = v.x * a.m[0][0] + v.y * a.m[0][1] + v.z * a.m[0][2] + a.m[0][3]; res.y = v.x * a.m[1][0] + v.y * a.m[1][1] + v.z * a.m[1][2] + a.m[1][3]; res.z = v.x * a.m[2][0] + v.y * a.m[2][1] + v.z * a.m[2][2] + a.m[2][3]; return res; } } @SuppressWarnings("serial") public static final class Matrix3D implements Serializable { public final double m[][] = new double[3][3]; public static Matrix3D identity() { Matrix3D res = new Matrix3D(); res.m[0][0] = 1.0; res.m[1][1] = 1.0; res.m[2][2] = 1.0; return res; } public static Matrix3D multiply(Matrix3D a, Matrix3D b) { Matrix3D res = new Matrix3D(); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { res.m[i][j] = 0.0; for (int s = 0; s < 3; s++) { res.m[i][j] += a.m[i][s] * b.m[s][j]; } } } return res; } public static VectorD multiply(Matrix3D a, VectorD v) { VectorD res = new VectorD(); res.x = v.x * a.m[0][0] + v.y * a.m[0][1] + v.z * a.m[0][2]; res.y = v.x * a.m[1][0] + v.y * a.m[1][1] + v.z * a.m[1][2]; res.z = v.x * a.m[2][0] + v.y * a.m[2][1] + v.z * a.m[2][2]; return res; } } }