summaryrefslogblamecommitdiffstats
path: root/matleap.cpp
blob: 9a3cb257950e328ca9f0e3ee39400803d8aac85e (plain) (tree)









































































































































































































































                                                                                                    
/// @file matleap.cpp
/// @brief leap motion controller interface
/// @author Jeff Perry <jeffsp@gmail.com>
/// @version 1.0
/// @date 2013-09-12

#include "matleap.h"
#include <array>

// Under Windows, a Leap::Controller must be allocated after the MEX
// startup code has completed.  Also, a Leap::Controller must be
// deleted in the function specified by mexAtExit after all global
// destructors are called.  If the Leap::Controller is not allocated
// and freed in this way, the MEX function will crash and cause MATLAB
// to hang or close abruptly.  Linux and OS/X don't have these
// constraints, and you can just create a global Leap::Controller
// instance.

// Global instance pointer
matleap::frame_grabber *fg = 0;
int version = 4; // 1: orig, 2: with arm info, 3: with more hand info

// Exit function
void matleap_exit() {
    fg->close_connection();
    delete fg;
    fg = 0;
}

/// @brief process interface arguments
///
/// @param nlhs matlab mex output interface
/// @param plhs[] matlab mex output interface
/// @param nrhs matlab mex input interface
/// @param prhs[] matlab mex input interface
///
/// @return command number
int get_command(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    int command;
    // check inputs
    switch (nrhs) {
        case 1:
            command = *mxGetPr(prhs[0]);
            break;
        case 0:
            mexErrMsgTxt("Not enough input arguments");
        default:
            mexErrMsgTxt("Too many input arguments");
    }
    // check that inputs agree with command
    switch (command) {
        case -1: {
            // set debug command requires 0 outputs
            if (nlhs != 0)
                mexErrMsgTxt("Wrong number of outputs specified");
        }
            break;
        case 0: {
            // get version command requires 1 outputs
            if (nlhs != 0 && nlhs != 1)
                mexErrMsgTxt("Wrong number of outputs specified");
        }
            break;
        case 1: {
            // frame grab command only requires one input
            if (nrhs > 1)
                mexErrMsgTxt("Too many inputs specified");
            // frame grab command requires exactly one output
            if (nlhs != 0 && nlhs != 1)
                mexErrMsgTxt("Wrong number of outputs specified");
        }
            break;
        default:
            mexErrMsgTxt("An unknown command was specified");
    }
    return command;
}

/// @brief helper function
///
/// @param v values to fill array with
///
/// @return created and filled array
mxArray *create_and_fill(const Leap::Vector &v) {
    mxArray *p = mxCreateNumericMatrix(1, 3, mxDOUBLE_CLASS, mxREAL);
    *(mxGetPr(p) + 0) = v.x;
    *(mxGetPr(p) + 1) = v.y;
    *(mxGetPr(p) + 2) = v.z;
    return p;
}

/// @brief get a frame from the leap controller
///
/// @param nlhs matlab mex output interface
/// @param plhs[] matlab mex output interface
void get_frame(int nlhs, mxArray *plhs[]) {
    // get the frame
    const matleap::frame &f = fg->get_frame();
    // create matlab frame struct
    const char *frame_field_names[] =
            {
                    "id",
                    "timestamp",
                    "gesture",
                    "hands",
                    "version"
            };
    int frame_fields = sizeof(frame_field_names) / sizeof(*frame_field_names);
    plhs[0] = mxCreateStructMatrix(1, 1, frame_fields, frame_field_names);
    // fill the frame struct
    mxSetFieldByNumber(plhs[0], 0, 0, mxCreateDoubleScalar(f.id));
    mxSetFieldByNumber(plhs[0], 0, 1, mxCreateDoubleScalar(f.timestamp));
    mxSetFieldByNumber(plhs[0], 0, 2, mxCreateDoubleScalar(f.has_gesture ? 1 : 0));

    if (f.detectedHands > 0) {
        const char *hand_field_names[] =
                {
                        "id", // 0
                        "confidence", // 2
                        "visible_time",
                        "pinch_strength",
                        "grab_strength",
                        "palm",
                        "digits"
                };

        int hand_fields = sizeof(hand_field_names) / sizeof(*hand_field_names);
        mxArray *p = mxCreateStructMatrix(1, f.detectedHands, hand_fields, hand_field_names);
        mxSetFieldByNumber(plhs[0], 0, 3, p);
        // 3 because hands is the third (fourth) field name in
        // the overall struct we are creating.

        for (int i = 0; i < f.detectedHands; ++i) {
            // one by one, get the fields for the hand
            mxSetFieldByNumber(p, i, 0, mxCreateDoubleScalar(f.hands[i].id()));
            mxSetFieldByNumber(p, i, 1, mxCreateDoubleScalar(f.hands[i].confidence()));
            mxSetFieldByNumber(p, i, 2, mxCreateDoubleScalar(f.hands[i].timeVisible()));
            mxSetFieldByNumber(p, i, 3, mxCreateDoubleScalar(f.hands[i].pinchStrength()));
            mxSetFieldByNumber(p, i, 4, mxCreateDoubleScalar(f.hands[i].grabStrength()));

            // palm
            const char *palm_field_names[] =
                    {
                            "position",
                            "stabilized_position",
                            "velocity",
                            "normal",
                            "width",
                            "direction",
                    };
            int palm_fields = sizeof(palm_field_names) / sizeof(*palm_field_names);
            mxArray *palm = mxCreateStructMatrix(1, 1, palm_fields, palm_field_names);
            mxSetFieldByNumber(p, i, 5, palm);
            mxSetFieldByNumber(palm, 0, 0, create_and_fill(f.hands[i].palmPosition()));
            mxSetFieldByNumber(palm, 0, 1, create_and_fill(f.hands[i].stabilizedPalmPosition()));
            mxSetFieldByNumber(palm, 0, 2, create_and_fill(f.hands[i].palmVelocity()));
            mxSetFieldByNumber(palm, 0, 3, create_and_fill(f.hands[i].palmNormal()));
            mxSetFieldByNumber(palm, 0, 4, mxCreateDoubleScalar(f.hands[i].palmWidth()));
            mxSetFieldByNumber(palm, 0, 5, create_and_fill(f.hands[i].direction()));
            // get bones for all fingers
            const char *digit_field_names[] =
                    {
                            "finger_id", // 0
                            "is_extended",
                            "bones",
                    };
            int digit_fields = sizeof(digit_field_names) / sizeof(*digit_field_names);
            Leap::Finger digits[5];
            std::copy(f.hands[i].fingers().begin(), f.hands[i].fingers().end(), std::begin(digits));
            mxArray *d = mxCreateStructMatrix(1, 5, digit_fields, digit_field_names);
            mxSetFieldByNumber(p, i, 6, d);

            for (int d_it = 0; d_it < 5; d_it++) {
                mxSetFieldByNumber(d, d_it, 0, mxCreateDoubleScalar(digits[d_it].id()));
                mxSetFieldByNumber(d, d_it, 1, mxCreateDoubleScalar(digits[d_it].isExtended()));
                const char *bone_field_names[] =
                        {
                                "prev_joint",    // 0
                                "next_joint",   // 1
                                "width",// 2
                                "rotation"
                        };
                int bone_fields = sizeof(bone_field_names) / sizeof(*bone_field_names);
                mxArray *bones = mxCreateStructMatrix(1, 4, bone_fields, bone_field_names);
                mxSetFieldByNumber(d, d_it, 2, bones);

                auto const boneParts = {Leap::Bone::TYPE_METACARPAL, Leap::Bone::TYPE_PROXIMAL,
                                        Leap::Bone::TYPE_INTERMEDIATE, Leap::Bone::TYPE_DISTAL};

                Leap::Bone bone;
                for (auto bi : boneParts) {
                    bone = digits[d_it].bone(bi);

                    mxSetFieldByNumber(bones, bi, 0, create_and_fill(bone.prevJoint())); // 0
                    mxSetFieldByNumber(bones, bi, 1, create_and_fill(bone.nextJoint())); // 1
                    mxSetFieldByNumber(bones, bi, 2, mxCreateDoubleScalar(bone.width())); // 2
                    mxSetFieldByNumber(bones, bi, 3, create_and_fill(bone.direction()));

                }
            }
        } // re: for f.hands.count()
    } // re: if f.hands.count() > 0

    mxSetFieldByNumber(plhs[0], 0, 5, mxCreateDoubleScalar(version));
}

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
    if (!fg) {
        fg = new matleap::frame_grabber;
        if (fg == 0)
            mexErrMsgTxt("Cannot allocate a frame grabber");
        fg->open_connection();
        mexAtExit(matleap_exit);
    }
    switch (get_command(nlhs, plhs, nrhs, prhs)) {
        // turn on debug
        case -1:
            fg->set_debug(true);
            return;
            // get version
        case 0:
            plhs[0] = mxCreateNumericMatrix(1, 2, mxDOUBLE_CLASS, mxREAL);
            *(mxGetPr(plhs[0]) + 0) = MAJOR_REVISION;
            *(mxGetPr(plhs[0]) + 1) = MINOR_REVISION;
            return;
            // get frame
        case 1:
            get_frame(nlhs, plhs);
            return;
        default:
            // this is a logic error
            mexErrMsgTxt("unknown error: please contact developer");
    }
}