/* * Copyright (c) 2008, 2009, 2010, 2011 * Jonathan Schleifer <js@webkeks.org> * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in * the packaging of this file. * * Alternatively, it may be distributed under the terms of the GNU General * Public License, either version 2 or 3, which can be found in the file * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this * file. */ #include "config.h" #include <stdlib.h> #include <string.h> #include <math.h> #import "OFFloatMatrix.h" #import "OFFloatVector.h" #import "OFString.h" #import "OFInvalidArgumentException.h" #import "OFNotImplementedException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "macros.h" static Class floatVector = Nil; @implementation OFFloatMatrix + (void)initialize { if (self == [OFFloatMatrix class]) floatVector = [OFFloatVector class]; } + matrixWithRows: (size_t)rows columns: (size_t)columns { return [[[self alloc] initWithRows: rows columns: columns] autorelease]; } + matrixWithRows: (size_t)rows columns: (size_t)columns data: (float)data, ... { id ret; va_list arguments; va_start(arguments, data); ret = [[[self alloc] initWithRows: rows columns: columns data: data arguments: arguments] autorelease]; va_end(arguments); return ret; } - init { Class c = isa; [self release]; @throw [OFNotImplementedException newWithClass: c selector: _cmd]; } - initWithRows: (size_t)rows_ columns: (size_t)columns_ { self = [super init]; @try { rows = rows_; columns = columns_; if (SIZE_MAX / rows < columns || SIZE_MAX / rows * columns < sizeof(float)) @throw [OFOutOfRangeException newWithClass: isa]; if ((data = malloc(rows * columns * sizeof(float))) == NULL) @throw [OFOutOfMemoryException newWithClass: isa requestedSize: rows * columns * sizeof(float)]; memset(data, 0, rows * columns * sizeof(float)); if (rows == columns) { size_t i; for (i = 0; i < rows * columns; i += rows + 1) data[i] = 1; } } @catch (id e) { [self release]; @throw e; } return self; } - initWithRows: (size_t)rows_ columns: (size_t)columns_ data: (float)data_, ... { id ret; va_list arguments; va_start(arguments, data_); ret = [self initWithRows: rows_ columns: columns_ data: data_ arguments: arguments]; va_end(arguments); return ret; } - initWithRows: (size_t)rows_ columns: (size_t)columns_ data: (float)data_ arguments: (va_list)arguments { self = [super init]; @try { size_t i; rows = rows_; columns = columns_; if (SIZE_MAX / rows < columns || SIZE_MAX / rows * columns < sizeof(float)) @throw [OFOutOfRangeException newWithClass: isa]; if ((data = malloc(rows * columns * sizeof(float))) == NULL) @throw [OFOutOfMemoryException newWithClass: isa requestedSize: rows * columns * sizeof(float)]; for (i = 0; i < rows; i++) { size_t j; for (j = i; j < rows * columns; j += rows) data[j] = (j == 0 ? data_ : (float)va_arg(arguments, double)); } } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { free(data); [super dealloc]; } - (void)setValue: (float)value forRow: (size_t)row column: (size_t)column { if (row >= rows || column >= columns) @throw [OFOutOfRangeException newWithClass: isa]; data[row * columns + column] = value; } - (float)valueForRow: (size_t)row column: (size_t)column { if (row >= rows || column >= columns) @throw [OFOutOfRangeException newWithClass: isa]; return data[row * columns + column]; } - (size_t)rows { return rows; } - (size_t)columns { return columns; } - (BOOL)isEqual: (id)object { OFFloatMatrix *otherMatrix; if (![object isKindOfClass: [OFFloatMatrix class]]) return NO; otherMatrix = object; if (otherMatrix->rows != rows || otherMatrix->columns != columns) return NO; if (memcmp(otherMatrix->data, data, rows * columns * sizeof(float))) return NO; return YES; } - (uint32_t)hash { size_t i; uint32_t hash; OF_HASH_INIT(hash); for (i = 0; i < rows * columns; i++) { union { float f; uint8_t b[sizeof(float)]; } u; uint8_t j; u.f = of_bswap_float_if_be(data[i]); for (j = 0; j < sizeof(float); j++) OF_HASH_ADD(hash, u.b[j]); } OF_HASH_FINALIZE(hash); return hash; } - copy { OFFloatMatrix *copy = [[isa alloc] initWithRows: rows columns: columns]; memcpy(copy->data, data, rows * columns * sizeof(float)); return copy; } - (OFString*)description { OFMutableString *description; size_t i; description = [OFMutableString stringWithFormat: @"<%@, (\n", [self className]]; for (i = 0; i < rows; i++) { size_t j; [description appendString: @"\t"]; for (j = 0; j < columns; j++) { if (j != columns - 1) [description appendFormat: @"%10f ", data[j * rows + i]]; else [description appendFormat: @"%10f\n", data[j * rows + i]]; } } [description appendString: @")>"]; [description makeImmutable]; return description; } - (float*)cArray { return data; } - (void)addMatrix: (OFFloatMatrix*)matrix { size_t i; if (matrix->isa != isa || matrix->rows != rows || matrix->columns != columns) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; for (i = 0; i < rows * columns; i++) data[i] += matrix->data[i]; } - (void)subtractMatrix: (OFFloatMatrix*)matrix { size_t i; if (matrix->isa != isa || matrix->rows != rows || matrix->columns != columns) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; for (i = 0; i < rows * columns; i++) data[i] -= matrix->data[i]; } - (void)multiplyWithScalar: (float)scalar { size_t i; for (i = 0; i < rows * columns; i++) data[i] *= scalar; } - (void)divideByScalar: (float)scalar { size_t i; for (i = 0; i < rows * columns; i++) data[i] /= scalar; } - (void)multiplyWithMatrix: (OFFloatMatrix*)matrix { float *newData; size_t i, base1, base2; if (rows != matrix->columns) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; if ((newData = malloc(matrix->rows * columns * sizeof(float))) == NULL) @throw [OFOutOfMemoryException newWithClass: isa requestedSize: matrix->rows * columns * sizeof(float)]; base1 = 0; base2 = 0; for (i = 0; i < columns; i++) { size_t base3 = base2; size_t j; for (j = 0; j < matrix->rows; j++) { size_t base4 = j; size_t base5 = base1; float tmp = 0.0f; size_t k; for (k = 0; k < matrix->columns; k++) { tmp += matrix->data[base4] * data[base5]; base4 += matrix->rows; base5++; } newData[base3] = tmp; base3++; } base1 += rows; base2 += matrix->rows; } free(data); data = newData; rows = matrix->rows; } - (void)transpose { float *newData; size_t i, k; if ((newData = malloc(rows * columns * sizeof(float))) == NULL) @throw [OFOutOfMemoryException newWithClass: isa requestedSize: rows * columns * sizeof(float)]; rows ^= columns; columns ^= rows; rows ^= columns; for (i = k = 0; i < rows; i++) { size_t j; for (j = i; j < rows * columns; j += rows) newData[j] = data[k++]; } free(data); data = newData; } - (void)translateWithVector: (OFFloatVector*)vector { OFFloatMatrix *translation; float *cArray; if (rows != columns || vector->isa != floatVector || vector->dimension != rows - 1) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; cArray = [vector cArray]; translation = [[OFFloatMatrix alloc] initWithRows: rows columns: columns]; memcpy(translation->data + (columns - 1) * rows, cArray, (rows - 1) * sizeof(float)); @try { [self multiplyWithMatrix: translation]; } @finally { [translation release]; } } - (void)rotateWithVector: (OFFloatVector*)vector angle: (float)angle { OFFloatMatrix *rotation; float n[3], m, angleCos, angleSin; if (rows != 4 || columns != 4 || vector->isa != floatVector || vector->dimension != 3) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; n[0] = vector->data[0]; n[1] = vector->data[1]; n[2] = vector->data[2]; m = sqrtf(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]); if (m != 1.0) { n[0] /= m; n[1] /= m; n[2] /= m; } angle = (float)(angle * M_PI / 180.0f); angleCos = cosf(angle); angleSin = sinf(angle); rotation = [[OFFloatMatrix alloc] initWithRows: rows columns: columns]; rotation->data[0] = angleCos + n[0] * n[0] * (1 - angleCos); rotation->data[1] = n[1] * n[0] * (1 - angleCos) + n[2] * angleSin; rotation->data[2] = n[2] * n[0] * (1 - angleCos) - n[1] * angleSin; rotation->data[4] = n[0] * n[1] * (1 - angleCos) - n[2] * angleSin; rotation->data[5] = angleCos + n[1] * n[1] * (1 - angleCos); rotation->data[6] = n[2] * n[1] * (1 - angleCos) + n[0] * angleSin; rotation->data[8] = n[0] * n[2] * (1 - angleCos) + n[1] * angleSin; rotation->data[9] = n[1] * n[2] * (1 - angleCos) - n[0] * angleSin; rotation->data[10] = angleCos + n[2] * n[2] * (1 - angleCos); @try { [self multiplyWithMatrix: rotation]; } @finally { [rotation release]; } } - (void)scaleWithVector: (OFFloatVector*)vector { OFFloatMatrix *scale; float *cArray; size_t i, j; if (rows != columns || vector->isa != floatVector || vector->dimension != rows - 1) @throw [OFInvalidArgumentException newWithClass: isa selector: _cmd]; cArray = [vector cArray]; scale = [[OFFloatMatrix alloc] initWithRows: rows columns: columns]; for (i = j = 0; i < ((rows - 1) * columns) - 1; i += rows + 1) scale->data[i] = cArray[j++]; @try { [self multiplyWithMatrix: scale]; } @finally { [scale release]; } } @end