From d2f833396f708a9f4b0a412d655fcfebba9c868c Mon Sep 17 00:00:00 2001 From: James Preiss Date: Mon, 4 Oct 2021 23:42:17 -0700 Subject: [PATCH 1/9] copy Crazyswarm py-bindings w/ minimal change --- .github/workflows/ci-bindings.yml | 42 + .gitignore | 1 + bindings/.gitignore | 5 + bindings/Makefile | 17 + bindings/cffirmware.i | 139 + bindings/numpy.i | 3178 +++++++++++++++++ bindings/setup.py | 37 + test/bindings/environment.yml | 10 + test/bindings/figure8.csv | 11 + test/bindings/pycrazyswarm/__init__.py | 3 + test/bindings/pycrazyswarm/crazyflieSim.py | 496 +++ test/bindings/pycrazyswarm/crazyswarm_py.py | 49 + test/bindings/pycrazyswarm/util.py | 85 + .../pycrazyswarm/visualizer/__init__.py | 0 .../pycrazyswarm/visualizer/crazyflie2.obj.gz | Bin 0 -> 208870 bytes .../pycrazyswarm/visualizer/visMatplotlib.py | 81 + .../pycrazyswarm/visualizer/visNull.py | 17 + .../pycrazyswarm/visualizer/visVispy.py | 166 + test/bindings/test_collisionAvoidance.py | 316 ++ test/bindings/test_highLevel.py | 181 + test/bindings/test_lowLevel.py | 105 + test/bindings/test_yamlString.py | 28 + test/bindings/uav_trajectory.py | 109 + 23 files changed, 5076 insertions(+) create mode 100644 .github/workflows/ci-bindings.yml create mode 100644 bindings/.gitignore create mode 100644 bindings/Makefile create mode 100755 bindings/cffirmware.i create mode 100644 bindings/numpy.i create mode 100644 bindings/setup.py create mode 100644 test/bindings/environment.yml create mode 100644 test/bindings/figure8.csv create mode 100644 test/bindings/pycrazyswarm/__init__.py create mode 100644 test/bindings/pycrazyswarm/crazyflieSim.py create mode 100644 test/bindings/pycrazyswarm/crazyswarm_py.py create mode 100644 test/bindings/pycrazyswarm/util.py create mode 100644 test/bindings/pycrazyswarm/visualizer/__init__.py create mode 100644 test/bindings/pycrazyswarm/visualizer/crazyflie2.obj.gz create mode 100644 test/bindings/pycrazyswarm/visualizer/visMatplotlib.py create mode 100644 test/bindings/pycrazyswarm/visualizer/visNull.py create mode 100644 test/bindings/pycrazyswarm/visualizer/visVispy.py create mode 100644 test/bindings/test_collisionAvoidance.py create mode 100644 test/bindings/test_highLevel.py create mode 100644 test/bindings/test_lowLevel.py create mode 100644 test/bindings/test_yamlString.py create mode 100644 test/bindings/uav_trajectory.py diff --git a/.github/workflows/ci-bindings.yml b/.github/workflows/ci-bindings.yml new file mode 100644 index 0000000000..d23995a7c6 --- /dev/null +++ b/.github/workflows/ci-bindings.yml @@ -0,0 +1,42 @@ +name: Python Bindings CI + +on: [push, pull_request] + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-18.04, ubuntu-20.04, macos-latest] + python: + - python-version: "3.7" + python-cmd: python3 + - python-version: "2.7" + python-cmd: python2 + + runs-on: ${{ matrix.os }} + + steps: + + - name: Checkout + uses: actions/checkout@v2 + + - name: Conda + uses: conda-incubator/setup-miniconda@v2.0.0 + with: + channels: conda-forge,defaults + mamba-version: "*" + python-version: ${{ matrix.python.python-version }} + environment-file: test/bindings/environment.yml + activate-environment: cfbindings + + - name: Build + shell: bash -l {0} + run: | + cd bindings + CF_PYTHON=${{ matrix.python.python-cmd }} make + + - name: Test + shell: bash -l {0} + run: | + PYTHONPATH=bindings ${{ matrix.python.python-cmd }} -m pytest test/bindings diff --git a/.gitignore b/.gitignore index 83d3a7cb12..39be2d0d3b 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ current_platform.mk /generated/** **/__pycache__/** +**/*.pyc /docs/.jekyll-metadata docs/.jekyll-cache diff --git a/bindings/.gitignore b/bindings/.gitignore new file mode 100644 index 0000000000..b87958aa9e --- /dev/null +++ b/bindings/.gitignore @@ -0,0 +1,5 @@ +cffirmware.py +cffirmware_wrap.c +*.so +__pycache__ +build diff --git a/bindings/Makefile b/bindings/Makefile new file mode 100644 index 0000000000..0f14b389e7 --- /dev/null +++ b/bindings/Makefile @@ -0,0 +1,17 @@ +firmdir = .. +modinc = $(firmdir)/src/modules/interface +modsrc = $(firmdir)/src/modules/src + +ifeq ($(CF_PYTHON),) + CF_PYTHON = python3 +endif + +swig: setup.py cffirmware_wrap.c $(modsrc)/*.c + $(CF_PYTHON) setup.py build_ext --inplace + +cffirmware_wrap.c: cffirmware.i $(modinc)/*.h + swig -python -I$(modinc) cffirmware.i + +clean: + rm -f cffirmware.py _cffirmware.so *.pyc cffirmware_wrap.c + rm -rf build diff --git a/bindings/cffirmware.i b/bindings/cffirmware.i new file mode 100755 index 0000000000..eaab0b25a7 --- /dev/null +++ b/bindings/cffirmware.i @@ -0,0 +1,139 @@ +%module cffirmware + +// ignore GNU specific compiler attributes +#define __attribute__(x) + +%{ +#define SWIG_FILE_WITH_INIT +#include "collision_avoidance.h" +#include "math3d.h" +#include "pptraj.h" +#include "planner.h" +#include "stabilizer_types.h" +%} + +%include "collision_avoidance.h" +%include "math3d.h" +%include "pptraj.h" +%include "planner.h" +%include "stabilizer_types.h" + +%include "numpy.i" + + +%init %{ + import_array() +%} + +%apply (int DIM1, float* IN_ARRAY1) {(int nOthers, float const *otherPositions)} + +%inline %{ +void poly4d_set(struct poly4d *poly, int dim, int coef, float val) +{ + poly->p[dim][coef] = val; +} +float poly4d_get(struct poly4d *poly, int dim, int coef) +{ + return poly->p[dim][coef]; +} +struct poly4d* pp_get_piece(struct piecewise_traj *pp, int i) +{ + return &pp->pieces[i]; +} +struct poly4d* malloc_poly4d(int size) +{ + return (struct poly4d*)malloc(sizeof(struct poly4d) * size); +} + +struct vec vec2svec(struct vec3_s v) +{ + return mkvec(v.x, v.y, v.z); +} + +struct vec3_s svec2vec(struct vec v) +{ + struct vec3_s vv = { .x = v.x, .y = v.y, .z = v.z, }; + return vv; +} + +void collisionAvoidanceUpdateSetpointWrap( + collision_avoidance_params_t const *params, + collision_avoidance_state_t *collisionState, + int nOthers, + float const *otherPositions, + setpoint_t *setpoint, sensorData_t const *sensorData, state_t const *state) +{ + nOthers /= 3; + float *workspace = malloc(sizeof(float) * 7 * (nOthers + 6)); + collisionAvoidanceUpdateSetpointCore( + params, + collisionState, + nOthers, + otherPositions, + workspace, + setpoint, sensorData, state + ); + free(workspace); +} +%} + + +%pythoncode %{ +import numpy as np +%} + +#define COPY_CTOR(structname) \ +structname(struct structname const *x) { \ + struct structname *y = malloc(sizeof(struct structname)); \ + *y = *x; \ + return y; \ +} \ +~structname() { \ + free($self); \ +} \ + +%extend vec { + COPY_CTOR(vec) + + %pythoncode %{ + def __repr__(self): + return "({}, {}, {})".format(self.x, self.y, self.z) + + def __array__(self): + return np.array([self.x, self.y, self.z]) + + def __len__(self): + return 3 + + def __getitem__(self, i): + if 0 <= i and i < 3: + return _cffirmware.vindex(self, i) + else: + raise IndexError("vec index must be in {0, 1, 2}.") + + # Unary operator overloads. + def __neg__(self): + return _cffirmware.vneg(self) + + # Vector-scalar binary operator overloads. + def __rmul__(self, s): + return _cffirmware.vscl(s, self) + + def __div__(self, s): + return self.__truediv__(s) + + def __truediv__(self, s): + return _cffirmware.vdiv(self, s) + + # Vector-vector binary operator overloads. + def __add__(self, other): + return _cffirmware.vadd(self, other) + + def __sub__(self, other): + return _cffirmware.vsub(self, other) + %} +}; + +%extend traj_eval { + COPY_CTOR(traj_eval) +}; diff --git a/bindings/numpy.i b/bindings/numpy.i new file mode 100644 index 0000000000..8416e82f3a --- /dev/null +++ b/bindings/numpy.i @@ -0,0 +1,3178 @@ +/* -*- C -*- (not really, but good for syntax highlighting) */ + +/* + * Copyright (c) 2005-2015, NumPy Developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * * Neither the name of the NumPy Developers nor the names of any + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef SWIGPYTHON + +%{ +#ifndef SWIG_FILE_WITH_INIT +#define NO_IMPORT_ARRAY +#endif +#include "stdio.h" +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include +%} + +/**********************************************************************/ + +%fragment("NumPy_Backward_Compatibility", "header") +{ +%#if NPY_API_VERSION < 0x00000007 +%#define NPY_ARRAY_DEFAULT NPY_DEFAULT +%#define NPY_ARRAY_FARRAY NPY_FARRAY +%#define NPY_FORTRANORDER NPY_FORTRAN +%#endif +} + +/**********************************************************************/ + +/* The following code originally appeared in + * enthought/kiva/agg/src/numeric.i written by Eric Jones. It was + * translated from C++ to C by John Hunter. Bill Spotz has modified + * it to fix some minor bugs, upgrade from Numeric to numpy (all + * versions), add some comments and functionality, and convert from + * direct code insertion to SWIG fragments. + */ + +%fragment("NumPy_Macros", "header") +{ +/* Macros to extract array attributes. + */ +%#if NPY_API_VERSION < 0x00000007 +%#define is_array(a) ((a) && PyArray_Check((PyArrayObject*)a)) +%#define array_type(a) (int)(PyArray_TYPE((PyArrayObject*)a)) +%#define array_numdims(a) (((PyArrayObject*)a)->nd) +%#define array_dimensions(a) (((PyArrayObject*)a)->dimensions) +%#define array_size(a,i) (((PyArrayObject*)a)->dimensions[i]) +%#define array_strides(a) (((PyArrayObject*)a)->strides) +%#define array_stride(a,i) (((PyArrayObject*)a)->strides[i]) +%#define array_data(a) (((PyArrayObject*)a)->data) +%#define array_descr(a) (((PyArrayObject*)a)->descr) +%#define array_flags(a) (((PyArrayObject*)a)->flags) +%#define array_clearflags(a,f) (((PyArrayObject*)a)->flags) &= ~f +%#define array_enableflags(a,f) (((PyArrayObject*)a)->flags) = f +%#define array_is_fortran(a) (PyArray_ISFORTRAN((PyArrayObject*)a)) +%#else +%#define is_array(a) ((a) && PyArray_Check(a)) +%#define array_type(a) PyArray_TYPE((PyArrayObject*)a) +%#define array_numdims(a) PyArray_NDIM((PyArrayObject*)a) +%#define array_dimensions(a) PyArray_DIMS((PyArrayObject*)a) +%#define array_strides(a) PyArray_STRIDES((PyArrayObject*)a) +%#define array_stride(a,i) PyArray_STRIDE((PyArrayObject*)a,i) +%#define array_size(a,i) PyArray_DIM((PyArrayObject*)a,i) +%#define array_data(a) PyArray_DATA((PyArrayObject*)a) +%#define array_descr(a) PyArray_DESCR((PyArrayObject*)a) +%#define array_flags(a) PyArray_FLAGS((PyArrayObject*)a) +%#define array_enableflags(a,f) PyArray_ENABLEFLAGS((PyArrayObject*)a,f) +%#define array_clearflags(a,f) PyArray_CLEARFLAGS((PyArrayObject*)a,f) +%#define array_is_fortran(a) (PyArray_IS_F_CONTIGUOUS((PyArrayObject*)a)) +%#endif +%#define array_is_contiguous(a) (PyArray_ISCONTIGUOUS((PyArrayObject*)a)) +%#define array_is_native(a) (PyArray_ISNOTSWAPPED((PyArrayObject*)a)) +} + +/**********************************************************************/ + +%fragment("NumPy_Utilities", + "header") +{ + /* Given a PyObject, return a string describing its type. + */ + const char* pytype_string(PyObject* py_obj) + { + if (py_obj == NULL ) return "C NULL value"; + if (py_obj == Py_None ) return "Python None" ; + if (PyCallable_Check(py_obj)) return "callable" ; + if (PyString_Check( py_obj)) return "string" ; + if (PyInt_Check( py_obj)) return "int" ; + if (PyFloat_Check( py_obj)) return "float" ; + if (PyDict_Check( py_obj)) return "dict" ; + if (PyList_Check( py_obj)) return "list" ; + if (PyTuple_Check( py_obj)) return "tuple" ; + + return "unknown type"; + } + + /* Given a NumPy typecode, return a string describing the type. + */ + const char* typecode_string(int typecode) + { + static const char* type_names[25] = {"bool", + "byte", + "unsigned byte", + "short", + "unsigned short", + "int", + "unsigned int", + "long", + "unsigned long", + "long long", + "unsigned long long", + "float", + "double", + "long double", + "complex float", + "complex double", + "complex long double", + "object", + "string", + "unicode", + "void", + "ntypes", + "notype", + "char", + "unknown"}; + return typecode < 24 ? type_names[typecode] : type_names[24]; + } + + /* Make sure input has correct numpy type. This now just calls + PyArray_EquivTypenums(). + */ + int type_match(int actual_type, + int desired_type) + { + return PyArray_EquivTypenums(actual_type, desired_type); + } + +%#ifdef SWIGPY_USE_CAPSULE + void free_cap(PyObject * cap) + { + void* array = (void*) PyCapsule_GetPointer(cap,SWIGPY_CAPSULE_NAME); + if (array != NULL) free(array); + } +%#endif + + +} + +/**********************************************************************/ + +%fragment("NumPy_Object_to_Array", + "header", + fragment="NumPy_Backward_Compatibility", + fragment="NumPy_Macros", + fragment="NumPy_Utilities") +{ + /* Given a PyObject pointer, cast it to a PyArrayObject pointer if + * legal. If not, set the python error string appropriately and + * return NULL. + */ + PyArrayObject* obj_to_array_no_conversion(PyObject* input, + int typecode) + { + PyArrayObject* ary = NULL; + if (is_array(input) && (typecode == NPY_NOTYPE || + PyArray_EquivTypenums(array_type(input), typecode))) + { + ary = (PyArrayObject*) input; + } + else if is_array(input) + { + const char* desired_type = typecode_string(typecode); + const char* actual_type = typecode_string(array_type(input)); + PyErr_Format(PyExc_TypeError, + "Array of type '%s' required. Array of type '%s' given", + desired_type, actual_type); + ary = NULL; + } + else + { + const char* desired_type = typecode_string(typecode); + const char* actual_type = pytype_string(input); + PyErr_Format(PyExc_TypeError, + "Array of type '%s' required. A '%s' was given", + desired_type, + actual_type); + ary = NULL; + } + return ary; + } + + /* Convert the given PyObject to a NumPy array with the given + * typecode. On success, return a valid PyArrayObject* with the + * correct type. On failure, the python error string will be set and + * the routine returns NULL. + */ + PyArrayObject* obj_to_array_allow_conversion(PyObject* input, + int typecode, + int* is_new_object) + { + PyArrayObject* ary = NULL; + PyObject* py_obj; + if (is_array(input) && (typecode == NPY_NOTYPE || + PyArray_EquivTypenums(array_type(input),typecode))) + { + ary = (PyArrayObject*) input; + *is_new_object = 0; + } + else + { + py_obj = PyArray_FROMANY(input, typecode, 0, 0, NPY_ARRAY_DEFAULT); + /* If NULL, PyArray_FromObject will have set python error value.*/ + ary = (PyArrayObject*) py_obj; + *is_new_object = 1; + } + return ary; + } + + /* Given a PyArrayObject, check to see if it is contiguous. If so, + * return the input pointer and flag it as not a new object. If it is + * not contiguous, create a new PyArrayObject using the original data, + * flag it as a new object and return the pointer. + */ + PyArrayObject* make_contiguous(PyArrayObject* ary, + int* is_new_object, + int min_dims, + int max_dims) + { + PyArrayObject* result; + if (array_is_contiguous(ary)) + { + result = ary; + *is_new_object = 0; + } + else + { + result = (PyArrayObject*) PyArray_ContiguousFromObject((PyObject*)ary, + array_type(ary), + min_dims, + max_dims); + *is_new_object = 1; + } + return result; + } + + /* Given a PyArrayObject, check to see if it is Fortran-contiguous. + * If so, return the input pointer, but do not flag it as not a new + * object. If it is not Fortran-contiguous, create a new + * PyArrayObject using the original data, flag it as a new object + * and return the pointer. + */ + PyArrayObject* make_fortran(PyArrayObject* ary, + int* is_new_object) + { + PyArrayObject* result; + if (array_is_fortran(ary)) + { + result = ary; + *is_new_object = 0; + } + else + { + Py_INCREF(array_descr(ary)); + result = (PyArrayObject*) PyArray_FromArray(ary, + array_descr(ary), +%#if NPY_API_VERSION < 0x00000007 + NPY_FORTRANORDER); +%#else + NPY_ARRAY_F_CONTIGUOUS); +%#endif + *is_new_object = 1; + } + return result; + } + + /* Convert a given PyObject to a contiguous PyArrayObject of the + * specified type. If the input object is not a contiguous + * PyArrayObject, a new one will be created and the new object flag + * will be set. + */ + PyArrayObject* obj_to_array_contiguous_allow_conversion(PyObject* input, + int typecode, + int* is_new_object) + { + int is_new1 = 0; + int is_new2 = 0; + PyArrayObject* ary2; + PyArrayObject* ary1 = obj_to_array_allow_conversion(input, + typecode, + &is_new1); + if (ary1) + { + ary2 = make_contiguous(ary1, &is_new2, 0, 0); + if ( is_new1 && is_new2) + { + Py_DECREF(ary1); + } + ary1 = ary2; + } + *is_new_object = is_new1 || is_new2; + return ary1; + } + + /* Convert a given PyObject to a Fortran-ordered PyArrayObject of the + * specified type. If the input object is not a Fortran-ordered + * PyArrayObject, a new one will be created and the new object flag + * will be set. + */ + PyArrayObject* obj_to_array_fortran_allow_conversion(PyObject* input, + int typecode, + int* is_new_object) + { + int is_new1 = 0; + int is_new2 = 0; + PyArrayObject* ary2; + PyArrayObject* ary1 = obj_to_array_allow_conversion(input, + typecode, + &is_new1); + if (ary1) + { + ary2 = make_fortran(ary1, &is_new2); + if (is_new1 && is_new2) + { + Py_DECREF(ary1); + } + ary1 = ary2; + } + *is_new_object = is_new1 || is_new2; + return ary1; + } +} /* end fragment */ + +/**********************************************************************/ + +%fragment("NumPy_Array_Requirements", + "header", + fragment="NumPy_Backward_Compatibility", + fragment="NumPy_Macros") +{ + /* Test whether a python object is contiguous. If array is + * contiguous, return 1. Otherwise, set the python error string and + * return 0. + */ + int require_contiguous(PyArrayObject* ary) + { + int contiguous = 1; + if (!array_is_contiguous(ary)) + { + PyErr_SetString(PyExc_TypeError, + "Array must be contiguous. A non-contiguous array was given"); + contiguous = 0; + } + return contiguous; + } + + /* Test whether a python object is (C_ or F_) contiguous. If array is + * contiguous, return 1. Otherwise, set the python error string and + * return 0. + */ + int require_c_or_f_contiguous(PyArrayObject* ary) + { + int contiguous = 1; + if (!(array_is_contiguous(ary) || array_is_fortran(ary))) + { + PyErr_SetString(PyExc_TypeError, + "Array must be contiguous (C_ or F_). A non-contiguous array was given"); + contiguous = 0; + } + return contiguous; + } + + /* Require that a numpy array is not byte-swapped. If the array is + * not byte-swapped, return 1. Otherwise, set the python error string + * and return 0. + */ + int require_native(PyArrayObject* ary) + { + int native = 1; + if (!array_is_native(ary)) + { + PyErr_SetString(PyExc_TypeError, + "Array must have native byteorder. " + "A byte-swapped array was given"); + native = 0; + } + return native; + } + + /* Require the given PyArrayObject to have a specified number of + * dimensions. If the array has the specified number of dimensions, + * return 1. Otherwise, set the python error string and return 0. + */ + int require_dimensions(PyArrayObject* ary, + int exact_dimensions) + { + int success = 1; + if (array_numdims(ary) != exact_dimensions) + { + PyErr_Format(PyExc_TypeError, + "Array must have %d dimensions. Given array has %d dimensions", + exact_dimensions, + array_numdims(ary)); + success = 0; + } + return success; + } + + /* Require the given PyArrayObject to have one of a list of specified + * number of dimensions. If the array has one of the specified number + * of dimensions, return 1. Otherwise, set the python error string + * and return 0. + */ + int require_dimensions_n(PyArrayObject* ary, + int* exact_dimensions, + int n) + { + int success = 0; + int i; + char dims_str[255] = ""; + char s[255]; + for (i = 0; i < n && !success; i++) + { + if (array_numdims(ary) == exact_dimensions[i]) + { + success = 1; + } + } + if (!success) + { + for (i = 0; i < n-1; i++) + { + sprintf(s, "%d, ", exact_dimensions[i]); + strcat(dims_str,s); + } + sprintf(s, " or %d", exact_dimensions[n-1]); + strcat(dims_str,s); + PyErr_Format(PyExc_TypeError, + "Array must have %s dimensions. Given array has %d dimensions", + dims_str, + array_numdims(ary)); + } + return success; + } + + /* Require the given PyArrayObject to have a specified shape. If the + * array has the specified shape, return 1. Otherwise, set the python + * error string and return 0. + */ + int require_size(PyArrayObject* ary, + npy_intp* size, + int n) + { + int i; + int success = 1; + size_t len; + char desired_dims[255] = "["; + char s[255]; + char actual_dims[255] = "["; + for(i=0; i < n;i++) + { + if (size[i] != -1 && size[i] != array_size(ary,i)) + { + success = 0; + } + } + if (!success) + { + for (i = 0; i < n; i++) + { + if (size[i] == -1) + { + sprintf(s, "*,"); + } + else + { + sprintf(s, "%ld,", (long int)size[i]); + } + strcat(desired_dims,s); + } + len = strlen(desired_dims); + desired_dims[len-1] = ']'; + for (i = 0; i < n; i++) + { + sprintf(s, "%ld,", (long int)array_size(ary,i)); + strcat(actual_dims,s); + } + len = strlen(actual_dims); + actual_dims[len-1] = ']'; + PyErr_Format(PyExc_TypeError, + "Array must have shape of %s. Given array has shape of %s", + desired_dims, + actual_dims); + } + return success; + } + + /* Require the given PyArrayObject to to be Fortran ordered. If the + * the PyArrayObject is already Fortran ordered, do nothing. Else, + * set the Fortran ordering flag and recompute the strides. + */ + int require_fortran(PyArrayObject* ary) + { + int success = 1; + int nd = array_numdims(ary); + int i; + npy_intp * strides = array_strides(ary); + if (array_is_fortran(ary)) return success; + int n_non_one = 0; + /* Set the Fortran ordered flag */ + const npy_intp *dims = array_dimensions(ary); + for (i=0; i < nd; ++i) + n_non_one += (dims[i] != 1) ? 1 : 0; + if (n_non_one > 1) + array_clearflags(ary,NPY_ARRAY_CARRAY); + array_enableflags(ary,NPY_ARRAY_FARRAY); + /* Recompute the strides */ + strides[0] = strides[nd-1]; + for (i=1; i < nd; ++i) + strides[i] = strides[i-1] * array_size(ary,i-1); + return success; + } +} + +/* Combine all NumPy fragments into one for convenience */ +%fragment("NumPy_Fragments", + "header", + fragment="NumPy_Backward_Compatibility", + fragment="NumPy_Macros", + fragment="NumPy_Utilities", + fragment="NumPy_Object_to_Array", + fragment="NumPy_Array_Requirements") +{ +} + +/* End John Hunter translation (with modifications by Bill Spotz) + */ + +/* %numpy_typemaps() macro + * + * This macro defines a family of 75 typemaps that allow C arguments + * of the form + * + * 1. (DATA_TYPE IN_ARRAY1[ANY]) + * 2. (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) + * 3. (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) + * + * 4. (DATA_TYPE IN_ARRAY2[ANY][ANY]) + * 5. (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + * 6. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) + * 7. (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + * 8. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) + * + * 9. (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) + * 10. (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + * 11. (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + * 12. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) + * 13. (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + * 14. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) + * + * 15. (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) + * 16. (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + * 17. (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + * 18. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, , DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) + * 19. (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + * 20. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) + * + * 21. (DATA_TYPE INPLACE_ARRAY1[ANY]) + * 22. (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) + * 23. (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) + * + * 24. (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) + * 25. (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + * 26. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) + * 27. (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + * 28. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) + * + * 29. (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) + * 30. (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + * 31. (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + * 32. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3) + * 33. (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + * 34. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3) + * + * 35. (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) + * 36. (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + * 37. (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + * 38. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_ARRAY4) + * 39. (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + * 40. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_FARRAY4) + * + * 41. (DATA_TYPE ARGOUT_ARRAY1[ANY]) + * 42. (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) + * 43. (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) + * + * 44. (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) + * + * 45. (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) + * + * 46. (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) + * + * 47. (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) + * 48. (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) + * + * 49. (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + * 50. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) + * 51. (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + * 52. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2) + * + * 53. (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) + * 54. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) + * 55. (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) + * 56. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3) + * + * 57. (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) + * 58. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_ARRAY4) + * 59. (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) + * 60. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_FARRAY4) + * + * 61. (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1) + * 62. (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEWM_ARRAY1) + * + * 63. (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + * 64. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_ARRAY2) + * 65. (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + * 66. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_FARRAY2) + * + * 67. (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) + * 68. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_ARRAY3) + * 69. (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) + * 70. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_FARRAY3) + * + * 71. (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) + * 72. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4) + * 73. (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) + * 74. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4) + * + * 75. (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) + * + * where "DATA_TYPE" is any type supported by the NumPy module, and + * "DIM_TYPE" is any int-like type suitable for specifying dimensions. + * The difference between "ARRAY" typemaps and "FARRAY" typemaps is + * that the "FARRAY" typemaps expect Fortran ordering of + * multidimensional arrays. In python, the dimensions will not need + * to be specified (except for the "DATA_TYPE* ARGOUT_ARRAY1" + * typemaps). The IN_ARRAYs can be a numpy array or any sequence that + * can be converted to a numpy array of the specified type. The + * INPLACE_ARRAYs must be numpy arrays of the appropriate type. The + * ARGOUT_ARRAYs will be returned as new numpy arrays of the + * appropriate type. + * + * These typemaps can be applied to existing functions using the + * %apply directive. For example: + * + * %apply (double* IN_ARRAY1, int DIM1) {(double* series, int length)}; + * double prod(double* series, int length); + * + * %apply (int DIM1, int DIM2, double* INPLACE_ARRAY2) + * {(int rows, int cols, double* matrix )}; + * void floor(int rows, int cols, double* matrix, double f); + * + * %apply (double IN_ARRAY3[ANY][ANY][ANY]) + * {(double tensor[2][2][2] )}; + * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY]) + * {(double low[2][2][2] )}; + * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY]) + * {(double upp[2][2][2] )}; + * void luSplit(double tensor[2][2][2], + * double low[2][2][2], + * double upp[2][2][2] ); + * + * or directly with + * + * double prod(double* IN_ARRAY1, int DIM1); + * + * void floor(int DIM1, int DIM2, double* INPLACE_ARRAY2, double f); + * + * void luSplit(double IN_ARRAY3[ANY][ANY][ANY], + * double ARGOUT_ARRAY3[ANY][ANY][ANY], + * double ARGOUT_ARRAY3[ANY][ANY][ANY]); + */ + +%define %numpy_typemaps(DATA_TYPE, DATA_TYPECODE, DIM_TYPE) + +/************************/ +/* Input Array Typemaps */ +/************************/ + +/* Typemap suite for (DATA_TYPE IN_ARRAY1[ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE IN_ARRAY1[ANY]) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE IN_ARRAY1[ANY]) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[1] = { $1_dim0 }; + array = obj_to_array_contiguous_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 1) || + !require_size(array, size, 1)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(freearg) + (DATA_TYPE IN_ARRAY1[ANY]) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[1] = { -1 }; + array = obj_to_array_contiguous_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 1) || + !require_size(array, size, 1)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); +} +%typemap(freearg) + (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[1] = {-1}; + array = obj_to_array_contiguous_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 1) || + !require_size(array, size, 1)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DATA_TYPE*) array_data(array); +} +%typemap(freearg) + (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE IN_ARRAY2[ANY][ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE IN_ARRAY2[ANY][ANY]) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE IN_ARRAY2[ANY][ANY]) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[2] = { $1_dim0, $1_dim1 }; + array = obj_to_array_contiguous_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 2) || + !require_size(array, size, 2)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(freearg) + (DATA_TYPE IN_ARRAY2[ANY][ANY]) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[2] = { -1, -1 }; + array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 2) || + !require_size(array, size, 2)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); +} +%typemap(freearg) + (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[2] = { -1, -1 }; + array = obj_to_array_contiguous_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 2) || + !require_size(array, size, 2)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DATA_TYPE*) array_data(array); +} +%typemap(freearg) + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[2] = { -1, -1 }; + array = obj_to_array_fortran_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 2) || + !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); +} +%typemap(freearg) + (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[2] = { -1, -1 }; + array = obj_to_array_fortran_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 2) || + !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DATA_TYPE*) array_data(array); +} +%typemap(freearg) + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 }; + array = obj_to_array_contiguous_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 3) || + !require_size(array, size, 3)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(freearg) + (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[3] = { -1, -1, -1 }; + array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 3) || + !require_size(array, size, 3)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); +} +%typemap(freearg) + (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + /* for now, only concerned with lists */ + $1 = PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL, int* is_new_object_array=NULL) +{ + npy_intp size[2] = { -1, -1 }; + PyArrayObject* temp_array; + Py_ssize_t i; + int is_new_object; + + /* length of the list */ + $2 = PyList_Size($input); + + /* the arrays */ + array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); + object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); + is_new_object_array = (int *)calloc($2,sizeof(int)); + + if (array == NULL || object_array == NULL || is_new_object_array == NULL) + { + SWIG_fail; + } + + for (i=0; i<$2; i++) + { + temp_array = obj_to_array_contiguous_allow_conversion(PySequence_GetItem($input,i), DATA_TYPECODE, &is_new_object); + + /* the new array must be stored so that it can be destroyed in freearg */ + object_array[i] = temp_array; + is_new_object_array[i] = is_new_object; + + if (!temp_array || !require_dimensions(temp_array, 2)) SWIG_fail; + + /* store the size of the first array in the list, then use that for comparison. */ + if (i == 0) + { + size[0] = array_size(temp_array,0); + size[1] = array_size(temp_array,1); + } + + if (!require_size(temp_array, size, 2)) SWIG_fail; + + array[i] = (DATA_TYPE*) array_data(temp_array); + } + + $1 = (DATA_TYPE**) array; + $3 = (DIM_TYPE) size[0]; + $4 = (DIM_TYPE) size[1]; +} +%typemap(freearg) + (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + Py_ssize_t i; + + if (array$argnum!=NULL) free(array$argnum); + + /*freeing the individual arrays if needed */ + if (object_array$argnum!=NULL) + { + if (is_new_object_array$argnum!=NULL) + { + for (i=0; i<$2; i++) + { + if (object_array$argnum[i] != NULL && is_new_object_array$argnum[i]) + { Py_DECREF(object_array$argnum[i]); } + } + free(is_new_object_array$argnum); + } + free(object_array$argnum); + } +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, + * DATA_TYPE* IN_ARRAY3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[3] = { -1, -1, -1 }; + array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 3) || + !require_size(array, size, 3)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DATA_TYPE*) array_data(array); +} +%typemap(freearg) + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[3] = { -1, -1, -1 }; + array = obj_to_array_fortran_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 3) || + !require_size(array, size, 3) | !require_fortran(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); +} +%typemap(freearg) + (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, + * DATA_TYPE* IN_FARRAY3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[3] = { -1, -1, -1 }; + array = obj_to_array_fortran_allow_conversion($input, + DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 3) || + !require_size(array, size, 3) || !require_fortran(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DATA_TYPE*) array_data(array); +} +%typemap(freearg) + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[4] = { $1_dim0, $1_dim1, $1_dim2 , $1_dim3}; + array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 4) || + !require_size(array, size, 4)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(freearg) + (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3, DIM_TYPE DIM4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[4] = { -1, -1, -1, -1 }; + array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 4) || + !require_size(array, size, 4)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); + $5 = (DIM_TYPE) array_size(array,3); +} +%typemap(freearg) + (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3, DIM_TYPE DIM4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + /* for now, only concerned with lists */ + $1 = PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL, int* is_new_object_array=NULL) +{ + npy_intp size[3] = { -1, -1, -1 }; + PyArrayObject* temp_array; + Py_ssize_t i; + int is_new_object; + + /* length of the list */ + $2 = PyList_Size($input); + + /* the arrays */ + array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); + object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); + is_new_object_array = (int *)calloc($2,sizeof(int)); + + if (array == NULL || object_array == NULL || is_new_object_array == NULL) + { + SWIG_fail; + } + + for (i=0; i<$2; i++) + { + temp_array = obj_to_array_contiguous_allow_conversion(PySequence_GetItem($input,i), DATA_TYPECODE, &is_new_object); + + /* the new array must be stored so that it can be destroyed in freearg */ + object_array[i] = temp_array; + is_new_object_array[i] = is_new_object; + + if (!temp_array || !require_dimensions(temp_array, 3)) SWIG_fail; + + /* store the size of the first array in the list, then use that for comparison. */ + if (i == 0) + { + size[0] = array_size(temp_array,0); + size[1] = array_size(temp_array,1); + size[2] = array_size(temp_array,2); + } + + if (!require_size(temp_array, size, 3)) SWIG_fail; + + array[i] = (DATA_TYPE*) array_data(temp_array); + } + + $1 = (DATA_TYPE**) array; + $3 = (DIM_TYPE) size[0]; + $4 = (DIM_TYPE) size[1]; + $5 = (DIM_TYPE) size[2]; +} +%typemap(freearg) + (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + Py_ssize_t i; + + if (array$argnum!=NULL) free(array$argnum); + + /*freeing the individual arrays if needed */ + if (object_array$argnum!=NULL) + { + if (is_new_object_array$argnum!=NULL) + { + for (i=0; i<$2; i++) + { + if (object_array$argnum[i] != NULL && is_new_object_array$argnum[i]) + { Py_DECREF(object_array$argnum[i]); } + } + free(is_new_object_array$argnum); + } + free(object_array$argnum); + } +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, + * DATA_TYPE* IN_ARRAY4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[4] = { -1, -1, -1 , -1}; + array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 4) || + !require_size(array, size, 4)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DIM_TYPE) array_size(array,3); + $5 = (DATA_TYPE*) array_data(array); +} +%typemap(freearg) + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3, DIM_TYPE DIM4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[4] = { -1, -1, -1, -1 }; + array = obj_to_array_fortran_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 4) || + !require_size(array, size, 4) | !require_fortran(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); + $5 = (DIM_TYPE) array_size(array,3); +} +%typemap(freearg) + (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, + * DATA_TYPE* IN_FARRAY4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) +{ + $1 = is_array($input) || PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) + (PyArrayObject* array=NULL, int is_new_object=0) +{ + npy_intp size[4] = { -1, -1, -1 , -1 }; + array = obj_to_array_fortran_allow_conversion($input, DATA_TYPECODE, + &is_new_object); + if (!array || !require_dimensions(array, 4) || + !require_size(array, size, 4) || !require_fortran(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DIM_TYPE) array_size(array,3); + $5 = (DATA_TYPE*) array_data(array); +} +%typemap(freearg) + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) +{ + if (is_new_object$argnum && array$argnum) + { Py_DECREF(array$argnum); } +} + +/***************************/ +/* In-Place Array Typemaps */ +/***************************/ + +/* Typemap suite for (DATA_TYPE INPLACE_ARRAY1[ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE INPLACE_ARRAY1[ANY]) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE INPLACE_ARRAY1[ANY]) + (PyArrayObject* array=NULL) +{ + npy_intp size[1] = { $1_dim0 }; + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,1) || !require_size(array, size, 1) || + !require_contiguous(array) || !require_native(array)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} + +/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) + (PyArrayObject* array=NULL, int i=1) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,1) || !require_contiguous(array) + || !require_native(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = 1; + for (i=0; i < array_numdims(array); ++i) $2 *= array_size(array,i); +} + +/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) + (PyArrayObject* array=NULL, int i=0) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,1) || !require_contiguous(array) + || !require_native(array)) SWIG_fail; + $1 = 1; + for (i=0; i < array_numdims(array); ++i) $1 *= array_size(array,i); + $2 = (DATA_TYPE*) array_data(array); +} + +/* Typemap suite for (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) + (PyArrayObject* array=NULL) +{ + npy_intp size[2] = { $1_dim0, $1_dim1 }; + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,2) || !require_size(array, size, 2) || + !require_contiguous(array) || !require_native(array)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} + +/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,2) || !require_contiguous(array) + || !require_native(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,2) || !require_contiguous(array) || + !require_native(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DATA_TYPE*) array_data(array); +} + +/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,2) || !require_contiguous(array) + || !require_native(array) || !require_fortran(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,2) || !require_contiguous(array) || + !require_native(array) || !require_fortran(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DATA_TYPE*) array_data(array); +} + +/* Typemap suite for (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) + (PyArrayObject* array=NULL) +{ + npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 }; + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,3) || !require_size(array, size, 3) || + !require_contiguous(array) || !require_native(array)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} + +/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,3) || !require_contiguous(array) || + !require_native(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); +} + +/* Typemap suite for (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + $1 = PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL) +{ + npy_intp size[2] = { -1, -1 }; + PyArrayObject* temp_array; + Py_ssize_t i; + + /* length of the list */ + $2 = PyList_Size($input); + + /* the arrays */ + array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); + object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); + + if (array == NULL || object_array == NULL) + { + SWIG_fail; + } + + for (i=0; i<$2; i++) + { + temp_array = obj_to_array_no_conversion(PySequence_GetItem($input,i), DATA_TYPECODE); + + /* the new array must be stored so that it can be destroyed in freearg */ + object_array[i] = temp_array; + + if ( !temp_array || !require_dimensions(temp_array, 2) || + !require_contiguous(temp_array) || + !require_native(temp_array) || + !PyArray_EquivTypenums(array_type(temp_array), DATA_TYPECODE) + ) SWIG_fail; + + /* store the size of the first array in the list, then use that for comparison. */ + if (i == 0) + { + size[0] = array_size(temp_array,0); + size[1] = array_size(temp_array,1); + } + + if (!require_size(temp_array, size, 2)) SWIG_fail; + + array[i] = (DATA_TYPE*) array_data(temp_array); + } + + $1 = (DATA_TYPE**) array; + $3 = (DIM_TYPE) size[0]; + $4 = (DIM_TYPE) size[1]; +} +%typemap(freearg) + (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + if (array$argnum!=NULL) free(array$argnum); + if (object_array$argnum!=NULL) free(object_array$argnum); +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, + * DATA_TYPE* INPLACE_ARRAY3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,3) || !require_contiguous(array) + || !require_native(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DATA_TYPE*) array_data(array); +} + +/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,3) || !require_contiguous(array) || + !require_native(array) || !require_fortran(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, + * DATA_TYPE* INPLACE_FARRAY3) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,3) || !require_contiguous(array) + || !require_native(array) || !require_fortran(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DATA_TYPE*) array_data(array); +} + +/* Typemap suite for (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) + (PyArrayObject* array=NULL) +{ + npy_intp size[4] = { $1_dim0, $1_dim1, $1_dim2 , $1_dim3 }; + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,4) || !require_size(array, size, 4) || + !require_contiguous(array) || !require_native(array)) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} + +/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3, DIM_TYPE DIM4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,4) || !require_contiguous(array) || + !require_native(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); + $5 = (DIM_TYPE) array_size(array,3); +} + +/* Typemap suite for (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3, DIM_TYPE DIM4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + $1 = PySequence_Check($input); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL) +{ + npy_intp size[3] = { -1, -1, -1 }; + PyArrayObject* temp_array; + Py_ssize_t i; + + /* length of the list */ + $2 = PyList_Size($input); + + /* the arrays */ + array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); + object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); + + if (array == NULL || object_array == NULL) + { + SWIG_fail; + } + + for (i=0; i<$2; i++) + { + temp_array = obj_to_array_no_conversion(PySequence_GetItem($input,i), DATA_TYPECODE); + + /* the new array must be stored so that it can be destroyed in freearg */ + object_array[i] = temp_array; + + if ( !temp_array || !require_dimensions(temp_array, 3) || + !require_contiguous(temp_array) || + !require_native(temp_array) || + !PyArray_EquivTypenums(array_type(temp_array), DATA_TYPECODE) + ) SWIG_fail; + + /* store the size of the first array in the list, then use that for comparison. */ + if (i == 0) + { + size[0] = array_size(temp_array,0); + size[1] = array_size(temp_array,1); + size[2] = array_size(temp_array,2); + } + + if (!require_size(temp_array, size, 3)) SWIG_fail; + + array[i] = (DATA_TYPE*) array_data(temp_array); + } + + $1 = (DATA_TYPE**) array; + $3 = (DIM_TYPE) size[0]; + $4 = (DIM_TYPE) size[1]; + $5 = (DIM_TYPE) size[2]; +} +%typemap(freearg) + (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + if (array$argnum!=NULL) free(array$argnum); + if (object_array$argnum!=NULL) free(object_array$argnum); +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, + * DATA_TYPE* INPLACE_ARRAY4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_ARRAY4) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_ARRAY4) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,4) || !require_contiguous(array) + || !require_native(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DIM_TYPE) array_size(array,3); + $5 = (DATA_TYPE*) array_data(array); +} + +/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, + * DIM_TYPE DIM3, DIM_TYPE DIM4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,4) || !require_contiguous(array) || + !require_native(array) || !require_fortran(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = (DIM_TYPE) array_size(array,0); + $3 = (DIM_TYPE) array_size(array,1); + $4 = (DIM_TYPE) array_size(array,2); + $5 = (DIM_TYPE) array_size(array,3); +} + +/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, + * DATA_TYPE* INPLACE_FARRAY4) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_FARRAY4) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_FARRAY4) + (PyArrayObject* array=NULL) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_dimensions(array,4) || !require_contiguous(array) + || !require_native(array) || !require_fortran(array)) SWIG_fail; + $1 = (DIM_TYPE) array_size(array,0); + $2 = (DIM_TYPE) array_size(array,1); + $3 = (DIM_TYPE) array_size(array,2); + $4 = (DIM_TYPE) array_size(array,3); + $5 = (DATA_TYPE*) array_data(array); +} + +/*************************/ +/* Argout Array Typemaps */ +/*************************/ + +/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY1[ANY]) + */ +%typemap(in,numinputs=0, + fragment="NumPy_Backward_Compatibility,NumPy_Macros") + (DATA_TYPE ARGOUT_ARRAY1[ANY]) + (PyObject* array = NULL) +{ + npy_intp dims[1] = { $1_dim0 }; + array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); + if (!array) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(argout) + (DATA_TYPE ARGOUT_ARRAY1[ANY]) +{ + $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); +} + +/* Typemap suite for (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) + */ +%typemap(in,numinputs=1, + fragment="NumPy_Fragments") + (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) + (PyObject* array = NULL) +{ + npy_intp dims[1]; + if (!PyInt_Check($input)) + { + const char* typestring = pytype_string($input); + PyErr_Format(PyExc_TypeError, + "Int dimension expected. '%s' given.", + typestring); + SWIG_fail; + } + $2 = (DIM_TYPE) PyInt_AsLong($input); + dims[0] = (npy_intp) $2; + array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); + if (!array) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); +} +%typemap(argout) + (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) +{ + $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); +} + +/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) + */ +%typemap(in,numinputs=1, + fragment="NumPy_Fragments") + (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) + (PyObject* array = NULL) +{ + npy_intp dims[1]; + if (!PyInt_Check($input)) + { + const char* typestring = pytype_string($input); + PyErr_Format(PyExc_TypeError, + "Int dimension expected. '%s' given.", + typestring); + SWIG_fail; + } + $1 = (DIM_TYPE) PyInt_AsLong($input); + dims[0] = (npy_intp) $1; + array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); + if (!array) SWIG_fail; + $2 = (DATA_TYPE*) array_data(array); +} +%typemap(argout) + (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) +{ + $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); +} + +/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) + */ +%typemap(in,numinputs=0, + fragment="NumPy_Backward_Compatibility,NumPy_Macros") + (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) + (PyObject* array = NULL) +{ + npy_intp dims[2] = { $1_dim0, $1_dim1 }; + array = PyArray_SimpleNew(2, dims, DATA_TYPECODE); + if (!array) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(argout) + (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) +{ + $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); +} + +/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) + */ +%typemap(in,numinputs=0, + fragment="NumPy_Backward_Compatibility,NumPy_Macros") + (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) + (PyObject* array = NULL) +{ + npy_intp dims[3] = { $1_dim0, $1_dim1, $1_dim2 }; + array = PyArray_SimpleNew(3, dims, DATA_TYPECODE); + if (!array) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(argout) + (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) +{ + $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); +} + +/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) + */ +%typemap(in,numinputs=0, + fragment="NumPy_Backward_Compatibility,NumPy_Macros") + (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) + (PyObject* array = NULL) +{ + npy_intp dims[4] = { $1_dim0, $1_dim1, $1_dim2, $1_dim3 }; + array = PyArray_SimpleNew(4, dims, DATA_TYPECODE); + if (!array) SWIG_fail; + $1 = ($1_ltype) array_data(array); +} +%typemap(argout) + (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) +{ + $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); +} + +/*****************************/ +/* Argoutview Array Typemaps */ +/*****************************/ + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim_temp) +{ + $1 = &data_temp; + $2 = &dim_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) +{ + npy_intp dims[1] = { *$2 }; + PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DATA_TYPE** ARGOUTVIEW_ARRAY1) + (DIM_TYPE dim_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim_temp; + $2 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) +{ + npy_intp dims[1] = { *$1 }; + PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$2)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) +{ + npy_intp dims[2] = { *$2, *$3 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_ARRAY2) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) +{ + npy_intp dims[2] = { *$1, *$2 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") + (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) +{ + npy_intp dims[2] = { *$2, *$3 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_FARRAY2) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2) +{ + npy_intp dims[2] = { *$1, *$2 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) +{ + npy_intp dims[3] = { *$2, *$3, *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, + DATA_TYPE** ARGOUTVIEW_ARRAY3) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) +{ + npy_intp dims[3] = { *$1, *$2, *$3 }; + PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") + (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) +{ + npy_intp dims[3] = { *$2, *$3, *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, + DATA_TYPE** ARGOUTVIEW_FARRAY3) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DATA_TYPE** ARGOUTVIEW_FARRAY3) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3) +{ + npy_intp dims[3] = { *$1, *$2, *$3 }; + PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3, DIM_TYPE* DIM4) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; + $5 = &dim4_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) +{ + npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, + DATA_TYPE** ARGOUTVIEW_ARRAY4) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEW_ARRAY4) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &dim4_temp; + $5 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_ARRAY4) +{ + npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3, DIM_TYPE* DIM4) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; + $5 = &dim4_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") + (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) +{ + npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, + DATA_TYPE** ARGOUTVIEW_FARRAY4) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEW_FARRAY4) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &dim4_temp; + $5 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_FARRAY4) +{ + npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + $result = SWIG_Python_AppendOutput($result,obj); +} + +/*************************************/ +/* Managed Argoutview Array Typemaps */ +/*************************************/ + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim_temp) +{ + $1 = &data_temp; + $2 = &dim_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1) +{ + npy_intp dims[1] = { *$2 }; + PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEWM_ARRAY1) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DATA_TYPE** ARGOUTVIEWM_ARRAY1) + (DIM_TYPE dim_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim_temp; + $2 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEWM_ARRAY1) +{ + npy_intp dims[1] = { *$1 }; + PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$2)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) +{ + npy_intp dims[2] = { *$2, *$3 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_ARRAY2) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEWM_ARRAY2) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_ARRAY2) +{ + npy_intp dims[2] = { *$1, *$2 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) +{ + npy_intp dims[2] = { *$2, *$3 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_FARRAY2) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEWM_FARRAY2) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_FARRAY2) +{ + npy_intp dims[2] = { *$1, *$2 }; + PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) +{ + npy_intp dims[3] = { *$2, *$3, *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, + DATA_TYPE** ARGOUTVIEWM_ARRAY3) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DATA_TYPE** ARGOUTVIEWM_ARRAY3) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_ARRAY3) +{ + npy_intp dims[3] = { *$1, *$2, *$3 }; + PyObject* obj= PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) +{ + npy_intp dims[3] = { *$2, *$3, *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, + DATA_TYPE** ARGOUTVIEWM_FARRAY3) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DATA_TYPE** ARGOUTVIEWM_FARRAY3) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_FARRAY3) +{ + npy_intp dims[3] = { *$1, *$2, *$3 }; + PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3, DIM_TYPE* DIM4) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; + $5 = &dim4_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) +{ + npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, + DATA_TYPE** ARGOUTVIEWM_ARRAY4) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_ARRAY4) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &dim4_temp; + $5 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4) +{ + npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3, DIM_TYPE* DIM4) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; + $5 = &dim4_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) +{ + npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, + DATA_TYPE** ARGOUTVIEWM_FARRAY4) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_FARRAY4) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &dim4_temp; + $5 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4) +{ + npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3, DIM_TYPE* DIM4) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; + $5 = &dim4_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) +{ + npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, + DATA_TYPE** ARGOUTVIEWM_ARRAY4) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_ARRAY4) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &dim4_temp; + $5 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4) +{ + npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, + DIM_TYPE* DIM3, DIM_TYPE* DIM4) + */ +%typemap(in,numinputs=0) + (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) + (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) +{ + $1 = &data_temp; + $2 = &dim1_temp; + $3 = &dim2_temp; + $4 = &dim3_temp; + $5 = &dim4_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) +{ + npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, + DATA_TYPE** ARGOUTVIEWM_FARRAY4) + */ +%typemap(in,numinputs=0) + (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_FARRAY4) + (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) +{ + $1 = &dim1_temp; + $2 = &dim2_temp; + $3 = &dim3_temp; + $4 = &dim4_temp; + $5 = &data_temp; +} +%typemap(argout, + fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") + (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4) +{ + npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; + PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); + PyArrayObject* array = (PyArrayObject*) obj; + + if (!array || !require_fortran(array)) SWIG_fail; + +%#ifdef SWIGPY_USE_CAPSULE + PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); +%#else + PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); +%#endif + +%#if NPY_API_VERSION < 0x00000007 + PyArray_BASE(array) = cap; +%#else + PyArray_SetBaseObject(array,cap); +%#endif + + $result = SWIG_Python_AppendOutput($result,obj); +} + +/**************************************/ +/* In-Place Array Typemap - flattened */ +/**************************************/ + +/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) + */ +%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, + fragment="NumPy_Macros") + (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) +{ + $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), + DATA_TYPECODE); +} +%typemap(in, + fragment="NumPy_Fragments") + (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) + (PyArrayObject* array=NULL, int i=1) +{ + array = obj_to_array_no_conversion($input, DATA_TYPECODE); + if (!array || !require_c_or_f_contiguous(array) + || !require_native(array)) SWIG_fail; + $1 = (DATA_TYPE*) array_data(array); + $2 = 1; + for (i=0; i < array_numdims(array); ++i) $2 *= array_size(array,i); +} + +%enddef /* %numpy_typemaps() macro */ +/* *************************************************************** */ + +/* Concrete instances of the %numpy_typemaps() macro: Each invocation + * below applies all of the typemaps above to the specified data type. + */ +%numpy_typemaps(signed char , NPY_BYTE , int) +%numpy_typemaps(unsigned char , NPY_UBYTE , int) +%numpy_typemaps(short , NPY_SHORT , int) +%numpy_typemaps(unsigned short , NPY_USHORT , int) +%numpy_typemaps(int , NPY_INT , int) +%numpy_typemaps(unsigned int , NPY_UINT , int) +%numpy_typemaps(long , NPY_LONG , int) +%numpy_typemaps(unsigned long , NPY_ULONG , int) +%numpy_typemaps(long long , NPY_LONGLONG , int) +%numpy_typemaps(unsigned long long, NPY_ULONGLONG, int) +%numpy_typemaps(float , NPY_FLOAT , int) +%numpy_typemaps(double , NPY_DOUBLE , int) +%numpy_typemaps(int8_t , NPY_INT8 , int) +%numpy_typemaps(int16_t , NPY_INT16 , int) +%numpy_typemaps(int32_t , NPY_INT32 , int) +%numpy_typemaps(int64_t , NPY_INT64 , int) +%numpy_typemaps(uint8_t , NPY_UINT8 , int) +%numpy_typemaps(uint16_t , NPY_UINT16 , int) +%numpy_typemaps(uint32_t , NPY_UINT32 , int) +%numpy_typemaps(uint64_t , NPY_UINT64 , int) + + +/* *************************************************************** + * The follow macro expansion does not work, because C++ bool is 4 + * bytes and NPY_BOOL is 1 byte + * + * %numpy_typemaps(bool, NPY_BOOL, int) + */ + +/* *************************************************************** + * On my Mac, I get the following warning for this macro expansion: + * 'swig/python detected a memory leak of type 'long double *', no destructor found.' + * + * %numpy_typemaps(long double, NPY_LONGDOUBLE, int) + */ + +#ifdef __cplusplus + +%include + +%numpy_typemaps(std::complex, NPY_CFLOAT , int) +%numpy_typemaps(std::complex, NPY_CDOUBLE, int) + +#endif + +#endif /* SWIGPYTHON */ diff --git a/bindings/setup.py b/bindings/setup.py new file mode 100644 index 0000000000..3cbc802548 --- /dev/null +++ b/bindings/setup.py @@ -0,0 +1,37 @@ +"""Compiles the cffirmware C extension.""" + +from distutils.core import setup, Extension +import os + +import numpy as np + + +fw_dir = ".." +include = [ + os.path.join(fw_dir, "src/modules/interface"), + os.path.join(fw_dir, "src/hal/interface"), + os.path.join(fw_dir, "src/utils/interface/lighthouse"), + np.get_include(), +] + +modules = [ + "collision_avoidance.c", + "planner.c", + "pptraj.c", + "pptraj_compressed.c", +] +fw_sources = [os.path.join(fw_dir, "src/modules/src", mod) for mod in modules] + +cffirmware = Extension( + "_cffirmware", + include_dirs=include, + sources=fw_sources + ["cffirmware_wrap.c"], + extra_compile_args=[ + "-O3", + # One Lighthouse header uses ARM's half-precision float, but we don't + # need it (for now) in bindings, so we simply pretend it's a uint16_t. + "-D__fp16=uint16_t", + ], +) + +setup(name="cffirmware", version="1.0", ext_modules=[cffirmware]) diff --git a/test/bindings/environment.yml b/test/bindings/environment.yml new file mode 100644 index 0000000000..00499cec28 --- /dev/null +++ b/test/bindings/environment.yml @@ -0,0 +1,10 @@ +name: cfbindings +dependencies: + - numpy + - matplotlib + - vispy + - pyyaml + - swig + - pyqt + - pytest + - scipy diff --git a/test/bindings/figure8.csv b/test/bindings/figure8.csv new file mode 100644 index 0000000000..4b25059b37 --- /dev/null +++ b/test/bindings/figure8.csv @@ -0,0 +1,11 @@ +duration,x^0,x^1,x^2,x^3,x^4,x^5,x^6,x^7,y^0,y^1,y^2,y^3,y^4,y^5,y^6,y^7,z^0,z^1,z^2,z^3,z^4,z^5,z^6,z^7,yaw^0,yaw^1,yaw^2,yaw^3,yaw^4,yaw^5,yaw^6,yaw^7, +1.050000,0.000000,-0.000000,0.000000,-0.000000,0.830443,-0.276140,-0.384219,0.180493,-0.000000,0.000000,-0.000000,0.000000,-1.356107,0.688430,0.587426,-0.329106,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.710000,0.396058,0.918033,0.128965,-0.773546,0.339704,0.034310,-0.026417,-0.030049,-0.445604,-0.684403,0.888433,1.493630,-1.361618,-0.139316,0.158875,0.095799,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.620000,0.922409,0.405715,-0.582968,-0.092188,-0.114670,0.101046,0.075834,-0.037926,-0.291165,0.967514,0.421451,-1.086348,0.545211,0.030109,-0.050046,-0.068177,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.700000,0.923174,-0.431533,-0.682975,0.177173,0.319468,-0.043852,-0.111269,0.023166,0.289869,0.724722,-0.512011,-0.209623,-0.218710,0.108797,0.128756,-0.055461,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.560000,0.405364,-0.834716,0.158939,0.288175,-0.373738,-0.054995,0.036090,0.078627,0.450742,-0.385534,-0.954089,0.128288,0.442620,0.055630,-0.060142,-0.076163,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.560000,0.001062,-0.646270,-0.012560,-0.324065,0.125327,0.119738,0.034567,-0.063130,0.001593,-1.031457,0.015159,0.820816,-0.152665,-0.130729,-0.045679,0.080444,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.700000,-0.402804,-0.820508,-0.132914,0.236278,0.235164,-0.053551,-0.088687,0.031253,-0.449354,-0.411507,0.902946,0.185335,-0.239125,-0.041696,0.016857,0.016709,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.620000,-0.921641,-0.464596,0.661875,0.286582,-0.228921,-0.051987,0.004669,0.038463,-0.292459,0.777682,0.565788,-0.432472,-0.060568,-0.082048,-0.009439,0.041158,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +0.710000,-0.923935,0.447832,0.627381,-0.259808,-0.042325,-0.032258,0.001420,0.005294,0.288570,0.873350,-0.515586,-0.730207,-0.026023,0.288755,0.215678,-0.148061,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, +1.053185,-0.398611,0.850510,-0.144007,-0.485368,-0.079781,0.176330,0.234482,-0.153567,0.447039,-0.532729,-0.855023,0.878509,0.775168,-0.391051,-0.713519,0.391628,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, diff --git a/test/bindings/pycrazyswarm/__init__.py b/test/bindings/pycrazyswarm/__init__.py new file mode 100644 index 0000000000..cb696d0947 --- /dev/null +++ b/test/bindings/pycrazyswarm/__init__.py @@ -0,0 +1,3 @@ +from .crazyswarm_py import * + +__all__ = ["Crazyswarm",] diff --git a/test/bindings/pycrazyswarm/crazyflieSim.py b/test/bindings/pycrazyswarm/crazyflieSim.py new file mode 100644 index 0000000000..eb708d9c70 --- /dev/null +++ b/test/bindings/pycrazyswarm/crazyflieSim.py @@ -0,0 +1,496 @@ +#!/usr/bin/env python + +import math + +import yaml +import numpy as np + +import cffirmware as firm + +# main class of simulation. +# crazyflies keep reference to this object to ask what time it is. +# also does the plotting. +# +class TimeHelper: + def __init__(self, vis, dt, writecsv, disturbanceSize, maxVel=np.inf, videopath=None): + if vis == "mpl": + from .visualizer import visMatplotlib + self.visualizer = visMatplotlib.VisMatplotlib() + elif vis == "vispy": + from .visualizer import visVispy + resizable = videopath is None + self.visualizer = visVispy.VisVispy(resizable=resizable) + elif vis == "null": + from .visualizer import visNull + self.visualizer = visNull.VisNull() + else: + raise Exception("Unknown visualization backend: {}".format(vis)) + self.t = 0.0 + self.dt = dt + # Since our integration/animation ticks are always the fixed duration + # dt, any call to sleep() with a non-multiple of dt will have some + # "leftover" time. Keep track of it here and add extra ticks in future. + self.sleepResidual = 0.0 + self.crazyflies = [] + self.disturbanceSize = disturbanceSize + self.maxVel = maxVel + if writecsv: + from . import output + self.output = output.Output() + else: + self.output = None + + if videopath is not None: + from .videowriter import VideoWriter + frame = self.visualizer.render() + self.videoWriter = VideoWriter(videopath, dt, frame.shape[:2]) + else: + self.videoWriter = None + + def time(self): + return self.t + + def step(self, duration): + self.t += duration + for cf in self.crazyflies: + cf.integrate(duration, self.disturbanceSize, self.maxVel) + for cf in self.crazyflies: + cf.flip() + + # should be called "animate" or something + # but called "sleep" for source-compatibility with real-robot scripts + def sleep(self, duration): + # operator // has unexpected (wrong ?) behavior for this calculation. + ticks = math.floor((duration + self.sleepResidual) / self.dt) + self.sleepResidual += duration - self.dt * ticks + assert -1e-9 <= self.sleepResidual < self.dt + + for _ in range(int(ticks)): + self.visualizer.update(self.t, self.crazyflies) + if self.output: + self.output.update(self.t, self.crazyflies) + if self.videoWriter is not None: + frame = self.visualizer.render() + self.videoWriter.writeFrame(frame) + self.step(self.dt) + + # Mock for abstraction of rospy.Rate.sleep(). + def sleepForRate(self, rate): + # TODO: account for rendering time, or is it worth the complexity? + self.sleep(1.0 / rate) + + # Mock for abstraction of rospy.is_shutdown(). + def isShutdown(self): + return False + + def addObserver(self, observer): + self.observers.append(observer) + + def _atexit(self): + if self.videoWriter is not None: + self.videoWriter.close() + + +def collisionAvoidanceUpdateSetpoint( + collisionParams, collisionState, mode, state, setState, otherCFs): + """Modifies a setpoint based on firmware collision-avoidance algorithm. + + Main purpose is to hide the firmware's stabilizer_types.h types, because we + prefer to work with cmath3d-based types. + + Args: + collisionParams (firmware collisionAvoidanceParams_t): Collision + avoidance algorithm parameters. Generally will remain constant. + collisionState: (firmware collisionAvoidanceState_t): Opaque collision + avoidance internal state. **Is modified in-place.** The same object + should be passed to this function in repeated calls. + mode (Crazyflie.MODE_* enum): The current flight mode. + state (firmware traj_eval): The Crazyflie's currents state. + setState (firmware traj_eval): The desired state generated by polynomial + trajectory, user low-level commands, etc. + otherCFs (array of Crazyflie): The other Crazyflie objects in the swarm. + + Returns: + newSetState (firmware traj_eval): A new desired state that attempts to + remain close to setState input while ensuring collision avoidance. + """ + + # This is significantly faster than calling position() on all the other CFs: + # 1.2 vs 1.8 seconds in test_collisionAvoidance.py::test_goToWithCA_random. + nOthers = len(otherCFs) + otherPositions = np.zeros((nOthers, 3), dtype=np.float32) + for i, cf in enumerate(otherCFs): + otherPositions[i][0] = cf.state.pos.x + otherPositions[i][1] = cf.state.pos.y + otherPositions[i][2] = cf.state.pos.z + + cmdState = firm.state_t() + # Position and velocity are the only states collision avoidance observes. + cmdState.position = firm.svec2vec(state.pos) + cmdState.velocity = firm.svec2vec(state.vel) + + # Dummy - it accepts the input to match the API of SitAw, but it's unused. + sensorData = firm.sensorData_t() + + setpoint = firm.setpoint_t() + if mode == Crazyflie.MODE_IDLE: + pass + elif mode in (Crazyflie.MODE_HIGH_POLY, Crazyflie.MODE_LOW_FULLSTATE): + setpoint.mode.x = firm.modeAbs + setpoint.position = firm.svec2vec(setState.pos) + setpoint.velocity = firm.svec2vec(setState.vel) + elif mode == Crazyflie.MODE_LOW_POSITION: + setpoint.mode.x = firm.modeAbs + setpoint.position = firm.svec2vec(setState.pos) + elif mode == Crazyflie.MODE_LOW_VELOCITY: + setpoint.mode.x = firm.modeVelocity + setpoint.velocity = firm.svec2vec(setState.vel) + else: + raise ValueError("Unknown flight mode.") + + firm.collisionAvoidanceUpdateSetpointWrap( + collisionParams, + collisionState, + otherPositions.flatten(), + setpoint, + sensorData, + cmdState) + + newSetState = firm.traj_eval_zero() + newSetState.pos = firm.vec2svec(setpoint.position) + newSetState.vel = firm.vec2svec(setpoint.velocity) + newSetState.yaw = setState.yaw + newSetState.omega.z = setState.omega[2] + return newSetState + + +class Crazyflie: + + # Flight modes. + MODE_IDLE = 0 + MODE_HIGH_POLY = 1 + MODE_LOW_FULLSTATE = 2 + MODE_LOW_POSITION = 3 + MODE_LOW_VELOCITY = 4 + + + def __init__(self, id, initialPosition, timeHelper): + + # Core. + self.id = id + self.groupMask = 0 + self.initialPosition = np.array(initialPosition) + self.time = lambda: timeHelper.time() + + # Commander. + self.mode = Crazyflie.MODE_IDLE + self.planner = firm.planner() + firm.plan_init(self.planner) + self.trajectories = dict() + self.setState = firm.traj_eval_zero() + + # State. Public np.array-returning getters below for physics state. + self.state = firm.traj_eval_zero() + self.state.pos = firm.mkvec(*initialPosition) + self.state.vel = firm.vzero() + self.state.acc = firm.vzero() + self.state.yaw = 0.0 + self.state.omega = firm.vzero() + self.ledRGB = (0.5, 0.5, 1) + + # Double-buffering: Ensure that all CFs observe the same world state + # during an integration step, regardless of the order in which their + # integrate() methods are called. flip() swaps front and back state. + # See http://gameprogrammingpatterns.com/double-buffer.html for more + # motivation. + self.backState = firm.traj_eval(self.state) + + # For collision avoidance. + self.otherCFs = [] + self.collisionAvoidanceParams = None + self.collisionAvoidanceState = None + + def setGroupMask(self, groupMask): + self.groupMask = groupMask + + def enableCollisionAvoidance(self, others, ellipsoidRadii, bboxMin=np.repeat(-np.inf, 3), bboxMax=np.repeat(np.inf, 3), horizonSecs=1.0, maxSpeed=2.0): + self.otherCFs = [cf for cf in others if cf is not self] + + # TODO: Accept more of these from arguments. + params = firm.collision_avoidance_params_t() + params.ellipsoidRadii = firm.mkvec(*ellipsoidRadii) + params.bboxMin = firm.mkvec(*bboxMin) + params.bboxMax = firm.mkvec(*bboxMax) + params.horizonSecs = horizonSecs + params.maxSpeed = maxSpeed + params.sidestepThreshold = 0.25 + params.voronoiProjectionTolerance = 1e-5 + params.voronoiProjectionMaxIters = 100 + self.collisionAvoidanceParams = params + + state = firm.collision_avoidance_state_t() + state.lastFeasibleSetPosition = firm.mkvec(np.nan, np.nan, np.nan) + self.collisionAvoidanceState = state + + def disableCollisionAvoidance(self): + self.otherCFs = None + self.collisionAvoidanceParams = None + self.collisionAvoidanceState = None + + def takeoff(self, targetHeight, duration, groupMask = 0): + if self._isGroup(groupMask): + self.mode = Crazyflie.MODE_HIGH_POLY + targetYaw = 0.0 + firm.plan_takeoff(self.planner, + self.state.pos, self.state.yaw, targetHeight, targetYaw, duration, self.time()) + + def land(self, targetHeight, duration, groupMask = 0): + if self._isGroup(groupMask): + self.mode = Crazyflie.MODE_HIGH_POLY + targetYaw = 0.0 + firm.plan_land(self.planner, + self.state.pos, self.state.yaw, targetHeight, targetYaw, duration, self.time()) + + def stop(self, groupMask = 0): + if self._isGroup(groupMask): + self.mode = Crazyflie.MODE_IDLE + firm.plan_stop(self.planner) + + def goTo(self, goal, yaw, duration, relative = False, groupMask = 0): + if self._isGroup(groupMask): + if self.mode != Crazyflie.MODE_HIGH_POLY: + # We need to update to the latest firmware that has go_to_from. + raise ValueError("goTo from low-level modes not yet supported.") + self.mode = Crazyflie.MODE_HIGH_POLY + firm.plan_go_to(self.planner, relative, firm.mkvec(*goal), yaw, duration, self.time()) + + def uploadTrajectory(self, trajectoryId, pieceOffset, trajectory): + traj = firm.piecewise_traj() + traj.t_begin = 0 + traj.timescale = 1.0 + traj.shift = firm.mkvec(0, 0, 0) + traj.n_pieces = len(trajectory.polynomials) + traj.pieces = firm.malloc_poly4d(len(trajectory.polynomials)) + for i, poly in enumerate(trajectory.polynomials): + piece = firm.pp_get_piece(traj, i) + piece.duration = poly.duration + for coef in range(0, 8): + firm.poly4d_set(piece, 0, coef, poly.px.p[coef]) + firm.poly4d_set(piece, 1, coef, poly.py.p[coef]) + firm.poly4d_set(piece, 2, coef, poly.pz.p[coef]) + firm.poly4d_set(piece, 3, coef, poly.pyaw.p[coef]) + self.trajectories[trajectoryId] = traj + + def startTrajectory(self, trajectoryId, timescale = 1.0, reverse = False, relative = True, groupMask = 0): + if self._isGroup(groupMask): + self.mode = Crazyflie.MODE_HIGH_POLY + traj = self.trajectories[trajectoryId] + traj.t_begin = self.time() + traj.timescale = timescale + if relative: + traj.shift = firm.vzero() + if reverse: + traj_init = firm.piecewise_eval_reversed(traj, traj.t_begin) + else: + traj_init = firm.piecewise_eval(traj, traj.t_begin) + traj.shift = self.state.pos - traj_init.pos + else: + traj.shift = firm.vzero() + firm.plan_start_trajectory(self.planner, traj, reverse) + + def notifySetpointsStop(self, remainValidMillisecs=100): + # No-op - the real Crazyflie prioritizes streaming setpoints over + # high-level commands. This tells it to stop doing that. We don't + # simulate this behavior. + pass + + def position(self): + return np.array(self.state.pos) + + def getParam(self, name): + print("WARNING: getParam not implemented in simulation!") + + def setParam(self, name, value): + print("WARNING: setParam not implemented in simulation!") + + def setParams(self, params): + print("WARNING: setParams not implemented in simulation!") + + # - this is a part of the param system on the real crazyflie, + # but we implement it in simulation too for debugging + # - is a blocking command on real CFs, so may cause stability problems + def setLEDColor(self, r, g, b): + self.ledRGB = (r, g, b) + + # simulation only functions + def yaw(self): + return float(self.state.yaw) + + def velocity(self): + return np.array(self.state.vel) + + def acceleration(self): + return np.array(self.state.acc) + + def rpy(self): + yaw = self.yaw() + # Unpack the matrix columns. + x_body, y_body, z_body = self.rotBodyToWorld().T + pitch = math.asin(-x_body[2]) + roll = math.atan2(y_body[2], z_body[2]) + return (roll, pitch, yaw) + + def rotBodyToWorld(self): + acc = self.acceleration() + yaw = self.yaw() + norm = np.linalg.norm(acc) + # TODO: This causes a vertical flip for faster-than-gravity vertical + # deceleration, but fixing it would essentially require introducing the + # idea of a controller, which we have avoided so far. + thrust = acc + np.array([0, 0, 9.81]) + z_body = thrust / np.linalg.norm(thrust) + x_world = np.array([math.cos(yaw), math.sin(yaw), 0]) + y_body = np.cross(z_body, x_world) + # TODO: This can have a singularity if z_body = x_world. + y_body /= np.linalg.norm(y_body) + x_body = np.cross(y_body, z_body) + return np.column_stack([x_body, y_body, z_body]) + + def cmdFullState(self, pos, vel, acc, yaw, omega): + self.mode = Crazyflie.MODE_LOW_FULLSTATE + self.setState.pos = firm.mkvec(*pos) + self.setState.vel = firm.mkvec(*vel) + self.setState.acc = firm.mkvec(*acc) + self.setState.yaw = yaw + self.setState.omega = firm.mkvec(*omega) + + def cmdPosition(self, pos, yaw = 0): + self.mode = Crazyflie.MODE_LOW_POSITION + self.setState.pos = firm.mkvec(*pos) + self.setState.yaw = yaw + # TODO: should we set vel, acc, omega to zero, or rely on modes to not read them? + + def cmdVelocityWorld(self, vel, yawRate): + self.mode = Crazyflie.MODE_LOW_VELOCITY + self.setState.vel = firm.mkvec(*vel) + self.setState.omega = firm.mkvec(0.0, 0.0, yawRate) + # TODO: should we set pos, acc, yaw to zero, or rely on modes to not read them? + + def cmdStop(self): + # TODO: set mode to MODE_IDLE? + pass + + def integrate(self, time, disturbanceSize, maxVel): + if self.mode == Crazyflie.MODE_HIGH_POLY: + self.setState = firm.plan_current_goal(self.planner, self.time()) + + if self.collisionAvoidanceState is not None: + setState = collisionAvoidanceUpdateSetpoint( + self.collisionAvoidanceParams, + self.collisionAvoidanceState, + self.mode, + self.state, + self.setState, + self.otherCFs, + ) + else: + setState = firm.traj_eval(self.setState) + + if self.mode == Crazyflie.MODE_IDLE: + return + + if self.mode in (Crazyflie.MODE_HIGH_POLY, Crazyflie.MODE_LOW_FULLSTATE, Crazyflie.MODE_LOW_POSITION): + velocity = (setState.pos - self.state.pos) / time + elif self.mode == Crazyflie.MODE_LOW_VELOCITY: + velocity = setState.vel + else: + raise ValueError("Unknown flight mode.") + + # Limit velocity for realism. + # Note: This will result in the state having a different velocity than + # the setState in HIGH_POLY and LOW_FULLSTATE modes even when no + # clamping occurs, because we are essentially getting rid of the + # feedforward commands. We assume this is not a problem. + + velocity = firm.vclampnorm(velocity, maxVel) + + disturbance = disturbanceSize * np.random.normal(size=3) + velocity = velocity + firm.mkvec(*disturbance) + self.backState = firm.traj_eval(setState) + self.backState.pos = self.state.pos + time * velocity + self.backState.vel = velocity + + if self.mode == Crazyflie.MODE_LOW_POSITION: + yawRate = (setState.yaw - self.state.yaw) / time + self.backState.yaw = setState.yaw + self.backState.omega = firm.mkvec(0.0, 0.0, yawRate) + elif self.mode == Crazyflie.MODE_LOW_VELOCITY: + # Omega is already set. + self.backState.yaw += time * self.setState.omega.z + + # In HIGH_POLY and LOW_FULLSTATE, yaw and omega are already specified + # in setState and have been copied. + + def flip(self): + # Swap double-buffered state. Called at the end of the tick update, + # after *all* CFs' integrate() methods have been called. + self.state, self.backState = self.backState, self.state + + # "private" methods + def _isGroup(self, groupMask): + return groupMask == 0 or (self.groupMask & groupMask) > 0 + + +class CrazyflieServer: + def __init__(self, timeHelper, crazyflies_yaml="../launch/crazyflies.yaml"): + """Initialize the server. + + Args: + timeHelper (TimeHelper): TimeHelper instance. + crazyflies_yaml (str): If ends in ".yaml", interpret as a path and load + from file. Otherwise, interpret as YAML string and parse + directly from string. + """ + if crazyflies_yaml.endswith(".yaml"): + with open(crazyflies_yaml, 'r') as ymlfile: + cfg = yaml.safe_load(ymlfile) + else: + cfg = yaml.safe_load(crazyflies_yaml) + + self.crazyflies = [] + self.crazyfliesById = dict() + for crazyflie in cfg["crazyflies"]: + id = int(crazyflie["id"]) + initialPosition = crazyflie["initialPosition"] + cf = Crazyflie(id, initialPosition, timeHelper) + self.crazyflies.append(cf) + self.crazyfliesById[id] = cf + + self.timeHelper = timeHelper + self.timeHelper.crazyflies = self.crazyflies + + def emergency(self): + print("WARNING: emergency not implemented in simulation!") + + def takeoff(self, targetHeight, duration, groupMask = 0): + for crazyflie in self.crazyflies: + crazyflie.takeoff(targetHeight, duration, groupMask) + + def land(self, targetHeight, duration, groupMask = 0): + for crazyflie in self.crazyflies: + crazyflie.land(targetHeight, duration, groupMask) + + def stop(self, groupMask = 0): + for crazyflie in self.crazyflies: + crazyflie.stop(groupMask) + + def goTo(self, goal, yaw, duration, groupMask = 0): + for crazyflie in self.crazyflies: + crazyflie.goTo(goal, yaw, duration, relative=True, groupMask=groupMask) + + def startTrajectory(self, trajectoryId, timescale = 1.0, reverse = False, relative = True, groupMask = 0): + for crazyflie in self.crazyflies: + crazyflie.startTrajectory(trajectoryId, timescale, reverse, relative, groupMask) + + def setParam(self, name, value): + print("WARNING: setParam not implemented in simulation!") diff --git a/test/bindings/pycrazyswarm/crazyswarm_py.py b/test/bindings/pycrazyswarm/crazyswarm_py.py new file mode 100644 index 0000000000..7394e6300f --- /dev/null +++ b/test/bindings/pycrazyswarm/crazyswarm_py.py @@ -0,0 +1,49 @@ +import argparse +import atexit + +import numpy as np + + +# Building the parser in a separate function allows sphinx-argparse to +# auto-generate the documentation for the command-line flags. +def build_argparser(parent_parsers=[]): + parser = argparse.ArgumentParser( + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + parents=parent_parsers + ) + parser.add_argument("--sim", help="Run using simulation.", action="store_true") + + group = parser.add_argument_group("Simulation-only", "") + group.add_argument("--vis", help="Visualization backend.", choices=['mpl', 'vispy', 'null'], default="mpl") + group.add_argument("--dt", help="Duration of seconds between rendered visualization frames.", type=float, default=0.1) + group.add_argument("--writecsv", help="Enable CSV output.", action="store_true") + group.add_argument("--disturbance", help="Simulate Gaussian-distributed disturbance when using cmdVelocityWorld.", type=float, default=0.0) + group.add_argument("--maxvel", help="Limit simulated velocity (meters/sec).", type=float, default=np.inf) + group.add_argument("--video", help="Video output path.", type=str) + + return parser + + +class Crazyswarm: + def __init__(self, crazyflies_yaml=None, parent_parser=None, args=None): + if parent_parser is not None: + parents = [parent_parser] + else: + parents = [] + parser = build_argparser(parents) + if isinstance(args, str): + args = args.split() + args, unknown = parser.parse_known_args(args) + + if crazyflies_yaml is None: + crazyflies_yaml = "../launch/crazyflies.yaml" + if crazyflies_yaml.endswith(".yaml"): + crazyflies_yaml = open(crazyflies_yaml, 'r').read() + + if args.sim: + from .crazyflieSim import TimeHelper, CrazyflieServer + self.timeHelper = TimeHelper(args.vis, args.dt, args.writecsv, disturbanceSize=args.disturbance, maxVel=args.maxvel, videopath=args.video) + self.allcfs = CrazyflieServer(self.timeHelper, crazyflies_yaml) + atexit.register(self.timeHelper._atexit) + else: + raise NotImplementedError("Sim only now!") diff --git a/test/bindings/pycrazyswarm/util.py b/test/bindings/pycrazyswarm/util.py new file mode 100644 index 0000000000..a501d6b931 --- /dev/null +++ b/test/bindings/pycrazyswarm/util.py @@ -0,0 +1,85 @@ +"""Useful functions for both pycrazyswarm internals and user scripts.""" + +import numpy as np +import scipy as sp +import scipy.spatial + + +def check_ellipsoid_collisions(positions, radii): + """Checks for collisions between a set of ellipsoids at given positions. + + Args: + positions (array float[n, 3]): The ellipsoid centers. + radii (array float[3]): The radii of the axis-aligned ellipsoids. + + Returns: + colliding (array bool[n]): True at index i if the i'th ellipsoid + intersects any of the other ellipsoids. + """ + scaled = positions / radii[None, :] + dists = sp.spatial.distance.pdist(scaled) + dists = sp.spatial.distance.squareform(dists) + # Do not consider 0 distance to self as a collision! + n, _ = positions.shape + dists[range(n), range(n)] = np.inf + colliding = np.any(dists < 1.97, axis=1) + return colliding + + +def poisson_disk_sample(n, dim, mindist): + """Generates random points with guaranteed minimum pairwise distance. + + Uses extremely naive and slow "dart throwing" algorithm. + TODO(jpreiss): find/implement a library with a fast algorithm. + + Args: + n (int): Number of points. + dim (int): Dimensionality of points. + mindist (float): Minimum Euclidean distance between any two points. + + Returns: + pts (array float[n, dim]): The sampled points. + """ + + # Select hypercube volume such that n points will not pack it too tightly. + # Note: Will be too sparse for dim >> 3, but reasonable for dim == 2 or 3. + measure_ratio = 1.25 + std = (measure_ratio * n) ** (1.0 / dim) * mindist + def sample(): + return std * np.random.uniform(-0.5, 0.5, size=dim) + + # Sample the points using dart-throwing. + pts = sample()[None,:] + while len(pts) < n: + pt = sample() + dists = np.linalg.norm(pts - pt, axis=1) + if np.all(dists >= mindist): + pts = np.concatenate([pts, pt[None,:]], axis=0) + return pts + + +def grid_yaml(rows, cols, spacing=0.5): + """Generate crazyflies.yaml string for a grid in the XZ plane. + + Args: + rows (int): Number of rows (distinct X-values) in the grid. + cols (int): Number of columns (distinct Y-values) in the grid. + spacing (float): Grid spacing for both axes. + + Returns: + yaml (str): String containing crazyflies.yaml. Will contain + (rows * cols) crazyflies, all assigned on radio channel 100. + Positions will be centered about the origin. No particular + id-position mapping should be assumed. + """ + x, y = spacing * np.mgrid[:rows, :cols] + x -= np.mean(x) + y -= np.mean(y) + return "crazyflies:\n" + "\n".join([ + """- channel: 100 + id: {} + initialPosition: [{}, {}, 0.0]""".format(i, x, y) + for i, (x, y) in enumerate(zip(x.flat, y.flat)) + ]) + + diff --git a/test/bindings/pycrazyswarm/visualizer/__init__.py b/test/bindings/pycrazyswarm/visualizer/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/bindings/pycrazyswarm/visualizer/crazyflie2.obj.gz b/test/bindings/pycrazyswarm/visualizer/crazyflie2.obj.gz new file mode 100644 index 0000000000000000000000000000000000000000..df8427c34759b6bec62bee56943a0f8d88a042b5 GIT binary patch literal 208870 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 z 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

JuLW0^ib9{###`+qV-v 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

FpQ;@v^2u|b?n@)ak#5Pof^YcyijexL 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 literal 0 HcmV?d00001 diff --git a/test/bindings/pycrazyswarm/visualizer/visMatplotlib.py b/test/bindings/pycrazyswarm/visualizer/visMatplotlib.py new file mode 100644 index 0000000000..83eeb03263 --- /dev/null +++ b/test/bindings/pycrazyswarm/visualizer/visMatplotlib.py @@ -0,0 +1,81 @@ +import warnings + +from mpl_toolkits.mplot3d import Axes3D +from mpl_toolkits.mplot3d.art3d import Line3DCollection +import matplotlib.pyplot as plt +import numpy as np + + +class VisMatplotlib: + def __init__(self): + self.fig = plt.figure() + self.ax = self.fig.add_subplot(111, projection='3d') + self.ax.set_xlim([-5, 5]) + self.ax.set_ylim([-5, 5]) + self.ax.set_zlim([0, 3]) + self.ax.set_xlabel("X") + self.ax.set_ylabel("Y") + self.ax.set_zlabel("Z") + self.plot = None + self.timeAnnotation = self.ax.annotate("Time", xy=(0, 0), xycoords='axes fraction', fontsize=12, ha='right', va='bottom') + + self.line_color = 0.3 * np.ones(3) + + # Lazy-constructed data for connectivity graph gfx. + self.graph_edges = None + self.graph_lines = None + self.graph = None + + def setGraph(self, edges): + """Set edges of graph visualization - sequence of (i,j) tuples.""" + + # Only allocate new memory if we need to. + n_edges = len(edges) + if self.graph_edges is None or n_edges != len(self.graph_edges): + self.graph_lines = np.zeros((n_edges, 2, 3)) + self.graph_edges = edges + + # Lazily construct Matplotlib object for graph. + if self.graph is None: + self.graph = Line3DCollection(self.graph_lines, edgecolor=self.line_color) + self.ax.add_collection(self.graph) + + def showEllipsoids(self, radii): + warnings.warn("showEllipsoids not implemented in Matplotlib visualizer.") + + def update(self, t, crazyflies): + xs = [] + ys = [] + zs = [] + cs = [] + for cf in crazyflies: + x, y, z = cf.position() + color = cf.ledRGB + xs.append(x) + ys.append(y) + zs.append(z) + cs.append(color) + + if self.plot is None: + self.plot = self.ax.scatter(xs, ys, zs, c=cs) + else: + # TODO: Don't use protected members. + self.plot._offsets3d = (xs, ys, zs) + self.plot.set_facecolors(cs) + self.plot.set_edgecolors(cs) + self.plot._facecolor3d = self.plot.get_facecolor() + self.plot._edgecolor3d = self.plot.get_edgecolor() + + if self.graph is not None: + # Update graph line segments to match new Crazyflie positions. + for k, (i, j) in enumerate(self.graph_edges): + self.graph_lines[k, 0, :] = xs[i], ys[i], zs[i] + self.graph_lines[k, 1, :] = xs[j], ys[j], zs[j] + self.graph.set_segments(self.graph_lines) + + self.timeAnnotation.set_text("{} s".format(t)) + plt.pause(0.0001) + + def render(self): + warnings.warn("Rendering video not supported in VisMatplotlib yet.") + return None diff --git a/test/bindings/pycrazyswarm/visualizer/visNull.py b/test/bindings/pycrazyswarm/visualizer/visNull.py new file mode 100644 index 0000000000..6073056fe0 --- /dev/null +++ b/test/bindings/pycrazyswarm/visualizer/visNull.py @@ -0,0 +1,17 @@ +"""No-op visualizer for real hardware runs, so script can be oblivious.""" + +class VisNull: + def __init__(self): + pass + + def setGraph(self, edges): + pass + + def showEllipsoids(self, radii): + pass + + def update(self, t, crazyflies): + pass + + def render(self): + return None diff --git a/test/bindings/pycrazyswarm/visualizer/visVispy.py b/test/bindings/pycrazyswarm/visualizer/visVispy.py new file mode 100644 index 0000000000..03ccf331e3 --- /dev/null +++ b/test/bindings/pycrazyswarm/visualizer/visVispy.py @@ -0,0 +1,166 @@ +import os +import math + +import ffmpeg +import numpy as np +from vispy import scene, app, io, geometry +from vispy.color import Color +from vispy.visuals import transforms +from vispy.scene.cameras import TurntableCamera + +from .. import util as util + + +CF_MESH_PATH = os.path.join(os.path.dirname(__file__), "crazyflie2.obj.gz") +# Convert millimeters to meters, but make twice as big so easier to see. +MESHFILE_SCALE = 2.0 * 0.001 +# The matrix that rotates the coordinates of the .obj file to agree with the +# Crazyflie's standard coordinate system. VisPy uses [row vector] * [matrix] +# (like DirectX), so this is the transpose of what we would expect. +UNROT_MESHFILE_TRANSPOSE = MESHFILE_SCALE * np.array([ + [-1, 0, 0], + [ 0, 0, 1], + [ 0, -1, 0], +]) +ELLIPSOID_COLOR_OK = Color("#11FF22", alpha=0.1) +ELLIPSOID_COLOR_COLLISION = Color("#FF0000", alpha=0.1) + + +class VisVispy: + def __init__(self, show=True, resizable=True): + self.canvas = scene.SceneCanvas( + keys='interactive', size=(1024, 768), show=show, config=dict(samples=4), resizable=resizable + ) + + self.plane_color = 0.25 * np.ones((1, 3)) + self.bg_color = 0.9 * np.ones((1, 3)) + self.line_color = 0.7 * np.ones((1, 3)) + + # Set up a viewbox to display the cube with interactive arcball + self.view = self.canvas.central_widget.add_view() + self.view.bgcolor = self.bg_color + self.view.camera = TurntableCamera( + fov=30.0, elevation=30.0, azimuth=90.0, center=(0.0, 0.0, 1.25) + ) + self.cam_state = self.view.camera.get_state() + + # add a colored 3D axis for orientation + axis = scene.visuals.XYZAxis(parent=self.view.scene) + self.cfs = [] + self.led_color_cache = [] + + ground = scene.visuals.Plane( + 32.0, 32.0, direction="+z", color=self.plane_color, parent=self.view.scene + ) + + # Lazy-constructed vispy objects and data for connectivity graph gfx. + self.graph_edges = None + self.graph_lines = None + self.graph = None + + # Lazy-constructed vispy objects for collision ellipsoids. + self.ellipsoids = None + self.ellipsoid_radii = None + + def setGraph(self, edges): + """Set edges of graph visualization - sequence of (i,j) tuples.""" + + # Only allocate new memory if we need to. + n_edges = len(edges) + if self.graph_edges is None or n_edges != len(self.graph_edges): + self.graph_lines = np.zeros((2 * n_edges, 3)) + self.graph_edges = edges + + # Lazily construct VisPy object for graph. + if self.graph is None: + self.graph = scene.visuals.Line( + parent=self.view.scene, + color=self.line_color, + pos=self.graph_lines, + connect="segments", + method="gl", + antialias=True, + ) + + def showEllipsoids(self, radii): + self.ellipsoid_radii = np.array(radii) + + def update(self, t, crazyflies): + if len(self.cfs) == 0: + verts, faces, normals, nothin = io.read_mesh(CF_MESH_PATH) + for i, cf in enumerate(crazyflies): + color = cf.ledRGB + mesh = scene.visuals.Mesh( + parent=self.view.scene, + vertices=verts, + faces=faces, + color=color, + shading="smooth", + ) + mesh.light_dir = (0.1, 0.1, 1.0) + mesh.shininess = 0.01 + mesh.ambient_light_color = [0.5] * 3 + mesh.transform = transforms.MatrixTransform() + self.cfs.append(mesh) + self.led_color_cache.append(color) + + if self.ellipsoid_radii is not None and self.ellipsoids is None: + sphere_mesh = geometry.create_sphere(radius=1.0) + self.ellipsoids = [ + scene.visuals.Mesh( + parent=self.view.scene, + meshdata=sphere_mesh, + color=ELLIPSOID_COLOR_OK, + shading="smooth", + ) + for _ in self.cfs + ] + for ell in self.ellipsoids: + ell.light_dir = (0.1, 0.1, 1.0) + ell.shininess = 0.0 + ell.ambient_light_color = [0.5] * 3 + ell.transform = transforms.MatrixTransform() + + positions = np.stack([cf.position() for cf in crazyflies]) + + for i in range(0, len(self.cfs)): + R_state = crazyflies[i].rotBodyToWorld() + # Recall VisPy uses [row vector] * [matrix]!! + T = np.eye(4) + T[:3, :3] = np.dot(UNROT_MESHFILE_TRANSPOSE, R_state.T) + T[3, :3] = positions[i] + self.cfs[i].transform = transforms.MatrixTransform(T) + # vispy does not do this check + color = crazyflies[i].ledRGB + if color != self.led_color_cache[i]: + self.led_color_cache[i] = color + self.cfs[i].color = color # sets dirty flag + + # Update graph line segments to match new Crazyflie positions. + if self.graph is not None: + for k, (i, j) in enumerate(self.graph_edges): + self.graph_lines[2 * k, :] = positions[i] + self.graph_lines[2 * k + 1, :] = positions[j] + self.graph.set_data(self.graph_lines) + + # Update collsiion ellipsoids. + if self.ellipsoids is not None: + colliding = util.check_ellipsoid_collisions(positions, self.ellipsoid_radii) + for i, pos in enumerate(positions): + ell = self.ellipsoids[i] + tf = ell.transform + tf.reset() + tf.scale(self.ellipsoid_radii) + tf.translate(pos) + new_color = ELLIPSOID_COLOR_COLLISION if colliding[i] else ELLIPSOID_COLOR_OK + if not (new_color == ell.color): # vispy Color lacks != override. + ell.color = new_color + + self.canvas.app.process_events() + + def render(self): + frame = self.canvas.render() + # Do not render alpha channel - we always use rgb24 format. + if frame.shape[2] == 4: + frame = frame[:, :, :3] + return frame diff --git a/test/bindings/test_collisionAvoidance.py b/test/bindings/test_collisionAvoidance.py new file mode 100644 index 0000000000..f62024b842 --- /dev/null +++ b/test/bindings/test_collisionAvoidance.py @@ -0,0 +1,316 @@ +import sys + +import numpy as np +import pytest + +from pycrazyswarm import * +from pycrazyswarm.util import * + + +RADII = np.array([0.125, 0.125, 0.375]) +Z = 1.0 + + +def setUp(args): + crazyflies_yaml = """ + crazyflies: + - id: 1 + channel: 100 + initialPosition: [-1.0, 0.0, 0.0] + - id: 2 + channel: 100 + initialPosition: [1.0, 0.0, 0.0] + """ + swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args=args) + timeHelper = swarm.timeHelper + return swarm.allcfs, timeHelper + + +def test_velocityMode_sidestepWorstCase(args=None): + if args is None: + args = "--sim --vis null --dt 0.05 --maxvel 1.0" + + allcfs, timeHelper = setUp(args) + a, b = allcfs.crazyflies + + a.enableCollisionAvoidance([b], RADII) + b.enableCollisionAvoidance([a], RADII) + + a.cmdVelocityWorld([1.0, 0.0, 0.0], yawRate=0.0) + b.cmdVelocityWorld([-1.0, 0.0, 0.0], yawRate=0.0) + + while timeHelper.time() < 10.0: + positions = np.stack([a.position(), b.position()]) + collisions = check_ellipsoid_collisions(positions, RADII) + assert not np.any(collisions) + + timeHelper.sleep(timeHelper.dt) + if a.position()[0] > 1.0 and b.position()[0] < -1.0: + return + assert False + + +def test_goToWithoutCA_CheckCollision(): + args = "--sim --vis null" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + + cf0.goTo([1.0, 0.0, 1.0], 0, 5.0) + cf1.goTo([-1.0, 0.0, 1.0], 0, 5.0) + while timeHelper.time() < 10.0: + positions = np.stack([cf0.position(), cf1.position()]) + collisions = check_ellipsoid_collisions(positions, RADII) + if np.any(collisions): + return + timeHelper.sleep(timeHelper.dt) + assert False + + +def test_goToWithCA_CheckCollision(): + args = "--sim --vis null" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + cf0.enableCollisionAvoidance([cf1], RADII) + cf1.enableCollisionAvoidance([cf0], RADII) + + cf0.goTo([1.0, 0.0, 1.0], 0, 5.0) + cf1.goTo([-1.0, 0.0, 1.0], 0, 5.0) + while timeHelper.time() < 10.0: + positions = np.stack([cf0.position(), cf1.position()]) + collisions = check_ellipsoid_collisions(positions, RADII) + assert not np.any(collisions) + timeHelper.sleep(timeHelper.dt) + + +def test_goToWithCA_CheckDestination(): + args = "--sim --vis null" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + cf0.enableCollisionAvoidance([cf1], RADII) + cf1.enableCollisionAvoidance([cf0], RADII) + + goal0 = [1.0, 0.0, 1.0] + goal1 = [-1.0, 0.0, 1.0] + cf0.goTo(goal0, 0, 5.0) + cf1.goTo(goal1, 0, 5.0) + timeHelper.sleep(5) + assert np.all(np.isclose(cf0.position(), goal0)) + assert np.all(np.isclose(cf1.position(), goal1)) + + +def test_goToWithCA_changeEllipsoid(): + args = "--sim --vis null" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + + newRADII = np.array([0.1, 0.1, 0.3]) + cf0.enableCollisionAvoidance([cf1], newRADII) + cf1.enableCollisionAvoidance([cf0], newRADII) + + goal0 = [1.0, 0.0, 1.0] + goal1 = [-1.0, 0.0, 1.0] + cf0.goTo(goal0, 0, 5.0) + cf1.goTo(goal1, 0, 5.0) + + # flag if a collision happened during sleep + collisionHappend = False + while timeHelper.time() < 10.0: + positions = np.stack([cf0.position(), cf1.position()]) + collisionsOld = check_ellipsoid_collisions(positions, RADII) + collisionsNew = check_ellipsoid_collisions(positions, newRADII) + if np.any(collisionsOld): + collisionHappend = True + assert not np.any(collisionsNew) + + timeHelper.sleep(timeHelper.dt) + assert collisionHappend + + +def test_goToWithCA_Intersection(): + args = "--sim --vis null --dt 0.01" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + cf0.enableCollisionAvoidance([cf1], RADII) + cf1.enableCollisionAvoidance([cf0], RADII) + + goal0 = [1.0, 1.0, 1.0] + goal1 = [-1.0, 1.0, 1.0] + cf0.goTo(goal0, 0, 5.0) + cf1.goTo(goal1, 0, 5.0) + + while timeHelper.time() < 10.0: + positions = np.stack([cf0.position(), cf1.position()]) + collisions = check_ellipsoid_collisions(positions, RADII) + assert not np.any(collisions) + + timeHelper.sleep(timeHelper.dt) + assert np.all(np.isclose(cf0.position(), goal0)) + assert np.all(np.isclose(cf1.position(), goal1)) + + +def test_goToWithoutCA_Intersection(): + args = "--sim --vis null --dt 0.05" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + + goal0 = [1.0, 1.0, 1.0] + goal1 = [-1.0, 1.0, 1.0] + cf0.goTo(goal0, 0, 5.0) + cf1.goTo(goal1, 0, 5.0) + + while timeHelper.time() < 10.0: + positions = np.stack([cf0.position(), cf1.position()]) + collisions = check_ellipsoid_collisions(positions, RADII) + if np.any(collisions): + return + + timeHelper.sleep(timeHelper.dt) + assert False + + +def test_goToWithCA_random(): + rows, cols = 3, 5 + N = rows * cols + crazyflies_yaml = grid_yaml(rows, cols, spacing=0.5) + + args = "--sim --vis null" + swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args=args) + timeHelper = swarm.timeHelper + allcfs = swarm.allcfs + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + + cfs = allcfs.crazyflies + + for _, cf in enumerate(cfs): + cf.enableCollisionAvoidance(cfs, RADII) + + np.random.seed(0) + xy_radius = 0.125 + for _ in range(5): + lastTime = timeHelper.time() + goals = poisson_disk_sample(N, dim=2, mindist=5*xy_radius) + goals_z = Z * np.ones(N) + 0.2 * np.random.uniform(-1.0, 1.0, size=N) + goals = np.hstack([goals, goals_z[:, None]]) + + for goal, cf in zip(goals, cfs): + cf.goTo(goal, yaw=0.0, duration=5) + while timeHelper.time() - lastTime < 20.0: + positions = np.stack([cf.position() for cf in cfs]) + timeHelper.sleep(timeHelper.dt) + collisions = check_ellipsoid_collisions(positions, RADII) + assert not np.any(collisions) + for goal, cf in zip(goals, cfs): + assert np.all(np.isclose(cf.position(), goal, atol=1e-4)) + + +def test_cmdPosition(): + args = "--sim --vis null --maxvel 1.0" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + cf0.enableCollisionAvoidance([cf1], RADII) + cf1.enableCollisionAvoidance([cf0], RADII) + + goal0 = np.array([1.0, 0.0, 2.0]) + goal1 = np.array([-1.0, 0.0, 2.0]) + cf0.cmdPosition(goal0, yaw=0.0) + cf1.cmdPosition(goal1, yaw=0.0) + while timeHelper.time() < 10.0: + positions = np.stack([cf0.position(), cf1.position()]) + collisions = check_ellipsoid_collisions(positions, RADII) + assert not np.any(collisions) + + timeHelper.sleep(timeHelper.dt) + assert np.all(np.isclose(cf0.position(), goal0)) + assert np.all(np.isclose(cf1.position(), goal1)) + + +def test_boundingBox(): + args = "--sim --vis null --maxvel 1.0" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + BBOXMAX = [5, 5, 5] + BBOXMIN = [-3, -3, -3] + cf0.enableCollisionAvoidance( + [cf1], RADII, bboxMax=BBOXMAX, bboxMin=BBOXMIN) + cf1.enableCollisionAvoidance( + [cf0], RADII, bboxMax=BBOXMAX, bboxMin=BBOXMIN) + + goal0 = np.array([BBOXMAX[0] - 1, 0.0, Z]) + goal1 = np.array([BBOXMIN[0] - 1, 0.0, Z]) + cf0.cmdPosition(goal0, yaw=0.0) + cf1.cmdPosition(goal1, yaw=0.0) + while timeHelper.time() < 10.0: + assert np.all(cf1.position() >= BBOXMIN) + assert np.all(cf1.position() <= BBOXMAX) + timeHelper.sleep(timeHelper.dt) + + assert np.all(np.isclose(cf0.position(), goal0)) + assert not np.all(np.isclose(cf1.position(), goal1)) + + +#@pytest.mark.xfail(reason="Bug in firmware") +def test_maxSpeed_zero(): + args = "--sim --vis null --maxvel 1.0" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + cf0.enableCollisionAvoidance([cf1], RADII, maxSpeed=0.0) + cf1.enableCollisionAvoidance([cf0], RADII, maxSpeed=0.0) + + goal0 = np.array([1.0, 0.0, Z]) + goal1 = np.array([-1.0, 0.0, Z]) + cf0.cmdPosition(goal0, yaw=0.0) + cf1.cmdPosition(goal1, yaw=0.0) + while timeHelper.time() < 10.0: + timeHelper.sleep(timeHelper.dt) + assert np.all(cf0.velocity() == np.zeros(3)) + assert np.all(cf1.velocity() == np.zeros(3)) + + assert np.all(np.isclose(cf0.position(), goal1)) + assert np.all(np.isclose(cf1.position(), goal0)) + + +def test_maxSpeed_limit(): + args = "--sim --vis null --maxvel 1.0" + allcfs, timeHelper = setUp(args) + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + cf0, cf1 = allcfs.crazyflies + + cf0.enableCollisionAvoidance([cf1], RADII, maxSpeed=1.0) + cf1.enableCollisionAvoidance([cf0], RADII, maxSpeed=3.0) + + cf0.cmdVelocityWorld([2.0, 0.0, 0.0], yawRate=0.0) + cf1.cmdVelocityWorld([-2.0, 0.0, 0.0], yawRate=0.0) + + while timeHelper.time() < 10.0: + assert np.all(np.abs(cf0.velocity()) < 1.2 * np.ones(3)) + assert np.all(np.abs(cf1.velocity()) < 3.2 * np.ones(3)) + timeHelper.sleep(timeHelper.dt) + if cf0.position()[0] > 2.0 and cf1.position()[0] < -2.0: + return + + assert False + + +if __name__ == "__main__": + # test_velocityMode_sidestepWorstCase(sys.argv) + test_maxSpeed_limit() diff --git a/test/bindings/test_highLevel.py b/test/bindings/test_highLevel.py new file mode 100644 index 0000000000..8578ec8f73 --- /dev/null +++ b/test/bindings/test_highLevel.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python + +import os + +import numpy as np +from pycrazyswarm import * +import uav_trajectory + +Z = 1.0 +FIGURE8_CSV = os.path.dirname(__file__) + "/figure8.csv" + +def setUp(): + crazyflies_yaml = """ + crazyflies: + - channel: 100 + id: 1 + initialPosition: [1.0, 0.0, 0.0] + - channel: 100 + id: 10 + initialPosition: [0.0, -1.0, 0.0] + """ + swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null") + timeHelper = swarm.timeHelper + return swarm.allcfs, timeHelper + +def _collectRelativePositions(timeHelper, cf, duration): + t0 = timeHelper.time() + positions = [] + while timeHelper.time() - t0 < duration: + positions.append(cf.position() - cf.initialPosition) + timeHelper.sleep(timeHelper.dt + 1e-6) + return np.stack(positions) + + +def test_takeOff(): + allcfs, timeHelper = setUp() + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + + for cf in allcfs.crazyflies: + pos = cf.initialPosition + np.array([0, 0, Z]) + assert np.all(np.isclose(cf.position(), pos, atol=0.0001)) + +def test_goTo_nonRelative(): + allcfs, timeHelper = setUp() + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + + for cf in allcfs.crazyflies: + pos = np.array(cf.initialPosition) + np.array([1, 1, Z]) + cf.goTo(pos, 0, 1.0) + timeHelper.sleep(1.0) + + for cf in allcfs.crazyflies: + pos = cf.initialPosition + np.array([1, 1, Z]) + assert np.all(np.isclose(cf.position(), pos)) + +def test_goTo_relative(): + allcfs, timeHelper = setUp() + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + + allcfs.goTo(np.array([1.0,1.0,1.0]), 0, Z) + timeHelper.sleep(2.0) + + for cf in allcfs.crazyflies: + pos = cf.initialPosition + np.array([1.0,1.0,2*Z]) + assert np.all(np.isclose(cf.position(), pos)) + +def test_landing(): + allcfs, timeHelper = setUp() + allcfs.takeoff(targetHeight=Z, duration=1.0+Z) + timeHelper.sleep(1.5+Z) + + allcfs.land(targetHeight=0.02, duration=1.0+Z) + timeHelper.sleep(1.0+Z) + + for cf in allcfs.crazyflies: + pos = cf.initialPosition + np.array([0, 0, 0.02]) + assert np.all(np.isclose(cf.position(), pos, atol=0.0001)) + +def test_uploadTrajectory_timescale(): + allcfs, timeHelper = setUp() + cf = allcfs.crazyflies[0] + + traj = uav_trajectory.Trajectory() + traj.loadcsv(FIGURE8_CSV) + trajId = 100 + cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) + + # We know the traj isn't close to origin at 3/4 of its duration + cf.startTrajectory(trajId) + timeHelper.sleep(0.75 * traj.duration) + assert np.linalg.norm(cf.position() - cf.initialPosition) >= 0.5 + + # Make sure we're back at origin + timeHelper.sleep(traj.duration) + + # Speeding up time by 2x, we should finish the trajectory in less time + cf.startTrajectory(trajId, timescale=0.5) + timeHelper.sleep(0.75 * traj.duration) + assert np.linalg.norm(cf.position() - cf.initialPosition) <= 0.001 + +def test_uploadTrajectory_fig8Bounds(): + allcfs, timeHelper = setUp() + cf = allcfs.crazyflies[0] + + traj = uav_trajectory.Trajectory() + traj.loadcsv(FIGURE8_CSV) + + trajId = 100 + cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) + cf.startTrajectory(trajId) + positions = _collectRelativePositions(timeHelper, cf, traj.duration) + + # We know the approximate range the bounding box should lie in by plotting + # the trajectory. + xs, ys, _ = positions.T + assert 0.9 < np.amax(xs) < 1.1 + assert -0.9 > np.amin(xs) > -1.1 + assert 0.4 < np.amax(ys) < 0.6 + assert -0.4 > np.amin(ys) > -0.6 + +def test_uploadTrajectory_reverse(): + allcfs, timeHelper = setUp() + cf = allcfs.crazyflies[0] + + traj = uav_trajectory.Trajectory() + traj.loadcsv(FIGURE8_CSV) + trajId = 100 + cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) + + cf.startTrajectory(trajId) + positions = _collectRelativePositions(timeHelper, cf, traj.duration) + + cf.startTrajectory(trajId, reverse=True) + positionsReverse = _collectRelativePositions(timeHelper, cf, traj.duration) + positions2 = np.flipud(positionsReverse) + + # The distance threshold must be large because the trajectory is not + # symmetrical, not because time/reversing is super sloppy. + dists = np.linalg.norm(positions - positions2, axis=1) + assert not np.any(dists > 0.2) + +def test_uploadTrajectory_broadcast(): + allcfs, timeHelper = setUp() + cf0, cf1 = allcfs.crazyflies + + relativeInitial = cf1.initialPosition - cf0.initialPosition + + traj = uav_trajectory.Trajectory() + traj.loadcsv(FIGURE8_CSV) + trajId = 100 + for cf in (cf0, cf1): + cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) + + allcfs.startTrajectory(trajId) + t0 = timeHelper.time() + while timeHelper.time() - t0 < traj.duration: + relative = cf1.position() - cf0.position() + assert np.all(np.isclose(relativeInitial, relative)) + timeHelper.sleep(timeHelper.dt + 1e-6) + +def test_setGroupMask(): + allcfs, timeHelper = setUp() + cf0, cf1 = allcfs.crazyflies + cf0.setGroupMask(1) + cf1.setGroupMask(2) + allcfs.takeoff(targetHeight=Z, duration=1.0 + Z, groupMask = 1) + timeHelper.sleep(1.5+Z) + + pos0 = cf0.initialPosition + np.array([0, 0, Z]) + assert np.all(np.isclose(cf0.position(), pos0, atol=0.0001)) + assert np.all(np.isclose(cf1.position(), cf1.initialPosition, atol=0.0001)) + + allcfs.takeoff(targetHeight=Z, duration=1.0 + Z, groupMask = 2) + timeHelper.sleep(1.5+Z) + + pos1 = cf1.initialPosition + np.array([0, 0, Z]) + assert np.all(np.isclose(cf0.position(), pos0, atol=0.0001)) + assert np.all(np.isclose(cf1.position(), pos1, atol=0.0001)) diff --git a/test/bindings/test_lowLevel.py b/test/bindings/test_lowLevel.py new file mode 100644 index 0000000000..7ae2b73699 --- /dev/null +++ b/test/bindings/test_lowLevel.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python + +import numpy as np +from pycrazyswarm import * + +Z = 1.0 + +def setUp(extra_args=""): + crazyflies_yaml = """ + crazyflies: + - channel: 100 + id: 1 + initialPosition: [1.0, 0.0, 0.0] + """ + swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null " + extra_args) + timeHelper = swarm.timeHelper + return swarm.allcfs, timeHelper + +def test_cmdFullState_zeroVel(): + allcfs, timeHelper = setUp() + cf = allcfs.crazyflies[0] + + pos = np.array(cf.initialPosition) + np.array([1, 1, Z]) + cf.cmdFullState(pos, np.zeros(3), np.zeros(3), 0, np.zeros(3)) + timeHelper.sleep(1.0) + + assert np.all(np.isclose(cf.position(), pos)) + +def test_cmdPosition(): + allcfs, timeHelper = setUp() + cf = allcfs.crazyflies[0] + + pos = np.array(cf.initialPosition) + np.array([1, 1, Z]) + cf.cmdPosition(pos,yaw=0.0) + timeHelper.sleep(1.0) + + assert np.all(np.isclose(cf.position(), pos)) + +def test_cmdVelocityWorld_checkVelocity(): + allcfs, timeHelper = setUp() + + cf = allcfs.crazyflies[0] + vel = np.ones(3) + cf.cmdVelocityWorld(vel, yawRate=0) + timeHelper.sleep(1.0) + + assert np.all(np.isclose(cf.velocity(), vel)) + +def test_cmdVelocityWorld_checkIntegrate(): + allcfs, timeHelper = setUp() + + cf = allcfs.crazyflies[0] + vel = np.ones(3) + cf.cmdVelocityWorld(vel, yawRate=0) + timeHelper.sleep(1.0) + + pos = cf.initialPosition + vel + assert np.all(np.isclose(cf.position(), pos)) + +def test_cmdVelocityWorld_disturbance(): + crazyflies_yaml = """ + crazyflies: + - channel: 100 + id: 1 + initialPosition: [1.0, 0.0, 0.0] + """ + swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null --disturbance 1.0") + timeHelper = swarm.timeHelper + + cf = swarm.allcfs.crazyflies[0] + + vel = np.ones(3) + cf.cmdVelocityWorld(vel, yawRate=0) + timeHelper.sleep(1.0) + + pos = cf.initialPosition + vel + assert not np.any(np.isclose(cf.position(), pos)) + +def test_sleepResidual(): + """Verify TimeHelper's time() is consistent with its integration steps.""" + np.random.seed(0) + TRIALS = 100 + for _ in range(TRIALS): + dtTick = 10 ** np.random.uniform(-2, 0) + dtSleep = 10 ** np.random.uniform(-2, 0) + allcfs, timeHelper = setUp("--dt {}".format(dtTick)) + + cf = allcfs.crazyflies[0] + vel = np.ones(3) + cf.cmdVelocityWorld(vel, yawRate=0) + time = 0.0 + while timeHelper.time() < 1.0: + timeHelper.sleep(dtSleep) + time += dtSleep + + assert time >= timeHelper.time() + + # We don't expect them to be exactly the same because timeHelper.time() + # will always be an integer multiple of dtTick. However, we should not + # be off by more than a tick. + assert time - timeHelper.time() < dtTick + + pos = cf.initialPosition + timeHelper.time() * vel + assert np.all(np.isclose(cf.position(), pos)) + diff --git a/test/bindings/test_yamlString.py b/test/bindings/test_yamlString.py new file mode 100644 index 0000000000..d01c8842a1 --- /dev/null +++ b/test/bindings/test_yamlString.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +import numpy as np +from pycrazyswarm import * + +def test_yaml_string_load(): + + crazyflies_yaml = """ + crazyflies: + - channel: 100 + id: 1 + initialPosition: [1.0, 0.0, 0.0] + - channel: 100 + id: 10 + initialPosition: [0.0, -1.0, 0.0] + """ + + swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null") + timeHelper = swarm.timeHelper + cfs = swarm.allcfs.crazyflies + byId = swarm.allcfs.crazyfliesById + + assert len(cfs) == 2 + cf1 = byId[1] + assert np.all(cf1.initialPosition == [1.0, 0.0, 0.0]) + + cf10 = byId[10] + assert np.all(cf10.initialPosition == [0.0, -1.0, 0.0]) diff --git a/test/bindings/uav_trajectory.py b/test/bindings/uav_trajectory.py new file mode 100644 index 0000000000..91939450ac --- /dev/null +++ b/test/bindings/uav_trajectory.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +import numpy as np + +def normalize(v): + norm = np.linalg.norm(v) + assert norm > 0 + return v / norm + + +class Polynomial: + def __init__(self, p): + self.p = p + + # evaluate a polynomial using horner's rule + def eval(self, t): + assert t >= 0 + x = 0.0 + for i in range(0, len(self.p)): + x = x * t + self.p[len(self.p) - 1 - i] + return x + + # compute and return derivative + def derivative(self): + return Polynomial([(i+1) * self.p[i+1] for i in range(0, len(self.p) - 1)]) + + +class TrajectoryOutput: + def __init__(self): + self.pos = None # position [m] + self.vel = None # velocity [m/s] + self.acc = None # acceleration [m/s^2] + self.omega = None # angular velocity [rad/s] + self.yaw = None # yaw angle [rad] + + +# 4d single polynomial piece for x-y-z-yaw, includes duration. +class Polynomial4D: + def __init__(self, duration, px, py, pz, pyaw): + self.duration = duration + self.px = Polynomial(px) + self.py = Polynomial(py) + self.pz = Polynomial(pz) + self.pyaw = Polynomial(pyaw) + + # compute and return derivative + def derivative(self): + return Polynomial4D( + self.duration, + self.px.derivative().p, + self.py.derivative().p, + self.pz.derivative().p, + self.pyaw.derivative().p) + + def eval(self, t): + result = TrajectoryOutput() + # flat variables + result.pos = np.array([self.px.eval(t), self.py.eval(t), self.pz.eval(t)]) + result.yaw = self.pyaw.eval(t) + + # 1st derivative + derivative = self.derivative() + result.vel = np.array([derivative.px.eval(t), derivative.py.eval(t), derivative.pz.eval(t)]) + dyaw = derivative.pyaw.eval(t) + + # 2nd derivative + derivative2 = derivative.derivative() + result.acc = np.array([derivative2.px.eval(t), derivative2.py.eval(t), derivative2.pz.eval(t)]) + + # 3rd derivative + derivative3 = derivative2.derivative() + jerk = np.array([derivative3.px.eval(t), derivative3.py.eval(t), derivative3.pz.eval(t)]) + + thrust = result.acc + np.array([0, 0, 9.81]) # add gravity + + z_body = normalize(thrust) + x_world = np.array([np.cos(result.yaw), np.sin(result.yaw), 0]) + y_body = normalize(np.cross(z_body, x_world)) + x_body = np.cross(y_body, z_body) + + jerk_orth_zbody = jerk - (np.dot(jerk, z_body) * z_body) + h_w = jerk_orth_zbody / np.linalg.norm(thrust) + + result.omega = np.array([-np.dot(h_w, y_body), np.dot(h_w, x_body), z_body[2] * dyaw]) + return result + + +class Trajectory: + def __init__(self): + self.polynomials = None + self.duration = None + + def n_pieces(self): + return len(self.polynomials) + + def loadcsv(self, filename): + data = np.loadtxt(filename, delimiter=",", skiprows=1, usecols=range(33)) + self.polynomials = [Polynomial4D(row[0], row[1:9], row[9:17], row[17:25], row[25:33]) for row in data] + self.duration = np.sum(data[:,0]) + + def eval(self, t): + assert t >= 0 + assert t <= self.duration + + current_t = 0.0 + for p in self.polynomials: + if t <= current_t + p.duration: + return p.eval(t - current_t) + current_t = current_t + p.duration From 709357c6968ca7295dad284ede2bd7a98b320800 Mon Sep 17 00:00:00 2001 From: James Preiss Date: Thu, 7 Oct 2021 01:02:12 -0700 Subject: [PATCH 2/9] pybindings CI: replace conda with pip --- .github/workflows/ci-bindings.yml | 25 +++++++++++-------------- test/bindings/environment.yml | 10 ---------- 2 files changed, 11 insertions(+), 24 deletions(-) delete mode 100644 test/bindings/environment.yml diff --git a/.github/workflows/ci-bindings.yml b/.github/workflows/ci-bindings.yml index d23995a7c6..ff1c8a42ce 100644 --- a/.github/workflows/ci-bindings.yml +++ b/.github/workflows/ci-bindings.yml @@ -8,11 +8,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-18.04, ubuntu-20.04, macos-latest] - python: - - python-version: "3.7" - python-cmd: python3 - - python-version: "2.7" - python-cmd: python2 + python-version: ["2.7", "3.7"] runs-on: ${{ matrix.os }} @@ -21,22 +17,23 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Conda - uses: conda-incubator/setup-miniconda@v2.0.0 + - name: Install Python + uses: actions/setup-python@v2 with: - channels: conda-forge,defaults - mamba-version: "*" - python-version: ${{ matrix.python.python-version }} - environment-file: test/bindings/environment.yml - activate-environment: cfbindings + python-version: ${{ matrix.python-version }} + + - name: Install Python Packages + shell: bash -l {0} + run: | + python -m pip install numpy pyyaml pytest scipy - name: Build shell: bash -l {0} run: | cd bindings - CF_PYTHON=${{ matrix.python.python-cmd }} make + CF_PYTHON=python make - name: Test shell: bash -l {0} run: | - PYTHONPATH=bindings ${{ matrix.python.python-cmd }} -m pytest test/bindings + PYTHONPATH=bindings python -m pytest test/bindings diff --git a/test/bindings/environment.yml b/test/bindings/environment.yml deleted file mode 100644 index 00499cec28..0000000000 --- a/test/bindings/environment.yml +++ /dev/null @@ -1,10 +0,0 @@ -name: cfbindings -dependencies: - - numpy - - matplotlib - - vispy - - pyyaml - - swig - - pyqt - - pytest - - scipy From 5cb400f8941ae4fa03bec73765a0c527d8c16527 Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Thu, 7 Oct 2021 20:39:24 +0200 Subject: [PATCH 3/9] rm crazyflie-related files --- test/bindings/figure8.csv | 11 - test/bindings/pycrazyswarm/__init__.py | 3 - test/bindings/pycrazyswarm/crazyflieSim.py | 496 ------------------ test/bindings/pycrazyswarm/crazyswarm_py.py | 49 -- test/bindings/pycrazyswarm/util.py | 85 --- .../pycrazyswarm/visualizer/__init__.py | 0 .../pycrazyswarm/visualizer/crazyflie2.obj.gz | Bin 208870 -> 0 bytes .../pycrazyswarm/visualizer/visMatplotlib.py | 81 --- .../pycrazyswarm/visualizer/visNull.py | 17 - .../pycrazyswarm/visualizer/visVispy.py | 166 ------ test/bindings/test_collisionAvoidance.py | 316 ----------- test/bindings/test_highLevel.py | 181 ------- test/bindings/test_lowLevel.py | 105 ---- test/bindings/test_yamlString.py | 28 - test/bindings/uav_trajectory.py | 109 ---- 15 files changed, 1647 deletions(-) delete mode 100644 test/bindings/figure8.csv delete mode 100644 test/bindings/pycrazyswarm/__init__.py delete mode 100644 test/bindings/pycrazyswarm/crazyflieSim.py delete mode 100644 test/bindings/pycrazyswarm/crazyswarm_py.py delete mode 100644 test/bindings/pycrazyswarm/util.py delete mode 100644 test/bindings/pycrazyswarm/visualizer/__init__.py delete mode 100644 test/bindings/pycrazyswarm/visualizer/crazyflie2.obj.gz delete mode 100644 test/bindings/pycrazyswarm/visualizer/visMatplotlib.py delete mode 100644 test/bindings/pycrazyswarm/visualizer/visNull.py delete mode 100644 test/bindings/pycrazyswarm/visualizer/visVispy.py delete mode 100644 test/bindings/test_collisionAvoidance.py delete mode 100644 test/bindings/test_highLevel.py delete mode 100644 test/bindings/test_lowLevel.py delete mode 100644 test/bindings/test_yamlString.py delete mode 100644 test/bindings/uav_trajectory.py diff --git a/test/bindings/figure8.csv b/test/bindings/figure8.csv deleted file mode 100644 index 4b25059b37..0000000000 --- a/test/bindings/figure8.csv +++ /dev/null @@ -1,11 +0,0 @@ -duration,x^0,x^1,x^2,x^3,x^4,x^5,x^6,x^7,y^0,y^1,y^2,y^3,y^4,y^5,y^6,y^7,z^0,z^1,z^2,z^3,z^4,z^5,z^6,z^7,yaw^0,yaw^1,yaw^2,yaw^3,yaw^4,yaw^5,yaw^6,yaw^7, -1.050000,0.000000,-0.000000,0.000000,-0.000000,0.830443,-0.276140,-0.384219,0.180493,-0.000000,0.000000,-0.000000,0.000000,-1.356107,0.688430,0.587426,-0.329106,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.710000,0.396058,0.918033,0.128965,-0.773546,0.339704,0.034310,-0.026417,-0.030049,-0.445604,-0.684403,0.888433,1.493630,-1.361618,-0.139316,0.158875,0.095799,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.620000,0.922409,0.405715,-0.582968,-0.092188,-0.114670,0.101046,0.075834,-0.037926,-0.291165,0.967514,0.421451,-1.086348,0.545211,0.030109,-0.050046,-0.068177,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.700000,0.923174,-0.431533,-0.682975,0.177173,0.319468,-0.043852,-0.111269,0.023166,0.289869,0.724722,-0.512011,-0.209623,-0.218710,0.108797,0.128756,-0.055461,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.560000,0.405364,-0.834716,0.158939,0.288175,-0.373738,-0.054995,0.036090,0.078627,0.450742,-0.385534,-0.954089,0.128288,0.442620,0.055630,-0.060142,-0.076163,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.560000,0.001062,-0.646270,-0.012560,-0.324065,0.125327,0.119738,0.034567,-0.063130,0.001593,-1.031457,0.015159,0.820816,-0.152665,-0.130729,-0.045679,0.080444,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.700000,-0.402804,-0.820508,-0.132914,0.236278,0.235164,-0.053551,-0.088687,0.031253,-0.449354,-0.411507,0.902946,0.185335,-0.239125,-0.041696,0.016857,0.016709,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.620000,-0.921641,-0.464596,0.661875,0.286582,-0.228921,-0.051987,0.004669,0.038463,-0.292459,0.777682,0.565788,-0.432472,-0.060568,-0.082048,-0.009439,0.041158,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -0.710000,-0.923935,0.447832,0.627381,-0.259808,-0.042325,-0.032258,0.001420,0.005294,0.288570,0.873350,-0.515586,-0.730207,-0.026023,0.288755,0.215678,-0.148061,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, -1.053185,-0.398611,0.850510,-0.144007,-0.485368,-0.079781,0.176330,0.234482,-0.153567,0.447039,-0.532729,-0.855023,0.878509,0.775168,-0.391051,-0.713519,0.391628,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000,0.000000, diff --git a/test/bindings/pycrazyswarm/__init__.py b/test/bindings/pycrazyswarm/__init__.py deleted file mode 100644 index cb696d0947..0000000000 --- a/test/bindings/pycrazyswarm/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from .crazyswarm_py import * - -__all__ = ["Crazyswarm",] diff --git a/test/bindings/pycrazyswarm/crazyflieSim.py b/test/bindings/pycrazyswarm/crazyflieSim.py deleted file mode 100644 index eb708d9c70..0000000000 --- a/test/bindings/pycrazyswarm/crazyflieSim.py +++ /dev/null @@ -1,496 +0,0 @@ -#!/usr/bin/env python - -import math - -import yaml -import numpy as np - -import cffirmware as firm - -# main class of simulation. -# crazyflies keep reference to this object to ask what time it is. -# also does the plotting. -# -class TimeHelper: - def __init__(self, vis, dt, writecsv, disturbanceSize, maxVel=np.inf, videopath=None): - if vis == "mpl": - from .visualizer import visMatplotlib - self.visualizer = visMatplotlib.VisMatplotlib() - elif vis == "vispy": - from .visualizer import visVispy - resizable = videopath is None - self.visualizer = visVispy.VisVispy(resizable=resizable) - elif vis == "null": - from .visualizer import visNull - self.visualizer = visNull.VisNull() - else: - raise Exception("Unknown visualization backend: {}".format(vis)) - self.t = 0.0 - self.dt = dt - # Since our integration/animation ticks are always the fixed duration - # dt, any call to sleep() with a non-multiple of dt will have some - # "leftover" time. Keep track of it here and add extra ticks in future. - self.sleepResidual = 0.0 - self.crazyflies = [] - self.disturbanceSize = disturbanceSize - self.maxVel = maxVel - if writecsv: - from . import output - self.output = output.Output() - else: - self.output = None - - if videopath is not None: - from .videowriter import VideoWriter - frame = self.visualizer.render() - self.videoWriter = VideoWriter(videopath, dt, frame.shape[:2]) - else: - self.videoWriter = None - - def time(self): - return self.t - - def step(self, duration): - self.t += duration - for cf in self.crazyflies: - cf.integrate(duration, self.disturbanceSize, self.maxVel) - for cf in self.crazyflies: - cf.flip() - - # should be called "animate" or something - # but called "sleep" for source-compatibility with real-robot scripts - def sleep(self, duration): - # operator // has unexpected (wrong ?) behavior for this calculation. - ticks = math.floor((duration + self.sleepResidual) / self.dt) - self.sleepResidual += duration - self.dt * ticks - assert -1e-9 <= self.sleepResidual < self.dt - - for _ in range(int(ticks)): - self.visualizer.update(self.t, self.crazyflies) - if self.output: - self.output.update(self.t, self.crazyflies) - if self.videoWriter is not None: - frame = self.visualizer.render() - self.videoWriter.writeFrame(frame) - self.step(self.dt) - - # Mock for abstraction of rospy.Rate.sleep(). - def sleepForRate(self, rate): - # TODO: account for rendering time, or is it worth the complexity? - self.sleep(1.0 / rate) - - # Mock for abstraction of rospy.is_shutdown(). - def isShutdown(self): - return False - - def addObserver(self, observer): - self.observers.append(observer) - - def _atexit(self): - if self.videoWriter is not None: - self.videoWriter.close() - - -def collisionAvoidanceUpdateSetpoint( - collisionParams, collisionState, mode, state, setState, otherCFs): - """Modifies a setpoint based on firmware collision-avoidance algorithm. - - Main purpose is to hide the firmware's stabilizer_types.h types, because we - prefer to work with cmath3d-based types. - - Args: - collisionParams (firmware collisionAvoidanceParams_t): Collision - avoidance algorithm parameters. Generally will remain constant. - collisionState: (firmware collisionAvoidanceState_t): Opaque collision - avoidance internal state. **Is modified in-place.** The same object - should be passed to this function in repeated calls. - mode (Crazyflie.MODE_* enum): The current flight mode. - state (firmware traj_eval): The Crazyflie's currents state. - setState (firmware traj_eval): The desired state generated by polynomial - trajectory, user low-level commands, etc. - otherCFs (array of Crazyflie): The other Crazyflie objects in the swarm. - - Returns: - newSetState (firmware traj_eval): A new desired state that attempts to - remain close to setState input while ensuring collision avoidance. - """ - - # This is significantly faster than calling position() on all the other CFs: - # 1.2 vs 1.8 seconds in test_collisionAvoidance.py::test_goToWithCA_random. - nOthers = len(otherCFs) - otherPositions = np.zeros((nOthers, 3), dtype=np.float32) - for i, cf in enumerate(otherCFs): - otherPositions[i][0] = cf.state.pos.x - otherPositions[i][1] = cf.state.pos.y - otherPositions[i][2] = cf.state.pos.z - - cmdState = firm.state_t() - # Position and velocity are the only states collision avoidance observes. - cmdState.position = firm.svec2vec(state.pos) - cmdState.velocity = firm.svec2vec(state.vel) - - # Dummy - it accepts the input to match the API of SitAw, but it's unused. - sensorData = firm.sensorData_t() - - setpoint = firm.setpoint_t() - if mode == Crazyflie.MODE_IDLE: - pass - elif mode in (Crazyflie.MODE_HIGH_POLY, Crazyflie.MODE_LOW_FULLSTATE): - setpoint.mode.x = firm.modeAbs - setpoint.position = firm.svec2vec(setState.pos) - setpoint.velocity = firm.svec2vec(setState.vel) - elif mode == Crazyflie.MODE_LOW_POSITION: - setpoint.mode.x = firm.modeAbs - setpoint.position = firm.svec2vec(setState.pos) - elif mode == Crazyflie.MODE_LOW_VELOCITY: - setpoint.mode.x = firm.modeVelocity - setpoint.velocity = firm.svec2vec(setState.vel) - else: - raise ValueError("Unknown flight mode.") - - firm.collisionAvoidanceUpdateSetpointWrap( - collisionParams, - collisionState, - otherPositions.flatten(), - setpoint, - sensorData, - cmdState) - - newSetState = firm.traj_eval_zero() - newSetState.pos = firm.vec2svec(setpoint.position) - newSetState.vel = firm.vec2svec(setpoint.velocity) - newSetState.yaw = setState.yaw - newSetState.omega.z = setState.omega[2] - return newSetState - - -class Crazyflie: - - # Flight modes. - MODE_IDLE = 0 - MODE_HIGH_POLY = 1 - MODE_LOW_FULLSTATE = 2 - MODE_LOW_POSITION = 3 - MODE_LOW_VELOCITY = 4 - - - def __init__(self, id, initialPosition, timeHelper): - - # Core. - self.id = id - self.groupMask = 0 - self.initialPosition = np.array(initialPosition) - self.time = lambda: timeHelper.time() - - # Commander. - self.mode = Crazyflie.MODE_IDLE - self.planner = firm.planner() - firm.plan_init(self.planner) - self.trajectories = dict() - self.setState = firm.traj_eval_zero() - - # State. Public np.array-returning getters below for physics state. - self.state = firm.traj_eval_zero() - self.state.pos = firm.mkvec(*initialPosition) - self.state.vel = firm.vzero() - self.state.acc = firm.vzero() - self.state.yaw = 0.0 - self.state.omega = firm.vzero() - self.ledRGB = (0.5, 0.5, 1) - - # Double-buffering: Ensure that all CFs observe the same world state - # during an integration step, regardless of the order in which their - # integrate() methods are called. flip() swaps front and back state. - # See http://gameprogrammingpatterns.com/double-buffer.html for more - # motivation. - self.backState = firm.traj_eval(self.state) - - # For collision avoidance. - self.otherCFs = [] - self.collisionAvoidanceParams = None - self.collisionAvoidanceState = None - - def setGroupMask(self, groupMask): - self.groupMask = groupMask - - def enableCollisionAvoidance(self, others, ellipsoidRadii, bboxMin=np.repeat(-np.inf, 3), bboxMax=np.repeat(np.inf, 3), horizonSecs=1.0, maxSpeed=2.0): - self.otherCFs = [cf for cf in others if cf is not self] - - # TODO: Accept more of these from arguments. - params = firm.collision_avoidance_params_t() - params.ellipsoidRadii = firm.mkvec(*ellipsoidRadii) - params.bboxMin = firm.mkvec(*bboxMin) - params.bboxMax = firm.mkvec(*bboxMax) - params.horizonSecs = horizonSecs - params.maxSpeed = maxSpeed - params.sidestepThreshold = 0.25 - params.voronoiProjectionTolerance = 1e-5 - params.voronoiProjectionMaxIters = 100 - self.collisionAvoidanceParams = params - - state = firm.collision_avoidance_state_t() - state.lastFeasibleSetPosition = firm.mkvec(np.nan, np.nan, np.nan) - self.collisionAvoidanceState = state - - def disableCollisionAvoidance(self): - self.otherCFs = None - self.collisionAvoidanceParams = None - self.collisionAvoidanceState = None - - def takeoff(self, targetHeight, duration, groupMask = 0): - if self._isGroup(groupMask): - self.mode = Crazyflie.MODE_HIGH_POLY - targetYaw = 0.0 - firm.plan_takeoff(self.planner, - self.state.pos, self.state.yaw, targetHeight, targetYaw, duration, self.time()) - - def land(self, targetHeight, duration, groupMask = 0): - if self._isGroup(groupMask): - self.mode = Crazyflie.MODE_HIGH_POLY - targetYaw = 0.0 - firm.plan_land(self.planner, - self.state.pos, self.state.yaw, targetHeight, targetYaw, duration, self.time()) - - def stop(self, groupMask = 0): - if self._isGroup(groupMask): - self.mode = Crazyflie.MODE_IDLE - firm.plan_stop(self.planner) - - def goTo(self, goal, yaw, duration, relative = False, groupMask = 0): - if self._isGroup(groupMask): - if self.mode != Crazyflie.MODE_HIGH_POLY: - # We need to update to the latest firmware that has go_to_from. - raise ValueError("goTo from low-level modes not yet supported.") - self.mode = Crazyflie.MODE_HIGH_POLY - firm.plan_go_to(self.planner, relative, firm.mkvec(*goal), yaw, duration, self.time()) - - def uploadTrajectory(self, trajectoryId, pieceOffset, trajectory): - traj = firm.piecewise_traj() - traj.t_begin = 0 - traj.timescale = 1.0 - traj.shift = firm.mkvec(0, 0, 0) - traj.n_pieces = len(trajectory.polynomials) - traj.pieces = firm.malloc_poly4d(len(trajectory.polynomials)) - for i, poly in enumerate(trajectory.polynomials): - piece = firm.pp_get_piece(traj, i) - piece.duration = poly.duration - for coef in range(0, 8): - firm.poly4d_set(piece, 0, coef, poly.px.p[coef]) - firm.poly4d_set(piece, 1, coef, poly.py.p[coef]) - firm.poly4d_set(piece, 2, coef, poly.pz.p[coef]) - firm.poly4d_set(piece, 3, coef, poly.pyaw.p[coef]) - self.trajectories[trajectoryId] = traj - - def startTrajectory(self, trajectoryId, timescale = 1.0, reverse = False, relative = True, groupMask = 0): - if self._isGroup(groupMask): - self.mode = Crazyflie.MODE_HIGH_POLY - traj = self.trajectories[trajectoryId] - traj.t_begin = self.time() - traj.timescale = timescale - if relative: - traj.shift = firm.vzero() - if reverse: - traj_init = firm.piecewise_eval_reversed(traj, traj.t_begin) - else: - traj_init = firm.piecewise_eval(traj, traj.t_begin) - traj.shift = self.state.pos - traj_init.pos - else: - traj.shift = firm.vzero() - firm.plan_start_trajectory(self.planner, traj, reverse) - - def notifySetpointsStop(self, remainValidMillisecs=100): - # No-op - the real Crazyflie prioritizes streaming setpoints over - # high-level commands. This tells it to stop doing that. We don't - # simulate this behavior. - pass - - def position(self): - return np.array(self.state.pos) - - def getParam(self, name): - print("WARNING: getParam not implemented in simulation!") - - def setParam(self, name, value): - print("WARNING: setParam not implemented in simulation!") - - def setParams(self, params): - print("WARNING: setParams not implemented in simulation!") - - # - this is a part of the param system on the real crazyflie, - # but we implement it in simulation too for debugging - # - is a blocking command on real CFs, so may cause stability problems - def setLEDColor(self, r, g, b): - self.ledRGB = (r, g, b) - - # simulation only functions - def yaw(self): - return float(self.state.yaw) - - def velocity(self): - return np.array(self.state.vel) - - def acceleration(self): - return np.array(self.state.acc) - - def rpy(self): - yaw = self.yaw() - # Unpack the matrix columns. - x_body, y_body, z_body = self.rotBodyToWorld().T - pitch = math.asin(-x_body[2]) - roll = math.atan2(y_body[2], z_body[2]) - return (roll, pitch, yaw) - - def rotBodyToWorld(self): - acc = self.acceleration() - yaw = self.yaw() - norm = np.linalg.norm(acc) - # TODO: This causes a vertical flip for faster-than-gravity vertical - # deceleration, but fixing it would essentially require introducing the - # idea of a controller, which we have avoided so far. - thrust = acc + np.array([0, 0, 9.81]) - z_body = thrust / np.linalg.norm(thrust) - x_world = np.array([math.cos(yaw), math.sin(yaw), 0]) - y_body = np.cross(z_body, x_world) - # TODO: This can have a singularity if z_body = x_world. - y_body /= np.linalg.norm(y_body) - x_body = np.cross(y_body, z_body) - return np.column_stack([x_body, y_body, z_body]) - - def cmdFullState(self, pos, vel, acc, yaw, omega): - self.mode = Crazyflie.MODE_LOW_FULLSTATE - self.setState.pos = firm.mkvec(*pos) - self.setState.vel = firm.mkvec(*vel) - self.setState.acc = firm.mkvec(*acc) - self.setState.yaw = yaw - self.setState.omega = firm.mkvec(*omega) - - def cmdPosition(self, pos, yaw = 0): - self.mode = Crazyflie.MODE_LOW_POSITION - self.setState.pos = firm.mkvec(*pos) - self.setState.yaw = yaw - # TODO: should we set vel, acc, omega to zero, or rely on modes to not read them? - - def cmdVelocityWorld(self, vel, yawRate): - self.mode = Crazyflie.MODE_LOW_VELOCITY - self.setState.vel = firm.mkvec(*vel) - self.setState.omega = firm.mkvec(0.0, 0.0, yawRate) - # TODO: should we set pos, acc, yaw to zero, or rely on modes to not read them? - - def cmdStop(self): - # TODO: set mode to MODE_IDLE? - pass - - def integrate(self, time, disturbanceSize, maxVel): - if self.mode == Crazyflie.MODE_HIGH_POLY: - self.setState = firm.plan_current_goal(self.planner, self.time()) - - if self.collisionAvoidanceState is not None: - setState = collisionAvoidanceUpdateSetpoint( - self.collisionAvoidanceParams, - self.collisionAvoidanceState, - self.mode, - self.state, - self.setState, - self.otherCFs, - ) - else: - setState = firm.traj_eval(self.setState) - - if self.mode == Crazyflie.MODE_IDLE: - return - - if self.mode in (Crazyflie.MODE_HIGH_POLY, Crazyflie.MODE_LOW_FULLSTATE, Crazyflie.MODE_LOW_POSITION): - velocity = (setState.pos - self.state.pos) / time - elif self.mode == Crazyflie.MODE_LOW_VELOCITY: - velocity = setState.vel - else: - raise ValueError("Unknown flight mode.") - - # Limit velocity for realism. - # Note: This will result in the state having a different velocity than - # the setState in HIGH_POLY and LOW_FULLSTATE modes even when no - # clamping occurs, because we are essentially getting rid of the - # feedforward commands. We assume this is not a problem. - - velocity = firm.vclampnorm(velocity, maxVel) - - disturbance = disturbanceSize * np.random.normal(size=3) - velocity = velocity + firm.mkvec(*disturbance) - self.backState = firm.traj_eval(setState) - self.backState.pos = self.state.pos + time * velocity - self.backState.vel = velocity - - if self.mode == Crazyflie.MODE_LOW_POSITION: - yawRate = (setState.yaw - self.state.yaw) / time - self.backState.yaw = setState.yaw - self.backState.omega = firm.mkvec(0.0, 0.0, yawRate) - elif self.mode == Crazyflie.MODE_LOW_VELOCITY: - # Omega is already set. - self.backState.yaw += time * self.setState.omega.z - - # In HIGH_POLY and LOW_FULLSTATE, yaw and omega are already specified - # in setState and have been copied. - - def flip(self): - # Swap double-buffered state. Called at the end of the tick update, - # after *all* CFs' integrate() methods have been called. - self.state, self.backState = self.backState, self.state - - # "private" methods - def _isGroup(self, groupMask): - return groupMask == 0 or (self.groupMask & groupMask) > 0 - - -class CrazyflieServer: - def __init__(self, timeHelper, crazyflies_yaml="../launch/crazyflies.yaml"): - """Initialize the server. - - Args: - timeHelper (TimeHelper): TimeHelper instance. - crazyflies_yaml (str): If ends in ".yaml", interpret as a path and load - from file. Otherwise, interpret as YAML string and parse - directly from string. - """ - if crazyflies_yaml.endswith(".yaml"): - with open(crazyflies_yaml, 'r') as ymlfile: - cfg = yaml.safe_load(ymlfile) - else: - cfg = yaml.safe_load(crazyflies_yaml) - - self.crazyflies = [] - self.crazyfliesById = dict() - for crazyflie in cfg["crazyflies"]: - id = int(crazyflie["id"]) - initialPosition = crazyflie["initialPosition"] - cf = Crazyflie(id, initialPosition, timeHelper) - self.crazyflies.append(cf) - self.crazyfliesById[id] = cf - - self.timeHelper = timeHelper - self.timeHelper.crazyflies = self.crazyflies - - def emergency(self): - print("WARNING: emergency not implemented in simulation!") - - def takeoff(self, targetHeight, duration, groupMask = 0): - for crazyflie in self.crazyflies: - crazyflie.takeoff(targetHeight, duration, groupMask) - - def land(self, targetHeight, duration, groupMask = 0): - for crazyflie in self.crazyflies: - crazyflie.land(targetHeight, duration, groupMask) - - def stop(self, groupMask = 0): - for crazyflie in self.crazyflies: - crazyflie.stop(groupMask) - - def goTo(self, goal, yaw, duration, groupMask = 0): - for crazyflie in self.crazyflies: - crazyflie.goTo(goal, yaw, duration, relative=True, groupMask=groupMask) - - def startTrajectory(self, trajectoryId, timescale = 1.0, reverse = False, relative = True, groupMask = 0): - for crazyflie in self.crazyflies: - crazyflie.startTrajectory(trajectoryId, timescale, reverse, relative, groupMask) - - def setParam(self, name, value): - print("WARNING: setParam not implemented in simulation!") diff --git a/test/bindings/pycrazyswarm/crazyswarm_py.py b/test/bindings/pycrazyswarm/crazyswarm_py.py deleted file mode 100644 index 7394e6300f..0000000000 --- a/test/bindings/pycrazyswarm/crazyswarm_py.py +++ /dev/null @@ -1,49 +0,0 @@ -import argparse -import atexit - -import numpy as np - - -# Building the parser in a separate function allows sphinx-argparse to -# auto-generate the documentation for the command-line flags. -def build_argparser(parent_parsers=[]): - parser = argparse.ArgumentParser( - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - parents=parent_parsers - ) - parser.add_argument("--sim", help="Run using simulation.", action="store_true") - - group = parser.add_argument_group("Simulation-only", "") - group.add_argument("--vis", help="Visualization backend.", choices=['mpl', 'vispy', 'null'], default="mpl") - group.add_argument("--dt", help="Duration of seconds between rendered visualization frames.", type=float, default=0.1) - group.add_argument("--writecsv", help="Enable CSV output.", action="store_true") - group.add_argument("--disturbance", help="Simulate Gaussian-distributed disturbance when using cmdVelocityWorld.", type=float, default=0.0) - group.add_argument("--maxvel", help="Limit simulated velocity (meters/sec).", type=float, default=np.inf) - group.add_argument("--video", help="Video output path.", type=str) - - return parser - - -class Crazyswarm: - def __init__(self, crazyflies_yaml=None, parent_parser=None, args=None): - if parent_parser is not None: - parents = [parent_parser] - else: - parents = [] - parser = build_argparser(parents) - if isinstance(args, str): - args = args.split() - args, unknown = parser.parse_known_args(args) - - if crazyflies_yaml is None: - crazyflies_yaml = "../launch/crazyflies.yaml" - if crazyflies_yaml.endswith(".yaml"): - crazyflies_yaml = open(crazyflies_yaml, 'r').read() - - if args.sim: - from .crazyflieSim import TimeHelper, CrazyflieServer - self.timeHelper = TimeHelper(args.vis, args.dt, args.writecsv, disturbanceSize=args.disturbance, maxVel=args.maxvel, videopath=args.video) - self.allcfs = CrazyflieServer(self.timeHelper, crazyflies_yaml) - atexit.register(self.timeHelper._atexit) - else: - raise NotImplementedError("Sim only now!") diff --git a/test/bindings/pycrazyswarm/util.py b/test/bindings/pycrazyswarm/util.py deleted file mode 100644 index a501d6b931..0000000000 --- a/test/bindings/pycrazyswarm/util.py +++ /dev/null @@ -1,85 +0,0 @@ -"""Useful functions for both pycrazyswarm internals and user scripts.""" - -import numpy as np -import scipy as sp -import scipy.spatial - - -def check_ellipsoid_collisions(positions, radii): - """Checks for collisions between a set of ellipsoids at given positions. - - Args: - positions (array float[n, 3]): The ellipsoid centers. - radii (array float[3]): The radii of the axis-aligned ellipsoids. - - Returns: - colliding (array bool[n]): True at index i if the i'th ellipsoid - intersects any of the other ellipsoids. - """ - scaled = positions / radii[None, :] - dists = sp.spatial.distance.pdist(scaled) - dists = sp.spatial.distance.squareform(dists) - # Do not consider 0 distance to self as a collision! - n, _ = positions.shape - dists[range(n), range(n)] = np.inf - colliding = np.any(dists < 1.97, axis=1) - return colliding - - -def poisson_disk_sample(n, dim, mindist): - """Generates random points with guaranteed minimum pairwise distance. - - Uses extremely naive and slow "dart throwing" algorithm. - TODO(jpreiss): find/implement a library with a fast algorithm. - - Args: - n (int): Number of points. - dim (int): Dimensionality of points. - mindist (float): Minimum Euclidean distance between any two points. - - Returns: - pts (array float[n, dim]): The sampled points. - """ - - # Select hypercube volume such that n points will not pack it too tightly. - # Note: Will be too sparse for dim >> 3, but reasonable for dim == 2 or 3. - measure_ratio = 1.25 - std = (measure_ratio * n) ** (1.0 / dim) * mindist - def sample(): - return std * np.random.uniform(-0.5, 0.5, size=dim) - - # Sample the points using dart-throwing. - pts = sample()[None,:] - while len(pts) < n: - pt = sample() - dists = np.linalg.norm(pts - pt, axis=1) - if np.all(dists >= mindist): - pts = np.concatenate([pts, pt[None,:]], axis=0) - return pts - - -def grid_yaml(rows, cols, spacing=0.5): - """Generate crazyflies.yaml string for a grid in the XZ plane. - - Args: - rows (int): Number of rows (distinct X-values) in the grid. - cols (int): Number of columns (distinct Y-values) in the grid. - spacing (float): Grid spacing for both axes. - - Returns: - yaml (str): String containing crazyflies.yaml. Will contain - (rows * cols) crazyflies, all assigned on radio channel 100. - Positions will be centered about the origin. No particular - id-position mapping should be assumed. - """ - x, y = spacing * np.mgrid[:rows, :cols] - x -= np.mean(x) - y -= np.mean(y) - return "crazyflies:\n" + "\n".join([ - """- channel: 100 - id: {} - initialPosition: [{}, {}, 0.0]""".format(i, x, y) - for i, (x, y) in enumerate(zip(x.flat, y.flat)) - ]) - - diff --git a/test/bindings/pycrazyswarm/visualizer/__init__.py b/test/bindings/pycrazyswarm/visualizer/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/bindings/pycrazyswarm/visualizer/crazyflie2.obj.gz b/test/bindings/pycrazyswarm/visualizer/crazyflie2.obj.gz deleted file mode 100644 index df8427c34759b6bec62bee56943a0f8d88a042b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 208870 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 z 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

JuLW0^ib9{###`+qV-v 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

FpQ;@v^2u|b?n@)ak#5Pof^YcyijexL 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 diff --git a/test/bindings/pycrazyswarm/visualizer/visMatplotlib.py b/test/bindings/pycrazyswarm/visualizer/visMatplotlib.py deleted file mode 100644 index 83eeb03263..0000000000 --- a/test/bindings/pycrazyswarm/visualizer/visMatplotlib.py +++ /dev/null @@ -1,81 +0,0 @@ -import warnings - -from mpl_toolkits.mplot3d import Axes3D -from mpl_toolkits.mplot3d.art3d import Line3DCollection -import matplotlib.pyplot as plt -import numpy as np - - -class VisMatplotlib: - def __init__(self): - self.fig = plt.figure() - self.ax = self.fig.add_subplot(111, projection='3d') - self.ax.set_xlim([-5, 5]) - self.ax.set_ylim([-5, 5]) - self.ax.set_zlim([0, 3]) - self.ax.set_xlabel("X") - self.ax.set_ylabel("Y") - self.ax.set_zlabel("Z") - self.plot = None - self.timeAnnotation = self.ax.annotate("Time", xy=(0, 0), xycoords='axes fraction', fontsize=12, ha='right', va='bottom') - - self.line_color = 0.3 * np.ones(3) - - # Lazy-constructed data for connectivity graph gfx. - self.graph_edges = None - self.graph_lines = None - self.graph = None - - def setGraph(self, edges): - """Set edges of graph visualization - sequence of (i,j) tuples.""" - - # Only allocate new memory if we need to. - n_edges = len(edges) - if self.graph_edges is None or n_edges != len(self.graph_edges): - self.graph_lines = np.zeros((n_edges, 2, 3)) - self.graph_edges = edges - - # Lazily construct Matplotlib object for graph. - if self.graph is None: - self.graph = Line3DCollection(self.graph_lines, edgecolor=self.line_color) - self.ax.add_collection(self.graph) - - def showEllipsoids(self, radii): - warnings.warn("showEllipsoids not implemented in Matplotlib visualizer.") - - def update(self, t, crazyflies): - xs = [] - ys = [] - zs = [] - cs = [] - for cf in crazyflies: - x, y, z = cf.position() - color = cf.ledRGB - xs.append(x) - ys.append(y) - zs.append(z) - cs.append(color) - - if self.plot is None: - self.plot = self.ax.scatter(xs, ys, zs, c=cs) - else: - # TODO: Don't use protected members. - self.plot._offsets3d = (xs, ys, zs) - self.plot.set_facecolors(cs) - self.plot.set_edgecolors(cs) - self.plot._facecolor3d = self.plot.get_facecolor() - self.plot._edgecolor3d = self.plot.get_edgecolor() - - if self.graph is not None: - # Update graph line segments to match new Crazyflie positions. - for k, (i, j) in enumerate(self.graph_edges): - self.graph_lines[k, 0, :] = xs[i], ys[i], zs[i] - self.graph_lines[k, 1, :] = xs[j], ys[j], zs[j] - self.graph.set_segments(self.graph_lines) - - self.timeAnnotation.set_text("{} s".format(t)) - plt.pause(0.0001) - - def render(self): - warnings.warn("Rendering video not supported in VisMatplotlib yet.") - return None diff --git a/test/bindings/pycrazyswarm/visualizer/visNull.py b/test/bindings/pycrazyswarm/visualizer/visNull.py deleted file mode 100644 index 6073056fe0..0000000000 --- a/test/bindings/pycrazyswarm/visualizer/visNull.py +++ /dev/null @@ -1,17 +0,0 @@ -"""No-op visualizer for real hardware runs, so script can be oblivious.""" - -class VisNull: - def __init__(self): - pass - - def setGraph(self, edges): - pass - - def showEllipsoids(self, radii): - pass - - def update(self, t, crazyflies): - pass - - def render(self): - return None diff --git a/test/bindings/pycrazyswarm/visualizer/visVispy.py b/test/bindings/pycrazyswarm/visualizer/visVispy.py deleted file mode 100644 index 03ccf331e3..0000000000 --- a/test/bindings/pycrazyswarm/visualizer/visVispy.py +++ /dev/null @@ -1,166 +0,0 @@ -import os -import math - -import ffmpeg -import numpy as np -from vispy import scene, app, io, geometry -from vispy.color import Color -from vispy.visuals import transforms -from vispy.scene.cameras import TurntableCamera - -from .. import util as util - - -CF_MESH_PATH = os.path.join(os.path.dirname(__file__), "crazyflie2.obj.gz") -# Convert millimeters to meters, but make twice as big so easier to see. -MESHFILE_SCALE = 2.0 * 0.001 -# The matrix that rotates the coordinates of the .obj file to agree with the -# Crazyflie's standard coordinate system. VisPy uses [row vector] * [matrix] -# (like DirectX), so this is the transpose of what we would expect. -UNROT_MESHFILE_TRANSPOSE = MESHFILE_SCALE * np.array([ - [-1, 0, 0], - [ 0, 0, 1], - [ 0, -1, 0], -]) -ELLIPSOID_COLOR_OK = Color("#11FF22", alpha=0.1) -ELLIPSOID_COLOR_COLLISION = Color("#FF0000", alpha=0.1) - - -class VisVispy: - def __init__(self, show=True, resizable=True): - self.canvas = scene.SceneCanvas( - keys='interactive', size=(1024, 768), show=show, config=dict(samples=4), resizable=resizable - ) - - self.plane_color = 0.25 * np.ones((1, 3)) - self.bg_color = 0.9 * np.ones((1, 3)) - self.line_color = 0.7 * np.ones((1, 3)) - - # Set up a viewbox to display the cube with interactive arcball - self.view = self.canvas.central_widget.add_view() - self.view.bgcolor = self.bg_color - self.view.camera = TurntableCamera( - fov=30.0, elevation=30.0, azimuth=90.0, center=(0.0, 0.0, 1.25) - ) - self.cam_state = self.view.camera.get_state() - - # add a colored 3D axis for orientation - axis = scene.visuals.XYZAxis(parent=self.view.scene) - self.cfs = [] - self.led_color_cache = [] - - ground = scene.visuals.Plane( - 32.0, 32.0, direction="+z", color=self.plane_color, parent=self.view.scene - ) - - # Lazy-constructed vispy objects and data for connectivity graph gfx. - self.graph_edges = None - self.graph_lines = None - self.graph = None - - # Lazy-constructed vispy objects for collision ellipsoids. - self.ellipsoids = None - self.ellipsoid_radii = None - - def setGraph(self, edges): - """Set edges of graph visualization - sequence of (i,j) tuples.""" - - # Only allocate new memory if we need to. - n_edges = len(edges) - if self.graph_edges is None or n_edges != len(self.graph_edges): - self.graph_lines = np.zeros((2 * n_edges, 3)) - self.graph_edges = edges - - # Lazily construct VisPy object for graph. - if self.graph is None: - self.graph = scene.visuals.Line( - parent=self.view.scene, - color=self.line_color, - pos=self.graph_lines, - connect="segments", - method="gl", - antialias=True, - ) - - def showEllipsoids(self, radii): - self.ellipsoid_radii = np.array(radii) - - def update(self, t, crazyflies): - if len(self.cfs) == 0: - verts, faces, normals, nothin = io.read_mesh(CF_MESH_PATH) - for i, cf in enumerate(crazyflies): - color = cf.ledRGB - mesh = scene.visuals.Mesh( - parent=self.view.scene, - vertices=verts, - faces=faces, - color=color, - shading="smooth", - ) - mesh.light_dir = (0.1, 0.1, 1.0) - mesh.shininess = 0.01 - mesh.ambient_light_color = [0.5] * 3 - mesh.transform = transforms.MatrixTransform() - self.cfs.append(mesh) - self.led_color_cache.append(color) - - if self.ellipsoid_radii is not None and self.ellipsoids is None: - sphere_mesh = geometry.create_sphere(radius=1.0) - self.ellipsoids = [ - scene.visuals.Mesh( - parent=self.view.scene, - meshdata=sphere_mesh, - color=ELLIPSOID_COLOR_OK, - shading="smooth", - ) - for _ in self.cfs - ] - for ell in self.ellipsoids: - ell.light_dir = (0.1, 0.1, 1.0) - ell.shininess = 0.0 - ell.ambient_light_color = [0.5] * 3 - ell.transform = transforms.MatrixTransform() - - positions = np.stack([cf.position() for cf in crazyflies]) - - for i in range(0, len(self.cfs)): - R_state = crazyflies[i].rotBodyToWorld() - # Recall VisPy uses [row vector] * [matrix]!! - T = np.eye(4) - T[:3, :3] = np.dot(UNROT_MESHFILE_TRANSPOSE, R_state.T) - T[3, :3] = positions[i] - self.cfs[i].transform = transforms.MatrixTransform(T) - # vispy does not do this check - color = crazyflies[i].ledRGB - if color != self.led_color_cache[i]: - self.led_color_cache[i] = color - self.cfs[i].color = color # sets dirty flag - - # Update graph line segments to match new Crazyflie positions. - if self.graph is not None: - for k, (i, j) in enumerate(self.graph_edges): - self.graph_lines[2 * k, :] = positions[i] - self.graph_lines[2 * k + 1, :] = positions[j] - self.graph.set_data(self.graph_lines) - - # Update collsiion ellipsoids. - if self.ellipsoids is not None: - colliding = util.check_ellipsoid_collisions(positions, self.ellipsoid_radii) - for i, pos in enumerate(positions): - ell = self.ellipsoids[i] - tf = ell.transform - tf.reset() - tf.scale(self.ellipsoid_radii) - tf.translate(pos) - new_color = ELLIPSOID_COLOR_COLLISION if colliding[i] else ELLIPSOID_COLOR_OK - if not (new_color == ell.color): # vispy Color lacks != override. - ell.color = new_color - - self.canvas.app.process_events() - - def render(self): - frame = self.canvas.render() - # Do not render alpha channel - we always use rgb24 format. - if frame.shape[2] == 4: - frame = frame[:, :, :3] - return frame diff --git a/test/bindings/test_collisionAvoidance.py b/test/bindings/test_collisionAvoidance.py deleted file mode 100644 index f62024b842..0000000000 --- a/test/bindings/test_collisionAvoidance.py +++ /dev/null @@ -1,316 +0,0 @@ -import sys - -import numpy as np -import pytest - -from pycrazyswarm import * -from pycrazyswarm.util import * - - -RADII = np.array([0.125, 0.125, 0.375]) -Z = 1.0 - - -def setUp(args): - crazyflies_yaml = """ - crazyflies: - - id: 1 - channel: 100 - initialPosition: [-1.0, 0.0, 0.0] - - id: 2 - channel: 100 - initialPosition: [1.0, 0.0, 0.0] - """ - swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args=args) - timeHelper = swarm.timeHelper - return swarm.allcfs, timeHelper - - -def test_velocityMode_sidestepWorstCase(args=None): - if args is None: - args = "--sim --vis null --dt 0.05 --maxvel 1.0" - - allcfs, timeHelper = setUp(args) - a, b = allcfs.crazyflies - - a.enableCollisionAvoidance([b], RADII) - b.enableCollisionAvoidance([a], RADII) - - a.cmdVelocityWorld([1.0, 0.0, 0.0], yawRate=0.0) - b.cmdVelocityWorld([-1.0, 0.0, 0.0], yawRate=0.0) - - while timeHelper.time() < 10.0: - positions = np.stack([a.position(), b.position()]) - collisions = check_ellipsoid_collisions(positions, RADII) - assert not np.any(collisions) - - timeHelper.sleep(timeHelper.dt) - if a.position()[0] > 1.0 and b.position()[0] < -1.0: - return - assert False - - -def test_goToWithoutCA_CheckCollision(): - args = "--sim --vis null" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - - cf0.goTo([1.0, 0.0, 1.0], 0, 5.0) - cf1.goTo([-1.0, 0.0, 1.0], 0, 5.0) - while timeHelper.time() < 10.0: - positions = np.stack([cf0.position(), cf1.position()]) - collisions = check_ellipsoid_collisions(positions, RADII) - if np.any(collisions): - return - timeHelper.sleep(timeHelper.dt) - assert False - - -def test_goToWithCA_CheckCollision(): - args = "--sim --vis null" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - cf0.enableCollisionAvoidance([cf1], RADII) - cf1.enableCollisionAvoidance([cf0], RADII) - - cf0.goTo([1.0, 0.0, 1.0], 0, 5.0) - cf1.goTo([-1.0, 0.0, 1.0], 0, 5.0) - while timeHelper.time() < 10.0: - positions = np.stack([cf0.position(), cf1.position()]) - collisions = check_ellipsoid_collisions(positions, RADII) - assert not np.any(collisions) - timeHelper.sleep(timeHelper.dt) - - -def test_goToWithCA_CheckDestination(): - args = "--sim --vis null" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - cf0.enableCollisionAvoidance([cf1], RADII) - cf1.enableCollisionAvoidance([cf0], RADII) - - goal0 = [1.0, 0.0, 1.0] - goal1 = [-1.0, 0.0, 1.0] - cf0.goTo(goal0, 0, 5.0) - cf1.goTo(goal1, 0, 5.0) - timeHelper.sleep(5) - assert np.all(np.isclose(cf0.position(), goal0)) - assert np.all(np.isclose(cf1.position(), goal1)) - - -def test_goToWithCA_changeEllipsoid(): - args = "--sim --vis null" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - - newRADII = np.array([0.1, 0.1, 0.3]) - cf0.enableCollisionAvoidance([cf1], newRADII) - cf1.enableCollisionAvoidance([cf0], newRADII) - - goal0 = [1.0, 0.0, 1.0] - goal1 = [-1.0, 0.0, 1.0] - cf0.goTo(goal0, 0, 5.0) - cf1.goTo(goal1, 0, 5.0) - - # flag if a collision happened during sleep - collisionHappend = False - while timeHelper.time() < 10.0: - positions = np.stack([cf0.position(), cf1.position()]) - collisionsOld = check_ellipsoid_collisions(positions, RADII) - collisionsNew = check_ellipsoid_collisions(positions, newRADII) - if np.any(collisionsOld): - collisionHappend = True - assert not np.any(collisionsNew) - - timeHelper.sleep(timeHelper.dt) - assert collisionHappend - - -def test_goToWithCA_Intersection(): - args = "--sim --vis null --dt 0.01" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - cf0.enableCollisionAvoidance([cf1], RADII) - cf1.enableCollisionAvoidance([cf0], RADII) - - goal0 = [1.0, 1.0, 1.0] - goal1 = [-1.0, 1.0, 1.0] - cf0.goTo(goal0, 0, 5.0) - cf1.goTo(goal1, 0, 5.0) - - while timeHelper.time() < 10.0: - positions = np.stack([cf0.position(), cf1.position()]) - collisions = check_ellipsoid_collisions(positions, RADII) - assert not np.any(collisions) - - timeHelper.sleep(timeHelper.dt) - assert np.all(np.isclose(cf0.position(), goal0)) - assert np.all(np.isclose(cf1.position(), goal1)) - - -def test_goToWithoutCA_Intersection(): - args = "--sim --vis null --dt 0.05" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - - goal0 = [1.0, 1.0, 1.0] - goal1 = [-1.0, 1.0, 1.0] - cf0.goTo(goal0, 0, 5.0) - cf1.goTo(goal1, 0, 5.0) - - while timeHelper.time() < 10.0: - positions = np.stack([cf0.position(), cf1.position()]) - collisions = check_ellipsoid_collisions(positions, RADII) - if np.any(collisions): - return - - timeHelper.sleep(timeHelper.dt) - assert False - - -def test_goToWithCA_random(): - rows, cols = 3, 5 - N = rows * cols - crazyflies_yaml = grid_yaml(rows, cols, spacing=0.5) - - args = "--sim --vis null" - swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args=args) - timeHelper = swarm.timeHelper - allcfs = swarm.allcfs - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - - cfs = allcfs.crazyflies - - for _, cf in enumerate(cfs): - cf.enableCollisionAvoidance(cfs, RADII) - - np.random.seed(0) - xy_radius = 0.125 - for _ in range(5): - lastTime = timeHelper.time() - goals = poisson_disk_sample(N, dim=2, mindist=5*xy_radius) - goals_z = Z * np.ones(N) + 0.2 * np.random.uniform(-1.0, 1.0, size=N) - goals = np.hstack([goals, goals_z[:, None]]) - - for goal, cf in zip(goals, cfs): - cf.goTo(goal, yaw=0.0, duration=5) - while timeHelper.time() - lastTime < 20.0: - positions = np.stack([cf.position() for cf in cfs]) - timeHelper.sleep(timeHelper.dt) - collisions = check_ellipsoid_collisions(positions, RADII) - assert not np.any(collisions) - for goal, cf in zip(goals, cfs): - assert np.all(np.isclose(cf.position(), goal, atol=1e-4)) - - -def test_cmdPosition(): - args = "--sim --vis null --maxvel 1.0" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - cf0.enableCollisionAvoidance([cf1], RADII) - cf1.enableCollisionAvoidance([cf0], RADII) - - goal0 = np.array([1.0, 0.0, 2.0]) - goal1 = np.array([-1.0, 0.0, 2.0]) - cf0.cmdPosition(goal0, yaw=0.0) - cf1.cmdPosition(goal1, yaw=0.0) - while timeHelper.time() < 10.0: - positions = np.stack([cf0.position(), cf1.position()]) - collisions = check_ellipsoid_collisions(positions, RADII) - assert not np.any(collisions) - - timeHelper.sleep(timeHelper.dt) - assert np.all(np.isclose(cf0.position(), goal0)) - assert np.all(np.isclose(cf1.position(), goal1)) - - -def test_boundingBox(): - args = "--sim --vis null --maxvel 1.0" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - BBOXMAX = [5, 5, 5] - BBOXMIN = [-3, -3, -3] - cf0.enableCollisionAvoidance( - [cf1], RADII, bboxMax=BBOXMAX, bboxMin=BBOXMIN) - cf1.enableCollisionAvoidance( - [cf0], RADII, bboxMax=BBOXMAX, bboxMin=BBOXMIN) - - goal0 = np.array([BBOXMAX[0] - 1, 0.0, Z]) - goal1 = np.array([BBOXMIN[0] - 1, 0.0, Z]) - cf0.cmdPosition(goal0, yaw=0.0) - cf1.cmdPosition(goal1, yaw=0.0) - while timeHelper.time() < 10.0: - assert np.all(cf1.position() >= BBOXMIN) - assert np.all(cf1.position() <= BBOXMAX) - timeHelper.sleep(timeHelper.dt) - - assert np.all(np.isclose(cf0.position(), goal0)) - assert not np.all(np.isclose(cf1.position(), goal1)) - - -#@pytest.mark.xfail(reason="Bug in firmware") -def test_maxSpeed_zero(): - args = "--sim --vis null --maxvel 1.0" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - cf0.enableCollisionAvoidance([cf1], RADII, maxSpeed=0.0) - cf1.enableCollisionAvoidance([cf0], RADII, maxSpeed=0.0) - - goal0 = np.array([1.0, 0.0, Z]) - goal1 = np.array([-1.0, 0.0, Z]) - cf0.cmdPosition(goal0, yaw=0.0) - cf1.cmdPosition(goal1, yaw=0.0) - while timeHelper.time() < 10.0: - timeHelper.sleep(timeHelper.dt) - assert np.all(cf0.velocity() == np.zeros(3)) - assert np.all(cf1.velocity() == np.zeros(3)) - - assert np.all(np.isclose(cf0.position(), goal1)) - assert np.all(np.isclose(cf1.position(), goal0)) - - -def test_maxSpeed_limit(): - args = "--sim --vis null --maxvel 1.0" - allcfs, timeHelper = setUp(args) - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - cf0, cf1 = allcfs.crazyflies - - cf0.enableCollisionAvoidance([cf1], RADII, maxSpeed=1.0) - cf1.enableCollisionAvoidance([cf0], RADII, maxSpeed=3.0) - - cf0.cmdVelocityWorld([2.0, 0.0, 0.0], yawRate=0.0) - cf1.cmdVelocityWorld([-2.0, 0.0, 0.0], yawRate=0.0) - - while timeHelper.time() < 10.0: - assert np.all(np.abs(cf0.velocity()) < 1.2 * np.ones(3)) - assert np.all(np.abs(cf1.velocity()) < 3.2 * np.ones(3)) - timeHelper.sleep(timeHelper.dt) - if cf0.position()[0] > 2.0 and cf1.position()[0] < -2.0: - return - - assert False - - -if __name__ == "__main__": - # test_velocityMode_sidestepWorstCase(sys.argv) - test_maxSpeed_limit() diff --git a/test/bindings/test_highLevel.py b/test/bindings/test_highLevel.py deleted file mode 100644 index 8578ec8f73..0000000000 --- a/test/bindings/test_highLevel.py +++ /dev/null @@ -1,181 +0,0 @@ -#!/usr/bin/env python - -import os - -import numpy as np -from pycrazyswarm import * -import uav_trajectory - -Z = 1.0 -FIGURE8_CSV = os.path.dirname(__file__) + "/figure8.csv" - -def setUp(): - crazyflies_yaml = """ - crazyflies: - - channel: 100 - id: 1 - initialPosition: [1.0, 0.0, 0.0] - - channel: 100 - id: 10 - initialPosition: [0.0, -1.0, 0.0] - """ - swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null") - timeHelper = swarm.timeHelper - return swarm.allcfs, timeHelper - -def _collectRelativePositions(timeHelper, cf, duration): - t0 = timeHelper.time() - positions = [] - while timeHelper.time() - t0 < duration: - positions.append(cf.position() - cf.initialPosition) - timeHelper.sleep(timeHelper.dt + 1e-6) - return np.stack(positions) - - -def test_takeOff(): - allcfs, timeHelper = setUp() - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - - for cf in allcfs.crazyflies: - pos = cf.initialPosition + np.array([0, 0, Z]) - assert np.all(np.isclose(cf.position(), pos, atol=0.0001)) - -def test_goTo_nonRelative(): - allcfs, timeHelper = setUp() - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - - for cf in allcfs.crazyflies: - pos = np.array(cf.initialPosition) + np.array([1, 1, Z]) - cf.goTo(pos, 0, 1.0) - timeHelper.sleep(1.0) - - for cf in allcfs.crazyflies: - pos = cf.initialPosition + np.array([1, 1, Z]) - assert np.all(np.isclose(cf.position(), pos)) - -def test_goTo_relative(): - allcfs, timeHelper = setUp() - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - - allcfs.goTo(np.array([1.0,1.0,1.0]), 0, Z) - timeHelper.sleep(2.0) - - for cf in allcfs.crazyflies: - pos = cf.initialPosition + np.array([1.0,1.0,2*Z]) - assert np.all(np.isclose(cf.position(), pos)) - -def test_landing(): - allcfs, timeHelper = setUp() - allcfs.takeoff(targetHeight=Z, duration=1.0+Z) - timeHelper.sleep(1.5+Z) - - allcfs.land(targetHeight=0.02, duration=1.0+Z) - timeHelper.sleep(1.0+Z) - - for cf in allcfs.crazyflies: - pos = cf.initialPosition + np.array([0, 0, 0.02]) - assert np.all(np.isclose(cf.position(), pos, atol=0.0001)) - -def test_uploadTrajectory_timescale(): - allcfs, timeHelper = setUp() - cf = allcfs.crazyflies[0] - - traj = uav_trajectory.Trajectory() - traj.loadcsv(FIGURE8_CSV) - trajId = 100 - cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) - - # We know the traj isn't close to origin at 3/4 of its duration - cf.startTrajectory(trajId) - timeHelper.sleep(0.75 * traj.duration) - assert np.linalg.norm(cf.position() - cf.initialPosition) >= 0.5 - - # Make sure we're back at origin - timeHelper.sleep(traj.duration) - - # Speeding up time by 2x, we should finish the trajectory in less time - cf.startTrajectory(trajId, timescale=0.5) - timeHelper.sleep(0.75 * traj.duration) - assert np.linalg.norm(cf.position() - cf.initialPosition) <= 0.001 - -def test_uploadTrajectory_fig8Bounds(): - allcfs, timeHelper = setUp() - cf = allcfs.crazyflies[0] - - traj = uav_trajectory.Trajectory() - traj.loadcsv(FIGURE8_CSV) - - trajId = 100 - cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) - cf.startTrajectory(trajId) - positions = _collectRelativePositions(timeHelper, cf, traj.duration) - - # We know the approximate range the bounding box should lie in by plotting - # the trajectory. - xs, ys, _ = positions.T - assert 0.9 < np.amax(xs) < 1.1 - assert -0.9 > np.amin(xs) > -1.1 - assert 0.4 < np.amax(ys) < 0.6 - assert -0.4 > np.amin(ys) > -0.6 - -def test_uploadTrajectory_reverse(): - allcfs, timeHelper = setUp() - cf = allcfs.crazyflies[0] - - traj = uav_trajectory.Trajectory() - traj.loadcsv(FIGURE8_CSV) - trajId = 100 - cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) - - cf.startTrajectory(trajId) - positions = _collectRelativePositions(timeHelper, cf, traj.duration) - - cf.startTrajectory(trajId, reverse=True) - positionsReverse = _collectRelativePositions(timeHelper, cf, traj.duration) - positions2 = np.flipud(positionsReverse) - - # The distance threshold must be large because the trajectory is not - # symmetrical, not because time/reversing is super sloppy. - dists = np.linalg.norm(positions - positions2, axis=1) - assert not np.any(dists > 0.2) - -def test_uploadTrajectory_broadcast(): - allcfs, timeHelper = setUp() - cf0, cf1 = allcfs.crazyflies - - relativeInitial = cf1.initialPosition - cf0.initialPosition - - traj = uav_trajectory.Trajectory() - traj.loadcsv(FIGURE8_CSV) - trajId = 100 - for cf in (cf0, cf1): - cf.uploadTrajectory(trajectoryId=trajId, pieceOffset=0, trajectory=traj) - - allcfs.startTrajectory(trajId) - t0 = timeHelper.time() - while timeHelper.time() - t0 < traj.duration: - relative = cf1.position() - cf0.position() - assert np.all(np.isclose(relativeInitial, relative)) - timeHelper.sleep(timeHelper.dt + 1e-6) - -def test_setGroupMask(): - allcfs, timeHelper = setUp() - cf0, cf1 = allcfs.crazyflies - cf0.setGroupMask(1) - cf1.setGroupMask(2) - allcfs.takeoff(targetHeight=Z, duration=1.0 + Z, groupMask = 1) - timeHelper.sleep(1.5+Z) - - pos0 = cf0.initialPosition + np.array([0, 0, Z]) - assert np.all(np.isclose(cf0.position(), pos0, atol=0.0001)) - assert np.all(np.isclose(cf1.position(), cf1.initialPosition, atol=0.0001)) - - allcfs.takeoff(targetHeight=Z, duration=1.0 + Z, groupMask = 2) - timeHelper.sleep(1.5+Z) - - pos1 = cf1.initialPosition + np.array([0, 0, Z]) - assert np.all(np.isclose(cf0.position(), pos0, atol=0.0001)) - assert np.all(np.isclose(cf1.position(), pos1, atol=0.0001)) diff --git a/test/bindings/test_lowLevel.py b/test/bindings/test_lowLevel.py deleted file mode 100644 index 7ae2b73699..0000000000 --- a/test/bindings/test_lowLevel.py +++ /dev/null @@ -1,105 +0,0 @@ -#!/usr/bin/env python - -import numpy as np -from pycrazyswarm import * - -Z = 1.0 - -def setUp(extra_args=""): - crazyflies_yaml = """ - crazyflies: - - channel: 100 - id: 1 - initialPosition: [1.0, 0.0, 0.0] - """ - swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null " + extra_args) - timeHelper = swarm.timeHelper - return swarm.allcfs, timeHelper - -def test_cmdFullState_zeroVel(): - allcfs, timeHelper = setUp() - cf = allcfs.crazyflies[0] - - pos = np.array(cf.initialPosition) + np.array([1, 1, Z]) - cf.cmdFullState(pos, np.zeros(3), np.zeros(3), 0, np.zeros(3)) - timeHelper.sleep(1.0) - - assert np.all(np.isclose(cf.position(), pos)) - -def test_cmdPosition(): - allcfs, timeHelper = setUp() - cf = allcfs.crazyflies[0] - - pos = np.array(cf.initialPosition) + np.array([1, 1, Z]) - cf.cmdPosition(pos,yaw=0.0) - timeHelper.sleep(1.0) - - assert np.all(np.isclose(cf.position(), pos)) - -def test_cmdVelocityWorld_checkVelocity(): - allcfs, timeHelper = setUp() - - cf = allcfs.crazyflies[0] - vel = np.ones(3) - cf.cmdVelocityWorld(vel, yawRate=0) - timeHelper.sleep(1.0) - - assert np.all(np.isclose(cf.velocity(), vel)) - -def test_cmdVelocityWorld_checkIntegrate(): - allcfs, timeHelper = setUp() - - cf = allcfs.crazyflies[0] - vel = np.ones(3) - cf.cmdVelocityWorld(vel, yawRate=0) - timeHelper.sleep(1.0) - - pos = cf.initialPosition + vel - assert np.all(np.isclose(cf.position(), pos)) - -def test_cmdVelocityWorld_disturbance(): - crazyflies_yaml = """ - crazyflies: - - channel: 100 - id: 1 - initialPosition: [1.0, 0.0, 0.0] - """ - swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null --disturbance 1.0") - timeHelper = swarm.timeHelper - - cf = swarm.allcfs.crazyflies[0] - - vel = np.ones(3) - cf.cmdVelocityWorld(vel, yawRate=0) - timeHelper.sleep(1.0) - - pos = cf.initialPosition + vel - assert not np.any(np.isclose(cf.position(), pos)) - -def test_sleepResidual(): - """Verify TimeHelper's time() is consistent with its integration steps.""" - np.random.seed(0) - TRIALS = 100 - for _ in range(TRIALS): - dtTick = 10 ** np.random.uniform(-2, 0) - dtSleep = 10 ** np.random.uniform(-2, 0) - allcfs, timeHelper = setUp("--dt {}".format(dtTick)) - - cf = allcfs.crazyflies[0] - vel = np.ones(3) - cf.cmdVelocityWorld(vel, yawRate=0) - time = 0.0 - while timeHelper.time() < 1.0: - timeHelper.sleep(dtSleep) - time += dtSleep - - assert time >= timeHelper.time() - - # We don't expect them to be exactly the same because timeHelper.time() - # will always be an integer multiple of dtTick. However, we should not - # be off by more than a tick. - assert time - timeHelper.time() < dtTick - - pos = cf.initialPosition + timeHelper.time() * vel - assert np.all(np.isclose(cf.position(), pos)) - diff --git a/test/bindings/test_yamlString.py b/test/bindings/test_yamlString.py deleted file mode 100644 index d01c8842a1..0000000000 --- a/test/bindings/test_yamlString.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python - -import numpy as np -from pycrazyswarm import * - -def test_yaml_string_load(): - - crazyflies_yaml = """ - crazyflies: - - channel: 100 - id: 1 - initialPosition: [1.0, 0.0, 0.0] - - channel: 100 - id: 10 - initialPosition: [0.0, -1.0, 0.0] - """ - - swarm = Crazyswarm(crazyflies_yaml=crazyflies_yaml, args="--sim --vis null") - timeHelper = swarm.timeHelper - cfs = swarm.allcfs.crazyflies - byId = swarm.allcfs.crazyfliesById - - assert len(cfs) == 2 - cf1 = byId[1] - assert np.all(cf1.initialPosition == [1.0, 0.0, 0.0]) - - cf10 = byId[10] - assert np.all(cf10.initialPosition == [0.0, -1.0, 0.0]) diff --git a/test/bindings/uav_trajectory.py b/test/bindings/uav_trajectory.py deleted file mode 100644 index 91939450ac..0000000000 --- a/test/bindings/uav_trajectory.py +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env python - -import numpy as np - -def normalize(v): - norm = np.linalg.norm(v) - assert norm > 0 - return v / norm - - -class Polynomial: - def __init__(self, p): - self.p = p - - # evaluate a polynomial using horner's rule - def eval(self, t): - assert t >= 0 - x = 0.0 - for i in range(0, len(self.p)): - x = x * t + self.p[len(self.p) - 1 - i] - return x - - # compute and return derivative - def derivative(self): - return Polynomial([(i+1) * self.p[i+1] for i in range(0, len(self.p) - 1)]) - - -class TrajectoryOutput: - def __init__(self): - self.pos = None # position [m] - self.vel = None # velocity [m/s] - self.acc = None # acceleration [m/s^2] - self.omega = None # angular velocity [rad/s] - self.yaw = None # yaw angle [rad] - - -# 4d single polynomial piece for x-y-z-yaw, includes duration. -class Polynomial4D: - def __init__(self, duration, px, py, pz, pyaw): - self.duration = duration - self.px = Polynomial(px) - self.py = Polynomial(py) - self.pz = Polynomial(pz) - self.pyaw = Polynomial(pyaw) - - # compute and return derivative - def derivative(self): - return Polynomial4D( - self.duration, - self.px.derivative().p, - self.py.derivative().p, - self.pz.derivative().p, - self.pyaw.derivative().p) - - def eval(self, t): - result = TrajectoryOutput() - # flat variables - result.pos = np.array([self.px.eval(t), self.py.eval(t), self.pz.eval(t)]) - result.yaw = self.pyaw.eval(t) - - # 1st derivative - derivative = self.derivative() - result.vel = np.array([derivative.px.eval(t), derivative.py.eval(t), derivative.pz.eval(t)]) - dyaw = derivative.pyaw.eval(t) - - # 2nd derivative - derivative2 = derivative.derivative() - result.acc = np.array([derivative2.px.eval(t), derivative2.py.eval(t), derivative2.pz.eval(t)]) - - # 3rd derivative - derivative3 = derivative2.derivative() - jerk = np.array([derivative3.px.eval(t), derivative3.py.eval(t), derivative3.pz.eval(t)]) - - thrust = result.acc + np.array([0, 0, 9.81]) # add gravity - - z_body = normalize(thrust) - x_world = np.array([np.cos(result.yaw), np.sin(result.yaw), 0]) - y_body = normalize(np.cross(z_body, x_world)) - x_body = np.cross(y_body, z_body) - - jerk_orth_zbody = jerk - (np.dot(jerk, z_body) * z_body) - h_w = jerk_orth_zbody / np.linalg.norm(thrust) - - result.omega = np.array([-np.dot(h_w, y_body), np.dot(h_w, x_body), z_body[2] * dyaw]) - return result - - -class Trajectory: - def __init__(self): - self.polynomials = None - self.duration = None - - def n_pieces(self): - return len(self.polynomials) - - def loadcsv(self, filename): - data = np.loadtxt(filename, delimiter=",", skiprows=1, usecols=range(33)) - self.polynomials = [Polynomial4D(row[0], row[1:9], row[9:17], row[17:25], row[25:33]) for row in data] - self.duration = np.sum(data[:,0]) - - def eval(self, t): - assert t >= 0 - assert t <= self.duration - - current_t = 0.0 - for p in self.polynomials: - if t <= current_t + p.duration: - return p.eval(t - current_t) - current_t = current_t + p.duration From 9b61e627eefaa3635d05b1bd1a15a6c40bc35e09 Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Thu, 7 Oct 2021 21:28:47 +0200 Subject: [PATCH 4/9] Add python bindings to primary Makefile --- .github/workflows/ci-bindings.yml | 18 ++++++------------ .gitignore | 2 ++ Makefile | 13 ++++++++++++- bindings/.gitignore | 5 ----- bindings/Makefile | 17 ----------------- bindings/setup.py | 4 ++-- tools/make/targets.mk | 2 +- 7 files changed, 23 insertions(+), 38 deletions(-) delete mode 100644 bindings/.gitignore delete mode 100644 bindings/Makefile diff --git a/.github/workflows/ci-bindings.yml b/.github/workflows/ci-bindings.yml index ff1c8a42ce..131e2fb06d 100644 --- a/.github/workflows/ci-bindings.yml +++ b/.github/workflows/ci-bindings.yml @@ -22,18 +22,12 @@ jobs: with: python-version: ${{ matrix.python-version }} - - name: Install Python Packages - shell: bash -l {0} - run: | - python -m pip install numpy pyyaml pytest scipy - - - name: Build - shell: bash -l {0} - run: | - cd bindings - CF_PYTHON=python make + # - name: Install Python Packages + # shell: bash -l {0} + # run: | + # python -m pip install numpy pyyaml pytest scipy - - name: Test + - name: Build Bindings shell: bash -l {0} run: | - PYTHONPATH=bindings python -m pytest test/bindings + make bindings_python diff --git a/.gitignore b/.gitignore index 39be2d0d3b..688a12a75e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ tools/make/config.mk cflie.* version.c tags +*cffirmware*.so +cffirmware.py /cf2.* /tag.* diff --git a/Makefile b/Makefile index c847bcfe2d..a3bb953e8d 100644 --- a/Makefile +++ b/Makefile @@ -487,4 +487,15 @@ unit: # The flag "-DUNITY_INCLUDE_DOUBLE" allows comparison of double values in Unity. See: https://stackoverflow.com/a/37790196 rake unit "DEFINES=$(CFLAGS) -DUNITY_INCLUDE_DOUBLE" "FILES=$(FILES)" "UNIT_TEST_STYLE=$(UNIT_TEST_STYLE)" -.PHONY: all clean build compile unit prep erase flash check_submodules trace openocd gdb halt reset flash_dfu flash_verify cload size print_version clean_version +# Python bindings +MOD_INC = $(CRAZYFLIE_BASE)/src/modules/interface +MOD_SRC = $(CRAZYFLIE_BASE)/src/modules/src + +bindings_python: bindings/setup.py bin/cffirmware_wrap.c $(MOD_SRC)/*.c + $(PYTHON) bindings/setup.py build_ext --inplace + +bin/cffirmware_wrap.c cffirmware.py: bindings/cffirmware.i $(MOD_INC)/*.h + swig -python -I$(MOD_INC) -o bin/cffirmware_wrap.c bindings/cffirmware.i + mv bin/cffirmware.py cffirmware.py + +.PHONY: all clean build compile unit prep erase flash check_submodules trace openocd gdb halt reset flash_dfu flash_verify cload size print_version clean_version bindings_python diff --git a/bindings/.gitignore b/bindings/.gitignore deleted file mode 100644 index b87958aa9e..0000000000 --- a/bindings/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -cffirmware.py -cffirmware_wrap.c -*.so -__pycache__ -build diff --git a/bindings/Makefile b/bindings/Makefile deleted file mode 100644 index 0f14b389e7..0000000000 --- a/bindings/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -firmdir = .. -modinc = $(firmdir)/src/modules/interface -modsrc = $(firmdir)/src/modules/src - -ifeq ($(CF_PYTHON),) - CF_PYTHON = python3 -endif - -swig: setup.py cffirmware_wrap.c $(modsrc)/*.c - $(CF_PYTHON) setup.py build_ext --inplace - -cffirmware_wrap.c: cffirmware.i $(modinc)/*.h - swig -python -I$(modinc) cffirmware.i - -clean: - rm -f cffirmware.py _cffirmware.so *.pyc cffirmware_wrap.c - rm -rf build diff --git a/bindings/setup.py b/bindings/setup.py index 3cbc802548..d45d9de7e9 100644 --- a/bindings/setup.py +++ b/bindings/setup.py @@ -6,7 +6,7 @@ import numpy as np -fw_dir = ".." +fw_dir = "." include = [ os.path.join(fw_dir, "src/modules/interface"), os.path.join(fw_dir, "src/hal/interface"), @@ -25,7 +25,7 @@ cffirmware = Extension( "_cffirmware", include_dirs=include, - sources=fw_sources + ["cffirmware_wrap.c"], + sources=fw_sources + ["bin/cffirmware_wrap.c"], extra_compile_args=[ "-O3", # One Lighthouse header uses ARM's half-precision float, but we don't diff --git a/tools/make/targets.mk b/tools/make/targets.mk index 52bc7a7bc2..cf65eac840 100644 --- a/tools/make/targets.mk +++ b/tools/make/targets.mk @@ -68,7 +68,7 @@ clean_o: clean_version @$(if $(QUIET), ,echo $(CLEAN_O_COMMAND$(VERBOSE)) ) @$(CLEAN_O_COMMAND) -CLEAN_COMMAND=rm -f cf*.elf cf*.hex cf*.bin cf*.dfu cf*.map $(BIN)/dep/*.d $(BIN)/*.o +CLEAN_COMMAND=rm -f cf*.elf cf*.hex cf*.bin cf*.dfu cf*.map cf*.py _cf*.so $(BIN)/dep/*.d $(BIN)/*.o $(BIN)/*.c CLEAN_COMMAND_SILENT=" CLEAN" clean: @$(if $(QUIET), ,echo $(CLEAN_COMMAND$(VERBOSE)) ) From 2dd9010b9cead7a6b83057ca5a5f08aa017f7847 Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Thu, 7 Oct 2021 21:33:07 +0200 Subject: [PATCH 5/9] CI: add git submodule init/update --- .github/workflows/ci-bindings.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci-bindings.yml b/.github/workflows/ci-bindings.yml index 131e2fb06d..929f4fd34e 100644 --- a/.github/workflows/ci-bindings.yml +++ b/.github/workflows/ci-bindings.yml @@ -30,4 +30,6 @@ jobs: - name: Build Bindings shell: bash -l {0} run: | + git submodule init + git submodule update make bindings_python From 4c14b6328ffd07e5db042f07345c1bc26ca30820 Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Thu, 7 Oct 2021 21:42:38 +0200 Subject: [PATCH 6/9] Reduce scope of bindings to just math3d.h --- bindings/cffirmware.i | 73 - bindings/numpy.i | 3178 ----------------------------------------- bindings/setup.py | 14 +- 3 files changed, 1 insertion(+), 3264 deletions(-) delete mode 100644 bindings/numpy.i diff --git a/bindings/cffirmware.i b/bindings/cffirmware.i index eaab0b25a7..49cc1aadc4 100755 --- a/bindings/cffirmware.i +++ b/bindings/cffirmware.i @@ -5,81 +5,12 @@ %{ #define SWIG_FILE_WITH_INIT -#include "collision_avoidance.h" #include "math3d.h" -#include "pptraj.h" -#include "planner.h" -#include "stabilizer_types.h" %} -%include "collision_avoidance.h" %include "math3d.h" -%include "pptraj.h" -%include "planner.h" -%include "stabilizer_types.h" - -%include "numpy.i" - - -%init %{ - import_array() -%} - -%apply (int DIM1, float* IN_ARRAY1) {(int nOthers, float const *otherPositions)} %inline %{ -void poly4d_set(struct poly4d *poly, int dim, int coef, float val) -{ - poly->p[dim][coef] = val; -} -float poly4d_get(struct poly4d *poly, int dim, int coef) -{ - return poly->p[dim][coef]; -} -struct poly4d* pp_get_piece(struct piecewise_traj *pp, int i) -{ - return &pp->pieces[i]; -} -struct poly4d* malloc_poly4d(int size) -{ - return (struct poly4d*)malloc(sizeof(struct poly4d) * size); -} - -struct vec vec2svec(struct vec3_s v) -{ - return mkvec(v.x, v.y, v.z); -} - -struct vec3_s svec2vec(struct vec v) -{ - struct vec3_s vv = { .x = v.x, .y = v.y, .z = v.z, }; - return vv; -} - -void collisionAvoidanceUpdateSetpointWrap( - collision_avoidance_params_t const *params, - collision_avoidance_state_t *collisionState, - int nOthers, - float const *otherPositions, - setpoint_t *setpoint, sensorData_t const *sensorData, state_t const *state) -{ - nOthers /= 3; - float *workspace = malloc(sizeof(float) * 7 * (nOthers + 6)); - collisionAvoidanceUpdateSetpointCore( - params, - collisionState, - nOthers, - otherPositions, - workspace, - setpoint, sensorData, state - ); - free(workspace); -} -%} - - -%pythoncode %{ -import numpy as np %} #define COPY_CTOR(structname) \ @@ -133,7 +64,3 @@ structname(struct structname const *x) { \ return _cffirmware.vsub(self, other) %} }; - -%extend traj_eval { - COPY_CTOR(traj_eval) -}; diff --git a/bindings/numpy.i b/bindings/numpy.i deleted file mode 100644 index 8416e82f3a..0000000000 --- a/bindings/numpy.i +++ /dev/null @@ -1,3178 +0,0 @@ -/* -*- C -*- (not really, but good for syntax highlighting) */ - -/* - * Copyright (c) 2005-2015, NumPy Developers. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following - * disclaimer in the documentation and/or other materials provided - * with the distribution. - * - * * Neither the name of the NumPy Developers nor the names of any - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifdef SWIGPYTHON - -%{ -#ifndef SWIG_FILE_WITH_INIT -#define NO_IMPORT_ARRAY -#endif -#include "stdio.h" -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include -%} - -/**********************************************************************/ - -%fragment("NumPy_Backward_Compatibility", "header") -{ -%#if NPY_API_VERSION < 0x00000007 -%#define NPY_ARRAY_DEFAULT NPY_DEFAULT -%#define NPY_ARRAY_FARRAY NPY_FARRAY -%#define NPY_FORTRANORDER NPY_FORTRAN -%#endif -} - -/**********************************************************************/ - -/* The following code originally appeared in - * enthought/kiva/agg/src/numeric.i written by Eric Jones. It was - * translated from C++ to C by John Hunter. Bill Spotz has modified - * it to fix some minor bugs, upgrade from Numeric to numpy (all - * versions), add some comments and functionality, and convert from - * direct code insertion to SWIG fragments. - */ - -%fragment("NumPy_Macros", "header") -{ -/* Macros to extract array attributes. - */ -%#if NPY_API_VERSION < 0x00000007 -%#define is_array(a) ((a) && PyArray_Check((PyArrayObject*)a)) -%#define array_type(a) (int)(PyArray_TYPE((PyArrayObject*)a)) -%#define array_numdims(a) (((PyArrayObject*)a)->nd) -%#define array_dimensions(a) (((PyArrayObject*)a)->dimensions) -%#define array_size(a,i) (((PyArrayObject*)a)->dimensions[i]) -%#define array_strides(a) (((PyArrayObject*)a)->strides) -%#define array_stride(a,i) (((PyArrayObject*)a)->strides[i]) -%#define array_data(a) (((PyArrayObject*)a)->data) -%#define array_descr(a) (((PyArrayObject*)a)->descr) -%#define array_flags(a) (((PyArrayObject*)a)->flags) -%#define array_clearflags(a,f) (((PyArrayObject*)a)->flags) &= ~f -%#define array_enableflags(a,f) (((PyArrayObject*)a)->flags) = f -%#define array_is_fortran(a) (PyArray_ISFORTRAN((PyArrayObject*)a)) -%#else -%#define is_array(a) ((a) && PyArray_Check(a)) -%#define array_type(a) PyArray_TYPE((PyArrayObject*)a) -%#define array_numdims(a) PyArray_NDIM((PyArrayObject*)a) -%#define array_dimensions(a) PyArray_DIMS((PyArrayObject*)a) -%#define array_strides(a) PyArray_STRIDES((PyArrayObject*)a) -%#define array_stride(a,i) PyArray_STRIDE((PyArrayObject*)a,i) -%#define array_size(a,i) PyArray_DIM((PyArrayObject*)a,i) -%#define array_data(a) PyArray_DATA((PyArrayObject*)a) -%#define array_descr(a) PyArray_DESCR((PyArrayObject*)a) -%#define array_flags(a) PyArray_FLAGS((PyArrayObject*)a) -%#define array_enableflags(a,f) PyArray_ENABLEFLAGS((PyArrayObject*)a,f) -%#define array_clearflags(a,f) PyArray_CLEARFLAGS((PyArrayObject*)a,f) -%#define array_is_fortran(a) (PyArray_IS_F_CONTIGUOUS((PyArrayObject*)a)) -%#endif -%#define array_is_contiguous(a) (PyArray_ISCONTIGUOUS((PyArrayObject*)a)) -%#define array_is_native(a) (PyArray_ISNOTSWAPPED((PyArrayObject*)a)) -} - -/**********************************************************************/ - -%fragment("NumPy_Utilities", - "header") -{ - /* Given a PyObject, return a string describing its type. - */ - const char* pytype_string(PyObject* py_obj) - { - if (py_obj == NULL ) return "C NULL value"; - if (py_obj == Py_None ) return "Python None" ; - if (PyCallable_Check(py_obj)) return "callable" ; - if (PyString_Check( py_obj)) return "string" ; - if (PyInt_Check( py_obj)) return "int" ; - if (PyFloat_Check( py_obj)) return "float" ; - if (PyDict_Check( py_obj)) return "dict" ; - if (PyList_Check( py_obj)) return "list" ; - if (PyTuple_Check( py_obj)) return "tuple" ; - - return "unknown type"; - } - - /* Given a NumPy typecode, return a string describing the type. - */ - const char* typecode_string(int typecode) - { - static const char* type_names[25] = {"bool", - "byte", - "unsigned byte", - "short", - "unsigned short", - "int", - "unsigned int", - "long", - "unsigned long", - "long long", - "unsigned long long", - "float", - "double", - "long double", - "complex float", - "complex double", - "complex long double", - "object", - "string", - "unicode", - "void", - "ntypes", - "notype", - "char", - "unknown"}; - return typecode < 24 ? type_names[typecode] : type_names[24]; - } - - /* Make sure input has correct numpy type. This now just calls - PyArray_EquivTypenums(). - */ - int type_match(int actual_type, - int desired_type) - { - return PyArray_EquivTypenums(actual_type, desired_type); - } - -%#ifdef SWIGPY_USE_CAPSULE - void free_cap(PyObject * cap) - { - void* array = (void*) PyCapsule_GetPointer(cap,SWIGPY_CAPSULE_NAME); - if (array != NULL) free(array); - } -%#endif - - -} - -/**********************************************************************/ - -%fragment("NumPy_Object_to_Array", - "header", - fragment="NumPy_Backward_Compatibility", - fragment="NumPy_Macros", - fragment="NumPy_Utilities") -{ - /* Given a PyObject pointer, cast it to a PyArrayObject pointer if - * legal. If not, set the python error string appropriately and - * return NULL. - */ - PyArrayObject* obj_to_array_no_conversion(PyObject* input, - int typecode) - { - PyArrayObject* ary = NULL; - if (is_array(input) && (typecode == NPY_NOTYPE || - PyArray_EquivTypenums(array_type(input), typecode))) - { - ary = (PyArrayObject*) input; - } - else if is_array(input) - { - const char* desired_type = typecode_string(typecode); - const char* actual_type = typecode_string(array_type(input)); - PyErr_Format(PyExc_TypeError, - "Array of type '%s' required. Array of type '%s' given", - desired_type, actual_type); - ary = NULL; - } - else - { - const char* desired_type = typecode_string(typecode); - const char* actual_type = pytype_string(input); - PyErr_Format(PyExc_TypeError, - "Array of type '%s' required. A '%s' was given", - desired_type, - actual_type); - ary = NULL; - } - return ary; - } - - /* Convert the given PyObject to a NumPy array with the given - * typecode. On success, return a valid PyArrayObject* with the - * correct type. On failure, the python error string will be set and - * the routine returns NULL. - */ - PyArrayObject* obj_to_array_allow_conversion(PyObject* input, - int typecode, - int* is_new_object) - { - PyArrayObject* ary = NULL; - PyObject* py_obj; - if (is_array(input) && (typecode == NPY_NOTYPE || - PyArray_EquivTypenums(array_type(input),typecode))) - { - ary = (PyArrayObject*) input; - *is_new_object = 0; - } - else - { - py_obj = PyArray_FROMANY(input, typecode, 0, 0, NPY_ARRAY_DEFAULT); - /* If NULL, PyArray_FromObject will have set python error value.*/ - ary = (PyArrayObject*) py_obj; - *is_new_object = 1; - } - return ary; - } - - /* Given a PyArrayObject, check to see if it is contiguous. If so, - * return the input pointer and flag it as not a new object. If it is - * not contiguous, create a new PyArrayObject using the original data, - * flag it as a new object and return the pointer. - */ - PyArrayObject* make_contiguous(PyArrayObject* ary, - int* is_new_object, - int min_dims, - int max_dims) - { - PyArrayObject* result; - if (array_is_contiguous(ary)) - { - result = ary; - *is_new_object = 0; - } - else - { - result = (PyArrayObject*) PyArray_ContiguousFromObject((PyObject*)ary, - array_type(ary), - min_dims, - max_dims); - *is_new_object = 1; - } - return result; - } - - /* Given a PyArrayObject, check to see if it is Fortran-contiguous. - * If so, return the input pointer, but do not flag it as not a new - * object. If it is not Fortran-contiguous, create a new - * PyArrayObject using the original data, flag it as a new object - * and return the pointer. - */ - PyArrayObject* make_fortran(PyArrayObject* ary, - int* is_new_object) - { - PyArrayObject* result; - if (array_is_fortran(ary)) - { - result = ary; - *is_new_object = 0; - } - else - { - Py_INCREF(array_descr(ary)); - result = (PyArrayObject*) PyArray_FromArray(ary, - array_descr(ary), -%#if NPY_API_VERSION < 0x00000007 - NPY_FORTRANORDER); -%#else - NPY_ARRAY_F_CONTIGUOUS); -%#endif - *is_new_object = 1; - } - return result; - } - - /* Convert a given PyObject to a contiguous PyArrayObject of the - * specified type. If the input object is not a contiguous - * PyArrayObject, a new one will be created and the new object flag - * will be set. - */ - PyArrayObject* obj_to_array_contiguous_allow_conversion(PyObject* input, - int typecode, - int* is_new_object) - { - int is_new1 = 0; - int is_new2 = 0; - PyArrayObject* ary2; - PyArrayObject* ary1 = obj_to_array_allow_conversion(input, - typecode, - &is_new1); - if (ary1) - { - ary2 = make_contiguous(ary1, &is_new2, 0, 0); - if ( is_new1 && is_new2) - { - Py_DECREF(ary1); - } - ary1 = ary2; - } - *is_new_object = is_new1 || is_new2; - return ary1; - } - - /* Convert a given PyObject to a Fortran-ordered PyArrayObject of the - * specified type. If the input object is not a Fortran-ordered - * PyArrayObject, a new one will be created and the new object flag - * will be set. - */ - PyArrayObject* obj_to_array_fortran_allow_conversion(PyObject* input, - int typecode, - int* is_new_object) - { - int is_new1 = 0; - int is_new2 = 0; - PyArrayObject* ary2; - PyArrayObject* ary1 = obj_to_array_allow_conversion(input, - typecode, - &is_new1); - if (ary1) - { - ary2 = make_fortran(ary1, &is_new2); - if (is_new1 && is_new2) - { - Py_DECREF(ary1); - } - ary1 = ary2; - } - *is_new_object = is_new1 || is_new2; - return ary1; - } -} /* end fragment */ - -/**********************************************************************/ - -%fragment("NumPy_Array_Requirements", - "header", - fragment="NumPy_Backward_Compatibility", - fragment="NumPy_Macros") -{ - /* Test whether a python object is contiguous. If array is - * contiguous, return 1. Otherwise, set the python error string and - * return 0. - */ - int require_contiguous(PyArrayObject* ary) - { - int contiguous = 1; - if (!array_is_contiguous(ary)) - { - PyErr_SetString(PyExc_TypeError, - "Array must be contiguous. A non-contiguous array was given"); - contiguous = 0; - } - return contiguous; - } - - /* Test whether a python object is (C_ or F_) contiguous. If array is - * contiguous, return 1. Otherwise, set the python error string and - * return 0. - */ - int require_c_or_f_contiguous(PyArrayObject* ary) - { - int contiguous = 1; - if (!(array_is_contiguous(ary) || array_is_fortran(ary))) - { - PyErr_SetString(PyExc_TypeError, - "Array must be contiguous (C_ or F_). A non-contiguous array was given"); - contiguous = 0; - } - return contiguous; - } - - /* Require that a numpy array is not byte-swapped. If the array is - * not byte-swapped, return 1. Otherwise, set the python error string - * and return 0. - */ - int require_native(PyArrayObject* ary) - { - int native = 1; - if (!array_is_native(ary)) - { - PyErr_SetString(PyExc_TypeError, - "Array must have native byteorder. " - "A byte-swapped array was given"); - native = 0; - } - return native; - } - - /* Require the given PyArrayObject to have a specified number of - * dimensions. If the array has the specified number of dimensions, - * return 1. Otherwise, set the python error string and return 0. - */ - int require_dimensions(PyArrayObject* ary, - int exact_dimensions) - { - int success = 1; - if (array_numdims(ary) != exact_dimensions) - { - PyErr_Format(PyExc_TypeError, - "Array must have %d dimensions. Given array has %d dimensions", - exact_dimensions, - array_numdims(ary)); - success = 0; - } - return success; - } - - /* Require the given PyArrayObject to have one of a list of specified - * number of dimensions. If the array has one of the specified number - * of dimensions, return 1. Otherwise, set the python error string - * and return 0. - */ - int require_dimensions_n(PyArrayObject* ary, - int* exact_dimensions, - int n) - { - int success = 0; - int i; - char dims_str[255] = ""; - char s[255]; - for (i = 0; i < n && !success; i++) - { - if (array_numdims(ary) == exact_dimensions[i]) - { - success = 1; - } - } - if (!success) - { - for (i = 0; i < n-1; i++) - { - sprintf(s, "%d, ", exact_dimensions[i]); - strcat(dims_str,s); - } - sprintf(s, " or %d", exact_dimensions[n-1]); - strcat(dims_str,s); - PyErr_Format(PyExc_TypeError, - "Array must have %s dimensions. Given array has %d dimensions", - dims_str, - array_numdims(ary)); - } - return success; - } - - /* Require the given PyArrayObject to have a specified shape. If the - * array has the specified shape, return 1. Otherwise, set the python - * error string and return 0. - */ - int require_size(PyArrayObject* ary, - npy_intp* size, - int n) - { - int i; - int success = 1; - size_t len; - char desired_dims[255] = "["; - char s[255]; - char actual_dims[255] = "["; - for(i=0; i < n;i++) - { - if (size[i] != -1 && size[i] != array_size(ary,i)) - { - success = 0; - } - } - if (!success) - { - for (i = 0; i < n; i++) - { - if (size[i] == -1) - { - sprintf(s, "*,"); - } - else - { - sprintf(s, "%ld,", (long int)size[i]); - } - strcat(desired_dims,s); - } - len = strlen(desired_dims); - desired_dims[len-1] = ']'; - for (i = 0; i < n; i++) - { - sprintf(s, "%ld,", (long int)array_size(ary,i)); - strcat(actual_dims,s); - } - len = strlen(actual_dims); - actual_dims[len-1] = ']'; - PyErr_Format(PyExc_TypeError, - "Array must have shape of %s. Given array has shape of %s", - desired_dims, - actual_dims); - } - return success; - } - - /* Require the given PyArrayObject to to be Fortran ordered. If the - * the PyArrayObject is already Fortran ordered, do nothing. Else, - * set the Fortran ordering flag and recompute the strides. - */ - int require_fortran(PyArrayObject* ary) - { - int success = 1; - int nd = array_numdims(ary); - int i; - npy_intp * strides = array_strides(ary); - if (array_is_fortran(ary)) return success; - int n_non_one = 0; - /* Set the Fortran ordered flag */ - const npy_intp *dims = array_dimensions(ary); - for (i=0; i < nd; ++i) - n_non_one += (dims[i] != 1) ? 1 : 0; - if (n_non_one > 1) - array_clearflags(ary,NPY_ARRAY_CARRAY); - array_enableflags(ary,NPY_ARRAY_FARRAY); - /* Recompute the strides */ - strides[0] = strides[nd-1]; - for (i=1; i < nd; ++i) - strides[i] = strides[i-1] * array_size(ary,i-1); - return success; - } -} - -/* Combine all NumPy fragments into one for convenience */ -%fragment("NumPy_Fragments", - "header", - fragment="NumPy_Backward_Compatibility", - fragment="NumPy_Macros", - fragment="NumPy_Utilities", - fragment="NumPy_Object_to_Array", - fragment="NumPy_Array_Requirements") -{ -} - -/* End John Hunter translation (with modifications by Bill Spotz) - */ - -/* %numpy_typemaps() macro - * - * This macro defines a family of 75 typemaps that allow C arguments - * of the form - * - * 1. (DATA_TYPE IN_ARRAY1[ANY]) - * 2. (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) - * 3. (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) - * - * 4. (DATA_TYPE IN_ARRAY2[ANY][ANY]) - * 5. (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - * 6. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) - * 7. (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - * 8. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) - * - * 9. (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) - * 10. (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - * 11. (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - * 12. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) - * 13. (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - * 14. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) - * - * 15. (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) - * 16. (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - * 17. (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - * 18. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, , DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) - * 19. (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - * 20. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) - * - * 21. (DATA_TYPE INPLACE_ARRAY1[ANY]) - * 22. (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) - * 23. (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) - * - * 24. (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) - * 25. (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - * 26. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) - * 27. (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - * 28. (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) - * - * 29. (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) - * 30. (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - * 31. (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - * 32. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3) - * 33. (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - * 34. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3) - * - * 35. (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) - * 36. (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - * 37. (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - * 38. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_ARRAY4) - * 39. (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - * 40. (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_FARRAY4) - * - * 41. (DATA_TYPE ARGOUT_ARRAY1[ANY]) - * 42. (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) - * 43. (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) - * - * 44. (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) - * - * 45. (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) - * - * 46. (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) - * - * 47. (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) - * 48. (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) - * - * 49. (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - * 50. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) - * 51. (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - * 52. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2) - * - * 53. (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) - * 54. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) - * 55. (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) - * 56. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3) - * - * 57. (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) - * 58. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_ARRAY4) - * 59. (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) - * 60. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_FARRAY4) - * - * 61. (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1) - * 62. (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEWM_ARRAY1) - * - * 63. (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - * 64. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_ARRAY2) - * 65. (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - * 66. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_FARRAY2) - * - * 67. (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) - * 68. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_ARRAY3) - * 69. (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) - * 70. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_FARRAY3) - * - * 71. (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) - * 72. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4) - * 73. (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) - * 74. (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4) - * - * 75. (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) - * - * where "DATA_TYPE" is any type supported by the NumPy module, and - * "DIM_TYPE" is any int-like type suitable for specifying dimensions. - * The difference between "ARRAY" typemaps and "FARRAY" typemaps is - * that the "FARRAY" typemaps expect Fortran ordering of - * multidimensional arrays. In python, the dimensions will not need - * to be specified (except for the "DATA_TYPE* ARGOUT_ARRAY1" - * typemaps). The IN_ARRAYs can be a numpy array or any sequence that - * can be converted to a numpy array of the specified type. The - * INPLACE_ARRAYs must be numpy arrays of the appropriate type. The - * ARGOUT_ARRAYs will be returned as new numpy arrays of the - * appropriate type. - * - * These typemaps can be applied to existing functions using the - * %apply directive. For example: - * - * %apply (double* IN_ARRAY1, int DIM1) {(double* series, int length)}; - * double prod(double* series, int length); - * - * %apply (int DIM1, int DIM2, double* INPLACE_ARRAY2) - * {(int rows, int cols, double* matrix )}; - * void floor(int rows, int cols, double* matrix, double f); - * - * %apply (double IN_ARRAY3[ANY][ANY][ANY]) - * {(double tensor[2][2][2] )}; - * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY]) - * {(double low[2][2][2] )}; - * %apply (double ARGOUT_ARRAY3[ANY][ANY][ANY]) - * {(double upp[2][2][2] )}; - * void luSplit(double tensor[2][2][2], - * double low[2][2][2], - * double upp[2][2][2] ); - * - * or directly with - * - * double prod(double* IN_ARRAY1, int DIM1); - * - * void floor(int DIM1, int DIM2, double* INPLACE_ARRAY2, double f); - * - * void luSplit(double IN_ARRAY3[ANY][ANY][ANY], - * double ARGOUT_ARRAY3[ANY][ANY][ANY], - * double ARGOUT_ARRAY3[ANY][ANY][ANY]); - */ - -%define %numpy_typemaps(DATA_TYPE, DATA_TYPECODE, DIM_TYPE) - -/************************/ -/* Input Array Typemaps */ -/************************/ - -/* Typemap suite for (DATA_TYPE IN_ARRAY1[ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE IN_ARRAY1[ANY]) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE IN_ARRAY1[ANY]) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[1] = { $1_dim0 }; - array = obj_to_array_contiguous_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 1) || - !require_size(array, size, 1)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(freearg) - (DATA_TYPE IN_ARRAY1[ANY]) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[1] = { -1 }; - array = obj_to_array_contiguous_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 1) || - !require_size(array, size, 1)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); -} -%typemap(freearg) - (DATA_TYPE* IN_ARRAY1, DIM_TYPE DIM1) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[1] = {-1}; - array = obj_to_array_contiguous_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 1) || - !require_size(array, size, 1)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DATA_TYPE*) array_data(array); -} -%typemap(freearg) - (DIM_TYPE DIM1, DATA_TYPE* IN_ARRAY1) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE IN_ARRAY2[ANY][ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE IN_ARRAY2[ANY][ANY]) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE IN_ARRAY2[ANY][ANY]) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[2] = { $1_dim0, $1_dim1 }; - array = obj_to_array_contiguous_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 2) || - !require_size(array, size, 2)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(freearg) - (DATA_TYPE IN_ARRAY2[ANY][ANY]) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[2] = { -1, -1 }; - array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 2) || - !require_size(array, size, 2)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); -} -%typemap(freearg) - (DATA_TYPE* IN_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[2] = { -1, -1 }; - array = obj_to_array_contiguous_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 2) || - !require_size(array, size, 2)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DATA_TYPE*) array_data(array); -} -%typemap(freearg) - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_ARRAY2) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[2] = { -1, -1 }; - array = obj_to_array_fortran_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 2) || - !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); -} -%typemap(freearg) - (DATA_TYPE* IN_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[2] = { -1, -1 }; - array = obj_to_array_fortran_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 2) || - !require_size(array, size, 2) || !require_fortran(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DATA_TYPE*) array_data(array); -} -%typemap(freearg) - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* IN_FARRAY2) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 }; - array = obj_to_array_contiguous_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 3) || - !require_size(array, size, 3)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(freearg) - (DATA_TYPE IN_ARRAY3[ANY][ANY][ANY]) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[3] = { -1, -1, -1 }; - array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 3) || - !require_size(array, size, 3)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); -} -%typemap(freearg) - (DATA_TYPE* IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - /* for now, only concerned with lists */ - $1 = PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL, int* is_new_object_array=NULL) -{ - npy_intp size[2] = { -1, -1 }; - PyArrayObject* temp_array; - Py_ssize_t i; - int is_new_object; - - /* length of the list */ - $2 = PyList_Size($input); - - /* the arrays */ - array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); - object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); - is_new_object_array = (int *)calloc($2,sizeof(int)); - - if (array == NULL || object_array == NULL || is_new_object_array == NULL) - { - SWIG_fail; - } - - for (i=0; i<$2; i++) - { - temp_array = obj_to_array_contiguous_allow_conversion(PySequence_GetItem($input,i), DATA_TYPECODE, &is_new_object); - - /* the new array must be stored so that it can be destroyed in freearg */ - object_array[i] = temp_array; - is_new_object_array[i] = is_new_object; - - if (!temp_array || !require_dimensions(temp_array, 2)) SWIG_fail; - - /* store the size of the first array in the list, then use that for comparison. */ - if (i == 0) - { - size[0] = array_size(temp_array,0); - size[1] = array_size(temp_array,1); - } - - if (!require_size(temp_array, size, 2)) SWIG_fail; - - array[i] = (DATA_TYPE*) array_data(temp_array); - } - - $1 = (DATA_TYPE**) array; - $3 = (DIM_TYPE) size[0]; - $4 = (DIM_TYPE) size[1]; -} -%typemap(freearg) - (DATA_TYPE** IN_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - Py_ssize_t i; - - if (array$argnum!=NULL) free(array$argnum); - - /*freeing the individual arrays if needed */ - if (object_array$argnum!=NULL) - { - if (is_new_object_array$argnum!=NULL) - { - for (i=0; i<$2; i++) - { - if (object_array$argnum[i] != NULL && is_new_object_array$argnum[i]) - { Py_DECREF(object_array$argnum[i]); } - } - free(is_new_object_array$argnum); - } - free(object_array$argnum); - } -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, - * DATA_TYPE* IN_ARRAY3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[3] = { -1, -1, -1 }; - array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 3) || - !require_size(array, size, 3)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DATA_TYPE*) array_data(array); -} -%typemap(freearg) - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_ARRAY3) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[3] = { -1, -1, -1 }; - array = obj_to_array_fortran_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 3) || - !require_size(array, size, 3) | !require_fortran(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); -} -%typemap(freearg) - (DATA_TYPE* IN_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, - * DATA_TYPE* IN_FARRAY3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[3] = { -1, -1, -1 }; - array = obj_to_array_fortran_allow_conversion($input, - DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 3) || - !require_size(array, size, 3) || !require_fortran(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DATA_TYPE*) array_data(array); -} -%typemap(freearg) - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* IN_FARRAY3) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[4] = { $1_dim0, $1_dim1, $1_dim2 , $1_dim3}; - array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 4) || - !require_size(array, size, 4)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(freearg) - (DATA_TYPE IN_ARRAY4[ANY][ANY][ANY][ANY]) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3, DIM_TYPE DIM4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[4] = { -1, -1, -1, -1 }; - array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 4) || - !require_size(array, size, 4)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); - $5 = (DIM_TYPE) array_size(array,3); -} -%typemap(freearg) - (DATA_TYPE* IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3, DIM_TYPE DIM4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - /* for now, only concerned with lists */ - $1 = PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL, int* is_new_object_array=NULL) -{ - npy_intp size[3] = { -1, -1, -1 }; - PyArrayObject* temp_array; - Py_ssize_t i; - int is_new_object; - - /* length of the list */ - $2 = PyList_Size($input); - - /* the arrays */ - array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); - object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); - is_new_object_array = (int *)calloc($2,sizeof(int)); - - if (array == NULL || object_array == NULL || is_new_object_array == NULL) - { - SWIG_fail; - } - - for (i=0; i<$2; i++) - { - temp_array = obj_to_array_contiguous_allow_conversion(PySequence_GetItem($input,i), DATA_TYPECODE, &is_new_object); - - /* the new array must be stored so that it can be destroyed in freearg */ - object_array[i] = temp_array; - is_new_object_array[i] = is_new_object; - - if (!temp_array || !require_dimensions(temp_array, 3)) SWIG_fail; - - /* store the size of the first array in the list, then use that for comparison. */ - if (i == 0) - { - size[0] = array_size(temp_array,0); - size[1] = array_size(temp_array,1); - size[2] = array_size(temp_array,2); - } - - if (!require_size(temp_array, size, 3)) SWIG_fail; - - array[i] = (DATA_TYPE*) array_data(temp_array); - } - - $1 = (DATA_TYPE**) array; - $3 = (DIM_TYPE) size[0]; - $4 = (DIM_TYPE) size[1]; - $5 = (DIM_TYPE) size[2]; -} -%typemap(freearg) - (DATA_TYPE** IN_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - Py_ssize_t i; - - if (array$argnum!=NULL) free(array$argnum); - - /*freeing the individual arrays if needed */ - if (object_array$argnum!=NULL) - { - if (is_new_object_array$argnum!=NULL) - { - for (i=0; i<$2; i++) - { - if (object_array$argnum[i] != NULL && is_new_object_array$argnum[i]) - { Py_DECREF(object_array$argnum[i]); } - } - free(is_new_object_array$argnum); - } - free(object_array$argnum); - } -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, - * DATA_TYPE* IN_ARRAY4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[4] = { -1, -1, -1 , -1}; - array = obj_to_array_contiguous_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 4) || - !require_size(array, size, 4)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DIM_TYPE) array_size(array,3); - $5 = (DATA_TYPE*) array_data(array); -} -%typemap(freearg) - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_ARRAY4) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3, DIM_TYPE DIM4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[4] = { -1, -1, -1, -1 }; - array = obj_to_array_fortran_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 4) || - !require_size(array, size, 4) | !require_fortran(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); - $5 = (DIM_TYPE) array_size(array,3); -} -%typemap(freearg) - (DATA_TYPE* IN_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, - * DATA_TYPE* IN_FARRAY4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) -{ - $1 = is_array($input) || PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) - (PyArrayObject* array=NULL, int is_new_object=0) -{ - npy_intp size[4] = { -1, -1, -1 , -1 }; - array = obj_to_array_fortran_allow_conversion($input, DATA_TYPECODE, - &is_new_object); - if (!array || !require_dimensions(array, 4) || - !require_size(array, size, 4) || !require_fortran(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DIM_TYPE) array_size(array,3); - $5 = (DATA_TYPE*) array_data(array); -} -%typemap(freearg) - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* IN_FARRAY4) -{ - if (is_new_object$argnum && array$argnum) - { Py_DECREF(array$argnum); } -} - -/***************************/ -/* In-Place Array Typemaps */ -/***************************/ - -/* Typemap suite for (DATA_TYPE INPLACE_ARRAY1[ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE INPLACE_ARRAY1[ANY]) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE INPLACE_ARRAY1[ANY]) - (PyArrayObject* array=NULL) -{ - npy_intp size[1] = { $1_dim0 }; - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,1) || !require_size(array, size, 1) || - !require_contiguous(array) || !require_native(array)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} - -/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_ARRAY1, DIM_TYPE DIM1) - (PyArrayObject* array=NULL, int i=1) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,1) || !require_contiguous(array) - || !require_native(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = 1; - for (i=0; i < array_numdims(array); ++i) $2 *= array_size(array,i); -} - -/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DATA_TYPE* INPLACE_ARRAY1) - (PyArrayObject* array=NULL, int i=0) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,1) || !require_contiguous(array) - || !require_native(array)) SWIG_fail; - $1 = 1; - for (i=0; i < array_numdims(array); ++i) $1 *= array_size(array,i); - $2 = (DATA_TYPE*) array_data(array); -} - -/* Typemap suite for (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE INPLACE_ARRAY2[ANY][ANY]) - (PyArrayObject* array=NULL) -{ - npy_intp size[2] = { $1_dim0, $1_dim1 }; - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,2) || !require_size(array, size, 2) || - !require_contiguous(array) || !require_native(array)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} - -/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_ARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,2) || !require_contiguous(array) - || !require_native(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_ARRAY2) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,2) || !require_contiguous(array) || - !require_native(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DATA_TYPE*) array_data(array); -} - -/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_FARRAY2, DIM_TYPE DIM1, DIM_TYPE DIM2) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,2) || !require_contiguous(array) - || !require_native(array) || !require_fortran(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DATA_TYPE* INPLACE_FARRAY2) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,2) || !require_contiguous(array) || - !require_native(array) || !require_fortran(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DATA_TYPE*) array_data(array); -} - -/* Typemap suite for (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE INPLACE_ARRAY3[ANY][ANY][ANY]) - (PyArrayObject* array=NULL) -{ - npy_intp size[3] = { $1_dim0, $1_dim1, $1_dim2 }; - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,3) || !require_size(array, size, 3) || - !require_contiguous(array) || !require_native(array)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} - -/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,3) || !require_contiguous(array) || - !require_native(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); -} - -/* Typemap suite for (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - $1 = PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL) -{ - npy_intp size[2] = { -1, -1 }; - PyArrayObject* temp_array; - Py_ssize_t i; - - /* length of the list */ - $2 = PyList_Size($input); - - /* the arrays */ - array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); - object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); - - if (array == NULL || object_array == NULL) - { - SWIG_fail; - } - - for (i=0; i<$2; i++) - { - temp_array = obj_to_array_no_conversion(PySequence_GetItem($input,i), DATA_TYPECODE); - - /* the new array must be stored so that it can be destroyed in freearg */ - object_array[i] = temp_array; - - if ( !temp_array || !require_dimensions(temp_array, 2) || - !require_contiguous(temp_array) || - !require_native(temp_array) || - !PyArray_EquivTypenums(array_type(temp_array), DATA_TYPECODE) - ) SWIG_fail; - - /* store the size of the first array in the list, then use that for comparison. */ - if (i == 0) - { - size[0] = array_size(temp_array,0); - size[1] = array_size(temp_array,1); - } - - if (!require_size(temp_array, size, 2)) SWIG_fail; - - array[i] = (DATA_TYPE*) array_data(temp_array); - } - - $1 = (DATA_TYPE**) array; - $3 = (DIM_TYPE) size[0]; - $4 = (DIM_TYPE) size[1]; -} -%typemap(freearg) - (DATA_TYPE** INPLACE_ARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - if (array$argnum!=NULL) free(array$argnum); - if (object_array$argnum!=NULL) free(object_array$argnum); -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, - * DATA_TYPE* INPLACE_ARRAY3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_ARRAY3) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,3) || !require_contiguous(array) - || !require_native(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DATA_TYPE*) array_data(array); -} - -/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_FARRAY3, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,3) || !require_contiguous(array) || - !require_native(array) || !require_fortran(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, - * DATA_TYPE* INPLACE_FARRAY3) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DATA_TYPE* INPLACE_FARRAY3) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,3) || !require_contiguous(array) - || !require_native(array) || !require_fortran(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DATA_TYPE*) array_data(array); -} - -/* Typemap suite for (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE INPLACE_ARRAY4[ANY][ANY][ANY][ANY]) - (PyArrayObject* array=NULL) -{ - npy_intp size[4] = { $1_dim0, $1_dim1, $1_dim2 , $1_dim3 }; - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,4) || !require_size(array, size, 4) || - !require_contiguous(array) || !require_native(array)) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} - -/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3, DIM_TYPE DIM4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,4) || !require_contiguous(array) || - !require_native(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); - $5 = (DIM_TYPE) array_size(array,3); -} - -/* Typemap suite for (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3, DIM_TYPE DIM4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - $1 = PySequence_Check($input); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - (DATA_TYPE** array=NULL, PyArrayObject** object_array=NULL) -{ - npy_intp size[3] = { -1, -1, -1 }; - PyArrayObject* temp_array; - Py_ssize_t i; - - /* length of the list */ - $2 = PyList_Size($input); - - /* the arrays */ - array = (DATA_TYPE **)malloc($2*sizeof(DATA_TYPE *)); - object_array = (PyArrayObject **)calloc($2,sizeof(PyArrayObject *)); - - if (array == NULL || object_array == NULL) - { - SWIG_fail; - } - - for (i=0; i<$2; i++) - { - temp_array = obj_to_array_no_conversion(PySequence_GetItem($input,i), DATA_TYPECODE); - - /* the new array must be stored so that it can be destroyed in freearg */ - object_array[i] = temp_array; - - if ( !temp_array || !require_dimensions(temp_array, 3) || - !require_contiguous(temp_array) || - !require_native(temp_array) || - !PyArray_EquivTypenums(array_type(temp_array), DATA_TYPECODE) - ) SWIG_fail; - - /* store the size of the first array in the list, then use that for comparison. */ - if (i == 0) - { - size[0] = array_size(temp_array,0); - size[1] = array_size(temp_array,1); - size[2] = array_size(temp_array,2); - } - - if (!require_size(temp_array, size, 3)) SWIG_fail; - - array[i] = (DATA_TYPE*) array_data(temp_array); - } - - $1 = (DATA_TYPE**) array; - $3 = (DIM_TYPE) size[0]; - $4 = (DIM_TYPE) size[1]; - $5 = (DIM_TYPE) size[2]; -} -%typemap(freearg) - (DATA_TYPE** INPLACE_ARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - if (array$argnum!=NULL) free(array$argnum); - if (object_array$argnum!=NULL) free(object_array$argnum); -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, - * DATA_TYPE* INPLACE_ARRAY4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_ARRAY4) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_ARRAY4) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,4) || !require_contiguous(array) - || !require_native(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DIM_TYPE) array_size(array,3); - $5 = (DATA_TYPE*) array_data(array); -} - -/* Typemap suite for (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, - * DIM_TYPE DIM3, DIM_TYPE DIM4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_FARRAY4, DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,4) || !require_contiguous(array) || - !require_native(array) || !require_fortran(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = (DIM_TYPE) array_size(array,0); - $3 = (DIM_TYPE) array_size(array,1); - $4 = (DIM_TYPE) array_size(array,2); - $5 = (DIM_TYPE) array_size(array,3); -} - -/* Typemap suite for (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, - * DATA_TYPE* INPLACE_FARRAY4) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_FARRAY4) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DIM_TYPE DIM2, DIM_TYPE DIM3, DIM_TYPE DIM4, DATA_TYPE* INPLACE_FARRAY4) - (PyArrayObject* array=NULL) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_dimensions(array,4) || !require_contiguous(array) - || !require_native(array) || !require_fortran(array)) SWIG_fail; - $1 = (DIM_TYPE) array_size(array,0); - $2 = (DIM_TYPE) array_size(array,1); - $3 = (DIM_TYPE) array_size(array,2); - $4 = (DIM_TYPE) array_size(array,3); - $5 = (DATA_TYPE*) array_data(array); -} - -/*************************/ -/* Argout Array Typemaps */ -/*************************/ - -/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY1[ANY]) - */ -%typemap(in,numinputs=0, - fragment="NumPy_Backward_Compatibility,NumPy_Macros") - (DATA_TYPE ARGOUT_ARRAY1[ANY]) - (PyObject* array = NULL) -{ - npy_intp dims[1] = { $1_dim0 }; - array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); - if (!array) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(argout) - (DATA_TYPE ARGOUT_ARRAY1[ANY]) -{ - $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); -} - -/* Typemap suite for (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) - */ -%typemap(in,numinputs=1, - fragment="NumPy_Fragments") - (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) - (PyObject* array = NULL) -{ - npy_intp dims[1]; - if (!PyInt_Check($input)) - { - const char* typestring = pytype_string($input); - PyErr_Format(PyExc_TypeError, - "Int dimension expected. '%s' given.", - typestring); - SWIG_fail; - } - $2 = (DIM_TYPE) PyInt_AsLong($input); - dims[0] = (npy_intp) $2; - array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); - if (!array) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); -} -%typemap(argout) - (DATA_TYPE* ARGOUT_ARRAY1, DIM_TYPE DIM1) -{ - $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); -} - -/* Typemap suite for (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) - */ -%typemap(in,numinputs=1, - fragment="NumPy_Fragments") - (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) - (PyObject* array = NULL) -{ - npy_intp dims[1]; - if (!PyInt_Check($input)) - { - const char* typestring = pytype_string($input); - PyErr_Format(PyExc_TypeError, - "Int dimension expected. '%s' given.", - typestring); - SWIG_fail; - } - $1 = (DIM_TYPE) PyInt_AsLong($input); - dims[0] = (npy_intp) $1; - array = PyArray_SimpleNew(1, dims, DATA_TYPECODE); - if (!array) SWIG_fail; - $2 = (DATA_TYPE*) array_data(array); -} -%typemap(argout) - (DIM_TYPE DIM1, DATA_TYPE* ARGOUT_ARRAY1) -{ - $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); -} - -/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) - */ -%typemap(in,numinputs=0, - fragment="NumPy_Backward_Compatibility,NumPy_Macros") - (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) - (PyObject* array = NULL) -{ - npy_intp dims[2] = { $1_dim0, $1_dim1 }; - array = PyArray_SimpleNew(2, dims, DATA_TYPECODE); - if (!array) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(argout) - (DATA_TYPE ARGOUT_ARRAY2[ANY][ANY]) -{ - $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); -} - -/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) - */ -%typemap(in,numinputs=0, - fragment="NumPy_Backward_Compatibility,NumPy_Macros") - (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) - (PyObject* array = NULL) -{ - npy_intp dims[3] = { $1_dim0, $1_dim1, $1_dim2 }; - array = PyArray_SimpleNew(3, dims, DATA_TYPECODE); - if (!array) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(argout) - (DATA_TYPE ARGOUT_ARRAY3[ANY][ANY][ANY]) -{ - $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); -} - -/* Typemap suite for (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) - */ -%typemap(in,numinputs=0, - fragment="NumPy_Backward_Compatibility,NumPy_Macros") - (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) - (PyObject* array = NULL) -{ - npy_intp dims[4] = { $1_dim0, $1_dim1, $1_dim2, $1_dim3 }; - array = PyArray_SimpleNew(4, dims, DATA_TYPECODE); - if (!array) SWIG_fail; - $1 = ($1_ltype) array_data(array); -} -%typemap(argout) - (DATA_TYPE ARGOUT_ARRAY4[ANY][ANY][ANY][ANY]) -{ - $result = SWIG_Python_AppendOutput($result,(PyObject*)array$argnum); -} - -/*****************************/ -/* Argoutview Array Typemaps */ -/*****************************/ - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim_temp) -{ - $1 = &data_temp; - $2 = &dim_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DATA_TYPE** ARGOUTVIEW_ARRAY1, DIM_TYPE* DIM1) -{ - npy_intp dims[1] = { *$2 }; - PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DATA_TYPE** ARGOUTVIEW_ARRAY1) - (DIM_TYPE dim_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim_temp; - $2 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEW_ARRAY1) -{ - npy_intp dims[1] = { *$1 }; - PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$2)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DATA_TYPE** ARGOUTVIEW_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) -{ - npy_intp dims[2] = { *$2, *$3 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_ARRAY2) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_ARRAY2) -{ - npy_intp dims[2] = { *$1, *$2 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") - (DATA_TYPE** ARGOUTVIEW_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) -{ - npy_intp dims[2] = { *$2, *$3 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEW_FARRAY2) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEW_FARRAY2) -{ - npy_intp dims[2] = { *$1, *$2 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DATA_TYPE** ARGOUTVIEW_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) -{ - npy_intp dims[3] = { *$2, *$3, *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, - DATA_TYPE** ARGOUTVIEW_ARRAY3) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_ARRAY3) -{ - npy_intp dims[3] = { *$1, *$2, *$3 }; - PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") - (DATA_TYPE** ARGOUTVIEW_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) -{ - npy_intp dims[3] = { *$2, *$3, *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, - DATA_TYPE** ARGOUTVIEW_FARRAY3) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DATA_TYPE** ARGOUTVIEW_FARRAY3) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEW_FARRAY3) -{ - npy_intp dims[3] = { *$1, *$2, *$3 }; - PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DATA_TYPE** ARGOUTVIEW_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEW_ARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEW_ARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_ARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") - (DATA_TYPE** ARGOUTVIEW_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEW_FARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEW_FARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEW_FARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - $result = SWIG_Python_AppendOutput($result,obj); -} - -/*************************************/ -/* Managed Argoutview Array Typemaps */ -/*************************************/ - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim_temp) -{ - $1 = &data_temp; - $2 = &dim_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_ARRAY1, DIM_TYPE* DIM1) -{ - npy_intp dims[1] = { *$2 }; - PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEWM_ARRAY1) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DATA_TYPE** ARGOUTVIEWM_ARRAY1) - (DIM_TYPE dim_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim_temp; - $2 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DIM_TYPE* DIM1, DATA_TYPE** ARGOUTVIEWM_ARRAY1) -{ - npy_intp dims[1] = { *$1 }; - PyObject* obj = PyArray_SimpleNewFromData(1, dims, DATA_TYPECODE, (void*)(*$2)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_ARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) -{ - npy_intp dims[2] = { *$2, *$3 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_ARRAY2) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEWM_ARRAY2) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_ARRAY2) -{ - npy_intp dims[2] = { *$1, *$2 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_FARRAY2, DIM_TYPE* DIM1, DIM_TYPE* DIM2) -{ - npy_intp dims[2] = { *$2, *$3 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_FARRAY2) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DATA_TYPE** ARGOUTVIEWM_FARRAY2) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DATA_TYPE** ARGOUTVIEWM_FARRAY2) -{ - npy_intp dims[2] = { *$1, *$2 }; - PyObject* obj = PyArray_SimpleNewFromData(2, dims, DATA_TYPECODE, (void*)(*$3)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_ARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) -{ - npy_intp dims[3] = { *$2, *$3, *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, - DATA_TYPE** ARGOUTVIEWM_ARRAY3) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DATA_TYPE** ARGOUTVIEWM_ARRAY3) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_ARRAY3) -{ - npy_intp dims[3] = { *$1, *$2, *$3 }; - PyObject* obj= PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_FARRAY3, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) -{ - npy_intp dims[3] = { *$2, *$3, *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, - DATA_TYPE** ARGOUTVIEWM_FARRAY3) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DATA_TYPE** ARGOUTVIEWM_FARRAY3) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DATA_TYPE** ARGOUTVIEWM_FARRAY3) -{ - npy_intp dims[3] = { *$1, *$2, *$3 }; - PyObject* obj = PyArray_SimpleNewFromData(3, dims, DATA_TYPECODE, (void*)(*$4)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEWM_ARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_ARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEWM_FARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_FARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_ARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEWM_ARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_ARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_ARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, - DIM_TYPE* DIM3, DIM_TYPE* DIM4) - */ -%typemap(in,numinputs=0) - (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 ) - (DATA_TYPE* data_temp = NULL , DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp) -{ - $1 = &data_temp; - $2 = &dim1_temp; - $3 = &dim2_temp; - $4 = &dim3_temp; - $5 = &dim4_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DATA_TYPE** ARGOUTVIEWM_FARRAY4, DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4) -{ - npy_intp dims[4] = { *$2, *$3, *$4 , *$5 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$1)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/* Typemap suite for (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, - DATA_TYPE** ARGOUTVIEWM_FARRAY4) - */ -%typemap(in,numinputs=0) - (DIM_TYPE* DIM1 , DIM_TYPE* DIM2 , DIM_TYPE* DIM3 , DIM_TYPE* DIM4 , DATA_TYPE** ARGOUTVIEWM_FARRAY4) - (DIM_TYPE dim1_temp, DIM_TYPE dim2_temp, DIM_TYPE dim3_temp, DIM_TYPE dim4_temp, DATA_TYPE* data_temp = NULL ) -{ - $1 = &dim1_temp; - $2 = &dim2_temp; - $3 = &dim3_temp; - $4 = &dim4_temp; - $5 = &data_temp; -} -%typemap(argout, - fragment="NumPy_Backward_Compatibility,NumPy_Array_Requirements,NumPy_Utilities") - (DIM_TYPE* DIM1, DIM_TYPE* DIM2, DIM_TYPE* DIM3, DIM_TYPE* DIM4, DATA_TYPE** ARGOUTVIEWM_FARRAY4) -{ - npy_intp dims[4] = { *$1, *$2, *$3 , *$4 }; - PyObject* obj = PyArray_SimpleNewFromData(4, dims, DATA_TYPECODE, (void*)(*$5)); - PyArrayObject* array = (PyArrayObject*) obj; - - if (!array || !require_fortran(array)) SWIG_fail; - -%#ifdef SWIGPY_USE_CAPSULE - PyObject* cap = PyCapsule_New((void*)(*$1), SWIGPY_CAPSULE_NAME, free_cap); -%#else - PyObject* cap = PyCObject_FromVoidPtr((void*)(*$1), free); -%#endif - -%#if NPY_API_VERSION < 0x00000007 - PyArray_BASE(array) = cap; -%#else - PyArray_SetBaseObject(array,cap); -%#endif - - $result = SWIG_Python_AppendOutput($result,obj); -} - -/**************************************/ -/* In-Place Array Typemap - flattened */ -/**************************************/ - -/* Typemap suite for (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) - */ -%typecheck(SWIG_TYPECHECK_DOUBLE_ARRAY, - fragment="NumPy_Macros") - (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) -{ - $1 = is_array($input) && PyArray_EquivTypenums(array_type($input), - DATA_TYPECODE); -} -%typemap(in, - fragment="NumPy_Fragments") - (DATA_TYPE* INPLACE_ARRAY_FLAT, DIM_TYPE DIM_FLAT) - (PyArrayObject* array=NULL, int i=1) -{ - array = obj_to_array_no_conversion($input, DATA_TYPECODE); - if (!array || !require_c_or_f_contiguous(array) - || !require_native(array)) SWIG_fail; - $1 = (DATA_TYPE*) array_data(array); - $2 = 1; - for (i=0; i < array_numdims(array); ++i) $2 *= array_size(array,i); -} - -%enddef /* %numpy_typemaps() macro */ -/* *************************************************************** */ - -/* Concrete instances of the %numpy_typemaps() macro: Each invocation - * below applies all of the typemaps above to the specified data type. - */ -%numpy_typemaps(signed char , NPY_BYTE , int) -%numpy_typemaps(unsigned char , NPY_UBYTE , int) -%numpy_typemaps(short , NPY_SHORT , int) -%numpy_typemaps(unsigned short , NPY_USHORT , int) -%numpy_typemaps(int , NPY_INT , int) -%numpy_typemaps(unsigned int , NPY_UINT , int) -%numpy_typemaps(long , NPY_LONG , int) -%numpy_typemaps(unsigned long , NPY_ULONG , int) -%numpy_typemaps(long long , NPY_LONGLONG , int) -%numpy_typemaps(unsigned long long, NPY_ULONGLONG, int) -%numpy_typemaps(float , NPY_FLOAT , int) -%numpy_typemaps(double , NPY_DOUBLE , int) -%numpy_typemaps(int8_t , NPY_INT8 , int) -%numpy_typemaps(int16_t , NPY_INT16 , int) -%numpy_typemaps(int32_t , NPY_INT32 , int) -%numpy_typemaps(int64_t , NPY_INT64 , int) -%numpy_typemaps(uint8_t , NPY_UINT8 , int) -%numpy_typemaps(uint16_t , NPY_UINT16 , int) -%numpy_typemaps(uint32_t , NPY_UINT32 , int) -%numpy_typemaps(uint64_t , NPY_UINT64 , int) - - -/* *************************************************************** - * The follow macro expansion does not work, because C++ bool is 4 - * bytes and NPY_BOOL is 1 byte - * - * %numpy_typemaps(bool, NPY_BOOL, int) - */ - -/* *************************************************************** - * On my Mac, I get the following warning for this macro expansion: - * 'swig/python detected a memory leak of type 'long double *', no destructor found.' - * - * %numpy_typemaps(long double, NPY_LONGDOUBLE, int) - */ - -#ifdef __cplusplus - -%include - -%numpy_typemaps(std::complex, NPY_CFLOAT , int) -%numpy_typemaps(std::complex, NPY_CDOUBLE, int) - -#endif - -#endif /* SWIGPYTHON */ diff --git a/bindings/setup.py b/bindings/setup.py index d45d9de7e9..2500f72589 100644 --- a/bindings/setup.py +++ b/bindings/setup.py @@ -3,22 +3,13 @@ from distutils.core import setup, Extension import os -import numpy as np - - fw_dir = "." include = [ os.path.join(fw_dir, "src/modules/interface"), - os.path.join(fw_dir, "src/hal/interface"), - os.path.join(fw_dir, "src/utils/interface/lighthouse"), - np.get_include(), ] modules = [ - "collision_avoidance.c", - "planner.c", - "pptraj.c", - "pptraj_compressed.c", + # list firmware c-files here ] fw_sources = [os.path.join(fw_dir, "src/modules/src", mod) for mod in modules] @@ -28,9 +19,6 @@ sources=fw_sources + ["bin/cffirmware_wrap.c"], extra_compile_args=[ "-O3", - # One Lighthouse header uses ARM's half-precision float, but we don't - # need it (for now) in bindings, so we simply pretend it's a uint16_t. - "-D__fp16=uint16_t", ], ) From 5ed999b6f953dbb2a94c489c815c74b1d9099c0e Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Fri, 29 Oct 2021 21:24:57 +0200 Subject: [PATCH 7/9] Remove CI Support and put temporary files in bin rather than build Addresses krichardsson comments --- .github/workflows/ci-bindings.yml | 35 ------------------------------- bindings/setup.py | 14 ++++++++++++- 2 files changed, 13 insertions(+), 36 deletions(-) delete mode 100644 .github/workflows/ci-bindings.yml diff --git a/.github/workflows/ci-bindings.yml b/.github/workflows/ci-bindings.yml deleted file mode 100644 index 929f4fd34e..0000000000 --- a/.github/workflows/ci-bindings.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Python Bindings CI - -on: [push, pull_request] - -jobs: - build: - strategy: - fail-fast: false - matrix: - os: [ubuntu-18.04, ubuntu-20.04, macos-latest] - python-version: ["2.7", "3.7"] - - runs-on: ${{ matrix.os }} - - steps: - - - name: Checkout - uses: actions/checkout@v2 - - - name: Install Python - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - # - name: Install Python Packages - # shell: bash -l {0} - # run: | - # python -m pip install numpy pyyaml pytest scipy - - - name: Build Bindings - shell: bash -l {0} - run: | - git submodule init - git submodule update - make bindings_python diff --git a/bindings/setup.py b/bindings/setup.py index 2500f72589..d0f2f3c072 100644 --- a/bindings/setup.py +++ b/bindings/setup.py @@ -1,5 +1,6 @@ """Compiles the cffirmware C extension.""" +import distutils.command.build from distutils.core import setup, Extension import os @@ -22,4 +23,15 @@ ], ) -setup(name="cffirmware", version="1.0", ext_modules=[cffirmware]) +# Override build command to specify custom "build" directory +class BuildCommand(distutils.command.build.build): + def initialize_options(self): + distutils.command.build.build.initialize_options(self) + self.build_base = "bin" + +setup( + name="cffirmware", + version="1.0", + cmdclass={"build": BuildCommand}, + ext_modules=[cffirmware] +) From 34459b234f26864996717951edaef8214b391960 Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Fri, 12 Nov 2021 14:16:19 +0100 Subject: [PATCH 8/9] add test_python target --- Makefile | 3 +++ bindings/cffirmware.i | 4 ++++ test_python/test_math3d.py | 9 +++++++++ 3 files changed, 16 insertions(+) create mode 100644 test_python/test_math3d.py diff --git a/Makefile b/Makefile index a3bb953e8d..a0b85a7ca7 100644 --- a/Makefile +++ b/Makefile @@ -498,4 +498,7 @@ bin/cffirmware_wrap.c cffirmware.py: bindings/cffirmware.i $(MOD_INC)/*.h swig -python -I$(MOD_INC) -o bin/cffirmware_wrap.c bindings/cffirmware.i mv bin/cffirmware.py cffirmware.py +test_python: bindings_python + $(PYTHON) -m pytest test_python + .PHONY: all clean build compile unit prep erase flash check_submodules trace openocd gdb halt reset flash_dfu flash_verify cload size print_version clean_version bindings_python diff --git a/bindings/cffirmware.i b/bindings/cffirmware.i index 49cc1aadc4..43928ba0af 100755 --- a/bindings/cffirmware.i +++ b/bindings/cffirmware.i @@ -13,6 +13,10 @@ %inline %{ %} +%pythoncode %{ +import numpy as np +%} + #define COPY_CTOR(structname) \ structname(struct structname const *x) { \ struct structname *y = malloc(sizeof(struct structname)); \ diff --git a/test_python/test_math3d.py b/test_python/test_math3d.py new file mode 100644 index 0000000000..e4aee7044a --- /dev/null +++ b/test_python/test_math3d.py @@ -0,0 +1,9 @@ +#!/usr/bin/env python + +import numpy as np +import cffirmware + +def test_conversion_to_numpy(): + v_cf = cffirmware.mkvec(1, 2, 3) + v_np = np.array(v_cf) + assert np.allclose(v_np, np.array([1,2,3])) From e3e2ecda0f59f1c81fa1c0a4e557d7775e081bbe Mon Sep 17 00:00:00 2001 From: Wolfgang Hoenig Date: Fri, 12 Nov 2021 19:14:43 +0100 Subject: [PATCH 9/9] add test_python target to CI --- tools/build/build | 1 + tools/build/test_python | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100755 tools/build/test_python diff --git a/tools/build/build b/tools/build/build index 452bd88d8c..925740adcb 100755 --- a/tools/build/build +++ b/tools/build/build @@ -4,5 +4,6 @@ set -e scriptDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) ${scriptDir}/test "${@}" +${scriptDir}/test_python "${@}" ${scriptDir}/make "${@}" ${scriptDir}/check_elf diff --git a/tools/build/test_python b/tools/build/test_python new file mode 100755 index 0000000000..7fd93b33c6 --- /dev/null +++ b/tools/build/test_python @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e + +scriptDir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) + +make test_python "${@}"