001/* 002 * Shredzone Commons - suncalc 003 * 004 * Copyright (C) 2017 Richard "Shred" Körber 005 * http://commons.shredzone.org 006 * 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 013 */ 014package org.shredzone.commons.suncalc.util; 015 016import static java.lang.Math.cos; 017import static java.lang.Math.sin; 018 019import java.util.Arrays; 020 021/** 022 * A three dimensional matrix. 023 * <p> 024 * Objects are immutable and threadsafe. 025 */ 026public class Matrix { 027 028 private final double[] mx; 029 030 private Matrix() { 031 mx = new double[9]; 032 } 033 034 private Matrix(double... values) { 035 if (values == null || values.length != 9) { 036 throw new IllegalArgumentException("requires 9 values"); 037 } 038 mx = values; 039 } 040 041 /** 042 * Creates an identity matrix. 043 * 044 * @return Identity {@link Matrix} 045 */ 046 public static Matrix identity() { 047 return new Matrix( 048 1.0, 0.0, 0.0, 049 0.0, 1.0, 0.0, 050 0.0, 0.0, 1.0); 051 } 052 053 /** 054 * Creates a matrix that rotates a vector by the given angle at the X axis. 055 * 056 * @param angle 057 * angle, in radians 058 * @return Rotation {@link Matrix} 059 */ 060 public static Matrix rotateX(double angle) { 061 double s = sin(angle); 062 double c = cos(angle); 063 return new Matrix( 064 1.0, 0.0, 0.0, 065 0.0, c, s, 066 0.0, -s, c 067 ); 068 } 069 070 /** 071 * Creates a matrix that rotates a vector by the given angle at the Y axis. 072 * 073 * @param angle 074 * angle, in radians 075 * @return Rotation {@link Matrix} 076 */ 077 public static Matrix rotateY(double angle) { 078 double s = sin(angle); 079 double c = cos(angle); 080 return new Matrix( 081 c, 0.0, -s, 082 0.0, 1.0, 0.0, 083 s, 0.0, c 084 ); 085 } 086 087 /** 088 * Creates a matrix that rotates a vector by the given angle at the Z axis. 089 * 090 * @param angle 091 * angle, in radians 092 * @return Rotation {@link Matrix} 093 */ 094 public static Matrix rotateZ(double angle) { 095 double s = sin(angle); 096 double c = cos(angle); 097 return new Matrix( 098 c, s, 0.0, 099 -s, c, 0.0, 100 0.0, 0.0, 1.0 101 ); 102 } 103 104 /** 105 * Transposes this matrix. 106 * 107 * @return {@link Matrix} that is a transposition of this matrix. 108 */ 109 public Matrix transpose() { 110 Matrix result = new Matrix(); 111 for (int i = 0; i < 3; i++) { 112 for (int j = 0; j < 3; j++) { 113 result.set(i, j, get(j, i)); 114 } 115 } 116 return result; 117 } 118 119 /** 120 * Negates this matrix. 121 * 122 * @return {@link Matrix} that is a negation of this matrix. 123 */ 124 public Matrix negate() { 125 Matrix result = new Matrix(); 126 for (int i = 0; i < 9; i++) { 127 result.mx[i] = -mx[i]; 128 } 129 return result; 130 } 131 132 /** 133 * Adds a matrix to this matrix. 134 * 135 * @param right 136 * {@link Matrix} to add 137 * @return {@link Matrix} that is a sum of both matrices 138 */ 139 public Matrix add(Matrix right) { 140 Matrix result = new Matrix(); 141 for (int i = 0; i < 9; i++) { 142 result.mx[i] = mx[i] + right.mx[i]; 143 } 144 return result; 145 } 146 147 /** 148 * Subtracts a matrix from this matrix. 149 * 150 * @param right 151 * {@link Matrix} to subtract 152 * @return {@link Matrix} that is the difference of both matrices 153 */ 154 public Matrix subtract(Matrix right) { 155 Matrix result = new Matrix(); 156 for (int i = 0; i < 9; i++) { 157 result.mx[i] = mx[i] - right.mx[i]; 158 } 159 return result; 160 } 161 162 /** 163 * Multiplies two matrices. 164 * 165 * @param right 166 * {@link Matrix} to multiply with 167 * @return {@link Matrix} that is the product of both matrices 168 */ 169 public Matrix multiply(Matrix right) { 170 Matrix result = new Matrix(); 171 for (int i = 0; i < 3; i++) { 172 for (int j = 0; j < 3; j++) { 173 double scalp = 0.0; 174 for (int k = 0; k < 3; k++) { 175 scalp += get(i, k) * right.get(k, j); 176 } 177 result.set(i, j, scalp); 178 } 179 } 180 return result; 181 } 182 183 /** 184 * Performs a scalar multiplication. 185 * 186 * @param scalar 187 * Scalar to multiply with 188 * @return {@link Matrix} that is the scalar product 189 */ 190 public Matrix multiply(double scalar) { 191 Matrix result = new Matrix(); 192 for (int i = 0; i < 9; i++) { 193 result.mx[i] = mx[i] * scalar; 194 } 195 return result; 196 } 197 198 /** 199 * Applies this matrix to a {@link Vector}. 200 * 201 * @param right 202 * {@link Vector} to multiply with 203 * @return {@link Vector} that is the product of this matrix and the given vector 204 */ 205 public Vector multiply(Vector right) { 206 double[] vec = new double[] {right.getX(), right.getY(), right.getZ()}; 207 double[] result = new double[3]; 208 209 for (int i = 0; i < 3; i++) { 210 double scalp = 0.0; 211 for (int j = 0; j < 3; j++) { 212 scalp += get(i, j) * vec[j]; 213 } 214 result[i] = scalp; 215 } 216 217 return new Vector(result); 218 } 219 220 /** 221 * Gets a value from the matrix. 222 * 223 * @param r 224 * Row number (0..2) 225 * @param c 226 * Column number (0..2) 227 * @return Value at that position 228 */ 229 public double get(int r, int c) { 230 if (r < 0 || r > 2 || c < 0 || c > 2) { 231 throw new IllegalArgumentException("row/column out of range: " + r + ":" + c); 232 } 233 return mx[r * 3 + c]; 234 } 235 236 /** 237 * Changes a value in the matrix. As a {@link Matrix} object is immutable from the 238 * outside, this method is private. 239 * 240 * @param r 241 * Row number (0..2) 242 * @param c 243 * Column number (0..2) 244 * @param v 245 * New value 246 */ 247 private void set(int r, int c, double v) { 248 if (r < 0 || r > 2 || c < 0 || c > 2) { 249 throw new IllegalArgumentException("row/column out of range: " + r + ":" + c); 250 } 251 mx[r * 3 + c] = v; 252 } 253 254 @Override 255 public boolean equals(Object obj) { 256 if (obj == null || !(obj instanceof Matrix)) { 257 return false; 258 } 259 return Arrays.equals(mx, ((Matrix) obj).mx); 260 } 261 262 @Override 263 public int hashCode() { 264 return Arrays.hashCode(mx); 265 } 266 267 @Override 268 public String toString() { 269 StringBuilder sb = new StringBuilder(); 270 sb.append('['); 271 for (int ix = 0; ix < 9; ix++) { 272 if (ix % 3 == 0) { 273 sb.append('['); 274 } 275 sb.append(mx[ix]); 276 if (ix % 3 == 2) { 277 sb.append(']'); 278 } 279 if (ix < 8) { 280 sb.append(", "); 281 } 282 } 283 sb.append(']'); 284 return sb.toString(); 285 } 286 287}