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 -- set true to enable debug logging
local DEBUG = false local DEBUG = true
-- Location connection directional options -- Location connection directional options
local BIDIRECTIONAL = 0 local BIDIRECTIONAL = 0
local ONE_WAY = 1 local ONE_WAY = 1
local INCOMING_ONE_WAY = 2
-- Connector draw parameters -- Connector draw parameters
local CONNECTION_THICKNESS = 0.015 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 CONNECTION_COLOR = { 0.4, 0.4, 0.4, 1 }
local DIRECTIONAL_ARROW_DISTANCE = 3.5 local DIRECTIONAL_ARROW_DISTANCE = 3.5
local ARROW_ARM_LENGTH = 0.9 local ARROW_ARM_LENGTH = 0.9
@ -91,7 +94,7 @@ end
function onCollisionEnter(collisionInfo) function onCollisionEnter(collisionInfo)
local obj = collisionInfo.collision_object local obj = collisionInfo.collision_object
local objType = obj.name local objType = obj.name
-- only continue for cards -- only continue for cards
if not collisionEnabled or (objType ~= "Card" and objType ~= "CardCustom") then return end 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 if obj == nil or not isInPlayArea(obj) then
draggingGuids[guid] = nil draggingGuids[guid] = nil
needsConnectionRebuild = true 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 end
-- Even if the last location left the play area, need one last draw to clear the lines -- Even if the last location left the play area, need one last draw to clear the lines
needsConnectionDraw = true needsConnectionDraw = true
@ -159,7 +167,7 @@ function onUpdate()
rebuildConnectionList() rebuildConnectionList()
end end
if needsConnectionDraw then if needsConnectionDraw then
drawConnections() drawDraggingConnections()
end end
end end
@ -174,7 +182,7 @@ function maybeTrackLocation(card)
if metadata.type == "Location" then if metadata.type == "Location" then
locations[card.getGUID()] = metadata locations[card.getGUID()] = metadata
rebuildConnectionList() rebuildConnectionList()
drawConnections() drawBaseConnections()
end end
end end
end end
@ -190,14 +198,14 @@ function maybeUntrackLocation(card)
if locations[card.getGUID()] ~= nil and not card.locked then if locations[card.getGUID()] ~= nil and not card.locked then
locations[card.getGUID()] = nil locations[card.getGUID()] = nil
rebuildConnectionList() rebuildConnectionList()
drawConnections() drawBaseConnections()
end end
end end
-- Builds a list of GUID to GUID connection information based on the currently tracked locations. -- 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, -- 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 -- but does not draw those connections. This should often be followed by a call to
-- drawConnections() -- drawBaseConnections()
function rebuildConnectionList() function rebuildConnectionList()
if not showLocationLinks() then if not showLocationLinks() then
locationConnections = { } locationConnections = { }
@ -256,10 +264,15 @@ function buildConnection(cardId, iconCardList)
for _, connectedGuid in ipairs(iconCardList[icon]) do for _, connectedGuid in ipairs(iconCardList[icon]) do
-- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way -- If the reciprocal exists, convert it to BiDi, otherwise add as a one-way
if locationConnections[connectedGuid] ~= nil 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[connectedGuid][card.getGUID()] = BIDIRECTIONAL
locationConnections[card.getGUID()][connectedGuid] = nil
else else
if locationConnections[connectedGuid] == nil then
locationConnections[connectedGuid] = { }
end
locationConnections[card.getGUID()][connectedGuid] = ONE_WAY locationConnections[card.getGUID()][connectedGuid] = ONE_WAY
locationConnections[connectedGuid][card.getGUID()] = INCOMING_ONE_WAY
end end
end end
end end
@ -283,8 +296,9 @@ function getLocationData(card)
end end
end end
-- Draws the lines for connections currently in locationConnections. -- Draws the lines for connections currently in locationConnections but not in draggingGuids.
function drawConnections() -- Constructed vectors will be set to the playmat
function drawBaseConnections()
if not showLocationLinks() then if not showLocationLinks() then
locationConnections = { } locationConnections = { }
return return
@ -295,14 +309,16 @@ function drawConnections()
-- Objects should reliably exist at this point, but since this can be called during onUpdate the -- Objects should reliably exist at this point, but since this can be called during onUpdate the
-- object checks are conservative just to make sure. -- object checks are conservative just to make sure.
local origin = getObjectFromGUID(originGuid) local origin = getObjectFromGUID(originGuid)
if origin != nil then if draggingGuids[originGuid] == nil and origin != nil then
for targetGuid, direction in pairs(targetGuids) do for targetGuid, direction in pairs(targetGuids) do
local target = getObjectFromGUID(targetGuid) 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 if direction == BIDIRECTIONAL then
addBidirectionalVector(origin, target, cardConnectionLines) addBidirectionalVector(origin, target, self, cardConnectionLines)
elseif direction == ONE_WAY then elseif direction == ONE_WAY then
addOneWayVector(origin, target, cardConnectionLines) addOneWayVector(origin, target, self, cardConnectionLines)
end end
end end
end end
@ -311,22 +327,61 @@ function drawConnections()
self.setVectorLines(cardConnectionLines) self.setVectorLines(cardConnectionLines)
end 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 -- Draws a bidirectional location connection between the two cards, adding the lines to do so to the
-- given lines list. -- given lines list.
---@param card1 Object One of the card objects to connect ---@param card1 Object One of the card objects to connect
---@param card2 Object The other card object 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 ---@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 cardPos1 = card1.getPosition()
local cardPos2 = card2.getPosition() local cardPos2 = card2.getPosition()
cardPos1.y = CONNECTION_LINE_Y cardPos1.y = CONNECTION_LINE_Y
cardPos2.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, { table.insert(lines, {
points = { pos1, pos2 }, points = { pos1, pos2 },
color = CONNECTION_COLOR, color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR,
thickness = CONNECTION_THICKNESS, thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS,
}) })
end end
@ -334,10 +389,12 @@ end
-- given lines list. Arrows will point towards the target card. -- given lines list. Arrows will point towards the target card.
---@param origin Object Origin card in the connection ---@param origin Object Origin card in the connection
---@param target Object Target card object to connect ---@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 ---@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 -- 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 originPos = origin.getPosition()
local targetPos = target.getPosition() local targetPos = target.getPosition()
originPos.y = CONNECTION_LINE_Y 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) local closeToTarget = Vector(targetPos):moveTowards(originPos, distanceFromCard - ARROW_ARM_LENGTH / 2)
if (originPos:distance(closeToOrigin) > originPos:distance(closeToTarget)) then if (originPos:distance(closeToOrigin) > originPos:distance(closeToTarget)) then
addArrowLines(midpoint, originPos, lines) addArrowLines(midpoint, originPos, vectorOwner, lines)
else else
addArrowLines(closeToOrigin, originPos, lines) addArrowLines(closeToOrigin, originPos, vectorOwner, lines)
addArrowLines(closeToTarget, originPos, lines) addArrowLines(closeToTarget, originPos, vectorOwner, lines)
end end
end end
-- Draws an arrowhead at the given position. -- Draws an arrowhead at the given position.
---@param arrowheadPosition Table Centerpoint of the arrowhead to draw (NOT the tip of the arrow) ---@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 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 ---@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 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 arrowArm2 = Vector(arrowheadPos):moveTowards(originPos, ARROW_ARM_LENGTH):sub(arrowheadPos):rotateOver("y", ARROW_ANGLE):add(arrowheadPos)
local head = self.positionToLocal(arrowheadPos) local head = vectorOwner.positionToLocal(arrowheadPos)
local arm1 = self.positionToLocal(arrowArm1) local arm1 = vectorOwner.positionToLocal(arrowArm1)
local arm2 = self.positionToLocal(arrowArm2) local arm2 = vectorOwner.positionToLocal(arrowArm2)
table.insert(lines, { table.insert(lines, {
points = { arm1, head, arm2}, points = { arm1, head, arm2},
color = CONNECTION_COLOR, color = vectorOwner == self and CONNECTION_COLOR or DRAGGING_CONNECTION_COLOR,
thickness = CONNECTION_THICKNESS thickness = vectorOwner == self and CONNECTION_THICKNESS or DRAGGING_CONNECTION_THICKNESS,
}) })
end end