Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -243,10 +243,14 @@ 4BB25E8B139C388A00F574EA /* OFString+Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB25E85139C388A00F574EA /* OFString+Serialization.m */; }; 4BB25E8C139C388A00F574EA /* OFXMLElement+Serialization.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BB25E86139C388A00F574EA /* OFXMLElement+Serialization.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BB25E8D139C388A00F574EA /* OFXMLElement+Serialization.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BB25E87139C388A00F574EA /* OFXMLElement+Serialization.m */; }; 4BD98C03133814220048DD5B /* objfw-defs.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BD98C011338140B0048DD5B /* objfw-defs.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BDF37B51338055600F9A81A /* config.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BDF37B41338055600F9A81A /* config.h */; }; + 4BE852D213B7671200C00856 /* OFDoubleMatrix.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BE852CE13B7671200C00856 /* OFDoubleMatrix.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4BE852D313B7671200C00856 /* OFDoubleMatrix.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE852CF13B7671200C00856 /* OFDoubleMatrix.m */; }; + 4BE852D413B7671200C00856 /* OFDoubleVector.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BE852D013B7671200C00856 /* OFDoubleVector.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4BE852D513B7671200C00856 /* OFDoubleVector.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE852D113B7671200C00856 /* OFDoubleVector.m */; }; 4BE920A813A2ECEF00154B94 /* OFFloatVector.h in Headers */ = {isa = PBXBuildFile; fileRef = 4BE920A613A2ECEF00154B94 /* OFFloatVector.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4BE920A913A2ECEF00154B94 /* OFFloatVector.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE920A713A2ECEF00154B94 /* OFFloatVector.m */; }; 4BF33AFB133807590059CEF7 /* ObjFW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B3D23761337FBC800DD29B8 /* ObjFW.framework */; }; 4BF33AFC133807A20059CEF7 /* OFArrayTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B6EF66E1235358D0076B512 /* OFArrayTests.m */; }; 4BF33AFD133807A20059CEF7 /* OFBlockTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BE5F0E412DF4259005C7A0C /* OFBlockTests.m */; }; @@ -562,10 +566,14 @@ 4BE5F0D812DF4225005C7A0C /* OFConstantString.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFConstantString.m; path = src/OFConstantString.m; sourceTree = SOURCE_ROOT; }; 4BE5F0D912DF4225005C7A0C /* OFDate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFDate.h; path = src/OFDate.h; sourceTree = SOURCE_ROOT; }; 4BE5F0DA12DF4225005C7A0C /* OFDate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFDate.m; path = src/OFDate.m; sourceTree = SOURCE_ROOT; }; 4BE5F0E412DF4259005C7A0C /* OFBlockTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFBlockTests.m; path = tests/OFBlockTests.m; sourceTree = SOURCE_ROOT; }; 4BE5F0E512DF4259005C7A0C /* OFDateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFDateTests.m; path = tests/OFDateTests.m; sourceTree = SOURCE_ROOT; }; + 4BE852CE13B7671200C00856 /* OFDoubleMatrix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFDoubleMatrix.h; path = src/OFDoubleMatrix.h; sourceTree = ""; }; + 4BE852CF13B7671200C00856 /* OFDoubleMatrix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFDoubleMatrix.m; path = src/OFDoubleMatrix.m; sourceTree = ""; }; + 4BE852D013B7671200C00856 /* OFDoubleVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFDoubleVector.h; path = src/OFDoubleVector.h; sourceTree = ""; }; + 4BE852D113B7671200C00856 /* OFDoubleVector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFDoubleVector.m; path = src/OFDoubleVector.m; sourceTree = ""; }; 4BE920A613A2ECEF00154B94 /* OFFloatVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFFloatVector.h; path = src/OFFloatVector.h; sourceTree = ""; }; 4BE920A713A2ECEF00154B94 /* OFFloatVector.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFFloatVector.m; path = src/OFFloatVector.m; sourceTree = ""; }; 4BF0749512DFAFCA00A4ADD1 /* OFURLTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFURLTests.m; path = tests/OFURLTests.m; sourceTree = SOURCE_ROOT; }; 4BF1BCBF11C9663F0025511F /* objfw-defs.h.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "objfw-defs.h.in"; path = "src/objfw-defs.h.in"; sourceTree = ""; }; 4BF1BCC011C9663F0025511F /* OFHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFHash.h; path = src/OFHash.h; sourceTree = ""; }; @@ -791,10 +799,14 @@ 4BE17ADA12FD746D002CEB0B /* OFDataArray+Hashing.m */, 4BE5F0D912DF4225005C7A0C /* OFDate.h */, 4BE5F0DA12DF4225005C7A0C /* OFDate.m */, 4B6799621099E7C50041064A /* OFDictionary.h */, 4B6799631099E7C50041064A /* OFDictionary.m */, + 4BE852CE13B7671200C00856 /* OFDoubleMatrix.h */, + 4BE852CF13B7671200C00856 /* OFDoubleMatrix.m */, + 4BE852D013B7671200C00856 /* OFDoubleVector.h */, + 4BE852D113B7671200C00856 /* OFDoubleVector.m */, 4B0108C910EB8C9300631877 /* OFEnumerator.h */, 4B0108CA10EB8C9300631877 /* OFEnumerator.m */, 4B6799661099E7C50041064A /* OFFile.h */, 4B6799671099E7C50041064A /* OFFile.m */, 4B6965E013A58B1B004F1C3A /* OFFloatMatrix.h */, @@ -977,10 +989,12 @@ 4B3D23C11337FC8300DD29B8 /* OFConstantString.h in Headers */, 4B3D23C21337FC8300DD29B8 /* OFDataArray.h in Headers */, 4B3D23C31337FC8300DD29B8 /* OFDataArray+Hashing.h in Headers */, 4B3D23C41337FC8300DD29B8 /* OFDate.h in Headers */, 4B3D23C51337FCB000DD29B8 /* OFDictionary.h in Headers */, + 4BE852D213B7671200C00856 /* OFDoubleMatrix.h in Headers */, + 4BE852D413B7671200C00856 /* OFDoubleVector.h in Headers */, 4B3D23C61337FCB000DD29B8 /* OFEnumerator.h in Headers */, 4B17FF74133A2AAB003E6DCD /* OFException.h in Headers */, 4B3D23C81337FCB000DD29B8 /* OFFile.h in Headers */, 4B6965E213A58B1B004F1C3A /* OFFloatMatrix.h in Headers */, 4BE920A813A2ECEF00154B94 /* OFFloatVector.h in Headers */, @@ -1231,10 +1245,12 @@ 4B3D238F1337FC0D00DD29B8 /* OFConstantString.m in Sources */, 4B3D23901337FC0D00DD29B8 /* OFDataArray.m in Sources */, 4B3D23911337FC0D00DD29B8 /* OFDataArray+Hashing.m in Sources */, 4B3D23921337FC0D00DD29B8 /* OFDate.m in Sources */, 4B3D23931337FC0D00DD29B8 /* OFDictionary.m in Sources */, + 4BE852D313B7671200C00856 /* OFDoubleMatrix.m in Sources */, + 4BE852D513B7671200C00856 /* OFDoubleVector.m in Sources */, 4B3D23941337FC0D00DD29B8 /* OFEnumerator.m in Sources */, 4B3D23961337FC0D00DD29B8 /* OFFile.m in Sources */, 4B6965E313A58B1B004F1C3A /* OFFloatMatrix.m in Sources */, 4BE920A913A2ECEF00154B94 /* OFFloatVector.m in Sources */, 4B3D23971337FC0D00DD29B8 /* OFHash.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -14,10 +14,12 @@ OFConstantString.m \ OFDataArray.m \ OFDataArray+Hashing.m \ OFDate.m \ OFDictionary.m \ + OFDoubleMatrix.m \ + OFDoubleVector.m \ OFEnumerator.m \ OFFile.m \ OFFloatMatrix.m \ OFFloatVector.m \ OFHash.m \ ADDED src/OFDoubleMatrix.h Index: src/OFDoubleMatrix.h ================================================================== --- src/OFDoubleMatrix.h +++ src/OFDoubleMatrix.h @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * Jonathan Schleifer + * + * 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 + +#import "OFObject.h" + +@class OFDoubleVector; + +/** + * \brief A class for storing and manipulating matrices of doubles. + */ +@interface OFDoubleMatrix: OFObject +{ +@public + size_t rows, columns; + double *data; +} + +/** + * \brief Creates a new matrix with the specified dimension. + * + * If the number of rows and columns is equal, the matrix is initialized to be + * the identity. + * + * \param rows The number of rows for the matrix + * \param columns The number of colums for the matrix + * \return A new autoreleased OFDoubleMatrix + */ ++ matrixWithRows: (size_t)rows + columns: (size_t)columns; + +/** + * \brief Creates a new matrix with the specified dimension and data. + * + * \param rows The number of rows for the matrix + * \param columns The number of colums for the matrix + * \return A new autoreleased OFDoubleMatrix + */ ++ matrixWithRows: (size_t)rows + columnsAndData: (size_t)columns, ...; + +/** + * \brief Initializes the matrix with the specified dimension. + * + * If the number of rows and columns is equal, the matrix is initialized to be + * the identity. + * + * \param rows The number of rows for the matrix + * \param columns The number of colums for the matrix + * \return An initialized OFDoubleMatrix + */ +- initWithRows: (size_t)rows + columns: (size_t)columns; + +/** + * \brief Initializes the matrix with the specified dimension and data. + * + * \param rows The number of rows for the matrix + * \param columns The number of colums for the matrix + * \return An initialized OFDoubleMatrix + */ +- initWithRows: (size_t)rows + columnsAndData: (size_t)columns, ...; + +/** + * \brief Initializes the matrix with the specified dimension and arguments. + * + * \param rows The number of rows for the matrix + * \param columns The number of colums for the matrix + * \param arguments A va_list with data for the matrix + * \return An initialized OFDoubleMatrix + */ +- initWithRows: (size_t)rows + columns: (size_t)columns + arguments: (va_list)arguments; + +/** + * \brief Sets the value for the specified row and colmn. + * + * \param value The value + * \param row The row for the value + * \param column The column for the value + */ +- (void)setValue: (double)value + forRow: (size_t)row + column: (size_t)column; + +/** + * \brief Returns the value for the specified row and column. + * + * \param row The row for which the value should be returned + * \param column The column for which the value should be returned + * \return The value for the specified row and column + */ +- (double)valueForRow: (size_t)row + column: (size_t)column; + +/** + * \brief Returns the number of rows of the matrix. + * + * \return The number of rows of the matrix + */ +- (size_t)rows; + +/** + * \brief Returns the number of columns of the matrix. + * + * \return The number of columns of the matrix + */ +- (size_t)columns; + +/** + * \brief Returns an array of doubles with the contents of the matrix. + * + * The returned array is in the format columns-rows. + * Modifying the returned array directly is allowed and will change the matrix. + * + * \brief An array of doubles with the contents of the vector + */ +- (double*)cArray; + +/** + * \brief Adds the specified matrix to the receiver. + * + * \param matrix The matrix to add + */ +- (void)addMatrix: (OFDoubleMatrix*)matrix; + +/** + * \brief Subtracts the specified matrix from the receiver. + * + * \param matrix The matrix to subtract + */ +- (void)subtractMatrix: (OFDoubleMatrix*)matrix; + +/** + * \brief Multiplies the receiver with the specified scalar. + * + * \param scalar The scalar to multiply with + */ +- (void)multiplyWithScalar: (double)scalar; + +/** + * \brief Divides the receiver by the specified scalar. + * + * \param scalar The scalar to divide by + */ +- (void)divideByScalar: (double)scalar; + +/** + * \brief Multiplies the receiver with the specified matrix on the left side and + * the receiver on the right. + * + * \param matrix The matrix to multiply the receiver with + */ +- (void)multiplyWithMatrix: (OFDoubleMatrix*)matrix; + +/** + * \brief Transposes the receiver. + */ +- (void)transpose; + +/** + * \brief Translates the nxn matrix of the receiver with an n-1 vector. + * + * \param vector The vector to translate with + */ +- (void)translateWithVector: (OFDoubleVector*)vector; + +/** + * \brief Rotates the 4x4 matrix of the receiver with a 3D vector and an angle. + * + * \param vector The vector to rotate with + * \param angle The angle to rotate with + */ +- (void)rotateWithVector: (OFDoubleVector*)vector + angle: (double)angle; + +/** + * \brief Scales the nxn matrix of the receiver with an n-1 vector. + * + * \param scale The vector to scale with + */ +- (void)scaleWithVector: (OFDoubleVector*)vector; +@end ADDED src/OFDoubleMatrix.m Index: src/OFDoubleMatrix.m ================================================================== --- src/OFDoubleMatrix.m +++ src/OFDoubleMatrix.m @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * Jonathan Schleifer + * + * 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 +#include +#include + +#import "OFDoubleMatrix.h" +#import "OFDoubleVector.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" +#import "OFNotImplementedException.h" +#import "OFOutOfMemoryException.h" +#import "OFOutOfRangeException.h" + +#import "macros.h" + +static Class doubleVector = Nil; + +@implementation OFDoubleMatrix ++ (void)initialize +{ + if (self == [OFDoubleMatrix class]) + doubleVector = [OFDoubleVector class]; +} + ++ matrixWithRows: (size_t)rows + columns: (size_t)columns +{ + return [[[self alloc] initWithRows: rows + columns: columns] autorelease]; +} + ++ matrixWithRows: (size_t)rows + columnsAndData: (size_t)columns, ... +{ + id ret; + va_list arguments; + + va_start(arguments, columns); + ret = [[[self alloc] initWithRows: rows + columns: columns + 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(double)) + @throw [OFOutOfRangeException + newWithClass: isa]; + + if ((data = malloc(rows * columns * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException + newWithClass: isa + requestedSize: rows * columns * sizeof(double)]; + + memset(data, 0, rows * columns * sizeof(double)); + + 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_ + columnsAndData: (size_t)columns_, ... +{ + id ret; + va_list arguments; + + va_start(arguments, columns_); + ret = [self initWithRows: rows_ + columns: columns_ + arguments: arguments]; + va_end(arguments); + + return ret; +} + +- initWithRows: (size_t)rows_ + columns: (size_t)columns_ + 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(double)) + @throw [OFOutOfRangeException newWithClass: isa]; + + if ((data = malloc(rows * columns * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException + newWithClass: isa + requestedSize: rows * columns * sizeof(double)]; + + for (i = 0; i < rows; i++) { + size_t j; + + for (j = i; j < rows * columns; j += rows) + data[j] = (double)va_arg(arguments, double); + } + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + free(data); + + [super dealloc]; +} + +- (void)setValue: (double)value + forRow: (size_t)row + column: (size_t)column +{ + if (row >= rows || column >= columns) + @throw [OFOutOfRangeException newWithClass: isa]; + + data[row * columns + column] = value; +} + +- (double)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 +{ + OFDoubleMatrix *otherMatrix; + + if (![object isKindOfClass: [OFDoubleMatrix class]]) + return NO; + + otherMatrix = object; + + if (otherMatrix->rows != rows || otherMatrix->columns != columns) + return NO; + + if (memcmp(otherMatrix->data, data, rows * columns * sizeof(double))) + 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 { + double f; + uint64_t i; + } u; + + u.f = data[i]; + + OF_HASH_ADD_INT64(hash, u.i); + } + + OF_HASH_FINALIZE(hash); + + return hash; +} + +- copy +{ + OFDoubleMatrix *copy = [[isa alloc] initWithRows: rows + columns: columns]; + + memcpy(copy->data, data, rows * columns * sizeof(double)); + + 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: @")>"]; + + /* + * Class swizzle the string to be immutable. We declared the return type + * to be OFString*, so it can't be modified anyway. But not swizzling it + * would create a real copy each time -[copy] is called. + */ + description->isa = [OFString class]; + return description; +} + +- (double*)cArray +{ + return data; +} + +- (void)addMatrix: (OFDoubleMatrix*)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: (OFDoubleMatrix*)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: (double)scalar +{ + size_t i; + + for (i = 0; i < rows * columns; i++) + data[i] *= scalar; +} + +- (void)divideByScalar: (double)scalar +{ + size_t i; + + for (i = 0; i < rows * columns; i++) + data[i] /= scalar; +} + +- (void)multiplyWithMatrix: (OFDoubleMatrix*)matrix +{ + double *newData; + size_t i, base1, base2; + + if (rows != matrix->columns) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + if ((newData = malloc(matrix->rows * columns * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException + newWithClass: isa + requestedSize: matrix->rows * columns * sizeof(double)]; + + 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; + double tmp = 0.0; + 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 +{ + double *newData; + size_t i, k; + + if ((newData = malloc(rows * columns * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException newWithClass: isa + requestedSize: rows * columns * + sizeof(double)]; + + 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: (OFDoubleVector*)vector +{ + OFDoubleMatrix *translation; + double *cArray; + + if (rows != columns || vector->isa != doubleVector || + vector->dimension != rows - 1) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + cArray = [vector cArray]; + translation = [[OFDoubleMatrix alloc] initWithRows: rows + columns: columns]; + + memcpy(translation->data + (columns - 1) * rows, cArray, + (rows - 1) * sizeof(double)); + + @try { + [self multiplyWithMatrix: translation]; + } @finally { + [translation release]; + } +} + +- (void)rotateWithVector: (OFDoubleVector*)vector + angle: (double)angle +{ + OFDoubleMatrix *rotation; + double n[3], m, angleCos, angleSin; + + if (rows != 4 || columns != 4 || vector->isa != doubleVector || + 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 = sqrt(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 = (double)(angle * M_PI / 180.0); + angleCos = cos(angle); + angleSin = sin(angle); + + rotation = [[OFDoubleMatrix 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: (OFDoubleVector*)vector +{ + OFDoubleMatrix *scale; + double *cArray; + size_t i, j; + + if (rows != columns || vector->isa != doubleVector || + vector->dimension != rows - 1) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + cArray = [vector cArray]; + scale = [[OFDoubleMatrix 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 ADDED src/OFDoubleVector.h Index: src/OFDoubleVector.h ================================================================== --- src/OFDoubleVector.h +++ src/OFDoubleVector.h @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * Jonathan Schleifer + * + * 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 + +#import "OFObject.h" + +@class OFDoubleMatrix; + +/** + * \brief A class for storing and manipulating vectors of doubles. + */ +@interface OFDoubleVector: OFObject +{ +@public + size_t dimension; + double *data; +} + +/** + * \brief Creates a new vector with the specified dimension. + * + * \param dimension The dimension for the vector + * \return A new autoreleased OFDoubleVector + */ ++ vectorWithDimension: (size_t)dimension; + +/** + * \brief Creates a new vector with the specified dimension and data. + * + * \param dimension The dimension for the vector + * \return A new autoreleased OFDoubleVector + */ ++ vectorWithDimensionAndData: (size_t)dimension, ...; + +/** + * \brief Initializes the vector with the specified dimension. + * + * \param dimension The dimension for the vector + * \return An initialized OFDoubleVector + */ +- initWithDimension: (size_t)dimension; + +/** + * \brief Initializes the vector with the specified dimension and data. + * + * \param dimension The dimension for the vector + * \return An initialized OFDoubleVector + */ +- initWithDimensionAndData: (size_t)dimension, ...; + +/** + * \brief Initializes the vector with the specified dimension and arguments. + * + * \param dimension The dimension for the vector + * \param arguments A va_list with data for the vector + * \return An initialized OFDoubleVector + */ +- initWithDimension: (size_t)dimension + arguments: (va_list)arguments; + +/** + * \brief Sets the value for the specified index. + * + * \param value The value + * \param index The index for the value + */ +- (void)setValue: (double)value + atIndex: (size_t)index; + +/** + * \brief Returns the value for the specified index. + * + * \param index The index for which the value should be returned + * \return The value for the specified index + */ +- (double)valueAtIndex: (size_t)index; + +/** + * \brief Returns the dimension of the vector. + * + * \return The dimension of the vector + */ +- (size_t)dimension; + +/** + * \brief Changes the dimension of the vector. + * + * If the new dimension is smaller, elements will be cut off. + * If the new dimension is bigger, new elements will be filled with zeros. + * + * \param dimension The new dimension for the vector + */ +- (void)setDimension: (size_t)dimension; + +/** + * \brief Returns an array of doubles with the contents of the vector. + * + * Modifying the returned array directly is allowed and will change the vector. + * + * \return An array of doubles with the contents of the vector + */ +- (double*)cArray; + +/** + * \brief Returns the magnitude or length of the vector. + * + * \return The magnitude or length of the vector + */ +- (double)magnitude; + +/** + * \brief Normalizes the vector. + */ +- (void)normalize; + +/** + * \brief Adds the specified vector to the receiver. + * + * \param vector The vector to add + */ +- (void)addVector: (OFDoubleVector*)vector; + +/** + * \brief Subtracts the specified vector from the receiver. + * + * \param vector The vector to subtract + */ +- (void)subtractVector: (OFDoubleVector*)vector; + +/** + * \brief Multiplies the receiver with the specified scalar. + * + * \param scalar The scalar to multiply with + */ +- (void)multiplyWithScalar: (double)scalar; + +/** + * \brief Divides the receiver by the specified scalar. + * + * \param scalar The scalar to divide by + */ +- (void)divideByScalar: (double)scalar; + +/** + * \brief Multiplies the components of the receiver with the components of the + * specified vector. + * + * \param vector The vector to multiply the receiver with + */ +- (void)multiplyWithComponentsOfVector: (OFDoubleVector*)vector; + +/** + * \brief Divides the components of the receiver by the components of the + * specified vector. + * + * \param vector The vector to divide the receiver by + */ +- (void)divideByComponentsOfVector: (OFDoubleVector*)vector; + +/** + * \brief Returns the dot product of the receiver and the specified vector. + * + * \return The dot product of the receiver and the specified vector + */ +- (double)dotProductWithVector: (OFDoubleVector*)vector; + +/** + * \brief Returns the cross product of the receiver and the specified vector. + * + * This currently only works for 3D vectors. + * + * \return The cross product of the receiver and the specified vector + */ +- (OFDoubleVector*)crossProductWithVector: (OFDoubleVector*)vector; + +/** + * \brief Multiplies the receiver with the specified matrix on the left side and + * the receiver on the right side. + * + * \param matrix The matrix to multiply the receiver with + */ +- (void)multiplyWithMatrix: (OFDoubleMatrix*)matrix; +@end ADDED src/OFDoubleVector.m Index: src/OFDoubleVector.m ================================================================== --- src/OFDoubleVector.m +++ src/OFDoubleVector.m @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011 + * Jonathan Schleifer + * + * 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 +#include +#include + +#import "OFDoubleVector.h" +#import "OFDoubleMatrix.h" +#import "OFString.h" + +#import "OFInvalidArgumentException.h" +#import "OFNotImplementedException.h" +#import "OFOutOfMemoryException.h" +#import "OFOutOfRangeException.h" + +#import "macros.h" + +static Class doubleMatrix = Nil; + +@implementation OFDoubleVector ++ (void)initialize +{ + if (self == [OFDoubleVector class]) + doubleMatrix = [OFDoubleMatrix class]; +} + ++ vectorWithDimension: (size_t)dimension +{ + return [[[self alloc] initWithDimension: dimension] autorelease]; +} + ++ vectorWithDimensionAndData: (size_t)dimension, ... +{ + id ret; + va_list arguments; + + va_start(arguments, dimension); + ret = [[[self alloc] initWithDimension: dimension + arguments: arguments] autorelease]; + va_end(arguments); + + return ret; +} + +- init +{ + Class c = isa; + [self release]; + @throw [OFNotImplementedException newWithClass: c + selector: _cmd]; +} + +- initWithDimension: (size_t)dimension_ +{ + self = [super init]; + + @try { + dimension = dimension_; + + if (SIZE_MAX / dimension < sizeof(double)) + @throw [OFOutOfRangeException newWithClass: isa]; + + if ((data = malloc(dimension * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException + newWithClass: isa + requestedSize: dimension * sizeof(double)]; + + memset(data, 0, dimension * sizeof(double)); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- initWithDimensionAndData: (size_t)dimension_, ... +{ + id ret; + va_list arguments; + + va_start(arguments, dimension_); + ret = [self initWithDimension: dimension_ + arguments: arguments]; + va_end(arguments); + + return ret; +} + +- initWithDimension: (size_t)dimension_ + arguments: (va_list)arguments +{ + self = [super init]; + + @try { + size_t i; + + dimension = dimension_; + + if (SIZE_MAX / dimension < sizeof(double)) + @throw [OFOutOfRangeException newWithClass: isa]; + + if ((data = malloc(dimension * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException + newWithClass: isa + requestedSize: dimension * sizeof(double)]; + + for (i = 0; i < dimension; i++) + data[i] = (double)va_arg(arguments, double); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + free(data); + + [super dealloc]; +} + +- (void)setValue: (double)value + atIndex: (size_t)index +{ + if (index >= dimension) + @throw [OFOutOfRangeException newWithClass: isa]; + + data[index] = value; +} + +- (double)valueAtIndex: (size_t)index +{ + if (index >= dimension) + @throw [OFOutOfRangeException newWithClass: isa]; + + return data[index]; +} + +- (size_t)dimension +{ + return dimension; +} + +- (void)setDimension: (size_t)dimension_ +{ + double *newData; + size_t i; + + if ((newData = realloc(data, dimension_ * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException newWithClass: isa + requestedSize: dimension_ * + sizeof(double)]; + + data = newData; + + for (i = dimension; i < dimension_; i++) + data[i] = 0; + + dimension = dimension_; +} + +- (BOOL)isEqual: (id)object +{ + OFDoubleVector *otherVector; + + if (![object isKindOfClass: [OFDoubleVector class]]) + return NO; + + otherVector = object; + + if (otherVector->dimension != dimension) + return NO; + + if (memcmp(otherVector->data, data, dimension * sizeof(double))) + return NO; + + return YES; +} + +- (uint32_t)hash +{ + size_t i; + uint32_t hash; + + OF_HASH_INIT(hash); + + for (i = 0; i < dimension; i++) { + union { + double f; + uint64_t i; + } u; + + u.f = data[i]; + + OF_HASH_ADD_INT64(hash, u.i); + } + + OF_HASH_FINALIZE(hash); + + return hash; +} + +- copy +{ + OFDoubleVector *copy = [[isa alloc] initWithDimension: dimension]; + + memcpy(copy->data, data, dimension * sizeof(double)); + + return copy; +} + +- (OFString*)description +{ + OFMutableString *description; + size_t i; + + description = [OFMutableString stringWithFormat: @"<%@: (", + [self className]]; + + for (i = 0; i < dimension; i++) { + if (i != dimension - 1) + [description appendFormat: @"%g, ", data[i]]; + else + [description appendFormat: @"%g)>", data[i]]; + } + + /* + * Class swizzle the string to be immutable. We declared the return type + * to be OFString*, so it can't be modified anyway. But not swizzling it + * would create a real copy each time -[copy] is called. + */ + description->isa = [OFString class]; + return description; +} + +- (double*)cArray +{ + return data; +} + +- (double)magnitude +{ + double magnitude; + size_t i; + + magnitude = 0.0; + + for (i = 0; i < dimension; i++) + magnitude += data[i] * data[i]; + + magnitude = sqrt(magnitude); + + return magnitude; +} + +- (void)normalize +{ + double magnitude; + size_t i; + + magnitude = 0.0; + + for (i = 0; i < dimension; i++) + magnitude += data[i] * data[i]; + + magnitude = sqrt(magnitude); + + for (i = 0; i < dimension; i++) + data[i] /= magnitude; +} + +- (void)addVector: (OFDoubleVector*)vector +{ + size_t i; + + if (vector->isa != isa || vector->dimension != dimension) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + for (i = 0; i < dimension; i++) + data[i] += vector->data[i]; +} + +- (void)subtractVector: (OFDoubleVector*)vector +{ + size_t i; + + if (vector->isa != isa || vector->dimension != dimension) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + for (i = 0; i < dimension; i++) + data[i] -= vector->data[i]; +} + +- (void)multiplyWithScalar: (double)scalar +{ + size_t i; + + for (i = 0; i < dimension; i++) + data[i] *= scalar; +} + +- (void)divideByScalar: (double)scalar +{ + size_t i; + + for (i = 0; i < dimension; i++) + data[i] /= scalar; +} + +- (void)multiplyWithComponentsOfVector: (OFDoubleVector*)vector +{ + size_t i; + + if (vector->isa != isa || vector->dimension != dimension) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + for (i = 0; i < dimension; i++) + data[i] *= vector->data[i]; +} + +- (void)divideByComponentsOfVector: (OFDoubleVector*)vector +{ + size_t i; + + if (vector->isa != isa || vector->dimension != dimension) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + for (i = 0; i < dimension; i++) + data[i] /= vector->data[i]; +} + +- (double)dotProductWithVector: (OFDoubleVector*)vector +{ + double dotProduct; + size_t i; + + if (vector->isa != isa || vector->dimension != dimension) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + dotProduct = 0.0; + + for (i = 0; i < dimension; i++) + dotProduct += data[i] * vector->data[i]; + + return dotProduct; +} + +- (OFDoubleVector*)crossProductWithVector: (OFDoubleVector*)vector +{ + OFDoubleVector *crossProduct; + + if (dimension != 3) + @throw [OFNotImplementedException newWithClass: isa + selector: _cmd]; + + if (vector->dimension != dimension) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + crossProduct = [OFDoubleVector vectorWithDimension: 3]; + + crossProduct->data[0] = + data[1] * vector->data[2] - data[2] * vector->data[1]; + crossProduct->data[1] = + data[2] * vector->data[0] - data[0] * vector->data[2]; + crossProduct->data[2] = + data[0] * vector->data[1] - data[1] * vector->data[0]; + + return crossProduct; +} + +- (void)multiplyWithMatrix: (OFDoubleMatrix*)matrix +{ + double *newData; + size_t i, j, k; + + if (matrix->isa != doubleMatrix || dimension != matrix->columns) + @throw [OFInvalidArgumentException newWithClass: isa + selector: _cmd]; + + if ((newData = malloc(matrix->rows * sizeof(double))) == NULL) + @throw [OFOutOfMemoryException + newWithClass: isa + requestedSize: matrix->rows * sizeof(double)]; + + memset(newData, 0, matrix->rows * sizeof(double)); + + for (i = j = k = 0; i < matrix->rows * matrix->columns; i++) { + newData[j] += matrix->data[i] * data[k]; + + if (++j == matrix->rows) { + k++; + j = 0; + } + } + + free(data); + data = newData; + + dimension = matrix->rows; +} +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -53,10 +53,12 @@ #import "OFXMLParser.h" #import "OFXMLElementBuilder.h" #import "OFFloatVector.h" #import "OFFloatMatrix.h" +#import "OFDoubleVector.h" +#import "OFDoubleMatrix.h" #import "OFSerialization.h" #import "OFApplication.h"