| Matrix.java |
1 package fi.jyu.mit.graphics;
2 import static java.lang.Math.round;
3
4 /**
5 * Matriisliluokka kuvamuunnoslaskuihin.
6 * @author Markus Kivioja
7 * @author Vesa Lappalainen
8 *
9 */
10 public class Matrix {
11
12 protected final static int VSIZE = 4;
13 private double mat[][] = new double[VSIZE][VSIZE];
14 private Matrix inversion;
15
16 /**
17 * Luo uuden 4x4 yksikkömatriin
18 */
19 public Matrix() {
20 this.ident();
21 }
22
23 /**
24 * Luo kopion matriisista M
25 * @param M matriisi josta kopio luodaan
26 */
27 public Matrix(Matrix M) {
28 for (int r = 0; r < VSIZE; r++)
29 for (int s = 0; s < VSIZE; s++)
30 mat[r][s] = M.mat[r][s];
31 }
32
33 /**
34 * Muuttaa matriisin yksikkömatriisiksi
35 */
36 protected final void ident() {
37 this.inversion = null;
38 for (int r = 0; r < VSIZE; r++)
39 for (int s = 0; s < VSIZE; s++)
40 mat[r][s] = 0.0;
41 for (int r = 0; r < VSIZE; r++)
42 mat[r][r] = 1.0;
43 }
44
45 /**
46 * Palauttaa matriisin r:n rivin kopion
47 * @param r palautettava rivi
48 * @return r:n rivin kopio
49 */
50 public Vector row(int r) { // NOPMD
51 Vector v = new Vector(); // NOPMD
52 for (int s = 0; s < VSIZE; s++) {
53 v.set(s, mat[r][s]);
54 }
55 return v;
56 }
57
58 /**
59 * Palauttaa matriisin s:n sarakkeen kopion
60 * @param s palautettava sarake
61 * @return s:n sarakkeen kopio
62 */
63 public Vector column(int s) { // NOPMD
64 Vector v = new Vector(); // NOPMD
65 for (int r = 0; r < VSIZE; r++) {
66 v.set(r, mat[r][s]);
67 }
68 return v;
69 }
70
71 /**
72 * Asettaa tämän matriisin annetuksi riviksi annetun vektorin
73 * @param r muutettava rivi
74 * @param v vektori joka asetetaan tämän matriisin riviksi
75 */
76 public void setRow(int r, Vector v) {
77 this.inversion = null;
78 for (int i = 0; i < VSIZE; i++)
79 mat[r][i] = v.a(i);
80 }
81
82 /**
83 * Asettaa tämän matriisin annetuksi sarakkeeksi annetun vektorin
84 * @param s muutettava sarake
85 * @param v vektori joka asetetaan tämän matriisin sarakkeeksi
86 */
87 public void setColumn(int s, Vector v) {
88 this.inversion = null;
89 for (int i = 0; i < VSIZE; i++)
90 mat[i][s] = v.a(i);
91 }
92
93 /**
94 * Palauttaa matriisin alkion paikassa r,s
95 * @param r rivi jolta alkio halutaan
96 * @param s sarake jolta alkio halutaan
97 * @return r,s paikassa oleva alkio
98 */
99 public double m(int r, int s) { // NOPMD
100 return mat[r][s];
101 }
102
103 /**
104 * Asettaa matriisin paikassa r,s olevan alkion
105 * @param r rivi jonka alkio asetetaan
106 * @param s sarake jonka alkio asetetaan
107 * @param d uusi arvo alkiolle
108 */
109 protected void set(int r, int s, double d) {
110 this.inversion = null;
111 mat[r][s] = d;
112 }
113
114 /**
115 * Kertoo matriisin b matriisilla ja palauttaa uuden matriisin tuloksena
116 * @param b matriisi joka kerrotaan
117 * @return uusi matriisi this * b
118 */
119 public Matrix multiply(Matrix b) {
120 if ( b == null ) return this;
121 return multiply(b,new Matrix());
122 }
123
124 /**
125 * Kertoo matriisin b matriisilla ja palauttaa uuden matriisin tuloksena.
126 * this eikä b ei saa olla c!
127 * @param b matriisi joka kerrotaan
128 * @param c matriisi johon tulos tulee
129 * @return uusi matriisi this * b
130 */
131 public Matrix multiply(Matrix b, Matrix c) {
132 // if ( b == c ) throw new Execption
133 for (int r = 0; r < VSIZE; r++)
134 for (int s = 0; s < VSIZE; s++) {
135 double sum = 0;
136 for (int i = 0; i < VSIZE; i++)
137 sum += mat[r][i] * b.mat[i][s];
138 c.mat[r][s] = sum;
139 }
140 return c;
141 }
142
143 /**
144 * Kertoo matriisin b matriisilla ja laittaa tuloksen itseensä
145 * @param b matriisi joka kerrotaan
146 * @return this * b
147 */
148 public Matrix multiplyThis(Matrix b) {
149 this.inversion = null;
150 double row[] = {0,0,0,0};
151 for (int r = 0; r < VSIZE; r++) {
152 for (int s = 0; s < VSIZE; s++) {
153 double sum = 0;
154 for (int i = 0; i < VSIZE; i++)
155 sum += mat[r][i] * b.mat[i][s];
156 row[s] = sum;
157 }
158 for (int s = 0; s < VSIZE; s++)
159 mat[r][s] = row[s];
160 }
161 return this;
162 }
163
164 /**
165 * Kertoo matriisin b matriisilla ja laittaa tuloksen itseensä
166 * @param b matriisi joka kerrotaan
167 * @param row lakun avuksi tuotava 4 paikkainen taulukko
168 * @return this * b
169 */
170 public Matrix multiplyThis(Matrix b,double row[]) {
171 this.inversion = null;
172 //double row[] = {0,0,0,0};
173 for (int r = 0; r < VSIZE; r++) {
174 for (int s = 0; s < VSIZE; s++) {
175 double sum = 0;
176 for (int i = 0; i < VSIZE; i++)
177 sum += mat[r][i] * b.mat[i][s];
178 row[s] = sum;
179 }
180 for (int s = 0; s < VSIZE; s++)
181 mat[r][s] = row[s];
182 }
183 return this;
184 }
185
186 /**
187 * Kertoo vektorin tällä matriisilla, luo uuden paluuvektorin
188 * @param b vektori joka kerrotaan
189 * @return this * b
190 */
191 public Vector multiply(Vector b) { // NOPMD
192 Vector y = new Vector(); // NOPMD
193 for (int r = 0; r < VSIZE; r++) {
194 double sum = 0;
195 for (int s = 0; s < VSIZE; s++)
196 sum += mat[r][s] * b.a(s);
197 y.set(r, sum );
198 }
199 return y;
200 }
201
202 /**
203 * Kertoo vektorin tällä matriisilla ja tuloksen vektoriin y
204 * @param b vektori joka kerrotaan
205 * @param y vektori johon tulos tulee
206 * @return this * b
207 */
208 public Vector multiply(Vector b, Vector y) { // NOPMD
209 for (int r = 0; r < VSIZE; r++) {
210 double sum = 0;
211 for (int s = 0; s < VSIZE; s++)
212 sum += mat[r][s] * b.a(s);
213 y.set(r, sum );
214 }
215 return y;
216 }
217
218 /**
219 * Kertoo vektorin tällä matriisilla ja sijoittaa tuloksen näyttökoordinaattiin iy.
220 * Spesiaalikertominen erityisesti kuvamuunnoksiin, joissa
221 * tarvitaan vain 2 ensimmäistä alkiota vektorista iy.
222 * @param b vektori joka kerrotaan
223 * @param iy vektori johon tulos tulee
224 * @return this * b
225 */
226 public SPoint transform(Vector b, SPoint iy) { // NOPMD
227 double sum0 = 0, sum1 = 0;
228 for (int s = 0; s < VSIZE; s++) sum0 += mat[0][s] * b.a(s);
229 for (int s = 0; s < VSIZE; s++) sum1 += mat[1][s] * b.a(s);
230 // TODO: pitäisi vielä laskea viimeisen rivin arvo, jos
231 // se on !=1; niin sillä jaetaan x ja y.
232 // Tarvitaan sitten kun mat[3] rivillä on jotakin
233 return iy.set((int)round(sum0), (int)round(sum1));
234 }
235
236 /**
237 * Kertoo vektorin tällä matriisilla ja sijoittaa tuloksen näyttökoordinaattiin iy.
238 * Spesiaalikertominen erityisesti kuvamuunnoksiin, joissa
239 * tarvitaan vain 2 ensimmäistä alkiota vektorista iy.
240 * @param b vektori joka kerrotaan
241 * @param iy vektori johon tulos tulee
242 * @return this * b
243 */
244 public RPoint transform(Vector b, RPoint iy) { // NOPMD
245 double sum0 = 0, sum1 = 0;
246 for (int s = 0; s < VSIZE; s++) sum0 += mat[0][s] * b.a(s);
247 for (int s = 0; s < VSIZE; s++) sum1 += mat[1][s] * b.a(s);
248 // TODO: pitäisi vielä laskea viimeisen rivin arvo, jos
249 // se on !=1; niin sillä jaetaan x ja y.
250 // Tarvitaan sitten kun mat[3] rivillä on jotakin
251 iy.set(sum0,sum1,0);
252 return iy;
253 }
254
255 /**
256 * Kertoo vektorin tällä matriisilla ja sijoittaa tuloksen näyttökoordinaattiin iy.
257 * Spesiaalikertominen erityisesti kuvamuunnoksiin, joissa
258 * tarvitaan vain 2 ensimmäistä alkiota vektorista iy.
259 * @param b vektori joka kerrotaan
260 * @param iy vektori johon tulos tulee
261 * @return this * b
262 */
263 public Vector transform(Vector b, Vector iy) { // NOPMD
264 double sum0 = 0, sum1 = 0;
265 for (int s = 0; s < VSIZE; s++) sum0 += mat[0][s] * b.a(s);
266 for (int s = 0; s < VSIZE; s++) sum1 += mat[1][s] * b.a(s);
267 // TODO: pitäisi vielä laskea viimeisen rivin arvo, jos
268 // se on !=1; niin sillä jaetaan x ja y.
269 // Tarvitaan sitten kun mat[3] rivillä on jotakin
270 return iy.set(sum0, sum1);
271 }
272
273 /**
274 * Muuntaa koordinaatin (x,y,z) näytön pisteeksi.
275 * @param x
276 * @param y
277 * @param z
278 * @return uusi näytön piste joka edustaa (x,y,z)
279 */
280 public SPoint transform(double x, double y, double z) {
281 Vector vr = new Vector(x, y, z); // NOPMD
282 SPoint sp = new SPoint(0,0);
283 return this.transform(vr,sp);
284 }
285
286 /**
287 * Muuntaa koordinaatin (x,y,z) näytön pisteeksi.
288 * "Optimoitu" versio, jolle viedään työtilat parametrina.
289 * @param x
290 * @param y
291 * @param z
292 * @param vr vektori johon saa sijoittaa väliaikaisesti (x,y,z)
293 * @param sp näytön piste johon tuloksen saa sijoittaa.
294 * @return parametrina tuotu näytön piste joka edustaa (x,y,z)
295 */
296 public SPoint transform(double x, double y, double z, Vector vr, SPoint sp) { // NOPMD
297 return this.transform(vr.set(x,y,z),sp);
298 }
299
300
301 /**
302 * Muuntaa koordinaatin (x,y,z) näytön pisteeksi.
303 * "Optimoitu" versio, jolle viedään työtilat parametrina.
304 * @param x
305 * @param y
306 * @param z
307 * @param vr vektori johon saa sijoittaa väliaikaisesti (x,y,z)
308 * @param sp näytön piste johon tuloksen saa sijoittaa.
309 * @return parametrina tuotu näytön piste joka edustaa (x,y,z)
310 */
311 public Vector transform(double x, double y, double z, Vector vr, Vector sp) { // NOPMD
312 return this.transform(vr.set(x,y,z),sp);
313 }
314
315
316 /**
317 * Muuntaa pisteen p näytön pisteeksi.
318 * @param p muunettava piste.
319 * @return uusi näytönpiste joka vastaa pistettä p.
320 */
321 public SPoint transform(RPoint p) {
322 SPoint sp = new SPoint(0,0);
323 return this.transform(p,sp);
324 }
325
326 /**
327 * Muuntaa pisteen p näytön pisteeksi.
328 * @param p muunettava piste.
329 * @return uusi näytönpiste joka vastaa pistettä p.
330 */
331 public RPoint transformR(RPoint p) {
332 RPoint rp = new RPoint(0,0);
333 return this.transform(p,rp);
334 }
335
336 /**
337 * Laskee tämän matriisin käänteismatriisin Gaussin
338 * eliminointimenetelmällä ja palauttaa sen.
339 * (Menetelmä on hidas suurilla VSIZE-muuttujan arvoilla)
340 * @return inversion, tämän matriisin käänteismatriisi
341 */
342 public Matrix getInversion() {
343 if (this.inversion == null) {
344 Matrix temp = new Matrix(this);
345 Matrix tempInversion = new Matrix();
346 double multiplier = 0;
347
348 // Nollataan diagonaalin alapuolella olevat alkiot
349 for (int i = 0; i < VSIZE; i++) {
350 for (int j = i + 1; j < VSIZE; j++) {
351 multiplier = -(temp.m(j, i) / temp.m(i, i));
352 temp.setRow(j, temp.row(j).sum(
353 temp.row(i).scalarProduct(multiplier)));
354 tempInversion.setRow(j, tempInversion.row(j).sum(
355 tempInversion.row(i).scalarProduct(multiplier)));
356 }
357 }
358
359 // Nollataan diagonaalin yläpuolella olevat alkiot
360 for (int i = VSIZE - 1; i >= 0; i--) {
361 for (int j = i - 1; j >= 0; j--) {
362 multiplier = -(temp.m(j, i) / temp.m(i, i));
363 temp.setRow(j, temp.row(j).sum(
364 temp.row(i).scalarProduct(multiplier)));
365 tempInversion.setRow(j, tempInversion.row(j).sum(
366 tempInversion.row(i).scalarProduct(multiplier)));
367 }
368 }
369
370 // Jaetaan rivit diagonaalialkioilla
371 for (int i = 0; i < VSIZE; i++) {
372 multiplier = 1 / temp.m(i, i);
373 tempInversion.setRow(i, tempInversion.row(i).scalarProduct(
374 multiplier));
375 }
376 this.inversion = tempInversion;
377 }
378 return this.inversion;
379 }
380
381 /**
382 * Palauttaa tämän matrisiin merkkijonona
383 * @return tämä matriisi merkkijonona
384 */
385 @Override
386 public String toString() {
387 StringBuilder sb = new StringBuilder();
388 for (int r = 0; r < VSIZE; r++) {
389 sb.append("( ");
390 for (int s = 0; s < VSIZE; s++)
391 sb.append(" " + mat[r][s]);
392 sb.append(" )\n");
393 }
394 return sb.toString();
395 }
396
397 }
398
399