Skip to content

Commit

Permalink
Array update (#83)
Browse files Browse the repository at this point in the history
* Updated array type

+ Added 'address' and 'length' fields
+ Added support for slicing
+ Added support for item deletion
+ Created wiki page
~ Fixed negative indices

* Documentation update

* Warnings gatherer (#82)

* Fixed a few warnings
  • Loading branch information
Zuzu-Typ authored Jun 25, 2020
1 parent 9733abb commit cf36581
Show file tree
Hide file tree
Showing 9 changed files with 615 additions and 39 deletions.
12 changes: 6 additions & 6 deletions PyGLM/internal_functions/type_checkers.h
Original file line number Diff line number Diff line change
Expand Up @@ -2433,11 +2433,11 @@ bool PyGLM_PTI_DEBUG_EQ_FUNC(PyObject* o, PyObject* arg) {
#define PyGLM_PTI_DEBUG_EQ(N, o)
#define PyGLM_PTI_DEBUG_SC(N, o)

#define PyGLM_PTI_IsVec(N) (sourceType ## N == PyGLM_VEC || sourceType ## N == PyGLM_MVEC || sourceType ## N == PTI && PTI ## N.isVec)
#define PyGLM_PTI_IsVec(N) (sourceType ## N == PyGLM_VEC || sourceType ## N == PyGLM_MVEC || (sourceType ## N == PTI && PTI ## N.isVec))

#define PyGLM_PTI_IsMat(N) (sourceType ## N == PyGLM_MAT || sourceType ## N == PTI && PTI ## N.isMat)
#define PyGLM_PTI_IsMat(N) (sourceType ## N == PyGLM_MAT || (sourceType ## N == PTI && PTI ## N.isMat))

#define PyGLM_PTI_IsQua(N) (sourceType ## N == PyGLM_QUA || sourceType ## N == PTI && PTI ## N.isQua)
#define PyGLM_PTI_IsQua(N) (sourceType ## N == PyGLM_QUA || (sourceType ## N == PTI && PTI ## N.isQua))

#define PyGLM_PTI_IsNone(N) (sourceType ## N == NONE)
#endif
Expand All @@ -2456,11 +2456,11 @@ inline bool assertAndReturn(bool expr) {

#define PyGLM_Qua_PTI_CheckN(N, T, o) (PyGLM_PTI_DEBUG_EQ(N, o) PyGLM_Qua_CheckExact(T, o) && assertAndReturn(sourceType ## N == PyGLM_QUA) || sourceType ## N == PTI && PTI ## N.info == get_qua_PTI_info<T>())
#else
#define PyGLM_Vec_PTI_CheckN(N, L, T, o) (PyGLM_PTI_DEBUG_EQ(N, o) PyGLM_Vec_CheckExact(L, T, o) || sourceType ## N == PTI && PTI ## N.info == get_vec_PTI_info<L, T>())
#define PyGLM_Vec_PTI_CheckN(N, L, T, o) (PyGLM_PTI_DEBUG_EQ(N, o) PyGLM_Vec_CheckExact(L, T, o) ||( sourceType ## N == PTI && PTI ## N.info == get_vec_PTI_info<L, T>()))

#define PyGLM_Mat_PTI_CheckN(N, C, R, T, o) (PyGLM_PTI_DEBUG_EQ(N, o) PyGLM_Mat_CheckExact(C, R, T, o) || sourceType ## N == PTI && PTI ## N.info == get_mat_PTI_info<C, R, T>())
#define PyGLM_Mat_PTI_CheckN(N, C, R, T, o) (PyGLM_PTI_DEBUG_EQ(N, o) PyGLM_Mat_CheckExact(C, R, T, o) || (sourceType ## N == PTI && PTI ## N.info == get_mat_PTI_info<C, R, T>()))

#define PyGLM_Qua_PTI_CheckN(N, T, o) (PyGLM_PTI_DEBUG_EQ(N, o) PyGLM_Qua_CheckExact(T, o) || sourceType ## N == PTI && PTI ## N.info == get_qua_PTI_info<T>())
#define PyGLM_Qua_PTI_CheckN(N, T, o) (PyGLM_PTI_DEBUG_EQ(N, o) PyGLM_Qua_CheckExact(T, o) || (sourceType ## N == PTI && PTI ## N.info == get_qua_PTI_info<T>()))
#endif

//#define PyGLM_Vec_PTI_CheckN(L, T, o, N) (PTI ## N = PyGLMTypeInfo(PyGLM_T_ANY_VEC | PyGLM_SHAPE_ ## L | PyGLM_DT_ ## T,o), sourceType ## N = PTI, PTI ## N.info == (PyGLM_T_VEC | PyGLM_SHAPE_ ## L | PyGLM_DT_ ## T))
Expand Down
140 changes: 117 additions & 23 deletions PyGLM/type_methods/glmArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,30 @@ return 0;
int glmArray_set(glmArray* self, ssize_t index, PyObject* value) {
if (index >= self->itemCount) {
PyErr_SetString(PyExc_IndexError, "index out of range");
return NULL;
return -1;
}
if (index < 0) {
return glmArray_set(self, self->itemCount - index, value);
return glmArray_set(self, self->itemCount + index, value);
}
if (value == NULL) {
void* tempDataCopy = malloc(self->nBytes);
if (tempDataCopy == NULL) {
PyErr_SetString(PyExc_MemoryError, "out of memory");
return -1;
}

memcpy(tempDataCopy, self->data, self->nBytes);

ssize_t outItemCount = (self->itemCount - 1);

self->data = realloc(self->data, outItemCount * self->itemSize);

memcpy(self->data, tempDataCopy, index * self->itemSize);
memcpy(((char*)self->data) + index * self->itemSize, ((char*)tempDataCopy) + (index + 1) * self->itemSize, (self->itemCount - index - 1) * self->itemSize);

self->itemCount = outItemCount;
self->nBytes = outItemCount * self->itemSize;
return 0;
}
if (self->glmType == PyGLM_TYPE_VEC) {
switch (self->format) {
Expand Down Expand Up @@ -252,7 +272,7 @@ int glmArray_set(glmArray* self, ssize_t index, PyObject* value) {
return -1;
}
}
return NULL;
return -1;
}

#define GLM_ARRAY_GET_IF_IS_MAT(T) switch (self->shape[0]) {\
Expand Down Expand Up @@ -314,7 +334,7 @@ PyObject* glmArray_get(glmArray* self, ssize_t index) {
return NULL;
}
if (index < 0) {
return glmArray_get(self, self->itemCount - index);
return glmArray_get(self, self->itemCount + index);
}
if (self->glmType == PyGLM_TYPE_VEC) {
switch (self->format) {
Expand Down Expand Up @@ -620,6 +640,99 @@ glmArray_sq_ass_item(glmArray* self, Py_ssize_t index, PyObject* value) {
return glmArray_set(self, index, value);
}

static PyObject*
glmArray_mp_subscript(glmArray* self, PyObject* key) {
if (PyLong_Check(key)) {
return glmArray_get(self, PyLong_AsSsize_t(key));
}
if (PySlice_Check(key)) {
Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx(key, self->itemCount, &start, &stop, &step, &slicelength) < 0) {
return NULL;
}
glmArray* out = (glmArray*)glmArrayType.tp_alloc(&glmArrayType, 0);
PyGLM_ASSERT((out != NULL), "generated array was NULL. (maybe we're out of memory?)");

out->dtSize = self->dtSize;
out->format = self->format;
out->glmType = self->glmType;
out->itemCount = slicelength;
out->itemSize = self->itemSize;
out->nBytes = slicelength * self->itemSize;
memcpy(out->shape, self->shape, sizeof(out->shape));
out->subtype = self->subtype;

out->data = malloc(out->nBytes);
if (out->data == NULL) {
PyErr_SetString(PyExc_MemoryError, "out of memory");
return NULL;
}
Py_ssize_t outIndex = 0;
for (Py_ssize_t i = start; i < stop; i += step) {
memcpy(((char*)out->data) + (outIndex++ * self->itemSize), ((char*)self->data) + (i * self->itemSize), self->itemSize);
}
return (PyObject*)out;
}
PyGLM_TYPEERROR_O("invalid operand type for []: ", key);
return NULL;
}

static int
glmArray_mp_ass_subscript(glmArray* self, PyObject* key, PyObject* value) {
if (PyLong_Check(key)) {
return glmArray_set(self, PyLong_AsSsize_t(key), value);
}
if (PySlice_Check(key)) {
Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx(key, self->itemCount, &start, &stop, &step, &slicelength) < 0) {
return -1;
}

if (value == NULL) {
void* tempDataCopy = malloc(self->nBytes);
if (tempDataCopy == NULL) {
PyErr_SetString(PyExc_MemoryError, "out of memory");
return -1;
}

memcpy(tempDataCopy, self->data, self->nBytes);

ssize_t outItemCount = (self->itemCount - slicelength);

self->data = realloc(self->data, outItemCount * self->itemSize);

Py_ssize_t outIndex = 0;
for (Py_ssize_t i = 0; i < self->itemCount; i++) {
if (i < start || i >= stop || (i - start) % step != 0)
memcpy(((char*)self->data) + (outIndex++ * self->itemSize), ((char*)tempDataCopy) + (i * self->itemSize), self->itemSize);
}
self->itemCount = outItemCount;
self->nBytes = outItemCount * self->itemSize;
return 0;
}

if (!PyObject_TypeCheck(value, &glmArrayType)) {
PyGLM_TYPEERROR_O("invalid operand type for []=: ", value);
return -1;
}
glmArray* valueArr = (glmArray*)value;
if (valueArr->itemCount != slicelength) {
PyErr_SetString(PyExc_ValueError, "array and slice don't have the same length");
return -1;
}
if (valueArr->subtype != self->subtype) {
PyErr_SetString(PyExc_ValueError, "incompatible array data types");
return -1;
}
Py_ssize_t inValueIndex = 0;
for (Py_ssize_t i = start; i < stop; i += step) {
memcpy(((char*)self->data) + (i * self->itemSize), ((char*)valueArr->data) + (inValueIndex++ * self->itemSize), self->itemSize);
}
return 0;
}
PyGLM_TYPEERROR_O("invalid operand type for []: ", key);
return -1;
}

static int
glmArray_contains(glmArray* self, PyObject* value) {
Expand Down Expand Up @@ -1049,25 +1162,6 @@ static PyObject* glmArray_repr(glmArray* self) {
}
}

static PyObject*
glmArray_getattr(PyObject* obj, PyObject* name) {
char* name_as_ccp = PyGLM_String_AsString(name);
if (strcmp(name_as_ccp, "ptr") == 0) {
return PyGLM_CtypesVoidP_FromVoidP(((glmArray*)obj)->data);
}
return PyObject_GenericGetAttr(obj, name);
}

static int
glmArray_setattr(PyObject* obj, PyObject* name, PyObject* value) {
char* name_as_ccp = PyGLM_String_AsString(name);
if (strcmp(name_as_ccp, "ptr") == 0) {
PyErr_SetString(PyExc_AttributeError, "readonly attribute");
return -1;
}
return PyObject_GenericSetAttr(obj, name, value);
}


static PyObject*
glmArray_richcompare(glmArray* self, PyObject* other, int comp_type) {
Expand Down
8 changes: 4 additions & 4 deletions PyGLM/types/glmArray/forward_declarations.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ static PyObject* glmArray_sq_item(glmArray * self, Py_ssize_t index);
static int glmArray_sq_ass_item(glmArray * self, Py_ssize_t index, PyObject * value);


static PyObject* glmArray_mp_subscript(glmArray* self, PyObject* key);

static int glmArray_mp_ass_subscript(glmArray* self, PyObject* key, PyObject* value);

static int glmArray_contains(glmArray * self, PyObject * value);

static void glmArray_dealloc(glmArray* self);
Expand All @@ -43,10 +47,6 @@ static PyObject* glmArray_str(glmArray* self);

static PyObject* glmArray_repr(glmArray* self);

static PyObject* glmArray_getattr(PyObject* obj, PyObject* name);

static int glmArray_setattr(PyObject* obj, PyObject* name, PyObject* value);


static PyObject* glmArray_richcompare(glmArray* self, PyObject* other, int comp_type);

Expand Down
11 changes: 9 additions & 2 deletions PyGLM/types/glmArray/glmArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ static PyMemberDef glmArray_members[] = {
{ "element_type", T_OBJECT, offsetof(glmArray, subtype), 1, "Type class of the contained elements" },
{ "itemsize", T_PYSSIZET, offsetof(glmArray, itemSize), 1, "The size of one array item in bytes " },
{ "dt_size", T_PYSSIZET, offsetof(glmArray, dtSize), 1, "The size of each single component of the elements in bytes (size of data type)" },
{ "address", T_ULONGLONG, offsetof(glmArray, data), 1, "The memory address where this array stores it's data" },
{ "length", T_PYSSIZET, offsetof(glmArray, itemCount), 1, "The count of elements contained by this array" },
{ NULL } /* Sentinel */
};
static PyGetSetDef glmArray_getSet[] = {
Expand Down Expand Up @@ -38,6 +40,11 @@ static PySequenceMethods glmArraySeqMethods = {
(binaryfunc)glmArray_inplace_concat, // sq_inplace_concat
(ssizeargfunc)glmArray_inplace_repeat, // sq_inplace_repeat
};
static PyMappingMethods glmArrayMapMethods = {
(lenfunc)glmArray_len,
(binaryfunc)glmArray_mp_subscript,
(objobjargproc)glmArray_mp_ass_subscript,
};
static PyTypeObject glmArrayType = {
PyObject_HEAD_INIT(NULL)
"glm.array", /* tp_name */
Expand All @@ -51,7 +58,7 @@ static PyTypeObject glmArrayType = {
(reprfunc)glmArray_repr, /* tp_repr */
0, /* tp_as_number */
&glmArraySeqMethods, /* tp_as_sequence */
0, /* tp_as_mapping */
&glmArrayMapMethods, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
(reprfunc)glmArray_str, /* tp_str */
Expand All @@ -60,7 +67,7 @@ static PyTypeObject glmArrayType = {
&glmArrayBufferMethods, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"glmArray( <glmArray compatible type(s)> )\nAn in-place copy of glm types.", /* tp_doc */
"array( <array compatible type(s)> )\nAn in-place copy of glm types.", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
(richcmpfunc)glmArray_richcompare, /* tp_richcompare */
Expand Down
14 changes: 14 additions & 0 deletions PyGLM_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,12 @@ def fail(*args):
arr = glm.array(glm.mat4(), glm.mat4(2))
assert arr[0] == glm.mat4(), arr
assert arr[1] == glm.mat4(2) == arr[-1], arr

arr = glm.array(*glm.mat4())
assert arr[:] == arr, arr
assert arr[1:] == glm.array(glm.vec4(0,1,0,0),glm.vec4(0,0,1,0),glm.vec4(0,0,0,1)), arr
assert arr[::2] == glm.array(glm.vec4(1,0,0,0),glm.vec4(0,0,1,0)), arr
assert arr[1::2] == glm.array(glm.vec4(0,1,0,0),glm.vec4(0,0,0,1)), arr
#/getitem #

# setitem #
Expand All @@ -593,6 +599,14 @@ def fail(*args):
arr = glm.array(glm.mat4(), glm.mat4(2))
arr[0] = glm.mat4(3)
assert arr[0] == glm.mat4(3), arr
del arr[0]
assert arr == glm.array(glm.mat4(2)), arr

arr = glm.array(*glm.mat4())
del arr[::2]
assert arr == glm.array(glm.vec4(0,1,0,0),glm.vec4(0,0,0,1)), arr
arr[:] = glm.array(glm.vec4(), glm.vec4(2))
assert arr == glm.array(glm.vec4(), glm.vec4(2)), arr
#/setitem #

# contains #
Expand Down
11 changes: 9 additions & 2 deletions wiki/PyGLM Types.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**PyGLM** brings a couple of types with it\.

These types represent either **vectors**, **matrices** or **quaternions**\.
These types represent either **vectors**, **matrices** or **quaternions** or an **array** containing the aforementioned\.


1. A **vector** usually is an array of **1 to 4 numbers** that serve as **positions** or **directions** in a
Expand All @@ -15,13 +15,20 @@ For example a **2D** vector would be expressed as ``` glm.vec2 ```\.
They aid in a lot of complex vector manipulations\.&nbsp;&nbsp;
Matrix types are expressed as **glm\.matNxM** \(*N* being the columns and *M* being the rows\)\.
So a **4x4** matrix would be ``` glm.mat4x4 ```, or ``` glm.mat4 ``` for short \(because *N* and *M* are equal\)\.
*Note: Yes, columns and rows are not in the natural order \- this is a flaw of glm\.*
Note: *Yes, columns and rows are not in the natural order \- this is a flaw of glm\.*

3. A **quaternion** is an **array of 4 numbers** that can be used for complex vector manipulations\.
They are made up of a scalar part ``` (w) ``` and a vector part ``` (x, y, z) ``` and are sometimes displayed as
``` (w + x*i + y*j + z*k) ```, where ``` i ```, ``` j ``` and ``` k ``` are imaginary numbers\.
Quaternion types are simply expressed as ``` glm.quat ```\.

4. A PyGLM **array** \(``` glm.array ```\) is a simple way of storing a copy of multiple instances of one of the aforementioned types\.
This array can then be used to **transfer that data over to external C functions**, such as ``` glBufferData ``` from PyOpenGL\.
Although PyGLM's arrays are a lot simpler than NumPy arrays, they're **quite a bit faster**\.


**The following section is devoted to vectors, matrices and quaternions only\.**

All of these types use **32\-bit floating point numbers** to store their values\.&nbsp;&nbsp;
PyGLM does however provide **other data types**\.&nbsp;&nbsp;
For **all of the aforementioned**:
Expand Down
11 changes: 9 additions & 2 deletions wiki/PyGLM Types.sb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
\b\PyGLM\b\ brings a couple of types with it.

These types represent either \b\vectors\b\, \b\matrices\b\ or \b\quaternions\b\.
These types represent either \b\vectors\b\, \b\matrices\b\ or \b\quaternions\b\ or an \b\array\b\ containing the aforementioned.

\lo \
\-\ A \b\vector\b\ usually is an array of \b\1 to 4 numbers\b\ that serve as \b\positions\b\ or \b\directions\b\ in a
Expand All @@ -13,13 +13,20 @@ For example a \b\2D\b\ vector would be expressed as \code\glm.vec2\code\.
They aid in a lot of complex vector manipulations.
Matrix types are expressed as \b\glm.matNxM\b\ (\i\N\i\ being the columns and \i\M\i\ being the rows).
So a \b\4x4\b\ matrix would be \code\glm.mat4x4\code\, or \code\glm.mat4\code\ for short (because \i\N\i\ and \i\M\i\ are equal).
\i\Note: Yes, columns and rows are not in the natural order - this is a flaw of glm.\i\
Note: \i\Yes, columns and rows are not in the natural order - this is a flaw of glm.\i\

\-\ A \b\quaternion\b\ is an \b\array of 4 numbers\b\ that can be used for complex vector manipulations.
They are made up of a scalar part \code\(w)\code\ and a vector part \code\(x, y, z)\code\ and are sometimes displayed as
\code\(w + x*i + y*j + z*k)\code\, where \code\i\code\, \code\j\code\ and \code\k\code\ are imaginary numbers.
Quaternion types are simply expressed as \code\glm.quat\code\.

\-\A PyGLM \b\array\b\ (\code\glm.array\code\) is a simple way of storing a copy of multiple instances of one of the aforementioned types.
This array can then be used to \b\transfer that data over to external C functions\b\, such as \code\glBufferData\code\ from PyOpenGL.
Although PyGLM's arrays are a lot simpler than NumPy arrays, they're \b\quite a bit faster\b\.
\ lo\

\b\The following section is devoted to vectors, matrices and quaternions only.\b\

All of these types use \b\32-bit floating point numbers\b\ to store their values.
PyGLM does however provide \b\other data types\b\.
For \b \all of the aforementioned\ b\:
Expand Down
Loading

0 comments on commit cf36581

Please sign in to comment.