Effect Radar Implementation v1 #46
|
@ -0,0 +1,74 @@
|
||||||
|
local util = require("common.util")
|
||||||
|
|
||||||
|
---@class CColorRGBA
|
||||||
|
ColorRGBA = {
|
||||||
|
---Create a new Color instance
|
||||||
|
---@param r integer # red or monochrome value
|
||||||
|
---@param g? integer # green value
|
||||||
|
---@param b? integer # blue value
|
||||||
|
---@param a? integer # alpha value, default 255
|
||||||
|
---@return ColorRGBA
|
||||||
|
new = function (r, g , b, a)
|
||||||
|
---@class ColorRGBA : CColorRGBA
|
||||||
|
---@field r integer
|
||||||
|
---@field g integer
|
||||||
|
---@field b integer
|
||||||
|
---@field a integer
|
||||||
|
local o = {
|
||||||
|
r = r or 0,
|
||||||
|
g = g or r,
|
||||||
|
b = b or r,
|
||||||
|
a = a or 255,
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(o, ColorRGBA)
|
||||||
|
return o
|
||||||
|
end,
|
||||||
|
|
||||||
|
---Mix two colors
|
||||||
|
---@param color1 ColorRGBA
|
||||||
|
---@param color2 ColorRGBA
|
||||||
|
---@param factor number
|
||||||
|
---@return ColorRGBA
|
||||||
|
mix = function (color1, color2, factor)
|
||||||
|
local r = math.floor(util.mix(color1.r, color2.r, factor))
|
||||||
|
local g = math.floor(util.mix(color1.g, color2.g, factor))
|
||||||
|
local b = math.floor(util.mix(color1.b, color2.b, factor))
|
||||||
|
local a = math.floor(util.mix(color1.a, color2.a, factor))
|
||||||
|
return ColorRGBA.new(r, g, b, a)
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorRGBA.__index = ColorRGBA
|
||||||
|
ColorRGBA.BLACK = ColorRGBA.new(0)
|
||||||
|
ColorRGBA.GREY = ColorRGBA.new(128)
|
||||||
|
ColorRGBA.WHITE = ColorRGBA.new(255)
|
||||||
|
ColorRGBA.RED = ColorRGBA.new(255, 0, 0)
|
||||||
|
ColorRGBA.GREEN = ColorRGBA.new(0, 255, 0)
|
||||||
|
ColorRGBA.BLUE = ColorRGBA.new(0, 0, 255)
|
||||||
|
ColorRGBA.YELLOW = ColorRGBA.new(255, 255, 0)
|
||||||
|
ColorRGBA.CYAN = ColorRGBA.new(0, 255, 255)
|
||||||
|
ColorRGBA.MAGENTA = ColorRGBA.new(255, 0, 255)
|
||||||
|
|
||||||
|
---Split to components
|
||||||
|
---@return integer # red
|
||||||
|
---@return integer # green
|
||||||
|
---@return integer # blue
|
||||||
|
---@return integer # alpha
|
||||||
|
function ColorRGBA:components()
|
||||||
|
---@cast self ColorRGBA
|
||||||
|
|
||||||
|
return self.r, self.g, self.b, self.a
|
||||||
|
end
|
||||||
|
|
||||||
|
---Split to components scaled to [0.0, 1.0]
|
||||||
|
---@return number # red
|
||||||
|
---@return number # green
|
||||||
|
---@return number # blue
|
||||||
|
---@return number # alpha
|
||||||
|
function ColorRGBA:componentsFloat()
|
||||||
|
---@cast self ColorRGBA
|
||||||
|
local scale = 255
|
||||||
|
|
||||||
|
return self.r / scale, self.g / scale, self.b / scale, self.a / scale
|
||||||
|
end
|
|
@ -0,0 +1,28 @@
|
||||||
|
---@class CPoint2D
|
||||||
|
Point2D = {
|
||||||
|
---Create a Point2D instance
|
||||||
|
---@param x? number # default 0.0
|
||||||
|
---@param y? number # default 0.0
|
||||||
|
---@return Point2D
|
||||||
|
new = function(x, y)
|
||||||
|
---@class Point2D : CPoint2D
|
||||||
|
---@field x number
|
||||||
|
---@field y number
|
||||||
|
local o = {
|
||||||
|
x = x + .0 or .0,
|
||||||
|
y = y + .0 or .0,
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(o, Point2D)
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
Point2D.__index = Point2D
|
||||||
|
Point2D.ZERO = Point2D.new(0, 0)
|
||||||
|
|
||||||
|
function Point2D:coords()
|
||||||
|
---@cast self Point2D
|
||||||
|
|
||||||
|
return self.x, self.y
|
||||||
|
end
|
|
@ -59,6 +59,10 @@ local function lerp(x, x0, y0, x1, y1)
|
||||||
return y0 + (x - x0) * (y1 - y0) / (x1 - x0)
|
return y0 + (x - x0) * (y1 - y0) / (x1 - x0)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function mix(x, y, a)
|
||||||
|
return (1 - a) * x + a * y
|
||||||
|
end
|
||||||
|
|
||||||
--modulo operation for index value
|
--modulo operation for index value
|
||||||
local function modIndex(index, mod)
|
local function modIndex(index, mod)
|
||||||
return (index - 1) % mod + 1
|
return (index - 1) % mod + 1
|
||||||
|
@ -84,6 +88,7 @@ return {
|
||||||
roundToZero = roundToZero,
|
roundToZero = roundToZero,
|
||||||
areaOverlap = areaOverlap,
|
areaOverlap = areaOverlap,
|
||||||
lerp = lerp,
|
lerp = lerp,
|
||||||
|
mix = mix,
|
||||||
modIndex = modIndex,
|
modIndex = modIndex,
|
||||||
firstAlphaNum = firstAlphaNum,
|
firstAlphaNum = firstAlphaNum,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,443 @@
|
||||||
|
--[[
|
||||||
|
S2 song attribute radar component
|
||||||
|
Original code thanks to RealFD, he's a real homie
|
||||||
|
]]
|
||||||
|
|
||||||
|
require("common.globals")
|
||||||
|
require("api.point2d")
|
||||||
|
require("api.color")
|
||||||
|
|
||||||
|
local Dim = require("common.dimensions")
|
||||||
|
|
||||||
|
Dim.updateResolution()
|
||||||
|
|
||||||
|
local RADAR_PURPLE = ColorRGBA.new(238, 130, 238)
|
||||||
|
local RADAR_MAGENTA = ColorRGBA.new(191, 70, 235)
|
||||||
|
local RADAR_GREEN = ColorRGBA.new(0, 255, 100)
|
||||||
|
|
||||||
|
---@param p1 Point2D
|
||||||
|
---@param p2 Point2D
|
||||||
|
---@param width number
|
||||||
|
---@param color ColorRGBA
|
||||||
|
local function drawLine(p1, p2, width, color)
|
||||||
|
gfx.BeginPath()
|
||||||
|
gfx.MoveTo(p1:coords())
|
||||||
|
gfx.LineTo(p2:coords())
|
||||||
|
gfx.StrokeColor(color:components())
|
||||||
|
gfx.StrokeWidth(width)
|
||||||
|
gfx.Stroke()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param pos Point2D
|
||||||
|
---@param text string
|
||||||
|
---@param outlineWidth number
|
||||||
|
---@param color ColorRGBA
|
||||||
|
local function renderOutlinedText(pos, text, outlineWidth, color)
|
||||||
|
local x, y = pos:coords()
|
||||||
|
|
||||||
|
gfx.FillColor(ColorRGBA.BLACK:components());
|
||||||
|
gfx.Text(text, x - outlineWidth, y + outlineWidth);
|
||||||
|
gfx.Text(text, x - outlineWidth, y - outlineWidth);
|
||||||
|
gfx.Text(text, x + outlineWidth, y + outlineWidth);
|
||||||
|
gfx.Text(text, x + outlineWidth, y - outlineWidth);
|
||||||
|
|
||||||
|
gfx.FillColor(color:components());
|
||||||
|
gfx.Text(text, x, y);
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param pos Point2D
|
||||||
|
---@param graphdata table
|
||||||
|
local function drawDebugText(pos, graphdata)
|
||||||
|
local color = ColorRGBA.WHITE
|
||||||
|
gfx.Save()
|
||||||
|
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_CENTER)
|
||||||
|
--renderOutlinedText(x, 20, '"' .. txtFilePath .. '"', 1, 255, 255, 255)
|
||||||
|
renderOutlinedText(pos, "NOTES = " .. graphdata.notes, 1, color)
|
||||||
|
renderOutlinedText(pos, "PEAK = " .. graphdata.peak, 1, color)
|
||||||
|
renderOutlinedText(pos, "TSUMAMI = " .. graphdata.tsumami, 1, color)
|
||||||
|
renderOutlinedText(pos, "TRICKY = " .. graphdata.tricky, 1, color)
|
||||||
|
renderOutlinedText(pos, "ONE-HAND = " .. graphdata.onehand, 1, color)
|
||||||
|
renderOutlinedText(pos, "HAND-TRIP = " .. graphdata.handtrip, 1, color)
|
||||||
|
--renderOutlinedText(pos, "NOTES (Relative) = " .. graphdata.notes_relative, 1, color)
|
||||||
|
--renderOutlinedText(pos, "TOTAL-MESURES = " .. graphdata.measures, 1, color)
|
||||||
|
gfx.Restore()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@class CRadarAttributes
|
||||||
|
RadarAttributes = {
|
||||||
|
---Create RadarAttributes instance
|
||||||
|
---@param text? string # default ""
|
||||||
|
---@param pos? Point2D # default (0, 0)
|
||||||
|
---@param color? ColorRGBA # default BLACK
|
||||||
|
---@return RadarAttributes
|
||||||
|
new = function (text, pos, color)
|
||||||
|
---@class RadarAttributes
|
||||||
|
---@field text string
|
||||||
|
---@field pos Point2D
|
||||||
|
---@field color ColorRGBA
|
||||||
|
local o = {
|
||||||
|
text = text or "",
|
||||||
|
pos = pos or Point2D.ZERO,
|
||||||
|
color = color or ColorRGBA.BLACK
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(o, RadarAttributes)
|
||||||
|
return o
|
||||||
|
end
|
||||||
|
}
|
||||||
|
RadarAttributes.__index = RadarAttributes
|
||||||
|
|
||||||
|
|
||||||
|
---@class CRadar
|
||||||
|
Radar = {
|
||||||
|
---@type RadarAttributes[][]
|
||||||
|
ATTRIBUTES = {
|
||||||
|
{RadarAttributes.new("notes", Point2D.new(0, -30), ColorRGBA.CYAN),},
|
||||||
|
{RadarAttributes.new("peak", Point2D.new(40, -20), ColorRGBA.RED),},
|
||||||
|
{RadarAttributes.new("tsumami", Point2D.new(40, 20), RADAR_PURPLE),},
|
||||||
|
{RadarAttributes.new("tricky", Point2D.new(0, 30), ColorRGBA.YELLOW),},
|
||||||
|
{
|
||||||
|
RadarAttributes.new("hand", Point2D.new(-40, 20), RADAR_MAGENTA),
|
||||||
|
RadarAttributes.new("trip", Point2D.new(5, 18), RADAR_MAGENTA),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RadarAttributes.new("one", Point2D.new(-40, -20), RADAR_GREEN),
|
||||||
|
RadarAttributes.new("hand", Point2D.new(-5, 18), RADAR_GREEN),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RADIUS = 100.0,
|
||||||
|
|
||||||
|
---Create Radar instance
|
||||||
|
---@param pos Point2D
|
||||||
|
---@param radius? number
|
||||||
|
---@return Radar
|
||||||
|
new = function (pos, radius)
|
||||||
|
---@class Radar : CRadar
|
||||||
|
local o = {
|
||||||
|
_graphdata = {
|
||||||
|
notes = 0,
|
||||||
|
peak = 0,
|
||||||
|
tsumami = 0,
|
||||||
|
tricky = 0,
|
||||||
|
handtrip = 0,
|
||||||
|
onehand = 0,
|
||||||
|
},
|
||||||
|
_hexagonMesh = gfx.CreateShadedMesh("radar"),
|
||||||
|
outlineVertices = {},
|
||||||
|
attributePositions = {},
|
||||||
|
pos = pos or Point2D.ZERO,
|
||||||
|
scale = radius and radius / Radar.RADIUS or 1.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
local sides = #Radar.ATTRIBUTES
|
||||||
|
local angleStep = (2 * math.pi) / sides
|
||||||
|
local rotationAngle = angleStep / 2
|
||||||
|
|
||||||
|
local outlineRadius = Radar.RADIUS
|
||||||
|
local attributeRadius = Radar.RADIUS + 20
|
||||||
|
|
||||||
|
for i = 0, sides - 1 do
|
||||||
|
local attrIdx = i + 1
|
||||||
|
local angle = i * angleStep - rotationAngle
|
||||||
|
local cosAngle = math.cos(angle)
|
||||||
|
local sinAngle = math.sin(angle)
|
||||||
|
-- cache outline vertices
|
||||||
|
table.insert(o.outlineVertices, Point2D.new(outlineRadius * cosAngle, outlineRadius * sinAngle))
|
||||||
|
-- cache attribute positions
|
||||||
|
table.insert(o.attributePositions, {})
|
||||||
|
for j = 1, #Radar.ATTRIBUTES[attrIdx] do
|
||||||
|
local attr = Radar.ATTRIBUTES[attrIdx][j]
|
||||||
|
local attributePos = Point2D.new(attributeRadius * cosAngle, attributeRadius * sinAngle)
|
||||||
|
attributePos.x = attributePos.x + attr.pos.x
|
||||||
|
attributePos.y = attributePos.y + attr.pos.y
|
||||||
|
table.insert(o.attributePositions, j, attributePos)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
setmetatable(o, Radar)
|
||||||
|
return o
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
Radar.__index = Radar
|
||||||
|
|
||||||
|
---@param w number
|
||||||
|
---@param color ColorRGBA
|
||||||
|
function Radar:drawOutline(w, color)
|
||||||
|
---@cast self Radar
|
||||||
|
|
||||||
|
for i = 1, #self.outlineVertices do
|
||||||
|
local j = i % #self.outlineVertices + 1
|
||||||
|
drawLine(self.outlineVertices[i], self.outlineVertices[j], w, color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param color ColorRGBA
|
||||||
|
---@param ticks? integer
|
||||||
|
function Radar:drawRadialTicks(color, ticks)
|
||||||
|
---@cast self Radar
|
||||||
|
|
||||||
|
ticks = ticks or 3
|
||||||
|
|
||||||
|
gfx.Save()
|
||||||
|
gfx.StrokeColor(color:components())
|
||||||
|
|
||||||
|
for i, vertex in ipairs(self.outlineVertices) do
|
||||||
|
gfx.BeginPath()
|
||||||
|
gfx.MoveTo(0, 0)
|
||||||
|
gfx.LineTo(vertex.x, vertex.y)
|
||||||
|
gfx.Stroke()
|
||||||
|
|
||||||
|
local lineLength = math.sqrt(vertex.x * vertex.x + vertex.y * vertex.y)
|
||||||
|
local tinyLineLength = 10
|
||||||
|
|
||||||
|
local tinyLineAngle = math.atan(vertex.y / vertex.x)
|
||||||
|
if vertex.x < 0 then
|
||||||
|
tinyLineAngle = tinyLineAngle + math.pi
|
||||||
|
end
|
||||||
|
|
||||||
|
local halfTinyLineLength = tinyLineLength / 2
|
||||||
|
|
||||||
|
for j = 1, ticks do
|
||||||
|
local distanceFromCenter = j * lineLength / (ticks + 1) -- Adjusted for 3 middle lines
|
||||||
|
|
||||||
|
local offsetX = distanceFromCenter * (vertex.x / lineLength)
|
||||||
|
local offsetY = distanceFromCenter * (vertex.y / lineLength)
|
||||||
|
|
||||||
|
local endX = halfTinyLineLength * math.cos(tinyLineAngle - math.pi / 2) -- Rotate by -90 degrees
|
||||||
|
local endY = halfTinyLineLength * math.sin(tinyLineAngle - math.pi / 2) -- Rotate by -90 degrees
|
||||||
|
|
||||||
|
local offsetX2 = halfTinyLineLength * math.cos(tinyLineAngle + math.pi / 2)
|
||||||
|
local offsetY2 = halfTinyLineLength * math.sin(tinyLineAngle + math.pi / 2)
|
||||||
|
|
||||||
|
gfx.BeginPath()
|
||||||
|
gfx.MoveTo(offsetX - offsetX2, offsetY - offsetY2)
|
||||||
|
gfx.LineTo(endX + offsetX + offsetX2 + offsetX2, endY + offsetY + offsetY2 + offsetY2)
|
||||||
|
gfx.Stroke()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
gfx.Restore()
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param fillColor ColorRGBA
|
||||||
|
function Radar:drawBackground(fillColor)
|
||||||
|
---@cast self Radar
|
||||||
|
|
||||||
|
gfx.Save()
|
||||||
|
|
||||||
|
gfx.BeginPath()
|
||||||
|
gfx.MoveTo(self.outlineVertices[1].x, self.outlineVertices[1].y)
|
||||||
|
for i = 2, #self.outlineVertices do
|
||||||
|
gfx.LineTo(self.outlineVertices[i].x, self.outlineVertices[i].y)
|
||||||
|
end
|
||||||
|
gfx.ClosePath()
|
||||||
|
|
||||||
|
gfx.FillColor(fillColor:components())
|
||||||
|
gfx.Fill()
|
||||||
|
|
||||||
|
gfx.Restore()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Radar:drawAttributes()
|
||||||
|
---@cast self Radar
|
||||||
|
|
||||||
|
gfx.Save()
|
||||||
|
|
||||||
|
gfx.FontSize(20)
|
||||||
|
for i = 1, #self.attributePositions do
|
||||||
|
local attrPos = self.attributePositions[i]
|
||||||
|
for j = 1, #attrPos do
|
||||||
|
local pos = attrPos[j]
|
||||||
|
local attr = Radar.ATTRIBUTES[i][j]
|
||||||
|
gfx.TextAlign(gfx.TEXT_ALIGN_MIDDLE + gfx.TEXT_ALIGN_CENTER)
|
||||||
|
renderOutlinedText(pos, string.upper(attr.text), 1, attr.color)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
gfx.Restore()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Radar:drawRadarMesh()
|
||||||
|
---@cast self Radar
|
||||||
|
|
||||||
|
local scaleFact = {
|
||||||
|
self._graphdata.notes,
|
||||||
|
self._graphdata.peak,
|
||||||
|
self._graphdata.tsumami,
|
||||||
|
self._graphdata.tricky,
|
||||||
|
self._graphdata.handtrip,
|
||||||
|
self._graphdata.onehand,
|
||||||
|
}
|
||||||
|
|
||||||
|
local color1 = ColorRGBA.new(255, 12, 48, 230) -- magenta-ish
|
||||||
|
local color2 = ColorRGBA.new(112, 119, 255, 230) -- light blue-ish purple
|
||||||
|
|
||||||
|
local currentTime = 0
|
||||||
|
|
||||||
|
-- Calculate the maximum size based on the constraint
|
||||||
|
local maxSize = self.RADIUS * self.scale + 10
|
||||||
|
local maxLineLength = maxSize + (maxSize / 2)
|
||||||
|
self._hexagonMesh:SetParam("maxSize", maxLineLength + .0)
|
||||||
|
|
||||||
|
-- Set the color of the hexagon
|
||||||
|
self._hexagonMesh:SetParamVec4("colorMax", color1:componentsFloat())
|
||||||
|
self._hexagonMesh:SetParamVec4("colorCenter", color2:componentsFloat())
|
||||||
|
|
||||||
|
-- Set the primitive type to triangles
|
||||||
|
self._hexagonMesh:SetPrimitiveType(self._hexagonMesh.PRIM_TRIFAN)
|
||||||
|
|
||||||
|
-- Calculate the vertices of the hexagon
|
||||||
|
local sides = #Radar.ATTRIBUTES
|
||||||
|
local angleStep = (2 * math.pi) / sides
|
||||||
|
local rotationAngle = angleStep / 2
|
||||||
|
--local rotationAngle = -math.pi / 2
|
||||||
|
local vertices = {}
|
||||||
|
|
||||||
|
table.insert(vertices, {{0, 0}, {0, 0}})
|
||||||
|
for i = 0, sides do
|
||||||
|
local j = i % sides + 1
|
||||||
|
local angle = i * angleStep - rotationAngle
|
||||||
|
|
||||||
|
--local angle = math.rad(60 * (i-1)) + rotationAngle
|
||||||
|
local scale = scaleFact[j]
|
||||||
|
local lineLength = maxLineLength * scale
|
||||||
|
lineLength = math.min(lineLength, maxLineLength) -- Cap the length
|
||||||
|
local px = lineLength * math.cos(angle)
|
||||||
|
local py = lineLength * math.sin(angle)
|
||||||
|
table.insert(vertices, {{px, py}, {0, 0}})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set the hexagon's vertices
|
||||||
|
self._hexagonMesh:SetData(vertices)
|
||||||
|
--self._hexagonMesh:SetPosition(-maxSize, maxSize)
|
||||||
|
self._hexagonMesh:Draw()
|
||||||
|
|
||||||
|
--NOTE: Bug: forcerender resets every transformation, need to re-setup view transform afterwards
|
||||||
|
gfx.ForceRender()
|
||||||
|
end
|
||||||
|
|
||||||
|
function Radar:drawGraph()
|
||||||
|
---@cast self Radar
|
||||||
|
|
||||||
|
gfx.Save()
|
||||||
|
gfx.Reset()
|
||||||
|
gfx.ResetScissor()
|
||||||
|
|
||||||
|
Dim.updateResolution()
|
||||||
|
Dim.transformToScreenSpace()
|
||||||
|
|
||||||
|
gfx.FontSize(28)
|
||||||
|
gfx.Translate(self.pos.x, self.pos.y)
|
||||||
|
gfx.Scale(self.scale, self.scale)
|
||||||
|
|
||||||
|
local strokeColor = ColorRGBA.new(255, 255, 255, 100)
|
||||||
|
local fillColor = ColorRGBA.new(0, 0, 0, 191)
|
||||||
|
|
||||||
|
self:drawBackground(strokeColor, fillColor)
|
||||||
|
self:drawOutline(3, ColorRGBA.WHITE)
|
||||||
|
self:drawRadarMesh()
|
||||||
|
self:drawRadialTicks()
|
||||||
|
self:drawAttributes()
|
||||||
|
|
||||||
|
local pos = Point2D.new(self.pos:coords())
|
||||||
|
pos.y = pos.y - self.RADIUS
|
||||||
|
--drawDebugText(pos, self._graphdata)
|
||||||
|
|
||||||
|
gfx.Restore()
|
||||||
|
|
||||||
|
--NOTE: Bug workaround: forcerender resets every transformation, re-setup view transform
|
||||||
|
Dim.transformToScreenSpace()
|
||||||
|
end
|
||||||
|
|
||||||
|
---Compute radar attribute values from ksh
|
||||||
|
---@param info string # chart directory path
|
||||||
|
---@param dif string # chart name without extension
|
||||||
|
function Radar:updateGraph(info, dif)
|
||||||
|
---@cast self Radar
|
||||||
|
|
||||||
|
--local pattern = "(.*[\\/])"
|
||||||
|
--local extractedSubstring = info:match(pattern)
|
||||||
|
--local txtFilePath = extractedSubstring .. "radar\\" .. dif .. ".txt"
|
||||||
|
|
||||||
|
--local song = io.open(txtFilePath, "r")
|
||||||
|
local song = io.open(info.."/"..dif..".ksh")
|
||||||
|
game.Log(info.."/"..dif..".ksh", game.LOGGER_DEBUG)
|
||||||
|
game.Log(song and "file open" or "file not found", game.LOGGER_DEBUG)
|
||||||
|
if song then
|
||||||
|
local chartData = song:read("*all")
|
||||||
|
song:close()
|
||||||
|
|
||||||
|
local notesCount, knobCount, oneHandCount, handTripCount = 0, 0, 0, 0
|
||||||
|
local trickyValue = 0
|
||||||
|
local totalMeasures = 0
|
||||||
|
local totalSongLength = 0
|
||||||
|
local tsumamiValue = 0
|
||||||
|
|
||||||
|
for line in chartData:gmatch("[^\r\n]+") do
|
||||||
|
local noteType, fxType, laserType = line:match("(%d%d%d%d)|(%d%d)|([%-%a ]+)")
|
||||||
|
|
||||||
|
if noteType and fxType then
|
||||||
|
local noteCount = noteType:match("1") and 1 or 0
|
||||||
|
|
||||||
|
notesCount = notesCount + noteCount
|
||||||
|
knobCount = knobCount + (fxType == "02" and 1 or 0)
|
||||||
|
|
||||||
|
oneHandCount = oneHandCount + (laserType:match("[79A-D:]") and 1 or 0)
|
||||||
|
handTripCount = handTripCount + (laserType:match("[HKPUV:]") and 1 or 0)
|
||||||
|
if laserType ~= "--" then
|
||||||
|
tsumamiValue = tsumamiValue + 0.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if line == "--" then
|
||||||
|
totalMeasures = totalMeasures + 1
|
||||||
|
elseif line:match("t=(%d+)") then
|
||||||
|
local bpm = tonumber(line:match("t=(%d+)"))
|
||||||
|
totalSongLength = math.max(totalSongLength, bpm)
|
||||||
|
end
|
||||||
|
|
||||||
|
if line:match("beat=") or line:match("stop=") or
|
||||||
|
line:match("zoom_top=") or line:match("zoom_bottom=") or line:match("center_split=") then
|
||||||
|
trickyValue = trickyValue + 0.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local graphValues = {
|
||||||
|
notes = (notesCount / totalSongLength),
|
||||||
|
peak = (notesCount / totalMeasures) * 10,
|
||||||
|
tsumami = tsumamiValue,
|
||||||
|
tricky = trickyValue,
|
||||||
|
handtrip = handTripCount,
|
||||||
|
onehand = oneHandCount,
|
||||||
|
}
|
||||||
|
|
||||||
|
game.Log("graphValues", game.LOGGER_DEBUG)
|
||||||
|
for k,v in pairs(graphValues) do
|
||||||
|
game.Log(k..": "..v, game.LOGGER_DEBUG)
|
||||||
|
end
|
||||||
|
|
||||||
|
local scaleFactors = {
|
||||||
|
notes = 2,
|
||||||
|
peak = 50,
|
||||||
|
tsumami = 500,
|
||||||
|
tricky = 50,
|
||||||
|
handtrip = 100,
|
||||||
|
onehand = 50,
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, factor in pairs(scaleFactors) do
|
||||||
|
-- Apply the scaling factor to each value
|
||||||
|
self._graphdata[key] = graphValues[key] / factor
|
||||||
|
|
||||||
|
-- Limit to a maximum of 125%
|
||||||
|
self._graphdata[key] = math.min(1.25, self._graphdata[key])
|
||||||
|
end
|
||||||
|
|
||||||
|
game.Log("_graphdata", game.LOGGER_DEBUG)
|
||||||
|
for k,v in pairs(self._graphdata) do
|
||||||
|
game.Log(k..": "..v, game.LOGGER_DEBUG)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return Radar
|
|
@ -9,6 +9,9 @@ local Numbers = require('components.numbers')
|
||||||
|
|
||||||
local VolforceCalc = require('components.volforceCalc')
|
local VolforceCalc = require('components.volforceCalc')
|
||||||
|
|
||||||
|
require("api.point2d")
|
||||||
|
require("components.radar")
|
||||||
|
|
||||||
local dataPanelImage = gfx.CreateSkinImage("song_select/data_bg_overlay.png", 1)
|
local dataPanelImage = gfx.CreateSkinImage("song_select/data_bg_overlay.png", 1)
|
||||||
local dataGlowOverlayImage = gfx.CreateSkinImage("song_select/data_panel/data_glow_overlay.png", 1)
|
local dataGlowOverlayImage = gfx.CreateSkinImage("song_select/data_panel/data_glow_overlay.png", 1)
|
||||||
local gradeBgImage = gfx.CreateSkinImage("song_select/data_panel/grade_bg.png", 1)
|
local gradeBgImage = gfx.CreateSkinImage("song_select/data_panel/grade_bg.png", 1)
|
||||||
|
@ -39,8 +42,6 @@ local searchInfoPanelImage = gfx.CreateSkinImage("song_select/search_info_panel.
|
||||||
|
|
||||||
local defaultJacketImage = gfx.CreateSkinImage("song_select/loading.png", 0)
|
local defaultJacketImage = gfx.CreateSkinImage("song_select/loading.png", 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local difficultyLabelImages = {
|
local difficultyLabelImages = {
|
||||||
gfx.CreateSkinImage("song_select/plate/difficulty_labels/novice.png", 1),
|
gfx.CreateSkinImage("song_select/plate/difficulty_labels/novice.png", 1),
|
||||||
gfx.CreateSkinImage("song_select/plate/difficulty_labels/advanced.png", 1),
|
gfx.CreateSkinImage("song_select/plate/difficulty_labels/advanced.png", 1),
|
||||||
|
@ -125,6 +126,9 @@ local songPlateHeight = 172
|
||||||
local selectedIndex = 1
|
local selectedIndex = 1
|
||||||
local selectedDifficulty = 1
|
local selectedDifficulty = 1
|
||||||
|
|
||||||
|
local radar = Radar.new(Point2D.new(0, 0))
|
||||||
|
local updateRadar = true
|
||||||
|
|
||||||
local jacketCache = {}
|
local jacketCache = {}
|
||||||
|
|
||||||
local top50diffs = {}
|
local top50diffs = {}
|
||||||
|
@ -424,8 +428,8 @@ function drawData() -- Draws the song data on the left panel
|
||||||
gfx.ImageRect(96, 529, 410*0.85, 168*0.85, top50JacketOverlayImage, 1, 0)
|
gfx.ImageRect(96, 529, 410*0.85, 168*0.85, top50JacketOverlayImage, 1, 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
gfx.Save()
|
|
||||||
-- Draw best score
|
-- Draw best score
|
||||||
|
gfx.Save()
|
||||||
gfx.BeginPath()
|
gfx.BeginPath()
|
||||||
|
|
||||||
local scoreNumber = 0
|
local scoreNumber = 0
|
||||||
|
@ -464,29 +468,32 @@ function drawData() -- Draws the song data on the left panel
|
||||||
gfx.Restore()
|
gfx.Restore()
|
||||||
|
|
||||||
-- Draw BPM
|
-- Draw BPM
|
||||||
|
gfx.Save()
|
||||||
gfx.BeginPath()
|
gfx.BeginPath()
|
||||||
gfx.FontSize(24)
|
gfx.FontSize(24)
|
||||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||||
gfx.Save()
|
|
||||||
gfx.LoadSkinFont('Digital-Serial-Bold.ttf')
|
gfx.LoadSkinFont('Digital-Serial-Bold.ttf')
|
||||||
gfx.GlobalAlpha(transitionAfterscrollDataOverlayAlpha) -- TODO: split this out
|
gfx.GlobalAlpha(transitionAfterscrollDataOverlayAlpha) -- TODO: split this out
|
||||||
gfx.Text(song.bpm, 85, 920)
|
gfx.Text(song.bpm, 85, 920)
|
||||||
gfx.Restore()
|
gfx.Restore()
|
||||||
|
|
||||||
-- Draw song title
|
-- Draw song title
|
||||||
|
gfx.Save()
|
||||||
gfx.FontSize(28)
|
gfx.FontSize(28)
|
||||||
gfx.GlobalAlpha(transitionAfterscrollTextSongTitle)
|
gfx.GlobalAlpha(transitionAfterscrollTextSongTitle)
|
||||||
gfx.Text(song.title, 30+(1-transitionAfterscrollTextSongTitle)*20, 955)
|
gfx.Text(song.title, 30+(1-transitionAfterscrollTextSongTitle)*20, 955)
|
||||||
|
gfx.Restore()
|
||||||
|
|
||||||
-- Draw artist
|
-- Draw artist
|
||||||
|
gfx.Save()
|
||||||
gfx.GlobalAlpha(transitionAfterscrollTextSongArtist)
|
gfx.GlobalAlpha(transitionAfterscrollTextSongArtist)
|
||||||
gfx.Text(song.artist, 30+(1-transitionAfterscrollTextSongArtist)*30, 997)
|
gfx.Text(song.artist, 30+(1-transitionAfterscrollTextSongArtist)*30, 997)
|
||||||
|
gfx.Restore()
|
||||||
gfx.GlobalAlpha(1)
|
|
||||||
|
|
||||||
-- Draw difficulties
|
-- Draw difficulties
|
||||||
local DIFF_X_START = 98.5
|
local DIFF_X_START = 98.5
|
||||||
local DIFF_GAP = 114.8
|
local DIFF_GAP = 114.8
|
||||||
|
gfx.Save()
|
||||||
gfx.GlobalAlpha(transitionAfterscrollDifficultiesAlpha)
|
gfx.GlobalAlpha(transitionAfterscrollDifficultiesAlpha)
|
||||||
for i, diff in ipairs(song.difficulties) do
|
for i, diff in ipairs(song.difficulties) do
|
||||||
gfx.BeginPath()
|
gfx.BeginPath()
|
||||||
|
@ -508,20 +515,20 @@ function drawData() -- Draws the song data on the left panel
|
||||||
gfx.BeginPath()
|
gfx.BeginPath()
|
||||||
gfx.ImageRect(DIFF_X_START+(index-1)*DIFF_GAP-tw/2, 1050, tw, th, diffLabelImage, 1, 0)
|
gfx.ImageRect(DIFF_X_START+(index-1)*DIFF_GAP-tw/2, 1050, tw, th, diffLabelImage, 1, 0)
|
||||||
end
|
end
|
||||||
gfx.GlobalAlpha(1)
|
gfx.Restore()
|
||||||
|
|
||||||
|
|
||||||
-- Scoreboard
|
-- Scoreboard
|
||||||
|
|
||||||
drawLocalLeaderboard(diff)
|
drawLocalLeaderboard(diff)
|
||||||
drawIrLeaderboard()
|
drawIrLeaderboard()
|
||||||
|
|
||||||
|
gfx.Save()
|
||||||
gfx.FontSize(22)
|
gfx.FontSize(22)
|
||||||
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE)
|
||||||
gfx.GlobalAlpha(transitionAfterscrollDataOverlayAlpha)
|
gfx.GlobalAlpha(transitionAfterscrollDataOverlayAlpha)
|
||||||
gfx.Text(diff.effector, 270, 1180) -- effected by
|
gfx.Text(diff.effector, 270, 1180) -- effected by
|
||||||
gfx.Text(diff.illustrator, 270, 1210) -- illustrated by
|
gfx.Text(diff.illustrator, 270, 1210) -- illustrated by
|
||||||
gfx.GlobalAlpha(1)
|
gfx.Restore()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1069,6 +1076,30 @@ function tickTransitions(deltaTime)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---This function is basically a workaround for the ForceRender call
|
||||||
|
local function drawRadar()
|
||||||
|
gfx.FontSize(28)
|
||||||
|
gfx.Translate(500, 500)
|
||||||
|
|
||||||
|
local strokeColor = ColorRGBA.new(255, 255, 255, 255)
|
||||||
|
local fillColor = ColorRGBA.new(0, 0, 0, 191)
|
||||||
|
|
||||||
|
gfx.ResetScissor()
|
||||||
|
radar:drawBackground(fillColor)
|
||||||
|
radar:drawOutline(3, strokeColor)
|
||||||
|
|
||||||
|
--NOTE: Bug: forcerender resets every transformation, need to re-setup view transform afterwards
|
||||||
|
radar:drawRadarMesh()
|
||||||
|
|
||||||
|
Dim.transformToScreenSpace()
|
||||||
|
|
||||||
|
gfx.Save()
|
||||||
|
gfx.Translate(500,500)
|
||||||
|
radar:drawRadialTicks(strokeColor)
|
||||||
|
radar:drawAttributes()
|
||||||
|
gfx.Restore()
|
||||||
|
end
|
||||||
|
|
||||||
draw_songwheel = function(deltaTime)
|
draw_songwheel = function(deltaTime)
|
||||||
drawBackground(deltaTime)
|
drawBackground(deltaTime)
|
||||||
|
|
||||||
|
@ -1077,6 +1108,9 @@ draw_songwheel = function(deltaTime)
|
||||||
isFilterWheelActive = game.GetSkinSetting('_songWheelOverlayActive') == 1
|
isFilterWheelActive = game.GetSkinSetting('_songWheelOverlayActive') == 1
|
||||||
|
|
||||||
drawData()
|
drawData()
|
||||||
|
|
||||||
|
drawRadar()
|
||||||
|
|
||||||
drawCursor()
|
drawCursor()
|
||||||
|
|
||||||
drawFilterInfo(deltaTime)
|
drawFilterInfo(deltaTime)
|
||||||
|
@ -1105,6 +1139,13 @@ render = function (deltaTime)
|
||||||
|
|
||||||
Sound.stopMusic()
|
Sound.stopMusic()
|
||||||
|
|
||||||
|
if updateRadar then
|
||||||
|
local difficultyNames = {"nov","adv","exh","mxm","inf","grv","hvn","vvd","exc"}
|
||||||
|
local diff = songwheel.songs[selectedIndex].difficulties[selectedDifficulty].difficulty + 1
|
||||||
|
radar:updateGraph(songwheel.songs[selectedIndex].path, difficultyNames[diff])
|
||||||
|
updateRadar = false
|
||||||
|
end
|
||||||
|
|
||||||
Dim.updateResolution()
|
Dim.updateResolution()
|
||||||
|
|
||||||
Wallpaper.render()
|
Wallpaper.render()
|
||||||
|
@ -1148,6 +1189,7 @@ songs_changed = function (withAll)
|
||||||
game.SetSkinSetting('_volforce', totalForce)
|
game.SetSkinSetting('_volforce', totalForce)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
set_index = function(newIndex)
|
set_index = function(newIndex)
|
||||||
transitionScrollScale = 0
|
transitionScrollScale = 0
|
||||||
transitionAfterscrollScale = 0
|
transitionAfterscrollScale = 0
|
||||||
|
@ -1162,11 +1204,15 @@ set_index = function(newIndex)
|
||||||
scrollingUp = true
|
scrollingUp = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
updateRadar = true
|
||||||
|
|
||||||
game.PlaySample('song_wheel/cursor_change.wav')
|
game.PlaySample('song_wheel/cursor_change.wav')
|
||||||
|
|
||||||
selectedIndex = newIndex
|
selectedIndex = newIndex
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local json = require("common.json")
|
||||||
|
|
||||||
set_diff = function(newDiff)
|
set_diff = function(newDiff)
|
||||||
if newDiff ~= selectedDifficulty then
|
if newDiff ~= selectedDifficulty then
|
||||||
jacketCache = {} -- Clear the jacket cache for the new diff jackets
|
jacketCache = {} -- Clear the jacket cache for the new diff jackets
|
||||||
|
@ -1174,6 +1220,8 @@ set_diff = function(newDiff)
|
||||||
game.PlaySample('song_wheel/diff_change.wav')
|
game.PlaySample('song_wheel/diff_change.wav')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
updateRadar = true
|
||||||
|
|
||||||
selectedDifficulty = newDiff
|
selectedDifficulty = newDiff
|
||||||
irLeaderboard = {}
|
irLeaderboard = {}
|
||||||
irRequestStatus = 1
|
irRequestStatus = 1
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
#version 330
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
const float PI = 3.1415926535897932384626433832795;
|
||||||
|
const float PI_2 = 1.57079632679489661923;
|
||||||
|
|
||||||
|
layout(location=1) in vec2 fsTex;
|
||||||
|
layout(location=2) in vec3 fragPos;
|
||||||
|
|
||||||
|
layout(location=0) out vec4 target;
|
||||||
|
|
||||||
|
uniform vec4 colorCenter;
|
||||||
|
uniform vec4 colorMax;
|
||||||
|
|
||||||
|
uniform float maxSize;
|
||||||
|
|
||||||
|
float lerp(float x, vec2 p0, vec2 p1)
|
||||||
|
{
|
||||||
|
return p0.y + (p1.y - p0.y) * ((x - p0.x)/(p1.x - p0.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 toHSV(vec4 rgb)
|
||||||
|
{
|
||||||
|
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||||
|
vec4 p = mix(vec4(rgb.bg, K.wz), vec4(rgb.gb, K.xy), step(rgb.b, rgb.g));
|
||||||
|
vec4 q = mix(vec4(p.xyw, rgb.r), vec4(rgb.r, p.yzx), step(p.x, rgb.r));
|
||||||
|
|
||||||
|
float d = q.x - min(q.w, q.y);
|
||||||
|
float e = 1.0e-10;
|
||||||
|
return vec4(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x, rgb.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 toRGB(vec4 hsv)
|
||||||
|
{
|
||||||
|
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||||
|
vec3 p = abs(fract(hsv.xxx + K.xyz) * 6.0 - K.www);
|
||||||
|
return vec4(hsv.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), hsv.y), hsv.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is in [0.0, 1.0]
|
||||||
|
vec4 lerpColor(float x, vec4 color1, vec4 color2)
|
||||||
|
{
|
||||||
|
// convert RGB color space to HSV
|
||||||
|
vec4 hsv1 = toHSV(color1), hsv2 = toHSV(color2);
|
||||||
|
float hue = (mod(mod((hsv2.x-hsv1.x), 1.) + 1.5, 1.)-0.5)*x + hsv1.x;
|
||||||
|
vec4 hsv = vec4(hue, mix(hsv1.yzw, hsv2.yzw, x));
|
||||||
|
// convert HSV color space back to RGB
|
||||||
|
return toRGB(hsv);
|
||||||
|
}
|
||||||
|
|
||||||
|
float EaseInSine(float x)
|
||||||
|
{
|
||||||
|
return 1.0 - cos((x * PI) / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 coordToColor(vec2 pos, float maxSize)
|
||||||
|
{
|
||||||
|
float r = length(pos);
|
||||||
|
float factor = lerp(r, vec2(0,0), vec2(maxSize,1));
|
||||||
|
return lerpColor(EaseInSine(factor), colorCenter, colorMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
target = coordToColor(fragPos.xy, maxSize);
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
#version 330
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
layout(location=0) in vec2 inPos;
|
||||||
|
layout(location=1) in vec2 inTex;
|
||||||
|
|
||||||
|
out gl_PerVertex
|
||||||
|
{
|
||||||
|
vec4 gl_Position;
|
||||||
|
};
|
||||||
|
layout(location=1) out vec2 fsTex;
|
||||||
|
layout(location=2) out vec3 fragPos;
|
||||||
|
|
||||||
|
uniform mat4 proj;
|
||||||
|
uniform mat4 world;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
fsTex = inTex;
|
||||||
|
gl_Position = proj * world * vec4(inPos.xy, 0, 1);
|
||||||
|
fragPos = vec3(inPos.xy, 0);
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#version 330
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
layout(location=1) in vec2 fsTex;
|
||||||
|
layout(location=0) out vec4 target;
|
||||||
|
layout(location=2) in vec4 inColor;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
target = inColor;
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
#version 330
|
||||||
|
#extension GL_ARB_separate_shader_objects : enable
|
||||||
|
|
||||||
|
const float PI = 3.1415926535897932384626433832795;
|
||||||
|
const float PI_2 = 1.57079632679489661923;
|
||||||
|
|
||||||
|
layout(location=0) in vec2 inPos;
|
||||||
|
layout(location=1) in vec2 inTex;
|
||||||
|
|
||||||
|
out gl_PerVertex
|
||||||
|
{
|
||||||
|
vec4 gl_Position;
|
||||||
|
};
|
||||||
|
layout(location=1) out vec2 fsTex;
|
||||||
|
layout(location=2) out vec4 vertexColor;
|
||||||
|
|
||||||
|
uniform mat4 proj;
|
||||||
|
uniform mat4 world;
|
||||||
|
|
||||||
|
uniform vec4 colorCenter;
|
||||||
|
uniform vec4 colorMax;
|
||||||
|
|
||||||
|
uniform float maxSize;
|
||||||
|
|
||||||
|
// Polar coordinate utility functions
|
||||||
|
float hypot(vec2 pos)
|
||||||
|
{
|
||||||
|
return sqrt(pos.x * pos.x + pos.y * pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
float atan2(vec2 pos)
|
||||||
|
{
|
||||||
|
if (pos.x > 0)
|
||||||
|
{
|
||||||
|
return atan(pos.y / pos.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.x < 0 && pos.y >= 0)
|
||||||
|
{
|
||||||
|
return atan(pos.y / pos.x) + PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.x < 0 && pos.y < 0)
|
||||||
|
{
|
||||||
|
return atan(pos.y / pos.x) - PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.x == 0 && pos.y > 0)
|
||||||
|
{
|
||||||
|
return PI_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos.x == 0 && pos.y < 0)
|
||||||
|
{
|
||||||
|
return -PI_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following is not mathematically correct, as it's undefined
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 toPolar(vec2 cartesian)
|
||||||
|
{
|
||||||
|
return vec2(hypot(pos), atan2(pos));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
float lerp(float x, vec2 p0, vec2 p1)
|
||||||
|
{
|
||||||
|
return p0.y + (p1.y - p0.y) * ((x - p0.x)/(p1.x - p0.x));
|
||||||
|
}
|
||||||
|
|
||||||
|
// x is in [0.0, 1.0]
|
||||||
|
vec4 lerpColor(float x, vec4 color1, vec4 color2)
|
||||||
|
{
|
||||||
|
return color1 + (color2 - color1) * x;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 coordToColor(vec2 pos, float maxSize)
|
||||||
|
{
|
||||||
|
float r = hypot(pos);
|
||||||
|
//float phi = atan2(pos);
|
||||||
|
|
||||||
|
float factor = lerp(r, vec2(0,0), vec2(maxSize,1));
|
||||||
|
|
||||||
|
return lerpColor(factor, colorCenter, colorMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
gl_Position = proj * world * vec4(inPos.xy, 0, 1);
|
||||||
|
vertexColor = coordToColor(inPos.xy, maxSize);
|
||||||
|
}
|
Loading…
Reference in New Issue