Merge pull request #184 from argonui/faster-connectors

Optimize location connection drawing during drag operations
This commit is contained in:
Chr1Z 2023-01-12 01:06:39 +01:00 committed by GitHub
commit 31006694b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 121 additions and 30 deletions

View File

@ -184,6 +184,14 @@ function onObjectSearchEnd(object, playerColor)
end
end
-- Pass object enter container events to the PlayArea to clear vector lines from dragged cards.
-- This requires the try method as cards won't exist any more after they enter a deck, so the lines
-- can't be cleared.
function tryObjectEnterContainer(container, object)
playAreaAPI.tryObjectEnterContainer(container, object)
return true
end
function drawEncountercard(params)
local position = params[1]
local rotation = params[2]

View File

@ -8,9 +8,12 @@ local DEBUG = false
-- Location connection directional options
local BIDIRECTIONAL = 0
local ONE_WAY = 1
local INCOMING_ONE_WAY = 2
-- Connector draw parameters
local CONNECTION_THICKNESS = 0.015
local DRAGGING_CONNECTION_THICKNESS = 0.15
local DRAGGING_CONNECTION_COLOR = { 0.8, 0.8, 0.8, 1 }
local CONNECTION_COLOR = { 0.4, 0.4, 0.4, 1 }
local DIRECTIONAL_ARROW_DISTANCE = 3.5
local ARROW_ARM_LENGTH = 0.9
@ -99,7 +102,12 @@ function onCollisionEnter(collisionInfo)
if shouldSpawnTokens(card) then
tokenManager.spawnForCard(card)
end
draggingGuids[card.getGUID()] = nil
-- If this card was being dragged, clear the dragging connections. A multi-drag/drop may send
-- the dropped card immediately into a deck, so this has to be done here
if draggingGuids[card.getGUID()] ~= nil then
card.setVectorLines(nil)
draggingGuids[card.getGUID()] = nil
end
maybeTrackLocation(card)
end
@ -150,6 +158,11 @@ function onUpdate()
if obj == nil or not isInPlayArea(obj) then
draggingGuids[guid] = nil
needsConnectionRebuild = true
-- If object still exists then it's been dragged outside the area and needs to clear the
-- lines attached to it
if obj ~= nil then
obj.setVectorLines(nil)
end
end
-- Even if the last location left the play area, need one last draw to clear the lines
needsConnectionDraw = true
@ -158,7 +171,7 @@ function onUpdate()
rebuildConnectionList()
end
if needsConnectionDraw then
drawConnections()
drawDraggingConnections()
end
end
@ -173,7 +186,7 @@ function maybeTrackLocation(card)
if metadata.type == "Location" then
locations[card.getGUID()] = metadata
rebuildConnectionList()
drawConnections()
drawBaseConnections()
end
end
end
@ -189,14 +202,26 @@ function maybeUntrackLocation(card)
if locations[card.getGUID()] ~= nil and not card.locked then
locations[card.getGUID()] = nil
rebuildConnectionList()
drawConnections()
drawBaseConnections()
end
end
-- Global event handler, delegated from Global. Clears any connection lines from dragged cards
-- before they are destroyed by entering a deck. Removal of the card from the dragging list will
-- be handled during the next onUpdate() call.
function tryObjectEnterContainer(params)
for draggedGuid, _ in pairs(draggingGuids) do
local draggedObj = getObjectFromGUID(draggedGuid)
if draggedObj ~= nil then
draggedObj.setVectorLines(nil)
end
end
end
-- Builds a list of GUID to GUID connection information based on the currently tracked locations.
-- This will update the connection information and store it in the locationConnections data member,
-- but does not draw those connections. This should often be followed by a call to
-- drawConnections()
-- drawBaseConnections()
function rebuildConnectionList()
if not showLocationLinks() then
locationConnections = { }
@ -255,10 +280,15 @@ function buildConnection(cardId, iconCardList)
for _, connectedGuid in ipairs(iconCardList[icon]) do
-- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way
if locationConnections[connectedGuid] ~= nil
and locationConnections[connectedGuid][card.getGUID()] ~= nil then
and locationConnections[connectedGuid][card.getGUID()] == ONE_WAY then
locationConnections[connectedGuid][card.getGUID()] = BIDIRECTIONAL
locationConnections[card.getGUID()][connectedGuid] = nil
else
if locationConnections[connectedGuid] == nil then
locationConnections[connectedGuid] = { }
end
locationConnections[card.getGUID()][connectedGuid] = ONE_WAY
locationConnections[connectedGuid][card.getGUID()] = INCOMING_ONE_WAY
end
end
end
@ -282,8 +312,9 @@ function getLocationData(card)
end
end
-- Draws the lines for connections currently in locationConnections.
function drawConnections()
-- Draws the lines for connections currently in locationConnections but not in draggingGuids.
-- Constructed vectors will be set to the playmat
function drawBaseConnections()
if not showLocationLinks() then
locationConnections = { }
return
@ -294,14 +325,16 @@ function drawConnections()
-- Objects should reliably exist at this point, but since this can be called during onUpdate the
-- object checks are conservative just to make sure.
local origin = getObjectFromGUID(originGuid)
if origin != nil then
if draggingGuids[originGuid] == nil and origin != nil then
for targetGuid, direction in pairs(targetGuids) do
local target = getObjectFromGUID(targetGuid)
if target != nil then
if draggingGuids[targetGuid] == nil and target != nil then
-- Since we process the full list, we're guaranteed to hit any ONE_WAY connections later
-- so we can ignore INCOMING_ONE_WAY
if direction == BIDIRECTIONAL then
addBidirectionalVector(origin, target, cardConnectionLines)
addBidirectionalVector(origin, target, self, cardConnectionLines)
elseif direction == ONE_WAY then
addOneWayVector(origin, target, cardConnectionLines)
addOneWayVector(origin, target, self, cardConnectionLines)
end
end
end
@ -310,22 +343,61 @@ function drawConnections()
self.setVectorLines(cardConnectionLines)
end
-- Draws the lines for cards which are currently being dragged.
function drawDraggingConnections()
if not showLocationLinks() then
return
end
local cardConnectionLines = { }
local ownedVectors = { }
for originGuid, _ in pairs(draggingGuids) do
targetGuids = locationConnections[originGuid]
-- Objects should reliably exist at this point, but since this can be called during onUpdate the
-- object checks are conservative just to make sure.
local origin = getObjectFromGUID(originGuid)
if draggingGuids[originGuid] and origin != nil then
ownedVectors[originGuid] = { }
for targetGuid, direction in pairs(targetGuids) do
local target = getObjectFromGUID(targetGuid)
if target != nil then
if direction == BIDIRECTIONAL then
addBidirectionalVector(origin, target, origin, ownedVectors[originGuid])
elseif direction == ONE_WAY then
addOneWayVector(origin, target, origin, ownedVectors[originGuid])
elseif direction == INCOMING_ONE_WAY and not draggingGuids[targetGuid] then
addOneWayVector(target, origin, origin, ownedVectors[originGuid])
end
end
end
end
end
for ownerGuid, vectors in pairs(ownedVectors) do
local card = getObjectFromGUID(ownerGuid)
card.setVectorLines(vectors)
end
end
-- Draws a bidirectional location connection between the two cards, adding the lines to do so to the
-- given lines list.
---@param card1 Object One of the card objects to connect
---@param card2 Object The other card object to connect
---@param vectorOwner Object The object which these lines will be set to. Used for relative
--- positioning and scaling, as well as highlighting connections during a drag operation
---@param lines Table List of vector line elements. Mutable, will be updated to add this connector
function addBidirectionalVector(card1, card2, lines)
function addBidirectionalVector(card1, card2, vectorOwner, lines)
local cardPos1 = card1.getPosition()
local cardPos2 = card2.getPosition()
cardPos1.y = CONNECTION_LINE_Y
cardPos2.y = CONNECTION_LINE_Y
local pos1 = self.positionToLocal(cardPos1)
local pos2 = self.positionToLocal(cardPos2)
local pos1 = vectorOwner.positionToLocal(cardPos1)
local pos2 = vectorOwner.positionToLocal(cardPos2)
table.insert(lines, {
points = { pos1, pos2 },
color = CONNECTION_COLOR,
thickness = CONNECTION_THICKNESS,
color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR,
thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS,
})
end
@ -333,10 +405,12 @@ end
-- given lines list. Arrows will point towards the target card.
---@param origin Object Origin card in the connection
---@param target Object Target card object to connect
---@param vectorOwner Object The object which these lines will be set to. Used for relative
--- positioning and scaling, as well as highlighting connections during a drag operation
---@param lines Table List of vector line elements. Mutable, will be updated to add this connector
function addOneWayVector(origin, target, lines)
function addOneWayVector(origin, target, vectorOwner, lines)
-- Start with the BiDi then add the arrow lines to it
addBidirectionalVector(origin, target, lines)
addBidirectionalVector(origin, target, vectorOwner, lines)
local originPos = origin.getPosition()
local targetPos = target.getPosition()
originPos.y = CONNECTION_LINE_Y
@ -354,28 +428,30 @@ function addOneWayVector(origin, target, lines)
local closeToTarget = Vector(targetPos):moveTowards(originPos, distanceFromCard - ARROW_ARM_LENGTH / 2)
if (originPos:distance(closeToOrigin) > originPos:distance(closeToTarget)) then
addArrowLines(midpoint, originPos, lines)
addArrowLines(midpoint, originPos, vectorOwner, lines)
else
addArrowLines(closeToOrigin, originPos, lines)
addArrowLines(closeToTarget, originPos, lines)
addArrowLines(closeToOrigin, originPos, vectorOwner, lines)
addArrowLines(closeToTarget, originPos, vectorOwner, lines)
end
end
-- Draws an arrowhead at the given position.
---@param arrowheadPosition Table Centerpoint of the arrowhead to draw (NOT the tip of the arrow)
---@param originPos Table Origin point of the connection, used to position the arrow arms
---@param vectorOwner Object The object which these lines will be set to. Used for relative
--- positioning and scaling, as well as highlighting connections during a drag operation
---@param lines Table List of vector line elements. Mutable, will be updated to add this arrow
function addArrowLines(arrowheadPos, originPos, lines)
function addArrowLines(arrowheadPos, originPos, vectorOwner, lines)
local arrowArm1 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", -1 * ARROW_ANGLE):add(arrowheadPos)
local arrowArm2 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", ARROW_ANGLE):add(arrowheadPos)
local head = self.positionToLocal(arrowheadPos)
local arm1 = self.positionToLocal(arrowArm1)
local arm2 = self.positionToLocal(arrowArm2)
local head = vectorOwner.positionToLocal(arrowheadPos)
local arm1 = vectorOwner.positionToLocal(arrowArm1)
local arm2 = vectorOwner.positionToLocal(arrowArm2)
table.insert(lines, {
points = { arm1, head, arm2},
color = CONNECTION_COLOR,
thickness = CONNECTION_THICKNESS
color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR,
thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS,
})
end

View File

@ -48,5 +48,12 @@ do
getObjectFromGUID(PLAY_AREA_GUID).call("setLimitSnapsByType", matchCardTypes)
end
-- Receiver for the Global tryObjectEnterContainer event. Used to clear vector lines from dragged
-- cards before they're destroyed by entering the container
PlayAreaApi.tryObjectEnterContainer = function(container, object)
getObjectFromGUID(PLAY_AREA_GUID).call("tryObjectEnterContainer",
{ container = container, object = object })
end
return PlayAreaApi
end