// Copyright 2020 The Defold Foundation
// Licensed under the Defold License version 1.0 (the "License"); you may not use
// this file except in compliance with the License.
//
// You may obtain a copy of the License, together with FAQs at
// https://www.defold.com/license
//
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.

#include "rig.h"

#include <dlib/log.h>
#include <dlib/profile.h>

namespace dmRig
{

    static const dmhash_t NULL_ANIMATION = dmHashString64("");
    static const uint32_t INVALID_BONE_INDEX = 0xffff;
    static const float CURSOR_EPSILON = 0.0001f;
    static const int SIGNAL_DELTA_UNCHANGED = 0x10cced; // Used to indicate if a draw order was unchanged for a certain slot
    static const uint32_t INVALID_ATTACHMENT_INDEX = 0xffffffffu;

    static const float white[] = {1.0f, 1.0f, 1.0, 1.0f};

    static void DoAnimate(HRigContext context, RigInstance* instance, float dt);
    static bool DoPostUpdate(RigInstance* instance);
    static void UpdateSlotDrawOrder(dmArray<int32_t>& draw_order, dmArray<int32_t>& deltas, int changed, dmArray<int32_t>& unchanged);

    Result NewContext(const NewContextParams& params)
    {
        *params.m_Context = new RigContext();
        RigContext* context = *params.m_Context;
        if (!context) {
            return dmRig::RESULT_ERROR;
        }

        context->m_Instances.SetCapacity(params.m_MaxRigInstanceCount);
        context->m_ScratchPoseTransformBuffer.SetCapacity(0);
        context->m_ScratchPoseMatrixBuffer.SetCapacity(0);

        return dmRig::RESULT_OK;
    }

    void DeleteContext(HRigContext context)
    {
        if (context) {
            delete context;
        }
    }

    static const dmRigDDF::RigAnimation* FindAnimation(const dmRigDDF::AnimationSet* anim_set, dmhash_t animation_id)
    {
        if(anim_set == 0x0)
            return 0x0;
        uint32_t anim_count = anim_set->m_Animations.m_Count;
        for (uint32_t i = 0; i < anim_count; ++i)
        {
            const dmRigDDF::RigAnimation* anim = &anim_set->m_Animations[i];
            if (anim->m_Id == animation_id)
            {
                return anim;
            }
        }
        return 0x0;
    }

    static RigPlayer* GetPlayer(HRigInstance instance)
    {
        return &instance->m_Players[instance->m_CurrentPlayer];
    }

    static RigPlayer* GetSecondaryPlayer(HRigInstance instance)
    {
        return &instance->m_Players[(instance->m_CurrentPlayer+1) % 2];
    }

    static RigPlayer* SwitchPlayer(HRigInstance instance)
    {
        instance->m_CurrentPlayer = (instance->m_CurrentPlayer + 1) % 2;
        return &instance->m_Players[instance->m_CurrentPlayer];
    }

    static void ResetMeshSlotPose(HRigInstance instance)
    {
        if (instance->m_MeshEntry != 0x0) {

            int32_t slot_count = instance->m_MeshSet->m_SlotCount;
            for (int32_t slot_index = 0; slot_index < slot_count; slot_index++)
            {
                // Update mesh pose
                MeshSlotPose* mesh_slot_pose = &instance->m_MeshSlotPose[slot_index];
                const dmRigDDF::MeshSlot* mesh_slot = mesh_slot_pose->m_MeshSlot;
                mesh_slot_pose->m_ActiveAttachment = mesh_slot->m_ActiveIndex;

                // Get color for slot
                const float* slot_color = mesh_slot->m_SlotColor.m_Count ? mesh_slot->m_SlotColor.m_Data : white;

                mesh_slot_pose->m_SlotColor[0] = slot_color[0];
                mesh_slot_pose->m_SlotColor[1] = slot_color[1];
                mesh_slot_pose->m_SlotColor[2] = slot_color[2];
                mesh_slot_pose->m_SlotColor[3] = slot_color[3];
            }
        }
    }

    Result PlayAnimation(HRigInstance instance, dmhash_t animation_id, dmRig::RigPlayback playback, float blend_duration, float offset, float playback_rate)
    {
        const dmRigDDF::RigAnimation* anim = FindAnimation(instance->m_AnimationSet, animation_id);
        if (anim == 0x0)
        {
            return dmRig::RESULT_ANIM_NOT_FOUND;
        }

        if (blend_duration > 0.0f)
        {
            instance->m_BlendTimer = 0.0f;
            instance->m_BlendDuration = blend_duration;
            instance->m_Blending = 1;
        }
        else
        {
            RigPlayer* player = GetPlayer(instance);
            player->m_Playing = 0;
        }

        RigPlayer* player = SwitchPlayer(instance);
        player->m_Initial = 1;
        player->m_BlendFinished = blend_duration > 0.0f ? 0 : 1;
        player->m_AnimationId = animation_id;
        player->m_Animation = anim;
        player->m_Playing = 1;
        player->m_Playback = playback;

        if (player->m_Playback == dmRig::PLAYBACK_ONCE_BACKWARD || player->m_Playback == dmRig::PLAYBACK_LOOP_BACKWARD) {
            player->m_Backwards = 1;
            offset = 1.0f - dmMath::Clamp(offset, 0.0f, 1.0f);
        } else {
            player->m_Backwards = 0;
        }

        SetCursor(instance, offset, true);
        SetPlaybackRate(instance, playback_rate);

        return dmRig::RESULT_OK;
    }

    Result CancelAnimation(HRigInstance instance)
    {
        RigPlayer* player = GetPlayer(instance);
        player->m_Playing = 0;

        return dmRig::RESULT_OK;
    }

    dmhash_t GetAnimation(HRigInstance instance)
    {
        RigPlayer* player = GetPlayer(instance);
        return player->m_AnimationId;
    }

    dmhash_t GetMesh(HRigInstance instance)
    {
        return instance->m_MeshId;
    }

    Result SetMesh(HRigInstance instance, dmhash_t mesh_id)
    {
        const dmRigDDF::MeshSet* mesh_set = instance->m_MeshSet;
        for (uint32_t i = 0; i < mesh_set->m_MeshEntries.m_Count; ++i)
        {
            if (mesh_id == mesh_set->m_MeshEntries[i].m_Id)
            {
                instance->m_MeshEntry = &mesh_set->m_MeshEntries[i];
                instance->m_MeshId = mesh_id;
                instance->m_DoRender = 0;

                // Go through all slot poses and fetch correct meshslot from skin.
                if (instance->m_MeshEntry != 0x0) {
                    int32_t slot_count = instance->m_MeshSet->m_SlotCount;
                    for (int32_t slot_index = 0; slot_index < slot_count; slot_index++)
                    {
                        // Reset draw order list
                        instance->m_DrawOrder[slot_index] = slot_index;

                        // Set mesh slot pose to correct MeshSlot pointer
                        MeshSlotPose* mesh_slot_pose = &instance->m_MeshSlotPose[slot_index];
                        mesh_slot_pose->m_MeshSlot = &instance->m_MeshEntry->m_MeshSlots[slot_index];
                    }
                    instance->m_DoRender = 1;
                }

                ResetMeshSlotPose(instance);
                return dmRig::RESULT_OK;
            }
        }
        return dmRig::RESULT_ERROR;
    }

    Result SetMeshSlot(HRigInstance instance, dmhash_t mesh_id, dmhash_t slot_id)
    {
        const dmRigDDF::MeshSet* mesh_set = instance->m_MeshSet;
        const dmRigDDF::MeshEntry* mesh_entry = instance->m_MeshEntry;

        // Find slot
        for (uint32_t i = 0; i < mesh_entry->m_MeshSlots.m_Count; ++i)
        {
            if (slot_id == mesh_entry->m_MeshSlots[i].m_Id)
            {
                // Find skin
                for (uint32_t j = 0; j < mesh_set->m_MeshEntries.m_Count; ++j)
                {
                    if (mesh_id == mesh_set->m_MeshEntries[j].m_Id)
                    {
                        const dmRigDDF::MeshSlot* mesh_slot = &mesh_set->m_MeshEntries[j].m_MeshSlots[i];
                        MeshSlotPose* mesh_slot_pose = &instance->m_MeshSlotPose[i];

                        // Update mesh pose
                        mesh_slot_pose->m_ActiveAttachment = mesh_slot->m_ActiveIndex;
                        mesh_slot_pose->m_MeshSlot = mesh_slot;

                        // Get color for slot
                        const float* slot_color = mesh_slot->m_SlotColor.m_Count ? mesh_slot->m_SlotColor.m_Data : white;

                        mesh_slot_pose->m_SlotColor[0] = slot_color[0];
                        mesh_slot_pose->m_SlotColor[1] = slot_color[1];
                        mesh_slot_pose->m_SlotColor[2] = slot_color[2];
                        mesh_slot_pose->m_SlotColor[3] = slot_color[3];

                        return dmRig::RESULT_OK;
                    }
                }

                return dmRig::RESULT_ERROR;
            }
        }

        return dmRig::RESULT_ERROR;
    }

    static void UpdateBlend(RigInstance* instance, float dt)
    {
        if (instance->m_Blending)
        {
            instance->m_BlendTimer += dt;
            if (instance->m_BlendTimer >= instance->m_BlendDuration)
            {
                instance->m_Blending = 0;
                RigPlayer* secondary = GetSecondaryPlayer(instance);
                secondary->m_Playing = 0;
            }
        }
    }

    static float GetCursorDuration(RigPlayer* player, const dmRigDDF::RigAnimation* animation)
    {
        if (!animation)
        {
            return 0.0f;
        }

        float duration = animation->m_Duration;
        if (player->m_Playback == dmRig::PLAYBACK_ONCE_PINGPONG)
        {
            duration *= 2.0f;
        }
        return duration;
    }

    static void PostEventsInterval(HRigInstance instance, const dmRigDDF::RigAnimation* animation, float start_cursor, float end_cursor, float duration, bool backwards, float blend_weight)
    {
        const uint32_t track_count = animation->m_EventTracks.m_Count;
        for (uint32_t ti = 0; ti < track_count; ++ti)
        {
            const dmRigDDF::EventTrack* track = &animation->m_EventTracks[ti];
            const uint32_t key_count = track->m_Keys.m_Count;
            for (uint32_t ki = 0; ki < key_count; ++ki)
            {
                const dmRigDDF::EventKey* key = &track->m_Keys[ki];
                float cursor = key->m_T;
                if (backwards)
                    cursor = duration - cursor;
                if (start_cursor <= cursor && cursor < end_cursor)
                {
                    RigKeyframeEventData event_data;
                    event_data.m_EventId = track->m_EventId;
                    event_data.m_AnimationId = animation->m_Id;
                    event_data.m_BlendWeight = blend_weight;
                    event_data.m_T = key->m_T;
                    event_data.m_Integer = key->m_Integer;
                    event_data.m_Float = key->m_Float;
                    event_data.m_String = key->m_String;

                    instance->m_EventCallback(RIG_EVENT_TYPE_KEYFRAME, (void*)&event_data, instance->m_EventCBUserData1, instance->m_EventCBUserData2);
                }
            }
        }
    }

    static void PostEvents(HRigInstance instance, RigPlayer* player, const dmRigDDF::RigAnimation* animation, float dt, float prev_cursor, float duration, bool completed, float blend_weight)
    {
        float cursor = player->m_Cursor;
        // Since the intervals are defined as t0 <= t < t1, make sure we include the end of the animation, i.e. when t1 == duration
        if (completed)
            cursor += dt;
        // If the start cursor is greater than the end cursor, we have looped and handle that as two distinct intervals: [0,end_cursor) and [start_cursor,duration)
        // Note that for looping ping pong, one event can be triggered twice during the same frame by appearing in both intervals
        if (prev_cursor > cursor)
        {
            bool prev_backwards = player->m_Backwards;
            // Handle the flipping nature of ping pong
            if (player->m_Playback == dmRig::PLAYBACK_LOOP_PINGPONG)
            {
                prev_backwards = !player->m_Backwards;
            }
            PostEventsInterval(instance, animation, prev_cursor, duration, duration, prev_backwards, blend_weight);
            PostEventsInterval(instance, animation, 0.0f, cursor, duration, player->m_Backwards, blend_weight);
        }
        else
        {
            // Special handling when we reach the way back of once ping pong playback
            float half_duration = duration * 0.5f;
            if (player->m_Playback == dmRig::PLAYBACK_ONCE_PINGPONG && cursor > half_duration)
            {
                // If the previous cursor was still in the forward direction, treat it as two distinct intervals: [start_cursor,half_duration) and [half_duration, end_cursor)
                if (prev_cursor < half_duration)
                {
                    PostEventsInterval(instance, animation, prev_cursor, half_duration, duration, false, blend_weight);
                    PostEventsInterval(instance, animation, half_duration, cursor, duration, true, blend_weight);
                }
                else
                {
                    PostEventsInterval(instance, animation, prev_cursor, cursor, duration, true, blend_weight);
                }
            }
            else
            {
                PostEventsInterval(instance, animation, prev_cursor, cursor, duration, player->m_Backwards, blend_weight);
            }
        }
    }

    static void UpdatePlayer(RigInstance* instance, RigPlayer* player, float dt, float blend_weight)
    {
        const dmRigDDF::RigAnimation* animation = player->m_Animation;
        if (animation == 0x0 || !player->m_Playing)
        {
            return;
        }

        // Advance cursor
        float prev_cursor = player->m_Cursor;
        if (player->m_Playback != dmRig::PLAYBACK_NONE)
        {
            player->m_Cursor += dt * player->m_PlaybackRate;
        }
        float duration = GetCursorDuration(player, animation);
        if (duration == 0.0f)
        {
            player->m_Cursor = 0.0f;
        }

        // Adjust cursor
        bool completed = false;
        switch (player->m_Playback)
        {
        case dmRig::PLAYBACK_ONCE_FORWARD:
        case dmRig::PLAYBACK_ONCE_BACKWARD:
        case dmRig::PLAYBACK_ONCE_PINGPONG:
            if (player->m_Cursor >= duration)
            {
                player->m_Cursor = duration;
                completed = true;
            }
            break;
        case dmRig::PLAYBACK_LOOP_FORWARD:
        case dmRig::PLAYBACK_LOOP_BACKWARD:
            while (player->m_Cursor >= duration && duration > 0.0f)
            {
                player->m_Cursor -= duration;
            }
            break;
        case dmRig::PLAYBACK_LOOP_PINGPONG:
            while (player->m_Cursor >= duration && duration > 0.0f)
            {
                player->m_Cursor -= duration;
                player->m_Backwards = ~player->m_Backwards;
            }
            break;
        default:
            break;
        }

        if (prev_cursor != player->m_Cursor && instance->m_EventCallback)
        {
            PostEvents(instance, player, animation, dt, prev_cursor, duration, completed, blend_weight);
        }

        if (completed)
        {
            player->m_Playing = 0;
            // Only report completeness for the primary player
            if (player == GetPlayer(instance) && instance->m_EventCallback)
            {
                RigCompletedEventData event_data;
                event_data.m_AnimationId = player->m_AnimationId;
                event_data.m_Playback = player->m_Playback;

                instance->m_EventCallback(RIG_EVENT_TYPE_COMPLETED, (void*)&event_data, instance->m_EventCBUserData1, instance->m_EventCBUserData2);
            }
        }

    }

    static Vector3 SampleVec3(uint32_t sample, float frac, float* data)
    {
        uint32_t i0 = sample*3;
        uint32_t i1 = i0+3;
        return lerp(frac, Vector3(data[i0+0], data[i0+1], data[i0+2]), Vector3(data[i1+0], data[i1+1], data[i1+2]));
    }

    static Vector4 SampleVec4(uint32_t sample, float frac, float* data)
    {
        uint32_t i0 = sample*4;
        uint32_t i1 = i0+4;
        return lerp(frac, Vector4(data[i0+0], data[i0+1], data[i0+2], data[i0+3]), Vector4(data[i1+0], data[i1+1], data[i1+2], data[i1+3]));
    }

    static Quat SampleQuat(uint32_t sample, float frac, float* data)
    {
        uint32_t i = sample*4;
        return slerp(frac, Quat(data[i+0], data[i+1], data[i+2], data[i+3]), Quat(data[i+0+4], data[i+1+4], data[i+2+4], data[i+3+4]));
    }

    static float CursorToTime(float cursor, float duration, bool backwards, bool once_pingpong)
    {
        float t = cursor;
        if (backwards)
            t = duration - t;
        if (once_pingpong && t > duration * 0.5f)
        {
            t = duration - t;
        }
        return t;
    }

    static inline dmTransform::Transform GetPoseTransform(const dmArray<RigBone>& bind_pose, const dmArray<dmTransform::Transform>& pose, dmTransform::Transform transform, const uint32_t index) {
        if(bind_pose[index].m_ParentIndex == INVALID_BONE_INDEX)
            return transform;
        transform = dmTransform::Mul(pose[bind_pose[index].m_ParentIndex], transform);
        return GetPoseTransform(bind_pose, pose, transform, bind_pose[index].m_ParentIndex);
    }

    static inline float ToEulerZ(const dmTransform::Transform& t)
    {
        Vectormath::Aos::Quat q(t.GetRotation());
        return dmVMath::QuatToEuler(q.getZ(), q.getY(), q.getX(), q.getW()).getZ() * (M_PI/180.0f);
    }

    static void ApplyOneBoneIKConstraint(const dmRigDDF::IK* ik, const dmArray<RigBone>& bind_pose, dmArray<dmTransform::Transform>& pose, const Vector3 target_wp, const Vector3 parent_wp, const float mix)
    {
        if (mix == 0.0f)
            return;
        const dmTransform::Transform& parent_bt = bind_pose[ik->m_Parent].m_LocalToParent;
        dmTransform::Transform& parent_t = pose[ik->m_Parent];
        float parentRotation = ToEulerZ(parent_bt);
        // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
        float rotationIK = atan2(target_wp.getY() - parent_wp.getY(), target_wp.getX() - parent_wp.getX());
        parentRotation = parentRotation + (rotationIK - parentRotation) * mix;
        parent_t.SetRotation( dmVMath::QuatFromAngle(2, parentRotation) );
    }

    // Based on http://www.ryanjuckett.com/programming/analytic-two-bone-ik-in-2d/
    static void ApplyTwoBoneIKConstraint(const dmRigDDF::IK* ik, const dmArray<RigBone>& bind_pose, dmArray<dmTransform::Transform>& pose, const Vector3 target_wp, const Vector3 parent_wp, const bool bend_positive, const float mix)
    {
        if (mix == 0.0f)
            return;
        const dmTransform::Transform& parent_bt = bind_pose[ik->m_Parent].m_LocalToParent;
        const dmTransform::Transform& child_bt = bind_pose[ik->m_Child].m_LocalToParent;
        dmTransform::Transform& parent_t = pose[ik->m_Parent];
        dmTransform::Transform& child_t = pose[ik->m_Child];
        float childRotation = ToEulerZ(child_bt), parentRotation = ToEulerZ(parent_bt);

        // recalc target position to local (relative parent)
        const Vector3 target(target_wp.getX() - parent_wp.getX(), target_wp.getY() - parent_wp.getY(), 0.0f);
        const Vector3 child_p = child_bt.GetTranslation();
        const float childX = child_p.getX(), childY = child_p.getY();
        const float offset = atan2(childY, childX);
        const float len1 = (float)sqrt(childX * childX + childY * childY);
        const float len2 = bind_pose[ik->m_Child].m_Length;

        // Based on code by Ryan Juckett with permission: Copyright (c) 2008-2009 Ryan Juckett, http://www.ryanjuckett.com/
        const float cosDenom = 2.0f * len1 * len2;
        if (cosDenom < 0.0001f) {
            childRotation = childRotation + ((float)atan2(target.getY(), target.getX()) - parentRotation - childRotation) * mix;
            child_t.SetRotation( dmVMath::QuatFromAngle(2, childRotation) );
            return;
        }
        float cosValue = (target.getX() * target.getX() + target.getY() * target.getY() - len1 * len1 - len2 * len2) / cosDenom;
        cosValue = dmMath::Max(-1.0f, dmMath::Min(1.0f, cosValue));
        const float childAngle = (float)acos(cosValue) * (bend_positive ? 1.0f : -1.0f);
        const float adjacent = len1 + len2 * cosValue;
        const float opposite = len2 * sin(childAngle);
        const float parentAngle = (float)atan2(target.getY() * adjacent - target.getX() * opposite, target.getX() * adjacent + target.getY() * opposite);
        parentRotation = ((parentAngle - offset) - parentRotation) * mix;
        childRotation = ((childAngle + offset) - childRotation) * mix;
        parent_t.SetRotation( dmVMath::QuatFromAngle(2, parentRotation) );
        child_t.SetRotation( dmVMath::QuatFromAngle(2, childRotation) );
    }

    static void ApplyAnimation(RigPlayer* player, dmArray<dmTransform::Transform>& pose, const dmArray<uint32_t>& track_idx_to_pose, dmArray<IKAnimation>& ik_animation, dmArray<MeshSlotPose>& mesh_slot_pose, bool update_draw_order, dmArray<int32_t>& draw_order, int& slot_changed, float blend_weight)
    {
        const dmRigDDF::RigAnimation* animation = player->m_Animation;
        if (animation == 0x0)
            return;
        float duration = GetCursorDuration(player, animation);
        float t = CursorToTime(player->m_Cursor, duration, player->m_Backwards, player->m_Playback == dmRig::PLAYBACK_ONCE_PINGPONG);

        float fraction = t * animation->m_SampleRate;
        uint32_t sample = (uint32_t)fraction;
        uint32_t rounded_sample = (uint32_t)(fraction + 0.5f);
        fraction -= sample;
        // Sample animation tracks
        uint32_t track_count = animation->m_Tracks.m_Count;
        for (uint32_t ti = 0; ti < track_count; ++ti)
        {
            const dmRigDDF::AnimationTrack* track = &animation->m_Tracks[ti];
            uint32_t bone_index = track->m_BoneIndex;
            if (bone_index >= track_idx_to_pose.Size()) {
                continue;
            }
            uint32_t pose_index = track_idx_to_pose[bone_index];
            dmTransform::Transform& transform = pose[pose_index];
            if (track->m_Positions.m_Count > 0)
            {
                transform.SetTranslation(lerp(blend_weight, transform.GetTranslation(), SampleVec3(sample, fraction, track->m_Positions.m_Data)));
            }
            if (track->m_Rotations.m_Count > 0)
            {
                transform.SetRotation(slerp(blend_weight, transform.GetRotation(), SampleQuat(sample, fraction, track->m_Rotations.m_Data)));
            }
            if (track->m_Scale.m_Count > 0)
            {
                transform.SetScale(lerp(blend_weight, transform.GetScale(), SampleVec3(sample, fraction, track->m_Scale.m_Data)));
            }
        }

        track_count = animation->m_IkTracks.m_Count;
        for (uint32_t ti = 0; ti < track_count; ++ti)
        {
            const dmRigDDF::IKAnimationTrack* track = &animation->m_IkTracks[ti];
            uint32_t ik_index = track->m_IkIndex;
            IKAnimation& anim = ik_animation[ik_index];
            if (track->m_Mix.m_Count > 0)
            {
                anim.m_Mix = dmMath::LinearBezier(blend_weight, anim.m_Mix, dmMath::LinearBezier(fraction, track->m_Mix.m_Data[sample], track->m_Mix.m_Data[sample+1]));
            }
            if (track->m_Positive.m_Count > 0)
            {
                if (blend_weight >= 0.5f)
                {
                    anim.m_Positive = track->m_Positive[sample];
                }
            }
        }

        track_count = animation->m_MeshTracks.m_Count;
        for (uint32_t ti = 0; ti < track_count; ++ti) {
            const dmRigDDF::MeshAnimationTrack* track = &animation->m_MeshTracks[ti];

            if (track->m_SlotColors.m_Count > 0) {
                MeshSlotPose& mesh_slot = mesh_slot_pose[track->m_MeshSlot];
                Vector4 color(mesh_slot.m_SlotColor[0], mesh_slot.m_SlotColor[1], mesh_slot.m_SlotColor[2], mesh_slot.m_SlotColor[3]);
                color = lerp(blend_weight, color, SampleVec4(sample, fraction, track->m_SlotColors.m_Data));
                mesh_slot.m_SlotColor[0] = color[0];
                mesh_slot.m_SlotColor[1] = color[1];
                mesh_slot.m_SlotColor[2] = color[2];
                mesh_slot.m_SlotColor[3] = color[3];
            }

            if (track->m_MeshAttachment.m_Count > 0) {
                if (update_draw_order) {
                    MeshSlotPose& mesh_slot = mesh_slot_pose[track->m_MeshSlot];
                    mesh_slot.m_ActiveAttachment = track->m_MeshAttachment[rounded_sample];
                }
            }

            if (track->m_OrderOffset.m_Count > 0) {
                if (update_draw_order) {
                    int32_t* order = &draw_order[track->m_MeshSlot];
                    *order = track->m_OrderOffset[rounded_sample];
                    slot_changed++;
                }
            }
        }
    }

    static void Animate(HRigContext context, float dt)
    {
        DM_PROFILE(Rig, "Animate");

        const dmArray<RigInstance*>& instances = context->m_Instances.m_Objects;
        uint32_t n = instances.Size();
        for (uint32_t i = 0; i < n; ++i)
        {
            RigInstance* instance = instances[i];
            DoAnimate(context, instance, dt);
        }
    }

    static void DoAnimate(HRigContext context, RigInstance* instance, float dt)
    {
            // NOTE we previously checked for (!instance->m_Enabled || !instance->m_AddedToUpdate) here also
            if (instance->m_Pose.Empty() || !instance->m_Enabled)
                return;

            const dmRigDDF::Skeleton* skeleton = instance->m_Skeleton;
            const dmArray<RigBone>& bind_pose = *instance->m_BindPose;
            const dmArray<uint32_t>& track_idx_to_pose = *instance->m_TrackIdxToPose;
            dmArray<dmTransform::Transform>& pose = instance->m_Pose;
            // Reset pose
            uint32_t bone_count = pose.Size();
            for (uint32_t bi = 0; bi < bone_count; ++bi)
            {
                pose[bi].SetIdentity();
            }
            // Reset IK animation
            dmArray<IKAnimation>& ik_animation = instance->m_IKAnimation;
            uint32_t ik_animation_count = ik_animation.Size();
            for (uint32_t ii = 0; ii < ik_animation_count; ++ii)
            {
                const dmRigDDF::IK* ik = &skeleton->m_Iks[ii];
                ik_animation[ii].m_Mix = ik->m_Mix;
                ik_animation[ii].m_Positive = ik->m_Positive;
            }

            UpdateBlend(instance, dt);

            RigPlayer* player = GetPlayer(instance);

            // If the animation has just started, we reset mesh properties (color, draw order etc)
            if (player->m_Initial) {
                ResetMeshSlotPose(instance);
                player->m_Initial = 0;
            }

            // Make sure we have enough space in the draw order deltas scratch buffer.
            uint32_t slot_count = instance->m_MeshSet->m_SlotCount;
            int slot_changed = 0;
            if (context->m_ScratchDrawOrderDeltas.Capacity() < slot_count) {
                context->m_ScratchDrawOrderDeltas.OffsetCapacity(slot_count - context->m_ScratchDrawOrderDeltas.Capacity());
            }
            context->m_ScratchDrawOrderDeltas.SetSize(slot_count);

            // Reset draw order deltas to "unchanged" constant.
            for (uint32_t i = 0; i < slot_count; i++) {
                instance->m_DrawOrder[i] = i;
                context->m_ScratchDrawOrderDeltas[i] = SIGNAL_DELTA_UNCHANGED;
            }

            if (instance->m_Blending)
            {
                float fade_rate = instance->m_BlendTimer / instance->m_BlendDuration;
                // How much to blend the pose, 1 first time to overwrite the bind pose, either fade_rate or 1 - fade_rate second depending on which one is the current player
                float alpha = 1.0f;
                for (uint32_t pi = 0; pi < 2; ++pi)
                {
                    RigPlayer* p = &instance->m_Players[pi];
                    // How much relative blending between the two players
                    float blend_weight = fade_rate;
                    if (player != p) {
                        blend_weight = 1.0f - fade_rate;
                    }

                    // Check if we should reset the mesh slot pose.
                    // This needs to be done once we are past 0.5 in blending, if the new player/animation
                    // don't have a mesh animation track it would otherwise be the same from previous animation.
                    if (p->m_BlendFinished == 0 && blend_weight > 0.5) {
                        p->m_BlendFinished = 1;
                        ResetMeshSlotPose(instance);
                    }

                    UpdatePlayer(instance, p, dt, blend_weight);
                    bool draw_order = player == p ? fade_rate >= 0.5f : fade_rate < 0.5f;
                    ApplyAnimation(p, pose, track_idx_to_pose, ik_animation, instance->m_MeshSlotPose, draw_order, context->m_ScratchDrawOrderDeltas, slot_changed, alpha);
                    if (player == p)
                    {
                        alpha = 1.0f - fade_rate;
                    }
                    else
                    {
                        alpha = fade_rate;
                    }
                }
            }
            else
            {
                UpdatePlayer(instance, player, dt, 1.0f);
                ApplyAnimation(player, pose, track_idx_to_pose, ik_animation, instance->m_MeshSlotPose, true, context->m_ScratchDrawOrderDeltas, slot_changed, 1.0f);
            }

            // Update draw order after animation
            if (slot_changed > 0) {
                UpdateSlotDrawOrder(instance->m_DrawOrder, context->m_ScratchDrawOrderDeltas, slot_changed, context->m_ScratchDrawOrderUnchanged);
            }

            for (uint32_t bi = 0; bi < bone_count; ++bi)
            {
                dmTransform::Transform& t = pose[bi];
                // Normalize quaternions while we blend
                if (instance->m_Blending)
                {
                    Quat rotation = t.GetRotation();
                    if (dot(rotation, rotation) > 0.001f)
                        rotation = normalize(rotation);
                    t.SetRotation(rotation);
                }
                const dmTransform::Transform& bind_t = bind_pose[bi].m_LocalToParent;
                t.SetTranslation(bind_t.GetTranslation() + t.GetTranslation());
                t.SetRotation(bind_t.GetRotation() * t.GetRotation());
                t.SetScale(mulPerElem(bind_t.GetScale(), t.GetScale()));
            }

            if (skeleton->m_Iks.m_Count > 0) {
                DM_PROFILE(Rig, "IK");
                const uint32_t count = skeleton->m_Iks.m_Count;
                dmArray<IKTarget>& ik_targets = instance->m_IKTargets;

                for (uint32_t i = 0; i < count; ++i) {
                    const dmRigDDF::IK* ik = &skeleton->m_Iks[i];

                    // transform local space hiearchy for pose
                    dmTransform::Transform parent_t = GetPoseTransform(bind_pose, pose, pose[ik->m_Parent], ik->m_Parent);
                    dmTransform::Transform target_t = GetPoseTransform(bind_pose, pose, pose[ik->m_Target], ik->m_Target);
                    const uint32_t parent_parent_index = skeleton->m_Bones[ik->m_Parent].m_Parent;
                    dmTransform::Transform parent_parent_t;
                    if(parent_parent_index != INVALID_BONE_INDEX)
                    {
                        parent_parent_t = dmTransform::Inv(GetPoseTransform(bind_pose, pose, pose[skeleton->m_Bones[ik->m_Parent].m_Parent], skeleton->m_Bones[ik->m_Parent].m_Parent));
                        parent_t = dmTransform::Mul(parent_parent_t, parent_t);
                        target_t = dmTransform::Mul(parent_parent_t, target_t);
                    }
                    Vector3 parent_position = parent_t.GetTranslation();
                    Vector3 target_position = target_t.GetTranslation();

                    if(ik_targets[i].m_Mix != 0.0f)
                    {
                        // get custom target position either from go or vector position
                        Vector3 user_target_position = target_position;
                        if(ik_targets[i].m_Callback != 0)
                        {
                            user_target_position = ik_targets[i].m_Callback(&ik_targets[i]);
                        } else {
                            // instance have been removed, disable animation
                            ik_targets[i].m_UserHash = 0;
                            ik_targets[i].m_Mix = 0.0f;
                        }

                        const float target_mix = ik_targets[i].m_Mix;

                        if (parent_parent_index != INVALID_BONE_INDEX) {
                            user_target_position = dmTransform::Apply(parent_parent_t, user_target_position);
                        }

                        // blend default target pose and target pose
                        target_position = target_mix == 1.0f ? user_target_position : dmTransform::lerp(target_mix, target_position, user_target_position);
                    }

                    if(ik->m_Child == ik->m_Parent)
                        ApplyOneBoneIKConstraint(ik, bind_pose, pose, target_position, parent_position, ik_animation[i].m_Mix);
                    else
                        ApplyTwoBoneIKConstraint(ik, bind_pose, pose, target_position, parent_position, ik_animation[i].m_Positive, ik_animation[i].m_Mix);
                }
            }
    }

    static Result PostUpdate(HRigContext context)
    {
        const dmArray<RigInstance*>& instances = context->m_Instances.m_Objects;
        uint32_t count = instances.Size();
        bool updated_pose = false;
        for (uint32_t i = 0; i < count; ++i)
        {
            RigInstance* instance = instances[i];
            if (DoPostUpdate(instance)) {
                updated_pose = true;
            }
        }

        return updated_pose ? dmRig::RESULT_UPDATED_POSE : dmRig::RESULT_OK;
    }

    static bool DoPostUpdate(RigInstance* instance)
    {
            // If pose is empty, there are no bones to update
            dmArray<dmTransform::Transform>& pose = instance->m_Pose;
            if (pose.Empty())
                return false;

            // Notify any listener that the pose has been recalculated
            if (instance->m_PoseCallback) {
                instance->m_PoseCallback(instance->m_PoseCBUserData1, instance->m_PoseCBUserData2);
                return true;
            }

        return false;
    }

    Result Update(HRigContext context, float dt)
    {
        DM_PROFILE(Rig, "Update");

        Animate(context, dt);

        return PostUpdate(context);
    }

    static void AllocateMeshSlotPose(const dmRig::MeshSet* mesh_set, dmArray<MeshSlotPose>& mesh_slot_pose, dmArray<int32_t>& draw_order) {
        mesh_slot_pose.SetCapacity(mesh_set->m_SlotCount);
        mesh_slot_pose.SetSize(mesh_set->m_SlotCount);
        draw_order.SetCapacity(mesh_set->m_SlotCount);
        draw_order.SetSize(mesh_set->m_SlotCount);
    }

    static dmRig::Result CreatePose(HRigContext context, HRigInstance instance)
    {
        if(!instance->m_Skeleton)
            return dmRig::RESULT_OK;

        const dmRigDDF::Skeleton* skeleton = instance->m_Skeleton;
        uint32_t bone_count = skeleton->m_Bones.m_Count;
        instance->m_Pose.SetCapacity(bone_count);
        instance->m_Pose.SetSize(bone_count);
        for (uint32_t i = 0; i < bone_count; ++i)
        {
            instance->m_Pose[i].SetIdentity();
        }

        instance->m_IKTargets.SetCapacity(skeleton->m_Iks.m_Count);
        instance->m_IKTargets.SetSize(skeleton->m_Iks.m_Count);
        memset(instance->m_IKTargets.Begin(), 0x0, instance->m_IKTargets.Size()*sizeof(IKTarget));

        instance->m_IKAnimation.SetCapacity(skeleton->m_Iks.m_Count);
        instance->m_IKAnimation.SetSize(skeleton->m_Iks.m_Count);

        return dmRig::RESULT_OK;
    }

    dmArray<dmTransform::Transform>* GetPose(HRigInstance instance)
    {
        return &instance->m_Pose;
    }

    float GetCursor(HRigInstance instance, bool normalized)
    {
        RigPlayer* player = GetPlayer(instance);

        if (!player || !player->m_Animation)
        {
            return 0.0f;
        }

        float duration = player->m_Animation->m_Duration;
        if (duration == 0.0f)
        {
            return 0.0f;
        }

        float t = player->m_Cursor;
        if (player->m_Playback == dmRig::PLAYBACK_ONCE_PINGPONG && t > duration)
        {
            // In once-pingpong the cursor will be greater than duration during the "pong" part, compensate for that
            t = (2.f * duration) - t;
        }

        if (player->m_Backwards)
        {
            t = duration - t;
        }

        if (normalized)
        {
            t = t / duration;
        }
        return t;
    }

    Result SetCursor(HRigInstance instance, float cursor, bool normalized)
    {
        float t = cursor;
        RigPlayer* player = GetPlayer(instance);

        if (!player)
        {
            return dmRig::RESULT_ERROR;
        }

        if (!player->m_Animation)
        {
            return RESULT_OK;
        }

        float duration = player->m_Animation->m_Duration;
        if (normalized)
        {
            t = t * duration;
        }

        if (player->m_Playback == dmRig::PLAYBACK_LOOP_PINGPONG && player->m_Backwards)
        {
            // NEVER set cursor on the "looped" part of a pingpong animation
            player->m_Backwards = 0;
        }

        if (fabs(t) > duration)
        {
            t = fmod(t, duration);
            if (fabs(t) < CURSOR_EPSILON)
            {
                t = duration;
            }
        }

        if (t < 0.0f)
        {
            t = duration - fmod(fabs(t), duration);
        }

        if (player->m_Backwards)
        {
            t = duration - t;
        }

        player->m_Cursor = t;

        return dmRig::RESULT_OK;
    }

    float GetPlaybackRate(HRigInstance instance)
    {
        RigPlayer* player = GetPlayer(instance);

        if (!player || !player->m_Animation)
        {
            return 1.0f;
        }

        return player->m_PlaybackRate;
    }

    Result SetPlaybackRate(HRigInstance instance, float playback_rate)
    {
        RigPlayer* player = GetPlayer(instance);

        if (!player)
        {
            return dmRig::RESULT_ERROR;
        }

        player->m_PlaybackRate = dmMath::Max(playback_rate, 0.0f);

        return dmRig::RESULT_OK;
    }

    // Slot ordering is based on the official Spine runtime
    // https://github.com/EsotericSoftware/spine-runtimes/blob/387b0afb80a775970c48099042be769e50258440/spine-c/spine-c/src/spine/SkeletonJson.c#L430
    static void UpdateSlotDrawOrder(dmArray<int32_t>& draw_order, dmArray<int32_t>& deltas, int changed, dmArray<int32_t>& unchanged)
    {
        int slot_count = draw_order.Size();

        // Make sure we have enough capacity to store our unchanged slots list.
        if (unchanged.Capacity() < (uint32_t)slot_count) {
            unchanged.OffsetCapacity(slot_count - unchanged.Capacity());
        }
        unchanged.SetSize(slot_count);

        for (int i = 0; i < slot_count; i++) {
            draw_order[i] = -1;
        }

        int original_index = 0;
        int unchanged_index = 0;
        for (int slot_index = 0; slot_index < slot_count; slot_index++)
        {
            int32_t delta = deltas[slot_index];
            if (delta != SIGNAL_DELTA_UNCHANGED) {
                while (original_index != slot_index) {
                    unchanged[unchanged_index++] = original_index++;
                }

                draw_order[original_index + delta] = original_index;
                original_index++;
            }
        }

        while (original_index < slot_count) {
            unchanged[unchanged_index++] = original_index++;
        }

        for (int i = slot_count - 1; i >= 0; i--)
        {
            if (draw_order[i] == -1) {
                draw_order[i] = unchanged[--unchanged_index];
            }
        }

    }

    uint32_t GetVertexCount(HRigInstance instance)
    {
        if (!instance->m_MeshEntry || !instance->m_DoRender) {
            return 0;
        }

        uint32_t vertex_count = 0;
        int32_t slot_count = instance->m_MeshSet->m_SlotCount;
        for (int32_t slot_index = 0; slot_index < slot_count; slot_index++)
        {
            MeshSlotPose* mesh_slot_pose = &instance->m_MeshSlotPose[slot_index];

            // Get attachment index for current slot
            uint32_t active_attachment = mesh_slot_pose->m_ActiveAttachment;
            if (active_attachment != INVALID_ATTACHMENT_INDEX) {

                // Check if there is any mesh on current attachment
                uint32_t mesh_attachment_index = mesh_slot_pose->m_MeshSlot->m_MeshAttachments[active_attachment];
                if (mesh_attachment_index != INVALID_ATTACHMENT_INDEX) {
                    const Mesh* mesh_attachment = &instance->m_MeshSet->m_MeshAttachments[mesh_attachment_index];
                    vertex_count += mesh_attachment->m_PositionIndices.m_Count;
                }
            }
        }

        return vertex_count;
    }

    static float* GenerateNormalData(const dmRigDDF::Mesh* mesh, const Matrix4& normal_matrix, const dmArray<Matrix4>& pose_matrices, float* out_buffer)
    {
        const float* normals_in = mesh->m_Normals.m_Data;
        const uint32_t* normal_indices = mesh->m_NormalsIndices.m_Data;
        uint32_t index_count = mesh->m_PositionIndices.m_Count;
        Vector4 v;

        if (!mesh->m_BoneIndices.m_Count || pose_matrices.Size() == 0)
        {
            for (uint32_t ii = 0; ii < index_count; ++ii)
            {
                uint32_t ni = normal_indices[ii];
                Vector3 normal_in(normals_in[ni*3+0], normals_in[ni*3+1], normals_in[ni*3+2]);
                v = normal_matrix * normal_in;
                if (lengthSqr(v) > 0.0f) {
                    normalize(v);
                }
                *out_buffer++ = v[0];
                *out_buffer++ = v[1];
                *out_buffer++ = v[2];
            }
            return out_buffer;
        }

        const uint32_t* indices = mesh->m_BoneIndices.m_Data;
        const float* weights = mesh->m_Weights.m_Data;
        const uint32_t* vertex_indices = mesh->m_PositionIndices.m_Data;
        for (uint32_t ii = 0; ii < index_count; ++ii)
        {
            const uint32_t ni = normal_indices[ii]*3;
            const Vector3 normal_in(normals_in[ni+0], normals_in[ni+1], normals_in[ni+2]);
            Vector4 normal_out(0.0f, 0.0f, 0.0f, 0.0f);

            const uint32_t bi_offset = vertex_indices[ii] << 2;
            const uint32_t* bone_indices = &indices[bi_offset];
            const float* bone_weights = &weights[bi_offset];

            if (bone_weights[0])
            {
                normal_out += (pose_matrices[bone_indices[0]] * normal_in) * bone_weights[0];
                if (bone_weights[1])
                {
                    normal_out += (pose_matrices[bone_indices[1]] * normal_in) * bone_weights[1];
                    if (bone_weights[2])
                    {
                        normal_out += (pose_matrices[bone_indices[2]] * normal_in) * bone_weights[2];
                        if (bone_weights[3])
                        {
                            normal_out += (pose_matrices[bone_indices[3]] * normal_in) * bone_weights[3];
                        }
                    }
                }
            }

            v = normal_matrix * Vector3(normal_out.getX(), normal_out.getY(), normal_out.getZ());
            if (lengthSqr(v) > 0.0f) {
                normalize(v);
            }
            *out_buffer++ = v[0];
            *out_buffer++ = v[1];
            *out_buffer++ = v[2];
        }

        return out_buffer;
    }

    static float* GeneratePositionData(const dmRigDDF::Mesh* mesh, const Matrix4& model_matrix, const dmArray<Matrix4>& pose_matrices, float* out_buffer)
    {
        const float *positions = mesh->m_Positions.m_Data;
        const size_t vertex_count = mesh->m_Positions.m_Count / 3;
        Point3 in_p;
        Vector4 v;
        if(!mesh->m_BoneIndices.m_Count || pose_matrices.Size() == 0)
        {
            for (uint32_t i = 0; i < vertex_count; ++i)
            {
                in_p[0] = *positions++;
                in_p[1] = *positions++;
                in_p[2] = *positions++;
                v = model_matrix * in_p;
                *out_buffer++ = v[0];
                *out_buffer++ = v[1];
                *out_buffer++ = v[2];
            }
            return out_buffer;
        }

        const uint32_t* indices = mesh->m_BoneIndices.m_Data;
        const float* weights = mesh->m_Weights.m_Data;
        for (uint32_t i = 0; i < vertex_count; ++i)
        {
            Vector4 in_v;
            in_v.setX(*positions++);
            in_v.setY(*positions++);
            in_v.setZ(*positions++);
            in_v.setW(1.0f);

            Vector4 out_p(0.0f, 0.0f, 0.0f, 0.0f);
            const uint32_t bi_offset = i << 2;
            const uint32_t* bone_indices = &indices[bi_offset];
            const float* bone_weights = &weights[bi_offset];

            if(bone_weights[0])
            {
                out_p += pose_matrices[bone_indices[0]] * in_v * bone_weights[0];
                if(bone_weights[1])
                {
                    out_p += pose_matrices[bone_indices[1]] * in_v * bone_weights[1];
                    if(bone_weights[2])
                    {
                        out_p += pose_matrices[bone_indices[2]] * in_v * bone_weights[2];
                        if(bone_weights[3])
                        {
                            out_p += pose_matrices[bone_indices[3]] * in_v * bone_weights[3];
                        }
                    }
                }
            }

            v = model_matrix * Point3(out_p.getX(), out_p.getY(), out_p.getZ());
            *out_buffer++ = v[0];
            *out_buffer++ = v[1];
            *out_buffer++ = v[2];
        }
        return out_buffer;
    }

    static void PoseToMatrix(const dmArray<dmTransform::Transform>& pose, dmArray<Matrix4>& out_matrices)
    {
        uint32_t bone_count = pose.Size();
        for (uint32_t bi = 0; bi < bone_count; ++bi)
        {
            out_matrices[bi] = dmTransform::ToMatrix4(pose[bi]);
        }
    }

    static void PoseToModelSpace(const dmRigDDF::Skeleton* skeleton, const dmArray<dmTransform::Transform>& pose, dmArray<dmTransform::Transform>& out_pose)
    {
        const dmRigDDF::Bone* bones = skeleton->m_Bones.m_Data;
        uint32_t bone_count = skeleton->m_Bones.m_Count;
        for (uint32_t bi = 0; bi < bone_count; ++bi)
        {
            const dmTransform::Transform& transform = pose[bi];
            dmTransform::Transform& out_transform = out_pose[bi];
            out_transform = transform;
            if (bi > 0) {
                const dmRigDDF::Bone* bone = &bones[bi];
                if (bone->m_InheritScale)
                {
                    out_transform = dmTransform::Mul(out_pose[bone->m_Parent], transform);
                }
                else
                {
                    Vector3 scale = transform.GetScale();
                    out_transform = dmTransform::Mul(out_pose[bone->m_Parent], transform);
                    out_transform.SetScale(scale);
                }
            }
        }
    }

    static void PoseToModelSpace(const dmRigDDF::Skeleton* skeleton, const dmArray<Matrix4>& pose, dmArray<Matrix4>& out_pose)
    {
        const dmRigDDF::Bone* bones = skeleton->m_Bones.m_Data;
        uint32_t bone_count = skeleton->m_Bones.m_Count;
        for (uint32_t bi = 0; bi < bone_count; ++bi)
        {
            const Matrix4& transform = pose[bi];
            Matrix4& out_transform = out_pose[bi];
            out_transform = transform;
            if (bi > 0) {
                const dmRigDDF::Bone* bone = &bones[bi];
                if (bone->m_InheritScale)
                {
                    out_transform = out_pose[bone->m_Parent] * transform;
                }
                else
                {
                    Vector3 scale = dmTransform::ExtractScale(out_pose[bone->m_Parent]);
                    out_transform.setUpper3x3(Matrix3::scale(Vector3(1.0f/scale.getX(), 1.0f/scale.getY(), 1.0f/scale.getZ())) * transform.getUpper3x3());
                    out_transform = out_pose[bone->m_Parent] * transform;
                }
            }
        }
    }

    static void PoseToInfluence(const dmArray<uint32_t>& pose_idx_to_influence, const dmArray<Matrix4>& in_pose, dmArray<Matrix4>& out_pose)
    {
        for (uint32_t i = 0; i < pose_idx_to_influence.Size(); ++i)
        {
            uint32_t j = pose_idx_to_influence[i];
            out_pose[j] = in_pose[i];
        }
    }

    // NOTE: We have two different vertex data write functions, since we expose two different vertex formats (spine and model).
    // This is a temporary fix until we have better support for custom vertex formats.
    static RigModelVertex* WriteVertexData(const dmRigDDF::Mesh* mesh, const float* positions, const float* normals, RigModelVertex* out_write_ptr)
    {
        uint32_t indices_count = mesh->m_PositionIndices.m_Count;
        const uint32_t* indices = mesh->m_PositionIndices.m_Data;
        const uint32_t* uv0_indices = mesh->m_Texcoord0Indices.m_Count ? mesh->m_Texcoord0Indices.m_Data : mesh->m_PositionIndices.m_Data;
        const float* uv0 = mesh->m_Texcoord0.m_Data;

        if (mesh->m_NormalsIndices.m_Count)
        {
            for (uint32_t i = 0; i < indices_count; ++i)
            {
                uint32_t vi = indices[i];
                uint32_t e = vi * 3;
                out_write_ptr->x = positions[e];
                out_write_ptr->y = positions[++e];
                out_write_ptr->z = positions[++e];
                vi = uv0_indices[i];
                e = vi << 1;
                out_write_ptr->u = uv0[e+0];
                out_write_ptr->v = uv0[e+1];
                e = i * 3;
                out_write_ptr->nx = normals[e];
                out_write_ptr->ny = normals[++e];
                out_write_ptr->nz = normals[++e];
                out_write_ptr++;
            }
        }
        else
        {
            for (uint32_t i = 0; i < indices_count; ++i)
            {
                uint32_t vi = indices[i];
                uint32_t e = vi * 3;
                out_write_ptr->x = positions[e];
                out_write_ptr->y = positions[++e];
                out_write_ptr->z = positions[++e];
                vi = uv0_indices[i];
                e = vi << 1;
                out_write_ptr->u = uv0[e+0];
                out_write_ptr->v = uv0[e+1];
                out_write_ptr->nx = 0.0f;
                out_write_ptr->ny = 0.0f;
                out_write_ptr->nz = 1.0f;
                out_write_ptr++;
            }
        }

        return out_write_ptr;
    }

    static RigSpineModelVertex* WriteVertexData(const dmRigDDF::Mesh* mesh, const float* positions, const Vector4 color, RigSpineModelVertex* out_write_ptr)
    {
        uint32_t indices_count = mesh->m_PositionIndices.m_Count;
        const uint32_t* indices = mesh->m_PositionIndices.m_Data;
        const uint32_t* uv0_indices = mesh->m_Texcoord0Indices.m_Count ? mesh->m_Texcoord0Indices.m_Data : mesh->m_PositionIndices.m_Data;
        const float* uv0 = mesh->m_Texcoord0.m_Data;

        for (uint32_t i = 0; i < indices_count; ++i)
        {
            uint32_t vi = indices[i];
            uint32_t e = vi*3;
            out_write_ptr->x = positions[e+0];
            out_write_ptr->y = positions[e+1];
            out_write_ptr->z = positions[e+2];
            vi = uv0_indices[i];
            e = vi << 1;
            out_write_ptr->u = (uv0[e+0]);
            out_write_ptr->v = (uv0[e+1]);
            out_write_ptr->r = color.getX();
            out_write_ptr->g = color.getY();
            out_write_ptr->b = color.getZ();
            out_write_ptr->a = color.getW();
            out_write_ptr++;
        }
        return out_write_ptr;
    }

    void* GenerateVertexData(dmRig::HRigContext context, dmRig::HRigInstance instance, const Matrix4& model_matrix, const Matrix4& normal_matrix, const Vector4 color, RigVertexFormat vertex_format, void* vertex_data_out)
    {
        const dmRigDDF::MeshEntry* mesh_entry = instance->m_MeshEntry;
        if (!instance->m_MeshEntry || !instance->m_DoRender) {
            return vertex_data_out;
        }

        // Early exit for rigs that has no mesh or only one mesh that is not visible.
        int mesh_slot_count = mesh_entry->m_MeshSlots.m_Count;
        if (mesh_slot_count == 0) {
            return vertex_data_out;

        } else if (mesh_slot_count == 1) {
            uint32_t active_attachment = instance->m_MeshSlotPose[0].m_ActiveAttachment;
            if (active_attachment == INVALID_ATTACHMENT_INDEX || mesh_entry->m_MeshSlots[0].m_MeshAttachments[active_attachment] == INVALID_ATTACHMENT_INDEX) {
                return vertex_data_out;
            }
        }

        dmArray<Matrix4>& pose_matrices      = context->m_ScratchPoseMatrixBuffer;
        dmArray<Matrix4>& influence_matrices = context->m_ScratchInfluenceMatrixBuffer;
        dmArray<Vector3>& positions          = context->m_ScratchPositionBuffer;
        dmArray<Vector3>& normals            = context->m_ScratchNormalBuffer;

        // If the rig has bones, update the pose to be local-to-model
        uint32_t bone_count = GetBoneCount(instance);
        influence_matrices.SetSize(0);
        if (bone_count && instance->m_PoseIdxToInfluence->Size() > 0) {

            // Make sure pose scratch buffers have enough space
            if (pose_matrices.Capacity() < bone_count) {
                uint32_t size_offset = bone_count - pose_matrices.Capacity();
                pose_matrices.OffsetCapacity(size_offset);
            }
            pose_matrices.SetSize(bone_count);

            // Make sure influence scratch buffers have enough space sufficient for max bones to be indexed
            uint32_t max_bone_count = instance->m_MaxBoneCount;
            if (influence_matrices.Capacity() < max_bone_count) {
                uint32_t capacity = influence_matrices.Capacity();
                uint32_t size_offset = max_bone_count - capacity;
                influence_matrices.OffsetCapacity(size_offset);
                influence_matrices.SetSize(max_bone_count);
                for(uint32_t i = capacity; i < capacity+size_offset; ++i)
                    influence_matrices[i] = Matrix4::identity();
            }
            influence_matrices.SetSize(max_bone_count);

            const dmArray<dmTransform::Transform>& pose = instance->m_Pose;
            const dmRigDDF::Skeleton* skeleton = instance->m_Skeleton;
            if (skeleton->m_LocalBoneScaling) {

                dmArray<dmTransform::Transform>& pose_transforms = context->m_ScratchPoseTransformBuffer;
                if (pose_transforms.Capacity() < bone_count) {
                    pose_transforms.OffsetCapacity(bone_count - pose_transforms.Capacity());
                }
                pose_transforms.SetSize(bone_count);

                PoseToModelSpace(skeleton, pose, pose_transforms);
                PoseToMatrix(pose_transforms, pose_matrices);
            } else {
                PoseToMatrix(pose, pose_matrices);
                PoseToModelSpace(skeleton, pose_matrices, pose_matrices);
            }

            // Premultiply pose matrices with the bind pose inverse so they
            // can be directly be used to transform each vertex.
            const dmArray<RigBone>& bind_pose = *instance->m_BindPose;
            for (uint32_t bi = 0; bi < pose_matrices.Size(); ++bi)
            {
                Matrix4& pose_matrix = pose_matrices[bi];
                pose_matrix = pose_matrix * bind_pose[bi].m_ModelToLocal;
            }

            // Rearrange pose matrices to indices that the mesh vertices understand.
            PoseToInfluence(*instance->m_PoseIdxToInfluence, pose_matrices, influence_matrices);
        }

        // Loop that generates actual vertex data for current mesh entry.
        // We loop over the slots in the mesh entry, check which attachment point is active,
        // then locate the actual mesh that has been assigned to that attatchment point.
        // (Instead of looping over the default slot orders directly, we go through the
        // draw order array to render the slots in the correct order.)
        int32_t slot_count = instance->m_MeshSet->m_SlotCount;
        for (int32_t i = 0; i < slot_count; i++)
        {
            int32_t slot_index = instance->m_DrawOrder[i];

            MeshSlotPose* mesh_slot_pose = &instance->m_MeshSlotPose[slot_index];
            const dmRigDDF::MeshSlot* mesh_slot = mesh_slot_pose->m_MeshSlot;

            // Get active attachment in the current slot.
            uint32_t active_attachment = mesh_slot_pose->m_ActiveAttachment;
            if (active_attachment != INVALID_ATTACHMENT_INDEX) {

                // Check if the attachment point has a mesh assigned
                uint32_t mesh_attachment_index = mesh_slot->m_MeshAttachments[active_attachment];
                if (mesh_attachment_index != INVALID_ATTACHMENT_INDEX)
                {
                    // Lookup the mesh from the list of all the available meshes.
                    const Mesh* mesh_attachment = &instance->m_MeshSet->m_MeshAttachments[mesh_attachment_index];

                    // Bump scratch buffer capacity to handle current vertex count
                    uint32_t index_count = mesh_attachment->m_PositionIndices.m_Count;
                    if (positions.Capacity() < index_count) {
                        positions.OffsetCapacity(index_count - positions.Capacity());
                    }
                    positions.SetSize(index_count);

                    if (vertex_format == RIG_VERTEX_FORMAT_MODEL && mesh_attachment->m_NormalsIndices.m_Count) {
                        if (normals.Capacity() < index_count) {
                            normals.OffsetCapacity(index_count - normals.Capacity());
                        }
                        normals.SetSize(index_count);
                    }

                    // Fill scratch buffers for positions, and normals if applicable, using pose matrices.
                    float* positions_buffer = (float*)positions.Begin();
                    float* normals_buffer = (float*)normals.Begin();
                    dmRig::GeneratePositionData(mesh_attachment, model_matrix, influence_matrices, positions_buffer);
                    if (vertex_format == RIG_VERTEX_FORMAT_MODEL && mesh_attachment->m_NormalsIndices.m_Count) {
                        dmRig::GenerateNormalData(mesh_attachment, normal_matrix, influence_matrices, normals_buffer);
                    }

                    // NOTE: We expose two different vertex format that GenerateVertexData can output.
                    // This is a temporary fix until we have better support for custom vertex formats.
                    if (vertex_format == RIG_VERTEX_FORMAT_MODEL) {
                        vertex_data_out = (void*)WriteVertexData(mesh_attachment, positions_buffer, normals_buffer, (RigModelVertex*)vertex_data_out);
                    } else {
                        Vector4 slot_color = Vector4(mesh_slot_pose->m_SlotColor[0], mesh_slot_pose->m_SlotColor[1], mesh_slot_pose->m_SlotColor[2], mesh_slot_pose->m_SlotColor[3]);
                        const float* mesh_color = mesh_attachment->m_MeshColor.m_Count ? mesh_attachment->m_MeshColor.m_Data : white;
                        slot_color[0] = mesh_color[0] * slot_color[0];
                        slot_color[1] = mesh_color[1] * slot_color[1];
                        slot_color[2] = mesh_color[2] * slot_color[2];
                        slot_color[3] = mesh_color[3] * slot_color[3];
                        slot_color = mulPerElem(color, slot_color);
                        vertex_data_out = (void*)WriteVertexData(mesh_attachment, positions_buffer, slot_color, (RigSpineModelVertex*)vertex_data_out);
                    }
                }
            }
        }

        // DEF-3610
        // Using Wasm on Microsoft Edge this function returns NULL after a couple of runs.
        // There is no code path that could result in NULL, leading us to suspect it has
        // to do with some runtime optimization.
        // If we add some logic that touches vertex_data_out the function keeps returning
        // valid values, so we add an assert to verify vertex_data_out never is NULL.
        // However this means we need assert on Emscripten, so for now we output a
        // compile error if the engine is built with NDEBUG.
#ifdef __EMSCRIPTEN__
#ifdef NDEBUG
        #error "DEF-3610 - Can't compile with NDEBUG since dmRig::GenerateVertexData currently depends on assert() to work on Emscripten builds."
#else
        assert(vertex_data_out != 0x0);
#endif
#endif

        return vertex_data_out;
    }

    static uint32_t FindIKIndex(HRigInstance instance, dmhash_t ik_constraint_id)
    {
        const dmRigDDF::Skeleton* skeleton = instance->m_Skeleton;
        uint32_t ik_count = skeleton->m_Iks.m_Count;
        uint32_t ik_index = ~0u;
        for (uint32_t i = 0; i < ik_count; ++i)
        {
            if (skeleton->m_Iks[i].m_Id == ik_constraint_id)
            {
                ik_index = i;
                break;
            }
        }
        return ik_index;
    }

    void SetEnabled(HRigInstance instance, bool enabled)
    {
        instance->m_Enabled = enabled;
    }

    bool GetEnabled(HRigInstance instance)
    {
        return instance->m_Enabled;
    }

    bool IsValid(HRigInstance instance)
    {
        return (instance->m_MeshEntry != 0x0);
    }

    uint32_t GetBoneCount(HRigInstance instance)
    {
        if (instance == 0x0 || instance->m_Skeleton == 0x0) {
            return 0;
        }

        return instance->m_Skeleton->m_Bones.m_Count;
    }

    uint32_t GetMaxBoneCount(HRigInstance instance)
    {
        if (instance == 0x0) {
            return 0;
        }
        return instance->m_MaxBoneCount;
    }

    void SetEventCallback(HRigInstance instance, RigEventCallback event_callback, void* user_data1, void* user_data2)
    {
        if (!instance) {
            return;
        }

        instance->m_EventCallback = event_callback;
        instance->m_EventCBUserData1 = user_data1;
        instance->m_EventCBUserData2 = user_data2;
    }

    IKTarget* GetIKTarget(HRigInstance instance, dmhash_t constraint_id)
    {
        if (!instance) {
            return 0x0;
        }
        uint32_t ik_index = FindIKIndex(instance, constraint_id);
        if (ik_index == ~0u) {
            dmLogError("Could not find IK constraint (%llu)", (unsigned long long)constraint_id);
            return 0x0;
        }

        return &instance->m_IKTargets[ik_index];
    }

    bool ResetIKTarget(HRigInstance instance, dmhash_t constraint_id)
    {
        if (!instance) {
            return false;
        }

        uint32_t ik_index = FindIKIndex(instance, constraint_id);
        if (ik_index == ~0u) {
            dmLogError("Could not find IK constraint (%llu)", (unsigned long long)constraint_id);
            return false;
        }

        // Clear target fields, see DoAnimate function of the fields usage.
        // If callback is NULL it is considered not active, clear rest of fields
        // to avoid confusion.
        IKTarget* target = &instance->m_IKTargets[ik_index];
        target->m_Callback = 0x0;
        target->m_Mix = 0.0f;
        target->m_UserPtr = 0x0;
        target->m_UserHash = 0x0;

        return true;
    }

    static void DestroyInstance(HRigContext context, uint32_t index)
    {
        RigInstance* instance = context->m_Instances.Get(index);
        // If we're going to use memset, then we should explicitly clear pose and instance arrays.
        instance->m_Pose.SetCapacity(0);
        instance->m_IKTargets.SetCapacity(0);
        instance->m_MeshSlotPose.SetCapacity(0);
        delete instance;
        context->m_Instances.Free(index, true);
    }

    Result InstanceCreate(const InstanceCreateParams& params)
    {
        RigContext* context = (RigContext*)params.m_Context;

        if (context->m_Instances.Full())
        {
            dmLogError("Rig instance could not be created since the buffer is full (%d).", context->m_Instances.Capacity());
            return dmRig::RESULT_ERROR_BUFFER_FULL;
        }

        *params.m_Instance = new RigInstance;
        RigInstance* instance = *params.m_Instance;

        uint32_t index = context->m_Instances.Alloc();
        memset(instance, 0, sizeof(RigInstance));
        instance->m_Index = index;
        context->m_Instances.Set(index, instance);
        instance->m_MeshId = params.m_MeshId;

        instance->m_PoseCallback     = params.m_PoseCallback;
        instance->m_PoseCBUserData1  = params.m_PoseCBUserData1;
        instance->m_PoseCBUserData2  = params.m_PoseCBUserData2;
        instance->m_EventCallback    = params.m_EventCallback;
        instance->m_EventCBUserData1 = params.m_EventCBUserData1;
        instance->m_EventCBUserData2 = params.m_EventCBUserData2;

        instance->m_BindPose           = params.m_BindPose;
        instance->m_Skeleton           = params.m_Skeleton;
        instance->m_MeshSet            = params.m_MeshSet;
        instance->m_AnimationSet       = params.m_AnimationSet;
        instance->m_PoseIdxToInfluence = params.m_PoseIdxToInfluence;
        instance->m_TrackIdxToPose     = params.m_TrackIdxToPose;

        instance->m_Enabled = 1;

        AllocateMeshSlotPose(params.m_MeshSet, instance->m_MeshSlotPose, instance->m_DrawOrder);
        SetMesh(instance, instance->m_MeshId);

        instance->m_MaxBoneCount = dmMath::Max(instance->m_MeshSet->m_MaxBoneCount, instance->m_Skeleton == 0x0 ? 0 : instance->m_Skeleton->m_Bones.m_Count);
        Result result = CreatePose(context, instance);
        if (result != dmRig::RESULT_OK) {
            DestroyInstance(context, index);
            return result;
        }

        if (params.m_DefaultAnimation != NULL_ANIMATION)
        {
            // Loop forward should be the most common for idle anims etc.
            (void)PlayAnimation(instance, params.m_DefaultAnimation, dmRig::PLAYBACK_LOOP_FORWARD, 0.0f, 0.0f, 1.0f);
        }

        // m_ForceAnimatePose should be set if the animation step needs to run once (with dt 0)
        // to setup the pose to the current cursor.
        // Useful if pose needs to be calculated before draw but dmRig::Update will not be called
        // before that happens, for example cloning a GUI spine node happens in script update,
        // which comes after the regular dmRig::Update.
        if (params.m_ForceAnimatePose) {
            DoAnimate(context, instance, 0.0f);
        }

        return dmRig::RESULT_OK;
    }

    Result InstanceDestroy(const InstanceDestroyParams& params)
    {
        if (!params.m_Context || !params.m_Instance) {
            return dmRig::RESULT_ERROR;
        }

        DestroyInstance((RigContext*)params.m_Context, params.m_Instance->m_Index);
        return dmRig::RESULT_OK;
    }

    void CreateBindPose(dmRigDDF::Skeleton& skeleton, dmArray<RigBone>& bind_pose)
    {
        uint32_t bone_count = skeleton.m_Bones.m_Count;
        bind_pose.SetCapacity(bone_count);
        bind_pose.SetSize(bone_count);
        for (uint32_t i = 0; i < bone_count; ++i)
        {
            dmRig::RigBone* bind_bone = &bind_pose[i];
            dmRigDDF::Bone* bone = &skeleton.m_Bones[i];
            bind_bone->m_LocalToParent = dmTransform::Transform(Vector3(bone->m_Position), bone->m_Rotation, bone->m_Scale);
            if (i > 0)
            {
                bind_bone->m_LocalToModel = dmTransform::Mul(bind_pose[bone->m_Parent].m_LocalToModel, bind_bone->m_LocalToParent);
                if (!bone->m_InheritScale)
                {
                    bind_bone->m_LocalToModel.SetScale(bind_bone->m_LocalToParent.GetScale());
                }
            }
            else
            {
                bind_bone->m_LocalToModel = bind_bone->m_LocalToParent;
            }
            bind_bone->m_ModelToLocal = inverse(dmTransform::ToMatrix4(bind_bone->m_LocalToModel));
            bind_bone->m_ParentIndex = bone->m_Parent;
            bind_bone->m_Length = bone->m_Length;
        }
    }

    static const uint32_t INVALID_BONE_IDX = 0xffffffff;
    static uint32_t FindBoneInList(uint64_t* list, uint32_t count, uint64_t bone_id)
    {
        for (uint32_t i = 0; i < count; ++i)
        {
            uint64_t entry = list[i];
            if (bone_id == entry) {
                return i;
            }
        }

        return INVALID_BONE_IDX;
    }

    void FillBoneListArrays(const dmRigDDF::MeshSet& meshset, const dmRigDDF::AnimationSet& animationset, const dmRigDDF::Skeleton& skeleton, dmArray<uint32_t>& track_idx_to_pose, dmArray<uint32_t>& pose_idx_to_influence)
    {
        // Create lookup arrays
        // - track-to-pose, used to convert animation track bonde index into correct pose transform index
        // - pose-to-influence, used during vertex generation to convert pose transform index into influence index
        uint32_t bone_count = skeleton.m_Bones.m_Count;

        track_idx_to_pose.SetCapacity(bone_count);
        track_idx_to_pose.SetSize(bone_count);
        memset((void*)track_idx_to_pose.Begin(), 0x0, track_idx_to_pose.Size()*sizeof(uint32_t));
        pose_idx_to_influence.SetCapacity(bone_count);
        pose_idx_to_influence.SetSize(bone_count);

        uint32_t anim_bone_list_count = animationset.m_BoneList.m_Count;
        uint32_t mesh_bone_list_count = meshset.m_BoneList.m_Count;

        for (uint32_t bi = 0; bi < bone_count; ++bi)
        {
            uint64_t bone_id = skeleton.m_Bones[bi].m_Id;

            if (anim_bone_list_count) {
                uint32_t track_idx = FindBoneInList(animationset.m_BoneList.m_Data, animationset.m_BoneList.m_Count, bone_id);
                if (track_idx != INVALID_BONE_IDX) {
                    track_idx_to_pose[track_idx] = bi;
                }
            } else {
                track_idx_to_pose[bi] = bi;
            }

            if (mesh_bone_list_count) {
                uint32_t influence_idx = FindBoneInList(meshset.m_BoneList.m_Data, meshset.m_BoneList.m_Count, bone_id);
                if (influence_idx != INVALID_BONE_IDX) {
                    pose_idx_to_influence[bi] = influence_idx;
                } else {
                    // If there is no influence index for the current bone
                    // we still need to put the pose matrix somewhere during
                    // pose-to-influence rearrangement so just put it last.
                    pose_idx_to_influence[bi] = bone_count - 1;
                }
            } else {
                pose_idx_to_influence[bi] = bi;
            }
        }
    }

}

{'repo_name': 'defold/defold', 'stars': '1066', 'repo_language': 'C', 'file_name': 'gitsrv.go', 'mime_type': 'text/x-c', 'hash': 3774922499995867066, 'source_dataset': 'data'}


//
//  User.m
//  mtalk
//
//  Created by maye on 13-10-25.
//  Copyright (c) 2013年 zuoye. All rights reserved.
//

#import "UserEntity.h"

@implementation UserEntity

-(void)copyContent:(UserEntity*)entity
{
    self.userId = [entity.userId copy];
    self.name = [entity.name copy];
    self.nick = [entity.nick copy];
    self.avatar = [entity.avatar copy];
    self.department = [entity.department copy];
    self.userRole = entity.userRole;
    self.userUpdated = entity.userUpdated;
}

- (NSString*)avatar
{
    if (_avatar && [_avatar length] > 0)
    {
        if (![_avatar hasSuffix:@"_100x100.jpg"]) {
            _avatar = [NSString stringWithFormat:@"%@%@",_avatar,@"_100x100.jpg"];
        }
    }
    return _avatar;
}

-(NSString*)userInfoAvatar
{
    if (_avatar && [_avatar length] > 0)
    {
        if (![_avatar hasSuffix:@"_200x200.jpg"]) {
            _avatar = [NSString stringWithFormat:@"%@%@",_avatar,@"_200x200.jpg"];
        }
    }
    return _avatar;
}

@end

{'repo_name': 'balloonwj/TeamTalk', 'stars': '258', 'repo_language': 'C++', 'file_name': 'jsonxx.h', 'mime_type': 'text/x-c++', 'hash': 1223305421072735905, 'source_dataset': 'data'}


<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <fabien@symfony.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Component\PropertyAccess\Tests;

class PropertyAccessorCollectionTest_Car
{
    private $axes;

    public function __construct($axes = null)
    {
        $this->axes = $axes;
    }

    // In the test, use a name that StringUtil can't uniquely singularify
    public function addAxis($axis)
    {
        $this->axes[] = $axis;
    }

    public function removeAxis($axis)
    {
        foreach ($this->axes as $key => $value) {
            if ($value === $axis) {
                unset($this->axes[$key]);

                return;
            }
        }
    }

    public function getAxes()
    {
        return $this->axes;
    }
}

class PropertyAccessorCollectionTest_CarOnlyAdder
{
    public function addAxis($axis)
    {
    }

    public function getAxes()
    {
    }
}

class PropertyAccessorCollectionTest_CarOnlyRemover
{
    public function removeAxis($axis)
    {
    }

    public function getAxes()
    {
    }
}

class PropertyAccessorCollectionTest_CarNoAdderAndRemover
{
    public function getAxes()
    {
    }
}

class PropertyAccessorCollectionTest_CompositeCar
{
    public function getStructure()
    {
    }

    public function setStructure($structure)
    {
    }
}

class PropertyAccessorCollectionTest_CarStructure
{
    public function addAxis($axis)
    {
    }

    public function removeAxis($axis)
    {
    }

    public function getAxes()
    {
    }
}

abstract class PropertyAccessorCollectionTest extends PropertyAccessorArrayAccessTest
{
    public function testSetValueCallsAdderAndRemoverForCollections()
    {
        $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth', 4 => 'fifth'));
        $axesMerged = $this->getContainer(array(1 => 'first', 2 => 'second', 3 => 'third'));
        $axesAfter = $this->getContainer(array(1 => 'second', 5 => 'first', 6 => 'third'));
        $axesMergedCopy = is_object($axesMerged) ? clone $axesMerged : $axesMerged;

        // Don't use a mock in order to test whether the collections are
        // modified while iterating them
        $car = new PropertyAccessorCollectionTest_Car($axesBefore);

        $this->propertyAccessor->setValue($car, 'axes', $axesMerged);

        $this->assertEquals($axesAfter, $car->getAxes());

        // The passed collection was not modified
        $this->assertEquals($axesMergedCopy, $axesMerged);
    }

    public function testSetValueCallsAdderAndRemoverForNestedCollections()
    {
        $car = $this->getMockBuilder(__CLASS__.'_CompositeCar')->getMock();
        $structure = $this->getMockBuilder(__CLASS__.'_CarStructure')->getMock();
        $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth'));
        $axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third'));

        $car->expects($this->any())
            ->method('getStructure')
            ->will($this->returnValue($structure));

        $structure->expects($this->at(0))
            ->method('getAxes')
            ->will($this->returnValue($axesBefore));
        $structure->expects($this->at(1))
            ->method('removeAxis')
            ->with('fourth');
        $structure->expects($this->at(2))
            ->method('addAxis')
            ->with('first');
        $structure->expects($this->at(3))
            ->method('addAxis')
            ->with('third');

        $this->propertyAccessor->setValue($car, 'structure.axes', $axesAfter);
    }

    /**
     * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
     * @expectedExceptionMessageRegExp /Could not determine access type for property "axes" in class "Mock_PropertyAccessorCollectionTest_CarNoAdderAndRemover_[^"]*"./
     */
    public function testSetValueFailsIfNoAdderNorRemoverFound()
    {
        $car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
        $axesBefore = $this->getContainer(array(1 => 'second', 3 => 'fourth'));
        $axesAfter = $this->getContainer(array(0 => 'first', 1 => 'second', 2 => 'third'));

        $car->expects($this->any())
            ->method('getAxes')
            ->will($this->returnValue($axesBefore));

        $this->propertyAccessor->setValue($car, 'axes', $axesAfter);
    }

    public function testIsWritableReturnsTrueIfAdderAndRemoverExists()
    {
        $car = $this->getMockBuilder(__CLASS__.'_Car')->getMock();
        $this->assertTrue($this->propertyAccessor->isWritable($car, 'axes'));
    }

    public function testIsWritableReturnsFalseIfOnlyAdderExists()
    {
        $car = $this->getMockBuilder(__CLASS__.'_CarOnlyAdder')->getMock();
        $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
    }

    public function testIsWritableReturnsFalseIfOnlyRemoverExists()
    {
        $car = $this->getMockBuilder(__CLASS__.'_CarOnlyRemover')->getMock();
        $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
    }

    public function testIsWritableReturnsFalseIfNoAdderNorRemoverExists()
    {
        $car = $this->getMockBuilder(__CLASS__.'_CarNoAdderAndRemover')->getMock();
        $this->assertFalse($this->propertyAccessor->isWritable($car, 'axes'));
    }

    /**
     * @expectedException \Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException
     * expectedExceptionMessageRegExp /The property "axes" in class "Mock_PropertyAccessorCollectionTest_Car[^"]*" can be defined with the methods "addAxis()", "removeAxis()" but the new value must be an array or an instance of \Traversable, "string" given./
     */
    public function testSetValueFailsIfAdderAndRemoverExistButValueIsNotTraversable()
    {
        $car = $this->getMockBuilder(__CLASS__.'_Car')->getMock();

        $this->propertyAccessor->setValue($car, 'axes', 'Not an array or Traversable');
    }
}

{'repo_name': 'pizepei/LayuiAdminProPHP', 'stars': '131', 'repo_language': 'TSQL', 'file_name': 'ssruserlist.js', 'mime_type': 'text/plain', 'hash': -5952587173345473704, 'source_dataset': 'data'}


# Search all header and source files
file(GLOB hdrs "*.hpp")
file(GLOB srcs "*.cpp")
file(GLOB detail_hdrs "detail/*.hpp")
file(GLOB detail_srcs "detail/*.cpp")
dart_add_core_headers(${hdrs} ${detail_hdrs})
dart_add_core_sources(${srcs} ${detail_srcs})

# Generate header for this namespace
dart_get_filename_components(header_names "constraint headers" ${hdrs})
dart_generate_include_header_file(
  "${CMAKE_CURRENT_BINARY_DIR}/constraint.hpp"
  "dart/constraint/"
  ${header_names}
)

# Install
install(
  FILES ${hdrs} ${CMAKE_CURRENT_BINARY_DIR}/constraint.hpp
  DESTINATION include/dart/constraint
  COMPONENT headers
)
install(
  FILES ${detail_hdrs}
  DESTINATION include/dart/constraint/detail
  COMPONENT headers
)

{'repo_name': 'dartsim/dart', 'stars': '512', 'repo_language': 'C++', 'file_name': 'torus2.path', 'mime_type': 'text/plain', 'hash': 6783568837648180661, 'source_dataset': 'data'}


/* Copyright (c) 2008-2015, Avian Contributors

   Permission to use, copy, modify, and/or distribute this software
   for any purpose with or without fee is hereby granted, provided
   that the above copyright notice and this permission notice appear
   in all copies.

   There is NO WARRANTY for this software.  See license.txt for
   details. */

package java.nio.channels;

import java.io.IOException;
import java.net.SocketException;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;

public class SocketChannel extends SelectableChannel
  implements ReadableByteChannel, GatheringByteChannel
{
  public static final int InvalidSocket = -1;

  int socket = makeSocket();
  boolean connected = false;
  boolean readyToConnect = false;
  boolean blocking = true;

  public static SocketChannel open() throws IOException {
    Socket.init();

    return new SocketChannel();
  }

  public SelectableChannel configureBlocking(boolean v) throws IOException {
    blocking = v;
    if (socket != InvalidSocket) {
      configureBlocking(socket, v);
    }
    return this;
  }

  public boolean isBlocking() {
    return blocking;
  }

  public boolean isConnected() {
    return connected;
  }

  public Socket socket() {
    try {
		return new Handle();
	} catch (IOException e) {
		return null;
	}
  }

  public boolean connect(SocketAddress address) throws IOException {
    InetSocketAddress a;
    try {
      a = (InetSocketAddress) address;
    } catch (ClassCastException e) {
      throw new UnsupportedAddressTypeException();
    }
    doConnect(socket, a.getAddress().getRawAddress(), a.getPort());
    configureBlocking(blocking);
    return connected;
  }

  public boolean finishConnect() throws IOException {
    if (! connected) {
      while (! readyToConnect) {
        Selector selector = Selector.open();
        SelectionKey key = register(selector, SelectionKey.OP_CONNECT, null);

        if (blocking) {
          selector.select();
        } else {
          selector.selectNow();
          break;
        }
      }

      natFinishConnect(socket);

      connected = readyToConnect;
    }

    return connected;
  }

  public void close() throws IOException {
    if (isOpen()) {
      super.close();
      closeSocket();
    }
  }

  private void doConnect(int socket, int host, int port)
    throws IOException
  {
    connected = natDoConnect(socket, host, port);
  }

  public int read(ByteBuffer b) throws IOException {
    if (! isOpen()) return -1;
    if (b.remaining() == 0) return 0;

    byte[] array = b.array();
    if (array == null) throw new NullPointerException();

    int r = natRead(socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking);
    if (r > 0) {
      b.position(b.position() + r);
    }
    return r;
  }

  public int write(ByteBuffer b) throws IOException {
    if (! connected) {
      natThrowWriteError(socket);
    }
    if (b.remaining() == 0) return 0;

    byte[] array = b.array();
    if (array == null) throw new NullPointerException();

    int w = natWrite(socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking);
    if (w > 0) {
      b.position(b.position() + w);
    }
    return w;
  }

  public long write(ByteBuffer[] srcs) throws IOException {
    return write(srcs, 0, srcs.length);
  }

  public long write(ByteBuffer[] srcs, int offset, int length)
    throws IOException
  {
    long total = 0;
    for (int i = offset; i < offset + length; ++i) {
      total += write(srcs[i]);
      if (srcs[i].hasRemaining()) {
        return total;
      }
    }
    return total;
  }

  private void closeSocket() {
    natCloseSocket(socket);
  }

  int socketFD() {
    return socket;
  }

  void handleReadyOps(int ops) {
    if ((ops & SelectionKey.OP_CONNECT) != 0) {
      readyToConnect = true;
    }
  }

  public class Handle extends Socket {
    public Handle() throws IOException {
      super();
    }

    public void setTcpNoDelay(boolean on) throws SocketException {
      natSetTcpNoDelay(socket, on);
    }

    public void bind(SocketAddress address)
      throws IOException
    {
      InetSocketAddress a;
      try {
        a = (InetSocketAddress) address;
      } catch (ClassCastException e) {
        throw new IllegalArgumentException();
      }

      if (a == null) {
        SocketChannel.bind(socket, 0, 0);
      } else {
        SocketChannel.bind
          (socket, a.getAddress().getRawAddress(), a.getPort());
      }
    }
  }

  private static native int makeSocket();
  private static native void configureBlocking(int socket, boolean blocking)
    throws IOException;

  private static native void natSetTcpNoDelay(int socket, boolean on)
    throws SocketException;
  private static native void bind(int socket, int host, int port)
    throws IOException;
  private static native boolean natDoConnect(int socket, int host, int port)
    throws IOException;
  private static native void natFinishConnect(int socket)
    throws IOException;
  private static native int natRead(int socket, byte[] buffer, int offset, int length, boolean blocking)
    throws IOException;
  private static native int natWrite(int socket, byte[] buffer, int offset, int length, boolean blocking)
    throws IOException;
  private static native void natThrowWriteError(int socket) throws IOException;
  private static native void natCloseSocket(int socket);
}

{'repo_name': 'ReadyTalk/avian', 'stars': '1176', 'repo_language': 'C++', 'file_name': 'Platform.cmake', 'mime_type': 'text/plain', 'hash': 1962909508975003730, 'source_dataset': 'data'}


fileFormatVersion: 2
guid: b507b699f2de6324596bb360a128b259
NativeFormatImporter:
  externalObjects: {}
  mainObjectFileID: 2100000
  userData:
  assetBundleName:
  assetBundleVariant:

{'repo_name': 'knapeczadam/Unity-Shaders', 'stars': '175', 'repo_language': 'ShaderLab', 'file_name': 'AudioManager.asset', 'mime_type': 'text/plain', 'hash': 1438129667256863878, 'source_dataset': 'data'}


; File generated automatically from ../wine/dlls/dwmapi/dwmapi.spec; do not edit!

LIBRARY dwmapi.dll

EXPORTS
  DwmpDxGetWindowSharedSurface @100 PRIVATE
  DwmpDxUpdateWindowSharedSurface @101 PRIVATE
  DwmEnableComposition @102
  DwmpRestartComposition @103 NONAME PRIVATE
  DwmpSetColorizationColor @104 NONAME PRIVATE
  DwmpStartOrStopFlip3D @105 NONAME PRIVATE
  DwmpIsCompositionCapable @106 NONAME PRIVATE
  DwmpGetGlobalState @107 NONAME PRIVATE
  DwmpEnableRedirection @108 NONAME PRIVATE
  DwmpOpenGraphicsStream @109 NONAME PRIVATE
  DwmpCloseGraphicsStream @110 NONAME PRIVATE
  DwmpSetGraphicsStreamTransformHint @112 NONAME PRIVATE
  DwmpActivateLivePreview @113 NONAME PRIVATE
  DwmpQueryThumbnailType @114 NONAME PRIVATE
  DwmpStartupViaUserInit @115 NONAME PRIVATE
  DwmpGetAssessment @118 NONAME PRIVATE
  DwmpGetAssessmentUsage @119 NONAME PRIVATE
  DwmpSetAssessmentUsage @120 NONAME PRIVATE
  DwmpIsSessionDWM @121 NONAME PRIVATE
  DwmpRegisterThumbnail @124 NONAME PRIVATE
  DwmpDxBindSwapChain @125 PRIVATE
  DwmpDxUnbindSwapChain @126 PRIVATE
  DwmpGetColorizationParameters @127 NONAME
  DwmpDxgiIsThreadDesktopComposited @128 PRIVATE
  DwmpDxgiDisableRedirection @129 NONAME PRIVATE
  DwmpDxgiEnableRedirection @130 NONAME PRIVATE
  DwmpSetColorizationParameters @131 NONAME PRIVATE
  DwmpGetCompositionTimingInfoEx @132 NONAME PRIVATE
  DwmpDxUpdateWindowRedirectionBltSurface @133 PRIVATE
  DwmpDxSetContentHostingInformation @134 NONAME PRIVATE
  DwmpRenderFlick @135 PRIVATE
  DwmpAllocateSecurityDescriptor @136 PRIVATE
  DwmpFreeSecurityDescriptor @137 PRIVATE
  DwmpEnableDDASupport @143 PRIVATE
  DwmTetherTextContact @156 PRIVATE
  DwmAttachMilContent @111
  DwmDefWindowProc @116
  DwmDetachMilContent @117
  DwmEnableBlurBehindWindow @122
  DwmEnableMMCSS @123
  DwmExtendFrameIntoClientArea @149
  DwmFlush @165
  DwmGetColorizationColor @166
  DwmGetCompositionTimingInfo @167
  DwmGetGraphicsStreamClient @168
  DwmGetGraphicsStreamTransformHint @169
  DwmGetTransportAttributes @170
  DwmGetWindowAttribute @171
  DwmInvalidateIconicBitmaps @172
  DwmIsCompositionEnabled @173
  DwmModifyPreviousDxFrameDuration @174 PRIVATE
  DwmQueryThumbnailSourceSize @175 PRIVATE
  DwmRegisterThumbnail @176
  DwmSetDxFrameDuration @177 PRIVATE
  DwmSetIconicLivePreviewBitmap @178
  DwmSetIconicThumbnail @179
  DwmSetPresentParameters @180
  DwmSetWindowAttribute @181
  DwmUnregisterThumbnail @182
  DwmUpdateThumbnailProperties @183

{'repo_name': 'nrlquaker/winbox-mac', 'stars': '405', 'repo_language': 'Shell', 'file_name': 'release', 'mime_type': 'text/x-shellscript', 'hash': 5500149350532467474, 'source_dataset': 'data'}


import sublime
import sublime_plugin

_ST3 = sublime.version() >= '3000'

if _ST3:
    from .getTeXRoot import get_tex_root
    from .latextools_utils import analysis, get_setting, quickpanel
else:
    from getTeXRoot import get_tex_root
    from latextools_utils import analysis, get_setting, quickpanel

def _make_caption(toc_indentations, com, indent_offset):
    indent = toc_indentations.get(com.command, 0)
    # lower the indent with the offset
    indent = max(0, indent - indent_offset)
    short = {
        "subsubsection": "sss",
        "minisec": "msec",
        "label": "L"
    }.get(com.command, com.command[0:3])
    short = short.title()
    # special handling for koma script
    if short.lower() == "add":
        short = "+" + com.command[3:6].title()

    return ("  " * indent) + short + com.star + " " + com.args

class show_toc_quickpanel(quickpanel.CancelEntriesQuickpanel):
    __show_string = "Show Labels"
    __hide_string = "Hide Labels"
    __toggles = [__show_string, __hide_string]

    def __init__(self, ana, only_file=None):
        # retrieve the labels and the sections
        toc_section_commands = get_setting("toc_section_commands", [])
        toc_indentations = get_setting("toc_indentations", {})
        toc_labels = get_setting("toc_labels", [])

        labels = ana.filter_commands(toc_section_commands + toc_labels)
        # filter the labels and sections to only get the labels
        # (faster than an additional query)
        secs = [c for c in labels if c.command in toc_section_commands]

        if only_file:
            labels = [l for l in labels if l.file_name == only_file]
            secs = [s for s in secs if s.file_name == only_file]

        # create the user readably captions
        # get the minimal indent (to lower the minimal section indent to 0)
        max_indent_value = max(toc_indentations.values())
        indent_offset = min([
            toc_indentations.get(com.command, max_indent_value)
            for com in secs
        ] + [0])

        caption_secs = [_make_caption(toc_indentations, s, indent_offset)
                        for s in secs]

        caption_labels = [_make_caption(toc_indentations, l, indent_offset)
                          for l in labels]

        self.__only_sec = True
        # init the superclass with a copy of the section elements
        super(show_toc_quickpanel, self).__init__(
            list(caption_secs), list(secs))

        # story necessary fields
        self.__secs = secs
        self.__labels = labels
        self.__caption_secs = caption_secs
        self.__caption_labels = caption_labels

        # add a item to show the labels
        self.add_item(quickpanel.AT_END, self.__show_string,
                      done_handler=self.__show_labels)
        # register a function to hide the labels
        self.done_handler[self.__hide_string] = self.__hide_labels

        # show the quickpanel
        self.show_quickpanel()

    def __hide_labels(self):
        """Handles the toggle to hide the labels"""
        # exchange the captions after the offset (with no navigation entries)
        self.captions = self.captions[0:self._offset] + self.__caption_secs
        # change the name of the toggle from "Hide" to "Show"
        index = self.captions.index(self.__hide_string)
        self.captions[index] = self.__show_string
        # update the entries
        self.entries = self.__secs

        # show the quickpanel such that the changes take effect
        self.show_quickpanel(index)

    def __show_labels(self):
        """Handles the toggle to show the labels"""
        # exchange the captions after the offset (with no navigation entries)
        self.captions = self.captions[0:self._offset] + self.__caption_labels
        # change the name of the toggle from "Show" to "Hide"
        index = self.captions.index(self.__show_string)
        self.captions[index] = self.__hide_string
        # update the entries
        self.entries = self.__labels

        # show the quickpanel such that the changes take effect
        self.show_quickpanel(index)

def show_commands(captions, entries, show_cancel=True):
    if show_cancel:
        Quickpanel = quickpanel.CancelEntriesQuickpanel
    else:
        Quickpanel = quickpanel.EntriesQuickpanel
    Quickpanel(captions, entries).show_quickpanel()

class LatexTocQuickpanelCommand(sublime_plugin.WindowCommand):
    def run(self, only_current_file=False):
        view = self.window.active_view()
        tex_root = get_tex_root(view)
        if not tex_root:
            return
        ana = analysis.analyze_document(tex_root)

        only_file = None if not only_current_file else view.file_name()
        show_toc_quickpanel(ana, only_file=only_file)

class LatexTocQuickpanelContext(sublime_plugin.EventListener):
    def on_query_context(self, view, key, *args):
        if (key != "overwrite_goto_overlay" or
                not view.score_selector(0, "text.tex.latex")):
            return None
        return get_setting("overwrite_goto_overlay")

{'repo_name': 'SublimeText/LaTeXTools', 'stars': '1869', 'repo_language': 'Python', 'file_name': 'music.biblatex-snippet', 'mime_type': 'text/plain', 'hash': -786347120301576730, 'source_dataset': 'data'}


<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>NPSP_Standard_User</members>
        <name>Profile</name>
    </types>
    <version>48.0</version>
</Package>

{'repo_name': 'SalesforceFoundation/NPSP', 'stars': '442', 'repo_language': 'Apex', 'file_name': 'npsp-common.css', 'mime_type': 'text/plain', 'hash': 887596539637736733, 'source_dataset': 'data'}


# Translocator 1.1.1.6
version:1.7
modname:Translocator

cfgfile:config/Translocator.cfg

texturepath:assets/translocator/textures/blocks/
texture:id=minecraft/obsidian,filename=assets/minecraft/textures/blocks/obsidian.png
texture:id=minecraft/planks_oak,filename=assets/minecraft/textures/blocks/planks_oak.png

# %translocator:* (translocator), render=(CUSTOM), opaque=false,cls=codechicken.translocator.BlockTranslocator
block:id=%translocator,data=*,stdrot=true,face0-5=0:minecraft/obsidian

# %craftingGrid:* (craftingGrid), render=(CUSTOM), opaque=false,cls=codechicken.translocator.BlockCraftingGrid
block:id=%craftingGrid,data=*,stdrot=true,face0-5=0:minecraft/planks_oak

{'repo_name': 'webbukkit/dynmap', 'stars': '1010', 'repo_language': 'Java', 'file_name': 'accesstransformer.cfg', 'mime_type': 'text/plain', 'hash': 644690553683069156, 'source_dataset': 'data'}


// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gxui

type KeyboardController struct {
	window Window
}

func CreateKeyboardController(w Window) *KeyboardController {
	c := &KeyboardController{
		window: w,
	}
	w.OnKeyDown(c.keyDown)
	w.OnKeyUp(c.keyUp)
	w.OnKeyRepeat(c.keyPress)
	w.OnKeyStroke(c.keyStroke)
	return c
}

func (c *KeyboardController) keyDown(ev KeyboardEvent) {
	f := Control(c.window.Focus())
	for f != nil {
		f.KeyDown(ev)
		f, _ = f.Parent().(Control)
	}
	c.keyPress(ev)
}

func (c *KeyboardController) keyUp(ev KeyboardEvent) {
	f := Control(c.window.Focus())
	for f != nil {
		f.KeyUp(ev)
		f, _ = f.Parent().(Control)
	}
}

func (c *KeyboardController) keyPress(ev KeyboardEvent) {
	f := Control(c.window.Focus())
	for f != nil {
		if f.KeyPress(ev) {
			return
		}
		f, _ = f.Parent().(Control)
	}
	c.window.KeyPress(ev)
}

func (c *KeyboardController) keyStroke(ev KeyStrokeEvent) {
	f := Control(c.window.Focus())
	for f != nil {
		if f.KeyStroke(ev) {
			return
		}
		f, _ = f.Parent().(Control)
	}
	c.window.KeyStroke(ev)
}

{'repo_name': 'esimov/diagram', 'stars': '590', 'repo_language': 'Go', 'file_name': 'utils.go', 'mime_type': 'text/plain', 'hash': -7744194437782839292, 'source_dataset': 'data'}