Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -38,10 +38,11 @@ OFLHAArchiveEntry.m \ OFList.m \ OFLocale.m \ OFMD5Hash.m \ OFMapTable.m \ + OFMatrix4x4.m \ OFMemoryStream.m \ OFMessagePackExtension.m \ OFMethodSignature.m \ OFMutableArray.m \ OFMutableData.m \ ADDED src/OFMatrix4x4.h Index: src/OFMatrix4x4.h ================================================================== --- src/OFMatrix4x4.h +++ src/OFMatrix4x4.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008-2023 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. + */ + +#import "OFObject.h" + +OF_ASSUME_NONNULL_BEGIN + +/** + * @brief A 4x4 matrix of floats. + */ +OF_SUBCLASSING_RESTRICTED +@interface OFMatrix4x4: OFObject +{ + float _values[16]; +} + +#ifdef OF_HAVE_CLASS_PROPERTIES +@property (readonly, class) OFMatrix4x4 *identityMatrix; +#endif + +/** + * @brief An array of the 16 floats of the 4x4 matrix in column-major format. + * + * These may be modified directly. + */ +@property (readonly, nonatomic) float *values; + +/** + * @brief Returns the 4x4 identity matrix. + */ ++ (OFMatrix4x4 *)identityMatrix; + +/** + * @brief Creates a new 4x4 matrix with the specified values. + * + * @param values An array of 16 floats in column-major format + * @return A new, autoreleased OFMatrix4x4 + */ ++ (instancetype)matrixWithValues: (const float [_Nonnull 16])values; + +/** + * @brief Initializes an already allocated 4x4 matrix with the specified values. + * + * @param values An array of 16 floats in column-major format + * @return An initialized OFMatrix4x4 + */ +- (instancetype)initWithValues: (const float [_Nonnull 16])values; + +/** + * @brief Transposes the matrix. + */ +- (void)transpose; + +/** + * @brief Mulitplies 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: (OFMatrix4x4 *)matrix; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFMatrix4x4.m Index: src/OFMatrix4x4.m ================================================================== --- src/OFMatrix4x4.m +++ src/OFMatrix4x4.m @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2008-2023 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" + +#import "OFMatrix4x4.h" +#import "OFString.h" + +#import "OFOnce.h" + +static const float identityValues[16] = { + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 +}; +static OFMatrix4x4 *identityMatrix; + +static void +initIdentityMatrix(void) +{ + identityMatrix = [[OFMatrix4x4 alloc] initWithValues: identityValues]; +} + +@implementation OFMatrix4x4 ++ (void)initialize +{ + if (self != [OFMatrix4x4 class]) + return; +} + ++ (OFMatrix4x4 *)identityMatrix +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + OFOnce(&onceControl, initIdentityMatrix); + + return identityMatrix; +} + ++ (instancetype)matrixWithValues: (const float [16])values +{ + return [[[self alloc] initWithValues: values] autorelease]; +} + +- (instancetype)initWithValues: (const float [16])values +{ + self = [super init]; + + memcpy(_values, values, 16 * sizeof(float)); + + return self; +} + +- (float *)values +{ + return _values; +} + +- (instancetype)copy +{ + return [[OFMatrix4x4 alloc] initWithValues: _values]; +} + +- (bool)isEqual: (OFMatrix4x4 *)matrix +{ + if (![matrix isKindOfClass: [OFMatrix4x4 class]]) + return false; + + return (memcmp(_values, matrix->_values, 16 * sizeof(float)) == 0); +} + +- (unsigned long)hash +{ + unsigned long hash; + + OFHashInit(&hash); + + for (size_t i = 0; i < 16; i++) + OFHashAddHash(&hash, OFFloatToRawUInt32(_values[i])); + + OFHashFinalize(&hash); + + return hash; +} + +- (void)transpose +{ + float copy[16]; + memcpy(copy, _values, 16 * sizeof(float)); + + _values[1] = copy[4]; + _values[2] = copy[8]; + _values[3] = copy[12]; + _values[4] = copy[1]; + _values[6] = copy[9]; + _values[7] = copy[13]; + _values[8] = copy[2]; + _values[9] = copy[6]; + _values[11] = copy[14]; + _values[12] = copy[3]; + _values[13] = copy[7]; + _values[14] = copy[11]; +} + +- (void)multiplyWithMatrix: (OFMatrix4x4 *)matrix +{ + float copy[16]; + memcpy(copy, _values, 16 * sizeof(float)); + + _values[0] = matrix->_values[0] * copy[0] + + matrix->_values[4] * copy[1] + + matrix->_values[8] * copy[2] + + matrix->_values[12] * copy[3]; + _values[1] = matrix->_values[1] * copy[0] + + matrix->_values[5] * copy[1] + + matrix->_values[9] * copy[2] + + matrix->_values[13] * copy[3]; + _values[2] = matrix->_values[2] * copy[0] + + matrix->_values[6] * copy[1] + + matrix->_values[10] * copy[2] + + matrix->_values[14] * copy[3]; + _values[3] = matrix->_values[3] * copy[0] + + matrix->_values[7] * copy[1] + + matrix->_values[11] * copy[2] + + matrix->_values[15] * copy[3]; + _values[4] = matrix->_values[0] * copy[4] + + matrix->_values[4] * copy[5] + + matrix->_values[8] * copy[6] + + matrix->_values[12] * copy[7]; + _values[5] = matrix->_values[1] * copy[4] + + matrix->_values[5] * copy[5] + + matrix->_values[9] * copy[6] + + matrix->_values[13] * copy[7]; + _values[6] = matrix->_values[2] * copy[4] + + matrix->_values[6] * copy[5] + + matrix->_values[10] * copy[6] + + matrix->_values[14] * copy[7]; + _values[7] = matrix->_values[3] * copy[4] + + matrix->_values[7] * copy[5] + + matrix->_values[11] * copy[6] + + matrix->_values[15] * copy[7]; + _values[8] = matrix->_values[0] * copy[8] + + matrix->_values[4] * copy[9] + + matrix->_values[8] * copy[10] + + matrix->_values[12] * copy[11]; + _values[9] = matrix->_values[1] * copy[8] + + matrix->_values[5] * copy[9] + + matrix->_values[9] * copy[10] + + matrix->_values[13] * copy[11]; + _values[10] = matrix->_values[2] * copy[8] + + matrix->_values[6] * copy[9] + + matrix->_values[10] * copy[10] + + matrix->_values[14] * copy[11]; + _values[11] = matrix->_values[3] * copy[8] + + matrix->_values[7] * copy[9] + + matrix->_values[11] * copy[10] + + matrix->_values[15] * copy[11]; + _values[12] = matrix->_values[0] * copy[12] + + matrix->_values[4] * copy[13] + + matrix->_values[8] * copy[14] + + matrix->_values[12] * copy[15]; + _values[13] = matrix->_values[1] * copy[12] + + matrix->_values[5] * copy[13] + + matrix->_values[9] * copy[14] + + matrix->_values[13] * copy[15]; + _values[14] = matrix->_values[2] * copy[12] + + matrix->_values[6] * copy[13] + + matrix->_values[10] * copy[14] + + matrix->_values[14] * copy[15]; + _values[15] = matrix->_values[3] * copy[12] + + matrix->_values[7] * copy[13] + + matrix->_values[11] * copy[14] + + matrix->_values[15] * copy[15]; +} + +- (OFString *)description +{ + return [OFString stringWithFormat: + @"", + _values[0], _values[4], _values[8], _values[12], + _values[1], _values[5], _values[9], _values[13], + _values[2], _values[6], _values[10], _values[14], + _values[3], _values[7], _values[11], _values[15]]; +} +@end Index: src/OFObject.h ================================================================== --- src/OFObject.h +++ src/OFObject.h @@ -290,10 +290,62 @@ return false; if (!OFEqualSizes(rect1.size, rect2.size)) return false; + return true; +} + +/** + * @struct OFVector3D OFObject.h ObjFW/OFObject.h + * + * @brief A vector in 3D space. + */ +typedef struct OF_BOXABLE { + /** The x coordinate of the vector */ + float x; + /** The y coordinate of the vector */ + float y; + /** The z coordinate of the vector */ + float z; +} OFVector3D; + +/** + * @brief Creates a new OFVector3D. + * + * @param x The x coordinate of the vector + * @param y The x coordinate of the vector + * @param z The z coordinate of the vector + * @return An OFVector3D with the specified coordinates + */ +static OF_INLINE OFVector3D OF_CONST_FUNC +OFMakeVector3D(float x, float y, float z) +{ + OFVector3D vector = { x, y, z }; + + return vector; +} + +/** + * @brief Returns whether the two vectors are equal. + * + * @param vector1 The first vector for the comparison + * @param vector2 The second vector for the comparison + * @return Whether the two vectors are equal + */ +static OF_INLINE bool +OFEqualVectors3D(OFVector3D vector1, OFVector3D vector2) +{ + if (vector1.x != vector2.x) + return false; + + if (vector1.y != vector2.y) + return false; + + if (vector1.z != vector2.z) + return false; + return true; } /** * @brief Adds the specified byte to the hash. Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -135,10 +135,12 @@ #import "OFSystemInfo.h" #import "OFLocale.h" #import "OFOptionsParser.h" #import "OFTimer.h" #import "OFRunLoop.h" + +#import "OFMatrix4x4.h" #ifdef OF_WINDOWS # import "OFWindowsRegistryKey.h" #endif Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -30,10 +30,11 @@ OFInvocationTests.m \ OFJSONTests.m \ OFListTests.m \ OFLocaleTests.m \ OFMD5HashTests.m \ + OFMatrix4x4Tests.m \ OFMemoryStreamTests.m \ OFMethodSignatureTests.m \ OFNotificationCenterTests.m \ OFNumberTests.m \ OFObjectTests.m \ ADDED tests/OFMatrix4x4Tests.m Index: tests/OFMatrix4x4Tests.m ================================================================== --- tests/OFMatrix4x4Tests.m +++ tests/OFMatrix4x4Tests.m @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2008-2023 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" + +#import "TestsAppDelegate.h" + +static OFString *const module = @"OFMatrix4x4Tests"; + +@implementation TestsAppDelegate (OFMatrix4x4Tests) +- (void)matrix4x4Tests +{ + void *pool = objc_autoreleasePoolPush(); + OFMatrix4x4 *matrix, *matrix2; + + TEST(@"+[identityMatrix]", + memcmp([[OFMatrix4x4 identityMatrix] values], (float [16]){ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }, 16 * sizeof(float)) == 0) + + TEST(@"+[matrixWithValues:]", + (matrix = [OFMatrix4x4 matrixWithValues: (float [16]){ + 1, 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16 + }])) + + TEST(@"-[description]", + [matrix.description isEqual: @""]) + + TEST(@"-[transpose]", + R([matrix transpose]) && memcmp(matrix.values, (float [16]){ + 1, 5, 9, 13, + 2, 6, 10, 14, + 3, 7, 11, 15, + 4, 8, 12, 16 + }, 16 * sizeof(float)) == 0) + + TEST(@"-[isEqual:]", [[OFMatrix4x4 identityMatrix] isEqual: + [OFMatrix4x4 matrixWithValues: (float [16]){ + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + }]]) + + TEST(@"-[copy]", (matrix2 = [matrix copy]) && [matrix2 isEqual: matrix]) + + TEST(@"-[multiplyWithMatrix:] #1", + R([matrix2 multiplyWithMatrix: [OFMatrix4x4 identityMatrix]]) && + [matrix2 isEqual: matrix]) + + matrix2 = [OFMatrix4x4 matrixWithValues: (float [16]){ + 100, 500, 900, 1300, + 200, 600, 1000, 1400, + 300, 700, 1100, 1500, + 400, 800, 1200, 1600 + }]; + TEST(@"-[multiplyWithMatrix:] #2", + R([matrix2 multiplyWithMatrix: matrix]) && + [matrix2 isEqual: [OFMatrix4x4 matrixWithValues: (float [16]){ + 9000, 20200, 31400, 42600, + 10000, 22800, 35600, 48400, + 11000, 25400, 39800, 54200, + 12000, 28000, 44000, 60000 + }]]) + + objc_autoreleasePoolPop(pool); +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -144,10 +144,14 @@ @end @interface TestsAppDelegate (OFMD5HashTests) - (void)MD5HashTests; @end + +@interface TestsAppDelegate (OFMatrix4x4Tests) +- (void)matrix4x4Tests; +@end @interface TestsAppDelegate (OFMemoryStreamTests) - (void)memoryStreamTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -440,10 +440,12 @@ [self XMLNodeTests]; [self XMLElementBuilderTests]; [self serializationTests]; [self JSONTests]; [self propertyListTests]; + [self matrix4x4Tests]; + #if defined(OF_HAVE_PLUGINS) [self pluginTests]; #endif #ifdef OF_WINDOWS [self windowsRegistryKeyTests];