diff --git a/neural/Sensor.lua b/neural/Sensor.lua index f2d0db5..7e0cf06 100644 --- a/neural/Sensor.lua +++ b/neural/Sensor.lua @@ -1,10 +1,11 @@ -local Config = require('config') -local Event = require('event') -local Project = require('neural.project') -local UI = require('ui') -local Util = require('util') +local Config = require('config') +local Event = require('event') +local UI = require('ui') +local Util = require('util') -local device = _G.device +local colors = _G.colors +local device = _G.device +local gps = _G.gps local glasses = device['plethora:glasses'] local intro = device['plethora:introspection'] @@ -13,71 +14,138 @@ local sensor = device['plethora:sensor'] or UI:configure('Sensor', ...) -local target +local projecting = { } +local offset +local canvas = glasses and intro and glasses.canvas3d().create() local config = Config.load('Sensor', { ignore = { } }) -if not config.ignore then - config.ignore = { } -end local page = UI.Page { + tabs = UI.Tabs { + listing = UI.Tab { + tabTitle = 'Listing', + menuBar = UI.MenuBar { + buttons = { + { text = 'Ignore', event = 'ignore' }, + { text = 'Details', event = 'detail' }, + }, + }, + grid = UI.ScrollingGrid { + y = 2, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'X', key = 'x', width = 3, align = 'right' }, + { heading = 'Y', key = 'y', width = 3, align = 'right' }, + { heading = 'Z', key = 'z', width = 3, align = 'right' }, + }, + sortColumn = 'displayName', + }, + }, + summary = UI.Tab { + tabTitle = 'Summary', + menuBar = UI.MenuBar { + buttons = { + { text = 'Projector', event = 'project' }, + { text = 'Ignore', event = 'ignore' }, + }, + }, + grid = UI.ScrollingGrid { + y = 2, + columns = { + { heading = 'Name', key = 'displayName' }, + { heading = 'Count', key = 'count', width = 5, align = 'right' }, + }, + sortColumn = 'displayName', + }, + }, + accelerators = { + q = 'quit', + }, + }, +} + +local listing = page.tabs.listing +local summary = page.tabs.summary + +local detail = UI.Page { menuBar = UI.MenuBar { buttons = { - { text = 'Projector', event = 'project' }, - { text = 'Totals', event = 'totals' }, - { text = 'Ignore', event = 'ignore' }, - { text = 'Details', event = 'detail' }, + { text = 'Back', event = 'back', x = -6 }, }, }, grid = UI.ScrollingGrid { y = 2, columns = { - { heading = 'Name', key = 'displayName' }, - { heading = ' X', key = 'x', width = 3, align = 'right' }, - { heading = ' Y', key = 'y', width = 3, align = 'right' }, - { heading = ' Z', key = 'z', width = 3, align = 'right' }, - }, - values = sensor.sense(), - sortColumn = 'displayName', - }, - accelerators = { - q = 'quit', - }, - detail = UI.SlideOut { - menuBar = UI.MenuBar { - buttons = { - { text = 'Projector', event = 'project-target' }, - { text = 'Back', event = 'hide', x = -6 }, - }, - }, - grid = UI.ScrollingGrid { - y = 2, - columns = { - { heading = 'Name', key = 'name' }, - { heading = 'Value', key = 'value' }, - }, - sortColumn = 'name', - autospace = true, + { heading = 'Name', key = 'name' }, + { heading = 'Value', key = 'value' }, }, + sortColumn = 'name', + autospace = true, }, } -function page.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.x = row.x and math.floor(row.x) or '' - row.y = row.y and math.floor(row.y) or '' - row.z = row.z and math.floor(row.z) or '' - return row +local function getPoint() + local pt = { gps.locate() } + return { + x = pt[1], + y = pt[2], + z = pt[3], + } end -function page.detail:show(entity) - self.entity = entity -- to allow for debugging in Lua +local function project(entities) + if canvas then + local pos = getPoint() + local pts = { } + if not offset then + offset = pos + end + + for _, b in pairs(entities) do + if b.x then + pts[table.concat({ + math.floor(pos.x + b.x), + math.floor(pos.y + b.y), + math.floor(pos.z + b.z) }, ':')] = b + end + end + + for key, b in pairs(pts) do + if not projecting[key] then + local box = canvas.addBox( + pos.x - offset.x + b.x, + pos.y - offset.y + b.y - .25, + pos.z - offset.z + b.z, + .25, .25, .25) + box.setDepthTested(false) + projecting[key] = box + end + end + + for key, box in pairs(projecting) do + if not pts[key] then + box.remove() + projecting[key] = nil + end + end + end +end + +local function ignoreEntity(entity) + if entity then + config.ignore[entity.name] = true + Config.update('Sensor', config) + end +end + +function detail:enable(entity) local function update() local t = { } - for k,v in pairs(self.entity) do + local meta = sensor.getMetaByID(entity.id) or { } + for k,v in pairs(meta) do if type(v) ~= 'table' then table.insert(t, { name = k, @@ -85,115 +153,151 @@ function page.detail:show(entity) }) end end + project({ meta }) return t end - if entity.id then - self.handler = Event.onInterval(.5, function() - local e = sensor.getMetaByID(self.entity.id) - if e then - self.entity = e - self.grid:setValues(update()) - self.grid:draw() - self.grid:sync() - end - end) - end + self.handler = Event.onInterval(.5, function() + self.grid:setValues(update()) + self.grid:draw() + self.grid:sync() + end) self.grid:setValues(update()) - return UI.SlideOut.show(self) + return UI.Page.enable(self) end -function page.detail:hide() +function detail:disable() if self.handler then Event.off(self.handler) self.handler = nil end - return UI.SlideOut.hide(self) + project({ }) + return UI.Page.disable(self) +end + +function detail:eventHandler(event) + if event.type == 'back' then + return UI:setPreviousPage() + end + return UI.Page.eventHandler(self, event) +end + +function listing.grid:getDisplayValues(row) + row = Util.shallowCopy(row) + row.x = row.x and math.floor(row.x) or '' + row.y = row.y and math.floor(row.y) or '' + row.z = row.z and math.floor(row.z) or '' + return row +end + +function listing:enable() + self.handler = Event.onInterval(.5, function() + local entities = sensor.sense() + Util.filterInplace(entities, function(e) return not config.ignore[e.name] end) + + self.grid:setValues(entities) + self.grid:draw() + self:sync() + end) + return UI.Tab.enable(self) +end + +function listing:disable() + Event.off(self.handler) + UI.Tab.disable(self) +end + +function listing:eventHandler(event) + if event.type == 'detail' or event.type == 'grid_select' then + local selected = self.grid:getSelected() + if selected then + UI:setPage(detail, selected) + end + + elseif event.type == 'ignore' then + ignoreEntity(self.grid:getSelected()) + end + + return UI.Tab.eventHandler(self, event) +end + +function summary:enable() + self.handler = Event.onInterval(.5, function() + local entities = sensor.sense() + Util.filterInplace(entities, function(e) return not config.ignore[e.name] end) + + local t = { } + local highlight = { } + for _,v in pairs(entities) do + if t[v.name] then + t[v.name].count = t[v.name].count + 1 + else + t[v.name] = { displayName = v.displayName, count = 1, name = v.name } + end + if self.target == v.name then + table.insert(highlight, v) + end + end + + project(highlight) + + self.grid:setValues(t) + self.grid:draw() + self:sync() + end) + + self.target = nil + return UI.Tab.enable(self) +end + +function summary:disable() + project({ }) + Event.off(self.handler) + UI.Tab.disable(self) +end + +function summary.grid:getRowTextColor(row, selected) + if row.name == self.parent.target then + return colors.yellow + end + return UI.Grid:getRowTextColor(row, selected) +end + +function summary:eventHandler(event) + if event.type == 'ignore' then + ignoreEntity(self.grid:getSelected()) + + elseif event.type == 'project' or event.type == 'grid_select' then + local selected = self.grid:getSelected() + if selected then + self.target = selected.name + self.grid:draw() + end + end + + return UI.Tab.eventHandler(self, event) end function page:eventHandler(event) if event.type == 'quit' then Event.exitPullEvents() - elseif event.type == 'totals' then - config.totals = not config.totals - Config.update('Sensor', config) - - elseif event.type == 'detail' or event.type == 'grid_select' then - local selected = self.grid:getSelected() - if selected then - target = selected.name - self.detail:show(selected) - end - - elseif event.type == 'hide' then - self.detail:hide() - - elseif event.type == 'ignore' then - local selected = self.grid:getSelected() - if selected then - config.ignore[selected.name] = true - end - Config.update('Sensor', config) - - elseif event.type == 'project' or event.type == 'project-target' then - if event.type == 'project' then - target = nil - end - - if glasses then - config.projecting = not config.projecting - if config.projecting then - Project:init(glasses.canvas()) - else - Project.canvas:clear() - end - end + elseif event.type == 'tab_activate' then + config.activeTab = event.activated.tabTitle Config.update('Sensor', config) end UI.Page.eventHandler(self, event) end -Event.onInterval(.5, function() - local entities = sensor.sense() - Util.filterInplace(entities, function(e) return not config.ignore[e.name] end) - - if config.projecting and glasses and intro then - local meta = intro.getMetaOwner() - Project.canvas:clear() - local t = entities - if target then - t = Util.filter(entities, function(e) return e.name == target end) - end - Project:drawPoints(meta, t, 'X', 0xFFDF50AA) - end - - if config.totals then - local t = { } - for _,v in pairs(entities) do - if t[v.name] then - t[v.name].z = t[v.name].z + 1 - else - t[v.name] = { displayName = v.displayName, z = 1, name = v.name } - end - end - entities = t - end - - page.grid:setValues(entities) - page.grid:draw() - page:sync() -end) - -if config.projecting and glasses then - Project:init(glasses.canvas()) +if config.activeTab then + page.tabs:selectTab(Util.find(page.tabs.children, 'tabTitle', config.activeTab)) end UI:setPage(page) UI:pullEvents() -if config.projecting and glasses then - Project.canvas:clear() +if canvas then + canvas:clear() end diff --git a/neural/apis/project.lua b/neural/apis/project.lua deleted file mode 100644 index 13125db..0000000 --- a/neural/apis/project.lua +++ /dev/null @@ -1,102 +0,0 @@ ------------------------------------------------------------------------------------------- --- ORE3D - Fully Immersive Augmented Reality X-RAY Vision for Ore Mining using Plethora -- ------------------------------------------------------------------------------------------- - --- CREATED BY: --- HydroNitrogen (a.k.a. GoogleTech, Wendelstein7) --- Bram S. (a.k.a ThatBram0101, bram0101) - --- LICENCE: ZLIB/libpng Licence (Zlib) (MODIFIED) --- Copyright (c) 2018 HydroNitrogen & Bram S. --- This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. --- Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: --- 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. --- 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. --- 3. Every version, altered and original, must always contain a link to the following internet page: https://energetic.pw/computercraft/ore3d --- 4. This notice may not be removed or altered from any source distribution. - --- VERSION 2018-03-29 15:52 (Dates are way easier than version numbers, :P ) - - -local FOV = math.rad(70) -- Change according to your Minecraft settings! Minecraft default: 75 -local ASPECT_RATIO = 1.8 -- Aspect ratio of your view - for a full HD screen: fullscreen: 1.7777, windowed: 1.8 -local hintToBehind = true -- Show all blocks behind you on the sides of your screen -local CHAR_SIZE = 16 -- the base size of the indicators - -local SCRHOR = (1 / math.tan(FOV / 2)) / ASPECT_RATIO -local SCRVER = (1 / math.tan(FOV / 2)) - --- Convert to perspective projection, removes a dimention (z) (optimized, uses precalculated values) -local function toPerspective(x, y, z) - return SCRHOR * x / z, SCRVER * y / z -end - -local function getCharSize(d) -- Calculates size of indicators using distance - return CHAR_SIZE / d -end - -local Project = { } - -function Project:init(canvas) - self.canvas = canvas - self.cx, self.cy = canvas.getSize() - self.cxhalf = self.cx / 2 - self.cyhalf = self.cy / 2 -end - -function Project:ndcToSpc(x, y) - return x * self.cxhalf + self.cxhalf, y * self.cyhalf + self.cyhalf -end - -function Project:isOnScreen(x, y, d) -- determines if something is visible - return (x >= 1 and x - getCharSize(d) < self.cx) and (y >= 1 and y - getCharSize(d) < self.cy) -end - -function Project:drawPoints(meta, pts, isBlock, color) - local yaw = math.rad(meta.yaw) - local pitch = math.rad(meta.pitch) - - local ysin = math.sin(yaw) - local ycos = math.cos(yaw) - - local psin = math.sin(pitch) - local pcos = math.cos(pitch) - - local function rotate(x, y, z) -- Matrix operation: rotate (optimized, uses precalculated values) - local newx = ycos * x + ysin * z - local newz = -ysin * x + ycos * z - - local newy = pcos * y - psin * newz - newz = psin * y + pcos * newz - - return newx,newy,newz - end - - for _, b in pairs(pts) do - - if isBlock then - --b.x = b.x - .5 - --b.y = b.y - 1 - --b.z = b.z - .5 - end - - local x, y, z = rotate(b.x - meta.x, -b.y + meta.y, b.z - meta.z) - local d = math.sqrt(x * x + y * y + z * z) - - if hintToBehind and z < 0 then z = 0.001 end - - if z >= 0 or hintToBehind then -- render only if point is visible OR hintToBehind is enabled - x,y = toPerspective(x, y, -z) - x,y = self:ndcToSpc(x, y) - - if hintToBehind or self:isOnScreen(x, y, d) then - x = math.min(math.max(x, 1), self.cx - 10 * getCharSize(d)) - y = math.min(math.max(self.cy - y, 1), self.cy - 10 * getCharSize(d)) - - self.canvas.addDot({ x, y }, color, 32 / d) - end - end - end -end - -return Project