Optimize location connection drawing during drag operations

Previously the entire location map was being rebuilt and redrawn in onUpdate() during a drag operation, including the connectors which weren't changing.  This was causing notable lag on some systems.

This splits the draw operations into two separate pieces - locations on the board which are unchanging, and locations which are being dragged.  This allows only connections which are actually moving to be recalculated.

Since TTS only allows a single list of vector lines per object the dragged lines are now set on the cards themselves rather than the PlayArea.  This may possibly improve performance in itself, by keeping each list of vectors smaller.  Supporting this required some changes in how pre-built connections are handled.
This commit is contained in:
Buhallin 2023-01-11 00:57:06 -08:00
parent 3211ae848d
commit a65cf33059
No known key found for this signature in database
GPG Key ID: DB3C362823852294

View File

@ -3,14 +3,17 @@
---------------------------------------------------------
-- set true to enable debug logging
local DEBUG = false
local DEBUG = true
-- 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
@ -91,7 +94,7 @@ end
function onCollisionEnter(collisionInfo)
local obj = collisionInfo.collision_object
local objType = obj.name
-- only continue for cards
if not collisionEnabled or (objType ~= "Card" and objType ~= "CardCustom") then return end
@ -151,6 +154,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
@ -159,7 +167,7 @@ function onUpdate()
rebuildConnectionList()
end
if needsConnectionDraw then
drawConnections()
drawDraggingConnections()
end
end
@ -174,7 +182,7 @@ function maybeTrackLocation(card)
if metadata.type == "Location" then
locations[card.getGUID()] = metadata
rebuildConnectionList()
drawConnections()
drawBaseConnections()
end
end
end
@ -190,14 +198,14 @@ function maybeUntrackLocation(card)
if locations[card.getGUID()] ~= nil and not card.locked then
locations[card.getGUID()] = nil
rebuildConnectionList()
drawConnections()
drawBaseConnections()
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 = { }
@ -256,10 +264,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
@ -283,8 +296,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
@ -295,14 +309,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
@ -311,22 +327,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
@ -334,10 +389,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
@ -355,28 +412,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