{"action":"created","comment":{"url":"https://api.github.com/repos/tangrams/tangram-es/pulls/comments/33514876","id":33514876,"diff_hunk":"@@ -3,237 +3,350 @@\n #include \"tile/mapTile.h\"\n #include \"view/view.h\"\n \n+#include <glm/gtx/norm.hpp>\n+\n #include <chrono>\n #include <algorithm>\n \n-TileManager::TileManager() {\n+#define MAX_PARALLEL_DOWNLOADS 4\n+\n+TileManager::TileManager()\n+    : m_loadPending(0) {\n+\n     // Instantiate workers\n     for (size_t i = 0; i < MAX_WORKERS; i++) {\n-        m_workers.push_back(std::unique_ptr<TileWorker>(new TileWorker()));\n+        m_workers.push_back(std::unique_ptr<TileWorker>(new TileWorker(*this)));\n     }\n-}\n-\n-TileManager::TileManager(TileManager&& _other) :\n-    m_view(std::move(_other.m_view)),\n-    m_tileSet(std::move(_other.m_tileSet)),\n-    m_dataSources(std::move(_other.m_dataSources)),\n-    m_workers(std::move(_other.m_workers)),\n-    m_queuedTiles(std::move(_other.m_queuedTiles)) {\n+    m_dataCallback = std::bind(&TileManager::tileLoaded, this, std::placeholders::_1);\n }\n \n TileManager::~TileManager() {\n     for (auto& worker : m_workers) {\n-        if (!worker->isFree()) {\n+        if (worker->isRunning()) {\n             worker->abort();\n-            worker->getTileResult();\n+            worker->drain();\n         }\n         // We stop all workers before we destroy the resources they use.\n         // TODO: This will wait for any pending network requests to finish,\n-        // which could delay closing of the application. \n+        // which could delay closing of the application.\n     }\n     m_dataSources.clear();\n     m_tileSet.clear();\n }\n \n-void TileManager::addToWorkerQueue(std::vector<char>&& _rawData, const TileID& _tileId, DataSource* _source) {\n-    \n+void TileManager::tileLoaded(TileTask task) {\n     std::lock_guard<std::mutex> lock(m_queueTileMutex);\n-    m_queuedTiles.emplace_back(std::unique_ptr<TileTask>(new TileTask(std::move(_rawData), _tileId, _source)));\n-    \n+    m_queuedTiles.push_back(std::move(task));\n }\n \n-void TileManager::addToWorkerQueue(std::shared_ptr<TileData>& _parsedData, const TileID& _tileID, DataSource* _source) {\n+void TileManager::tileProcessed(TileTask task) {\n+    std::lock_guard<std::mutex> lock(m_readyTileMutex);\n+    m_readyTiles.push_back(std::move(task));\n+}\n \n+TileTask TileManager::pollProcessQueue() {\n     std::lock_guard<std::mutex> lock(m_queueTileMutex);\n-    m_queuedTiles.emplace_back(std::unique_ptr<TileTask>(new TileTask(_parsedData, _tileID, _source)));\n \n-}\n+    while (!m_queuedTiles.empty()) {\n+        auto task = std::move(m_queuedTiles.front());\n+        m_queuedTiles.pop_front();\n \n-void TileManager::updateTileSet() {\n-    \n-    m_tileSetChanged = false;\n-    \n-    // Check if any native worker needs to be dispatched i.e. queuedTiles is not empty\n-    {\n-        auto workersIter = m_workers.begin();\n-        auto queuedTilesIter = m_queuedTiles.begin();\n+        if (!setTileState(*(task->tile), MapTile::Processing)) {\n+            // Drop canceled task.\n+            continue;\n+        }\n+        return task;\n+    }\n+\n+    return TileTask(nullptr);\n+}\n \n-        while (workersIter != m_workers.end() && queuedTilesIter != m_queuedTiles.end()) {\n+bool TileManager::setTileState(MapTile& tile, MapTile::State state) {\n+    std::lock_guard<std::mutex> lock(m_tileStateMutex);\n \n-            auto& worker = *workersIter;\n+    switch (tile.state()) {\n+    case MapTile::None:\n+        if (state == MapTile::Loading) {\n+            tile.setState(state);\n+            m_loadPending++;\n+            return true;\n+        }\n+        break;\n \n-            if (worker->isFree()) {\n-                worker->processTileData(std::move(*queuedTilesIter), m_scene->getStyles(), *m_view);\n-                queuedTilesIter = m_queuedTiles.erase(queuedTilesIter);\n-            }\n+    case MapTile::Loading:\n+        if (state == MapTile::Processing) {\n+            tile.setState(state);\n+            m_loadPending--;\n+            return true;\n+        }\n+        if (state == MapTile::Canceled) {\n+            tile.setState(state);\n+            m_loadPending--;\n+            return true;\n+        }\n+        break;\n \n-            ++workersIter;\n+    case MapTile::Processing:\n+        if (state == MapTile::Ready) {\n+            tile.setState(state);\n+            return true;\n         }\n+        break;\n+\n+    case MapTile::Ready:\n+        return false;\n+\n+    case MapTile::Canceled:\n+        return false;\n+\n     }\n \n-    // Check if any incoming tiles are finished\n-    for (auto& worker : m_workers) {\n-        \n-        if (!worker->isFree() && worker->isFinished()) {\n-            \n-            // Get result from worker and move it into tile set\n-            auto tile = worker->getTileResult();\n-            const TileID& id = tile->getID();\n-            logMsg(\"Tile [%d, %d, %d] finished loading\\n\", id.z, id.x, id.y);\n-            std::swap(m_tileSet[id], tile);\n-            cleanProxyTiles(id);\n-            m_tileSetChanged = true;\n-            \n-        }\n-        \n+    if (state == MapTile::Canceled) {\n+        return false;\n     }\n-    \n-    if (! (m_view->changedOnLastUpdate() || m_tileSetChanged) ) {\n-        // No new tiles have come into view and no tiles have finished loading, \n-        // so the tileset is unchanged\n-        return;\n+\n+    logMsg(\"Wrong state change %d -> %d<<<\", tile.state(), state);\n+    assert(false);\n+}\n+\n+\n+void TileManager::updateTileSet() {\n+\n+    m_tileSetChanged = false;\n+\n+    // Check if any native worker needs to be dispatched.\n+    if (!m_queuedTiles.empty()) {\n+\n+        for (auto& worker : m_workers) {\n+            if (worker->isRunning()) { continue; }\n+\n+            worker->process(m_scene->getStyles());\n+        }\n     }\n-    \n-    const std::set<TileID>& visibleTiles = m_view->getVisibleTiles();\n-    \n-    // Loop over visibleTiles and add any needed tiles to tileSet\n-    {\n-        auto setTilesIter = m_tileSet.begin();\n-        auto visTilesIter = visibleTiles.begin();\n-        \n-        while (visTilesIter != visibleTiles.end()) {\n-            \n-            if (setTilesIter == m_tileSet.end() || *visTilesIter < setTilesIter->first) {\n-                // tileSet is missing an element present in visibleTiles\n-                addTile(*visTilesIter);\n+\n+    if (!m_readyTiles.empty()) {\n+        std::lock_guard<std::mutex> lock(m_readyTileMutex);\n+        auto it = m_readyTiles.begin();\n+\n+        while (it != m_readyTiles.end()) {\n+            auto& task = *it;\n+            auto& tile = *(task->tile);\n+\n+            if (setTileState(tile, MapTile::Ready)) {\n+                cleanProxyTiles(tile);\n                 m_tileSetChanged = true;\n-                ++visTilesIter;\n-            } else if (setTilesIter->first < *visTilesIter) {\n-                // visibleTiles is missing an element present in tileSet (handled below)\n-                ++setTilesIter;\n-            } else {\n-                // tiles in both sets match, move on\n-                ++setTilesIter;\n-                ++visTilesIter;\n             }\n+\n+            it = m_readyTiles.erase(it);\n         }\n     }\n-    \n-    // Loop over tileSet and remove any tiles that are neither visible nor proxies\n-    {\n-        auto setTilesIter = m_tileSet.begin();\n-        auto visTilesIter = visibleTiles.begin();\n-        \n-        while (setTilesIter != m_tileSet.end()) {\n-            \n-            if (visTilesIter == visibleTiles.end() || setTilesIter->first < *visTilesIter) {\n-                // visibleTiles is missing an element present in tileSet\n-                if (setTilesIter->second->getProxyCounter() <= 0) {\n-                    removeTile(setTilesIter);\n-                    m_tileSetChanged = true;\n+\n+    if (m_view->changedOnLastUpdate() || m_tileSetChanged) {\n+\n+        const std::set<TileID>& visibleTiles = m_view->getVisibleTiles();\n+\n+        // Loop over visibleTiles and add any needed tiles to tileSet\n+        {\n+            auto setTilesIter = m_tileSet.begin();\n+            auto visTilesIter = visibleTiles.begin();\n+\n+            while (visTilesIter != visibleTiles.end()) {\n+\n+                auto& visTileId = *visTilesIter;\n+                auto& curTileId = setTilesIter == m_tileSet.end() ? NOT_A_TILE : setTilesIter->first;\n+\n+                if (visTileId == curTileId) {\n+                    if (setTilesIter->second->state() == MapTile::None) {\n+                        logMsg(\"[%d, %d, %d] - Enqueue\\n\", curTileId.z, curTileId.x, curTileId.y);\n+                        m_loadQueue.push_back(curTileId);","path":"core/src/tile/tileManager.cpp","position":248,"original_position":248,"commit_id":"e6b84a6f03dd7a843a1aff49812ddbe1be1e7538","original_commit_id":"e6b84a6f03dd7a843a1aff49812ddbe1be1e7538","user":{"login":"tallytalwar","id":360641,"avatar_url":"https://avatars.githubusercontent.com/u/360641?v=3","gravatar_id":"","url":"https://api.github.com/users/tallytalwar","html_url":"https://github.com/tallytalwar","followers_url":"https://api.github.com/users/tallytalwar/followers","following_url":"https://api.github.com/users/tallytalwar/following{/other_user}","gists_url":"https://api.github.com/users/tallytalwar/gists{/gist_id}","starred_url":"https://api.github.com/users/tallytalwar/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tallytalwar/subscriptions","organizations_url":"https://api.github.com/users/tallytalwar/orgs","repos_url":"https://api.github.com/users/tallytalwar/repos","events_url":"https://api.github.com/users/tallytalwar/events{/privacy}","received_events_url":"https://api.github.com/users/tallytalwar/received_events","type":"User","site_admin":false},"body":"I get whats the relationship here with MapTile::None, m_tilesetChanged and enqueueing tiles.\r\n\r\nBut don't you feel its wasteful to clear the loadQueue every iteration. I could be missing something though.","created_at":"2015-06-29T21:20:07Z","updated_at":"2015-06-29T21:20:07Z","html_url":"https://github.com/tangrams/tangram-es/pull/161#discussion_r33514876","pull_request_url":"https://api.github.com/repos/tangrams/tangram-es/pulls/161","_links":{"self":{"href":"https://api.github.com/repos/tangrams/tangram-es/pulls/comments/33514876"},"html":{"href":"https://github.com/tangrams/tangram-es/pull/161#discussion_r33514876"},"pull_request":{"href":"https://api.github.com/repos/tangrams/tangram-es/pulls/161"}}},"pull_request":{"url":"https://api.github.com/repos/tangrams/tangram-es/pulls/161","id":38558258,"html_url":"https://github.com/tangrams/tangram-es/pull/161","diff_url":"https://github.com/tangrams/tangram-es/pull/161.diff","patch_url":"https://github.com/tangrams/tangram-es/pull/161.patch","issue_url":"https://api.github.com/repos/tangrams/tangram-es/issues/161","number":161,"state":"open","locked":false,"title":"tile management","user":{"login":"hjanetzek","id":1747,"avatar_url":"https://avatars.githubusercontent.com/u/1747?v=3","gravatar_id":"","url":"https://api.github.com/users/hjanetzek","html_url":"https://github.com/hjanetzek","followers_url":"https://api.github.com/users/hjanetzek/followers","following_url":"https://api.github.com/users/hjanetzek/following{/other_user}","gists_url":"https://api.github.com/users/hjanetzek/gists{/gist_id}","starred_url":"https://api.github.com/users/hjanetzek/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/hjanetzek/subscriptions","organizations_url":"https://api.github.com/users/hjanetzek/orgs","repos_url":"https://api.github.com/users/hjanetzek/repos","events_url":"https://api.github.com/users/hjanetzek/events{/privacy}","received_events_url":"https://api.github.com/users/hjanetzek/received_events","type":"User","site_admin":false},"body":"This is far from ready - but works much better already :)\r\n\r\nNext step would be to let the workers pull their jobs from the TileManager. Suggestions are welcome! I'll probably be off until Monday.\r\n","created_at":"2015-06-25T15:12:52Z","updated_at":"2015-06-29T21:20:07Z","closed_at":null,"merged_at":null,"merge_commit_sha":"be4abdfd520a1e7f22bc096d75029a118b41f0a7","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/tangrams/tangram-es/pulls/161/commits","review_comments_url":"https://api.github.com/repos/tangrams/tangram-es/pulls/161/comments","review_comment_url":"https://api.github.com/repos/tangrams/tangram-es/pulls/comments{/number}","comments_url":"https://api.github.com/repos/tangrams/tangram-es/issues/161/comments","statuses_url":"https://api.github.com/repos/tangrams/tangram-es/statuses/e6b84a6f03dd7a843a1aff49812ddbe1be1e7538","head":{"label":"tangrams:wip-tile-management","ref":"wip-tile-management","sha":"e6b84a6f03dd7a843a1aff49812ddbe1be1e7538","user":{"login":"tangrams","id":8495302,"avatar_url":"https://avatars.githubusercontent.com/u/8495302?v=3","gravatar_id":"","url":"https://api.github.com/users/tangrams","html_url":"https://github.com/tangrams","followers_url":"https://api.github.com/users/tangrams/followers","following_url":"https://api.github.com/users/tangrams/following{/other_user}","gists_url":"https://api.github.com/users/tangrams/gists{/gist_id}","starred_url":"https://api.github.com/users/tangrams/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tangrams/subscriptions","organizations_url":"https://api.github.com/users/tangrams/orgs","repos_url":"https://api.github.com/users/tangrams/repos","events_url":"https://api.github.com/users/tangrams/events{/privacy}","received_events_url":"https://api.github.com/users/tangrams/received_events","type":"Organization","site_admin":false},"repo":{"id":23391501,"name":"tangram-es","full_name":"tangrams/tangram-es","owner":{"login":"tangrams","id":8495302,"avatar_url":"https://avatars.githubusercontent.com/u/8495302?v=3","gravatar_id":"","url":"https://api.github.com/users/tangrams","html_url":"https://github.com/tangrams","followers_url":"https://api.github.com/users/tangrams/followers","following_url":"https://api.github.com/users/tangrams/following{/other_user}","gists_url":"https://api.github.com/users/tangrams/gists{/gist_id}","starred_url":"https://api.github.com/users/tangrams/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tangrams/subscriptions","organizations_url":"https://api.github.com/users/tangrams/orgs","repos_url":"https://api.github.com/users/tangrams/repos","events_url":"https://api.github.com/users/tangrams/events{/privacy}","received_events_url":"https://api.github.com/users/tangrams/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/tangrams/tangram-es","description":"2D and 3D map renderer using OpenGL ES","fork":false,"url":"https://api.github.com/repos/tangrams/tangram-es","forks_url":"https://api.github.com/repos/tangrams/tangram-es/forks","keys_url":"https://api.github.com/repos/tangrams/tangram-es/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tangrams/tangram-es/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tangrams/tangram-es/teams","hooks_url":"https://api.github.com/repos/tangrams/tangram-es/hooks","issue_events_url":"https://api.github.com/repos/tangrams/tangram-es/issues/events{/number}","events_url":"https://api.github.com/repos/tangrams/tangram-es/events","assignees_url":"https://api.github.com/repos/tangrams/tangram-es/assignees{/user}","branches_url":"https://api.github.com/repos/tangrams/tangram-es/branches{/branch}","tags_url":"https://api.github.com/repos/tangrams/tangram-es/tags","blobs_url":"https://api.github.com/repos/tangrams/tangram-es/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tangrams/tangram-es/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tangrams/tangram-es/git/refs{/sha}","trees_url":"https://api.github.com/repos/tangrams/tangram-es/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tangrams/tangram-es/statuses/{sha}","languages_url":"https://api.github.com/repos/tangrams/tangram-es/languages","stargazers_url":"https://api.github.com/repos/tangrams/tangram-es/stargazers","contributors_url":"https://api.github.com/repos/tangrams/tangram-es/contributors","subscribers_url":"https://api.github.com/repos/tangrams/tangram-es/subscribers","subscription_url":"https://api.github.com/repos/tangrams/tangram-es/subscription","commits_url":"https://api.github.com/repos/tangrams/tangram-es/commits{/sha}","git_commits_url":"https://api.github.com/repos/tangrams/tangram-es/git/commits{/sha}","comments_url":"https://api.github.com/repos/tangrams/tangram-es/comments{/number}","issue_comment_url":"https://api.github.com/repos/tangrams/tangram-es/issues/comments{/number}","contents_url":"https://api.github.com/repos/tangrams/tangram-es/contents/{+path}","compare_url":"https://api.github.com/repos/tangrams/tangram-es/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tangrams/tangram-es/merges","archive_url":"https://api.github.com/repos/tangrams/tangram-es/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tangrams/tangram-es/downloads","issues_url":"https://api.github.com/repos/tangrams/tangram-es/issues{/number}","pulls_url":"https://api.github.com/repos/tangrams/tangram-es/pulls{/number}","milestones_url":"https://api.github.com/repos/tangrams/tangram-es/milestones{/number}","notifications_url":"https://api.github.com/repos/tangrams/tangram-es/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tangrams/tangram-es/labels{/name}","releases_url":"https://api.github.com/repos/tangrams/tangram-es/releases{/id}","created_at":"2014-08-27T13:56:56Z","updated_at":"2015-06-16T07:46:09Z","pushed_at":"2015-06-29T20:25:20Z","git_url":"git://github.com/tangrams/tangram-es.git","ssh_url":"git@github.com:tangrams/tangram-es.git","clone_url":"https://github.com/tangrams/tangram-es.git","svn_url":"https://github.com/tangrams/tangram-es","homepage":"","size":27021,"stargazers_count":58,"watchers_count":58,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":12,"mirror_url":null,"open_issues_count":8,"forks":12,"open_issues":8,"watchers":58,"default_branch":"master"}},"base":{"label":"tangrams:master","ref":"master","sha":"69b0d492c9b780c8e23155b314481ae816e36155","user":{"login":"tangrams","id":8495302,"avatar_url":"https://avatars.githubusercontent.com/u/8495302?v=3","gravatar_id":"","url":"https://api.github.com/users/tangrams","html_url":"https://github.com/tangrams","followers_url":"https://api.github.com/users/tangrams/followers","following_url":"https://api.github.com/users/tangrams/following{/other_user}","gists_url":"https://api.github.com/users/tangrams/gists{/gist_id}","starred_url":"https://api.github.com/users/tangrams/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tangrams/subscriptions","organizations_url":"https://api.github.com/users/tangrams/orgs","repos_url":"https://api.github.com/users/tangrams/repos","events_url":"https://api.github.com/users/tangrams/events{/privacy}","received_events_url":"https://api.github.com/users/tangrams/received_events","type":"Organization","site_admin":false},"repo":{"id":23391501,"name":"tangram-es","full_name":"tangrams/tangram-es","owner":{"login":"tangrams","id":8495302,"avatar_url":"https://avatars.githubusercontent.com/u/8495302?v=3","gravatar_id":"","url":"https://api.github.com/users/tangrams","html_url":"https://github.com/tangrams","followers_url":"https://api.github.com/users/tangrams/followers","following_url":"https://api.github.com/users/tangrams/following{/other_user}","gists_url":"https://api.github.com/users/tangrams/gists{/gist_id}","starred_url":"https://api.github.com/users/tangrams/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/tangrams/subscriptions","organizations_url":"https://api.github.com/users/tangrams/orgs","repos_url":"https://api.github.com/users/tangrams/repos","events_url":"https://api.github.com/users/tangrams/events{/privacy}","received_events_url":"https://api.github.com/users/tangrams/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/tangrams/tangram-es","description":"2D and 3D map renderer using OpenGL ES","fork":false,"url":"https://api.github.com/repos/tangrams/tangram-es","forks_url":"https://api.github.com/repos/tangrams/tangram-es/forks","keys_url":"https://api.github.com/repos/tangrams/tangram-es/keys{/key_id}","collaborators_url":"https://api.github.com/repos/tangrams/tangram-es/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/tangrams/tangram-es/teams","hooks_url":"https://api.github.com/repos/tangrams/tangram-es/hooks","issue_events_url":"https://api.github.com/repos/tangrams/tangram-es/issues/events{/number}","events_url":"https://api.github.com/repos/tangrams/tangram-es/events","assignees_url":"https://api.github.com/repos/tangrams/tangram-es/assignees{/user}","branches_url":"https://api.github.com/repos/tangrams/tangram-es/branches{/branch}","tags_url":"https://api.github.com/repos/tangrams/tangram-es/tags","blobs_url":"https://api.github.com/repos/tangrams/tangram-es/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/tangrams/tangram-es/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/tangrams/tangram-es/git/refs{/sha}","trees_url":"https://api.github.com/repos/tangrams/tangram-es/git/trees{/sha}","statuses_url":"https://api.github.com/repos/tangrams/tangram-es/statuses/{sha}","languages_url":"https://api.github.com/repos/tangrams/tangram-es/languages","stargazers_url":"https://api.github.com/repos/tangrams/tangram-es/stargazers","contributors_url":"https://api.github.com/repos/tangrams/tangram-es/contributors","subscribers_url":"https://api.github.com/repos/tangrams/tangram-es/subscribers","subscription_url":"https://api.github.com/repos/tangrams/tangram-es/subscription","commits_url":"https://api.github.com/repos/tangrams/tangram-es/commits{/sha}","git_commits_url":"https://api.github.com/repos/tangrams/tangram-es/git/commits{/sha}","comments_url":"https://api.github.com/repos/tangrams/tangram-es/comments{/number}","issue_comment_url":"https://api.github.com/repos/tangrams/tangram-es/issues/comments{/number}","contents_url":"https://api.github.com/repos/tangrams/tangram-es/contents/{+path}","compare_url":"https://api.github.com/repos/tangrams/tangram-es/compare/{base}...{head}","merges_url":"https://api.github.com/repos/tangrams/tangram-es/merges","archive_url":"https://api.github.com/repos/tangrams/tangram-es/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/tangrams/tangram-es/downloads","issues_url":"https://api.github.com/repos/tangrams/tangram-es/issues{/number}","pulls_url":"https://api.github.com/repos/tangrams/tangram-es/pulls{/number}","milestones_url":"https://api.github.com/repos/tangrams/tangram-es/milestones{/number}","notifications_url":"https://api.github.com/repos/tangrams/tangram-es/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/tangrams/tangram-es/labels{/name}","releases_url":"https://api.github.com/repos/tangrams/tangram-es/releases{/id}","created_at":"2014-08-27T13:56:56Z","updated_at":"2015-06-16T07:46:09Z","pushed_at":"2015-06-29T20:25:20Z","git_url":"git://github.com/tangrams/tangram-es.git","ssh_url":"git@github.com:tangrams/tangram-es.git","clone_url":"https://github.com/tangrams/tangram-es.git","svn_url":"https://github.com/tangrams/tangram-es","homepage":"","size":27021,"stargazers_count":58,"watchers_count":58,"language":"C++","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":12,"mirror_url":null,"open_issues_count":8,"forks":12,"open_issues":8,"watchers":58,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/tangrams/tangram-es/pulls/161"},"html":{"href":"https://github.com/tangrams/tangram-es/pull/161"},"issue":{"href":"https://api.github.com/repos/tangrams/tangram-es/issues/161"},"comments":{"href":"https://api.github.com/repos/tangrams/tangram-es/issues/161/comments"},"review_comments":{"href":"https://api.github.com/repos/tangrams/tangram-es/pulls/161/comments"},"review_comment":{"href":"https://api.github.com/repos/tangrams/tangram-es/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/tangrams/tangram-es/pulls/161/commits"},"statuses":{"href":"https://api.github.com/repos/tangrams/tangram-es/statuses/e6b84a6f03dd7a843a1aff49812ddbe1be1e7538"}}}}