Code for finding the colors of C# types, translated from the Resonite source code.
| Function | Purpose | Example |
|---|---|---|
get_impulse_color_css |
Gets the color of an impulse | {{#invoke:ProtoFlux_Type_Color|get_impulse_color_css|Continuation}}
|
get_type_color_css |
Gets the color of a data type | {{#invoke:ProtoFlux_Type_Color|get_type_color_css|User}}
|
get_color_css |
First attempts to interpret the type as an impulse | {{#invoke:ProtoFlux_Type_Color|get_color_css|int}}
|
The following functions are exported, but not invokable by Media Wiki.
| Function | Purpose | Example |
|---|---|---|
get_impulse_color |
Gets the color of an impulse | get_impulse_color("Continuation")
|
get_type_color |
Gets the color of a data type | get_type_color("User")
|
color_from_crc |
Derives a color from the given CRC32 checksum | color_from_crc(0x9CD97842)
|
How exactly these invokations may be used can be seen by inspecting the source of this wiki and related templates.
Examples
Continuation
Call
AsyncCall
SyncResumption
AsyncResumption
Types
Values and String
| Signed |
|
| Unsigned |
|
| Decimal |
|
| String |
|
Non-Values
| A |
|
| B |
|
| C |
|
| D |
|
| E |
|
| F |
|
| G |
|
| H |
|
| I |
|
| J |
|
| K | |
| L |
|
| M | |
| N |
|
| O |
|
| P |
|
| Q | |
| R |
|
| S |
|
| T |
|
| U |
|
| V | |
| W |
|
| XYZ |
-- Code for finding the colors of C# types, translated from the Resonite source code
-- Source: FrooxEngine.ProtoFlux.DatatypeColorHelper (decompiled with ILSpy)
-- Binary found at Resonite_Data/Managed/FrooxEngine.dll
local p = {}
local checksum = require("Module:CRC_32_ISO_HDLC")
local bit32 = require("bit32")
local colors = {
CONTINUATION = {r=1, g=1, b=0.7},
SYNC_FLOW = {r=0.7, g=1, b=1},
ASYNC_FLOW = {r=0.8, g=0.7, b=1},
SYNC_RESUMPTION = {r=0.7, g=0.7, b=1},
ASYNC_RESUMPTION = {r=1, g=0.7, b=0.7},
INPUT_LABEL = {r=0.8, g=0.9, b=1},
OUTPUT_LABEL = {r=0.9, g=0.8, b=1},
COLOR_BASE = {r=1, g=1, b=0},
COLORX_BASE = {r=1, g=0.5, b=0},
SIGNED_INT_BASE = {r=0, g=1, b=0},
UNSIGNED_INT_BASE = {r=0, g=1, b=0.5},
FLOAT_BASE = {r=0, g=1, b=1},
QUATERNION_BASE = {r=0, g=0.5, b=1},
DECIMAL_BASE = {r=0, g=0, b=1},
BOOL_BASE = {r=0.3, g=0.3, b=0.3},
CHAR_BASE = {r=0.6392157, g=37/85, b=7/85},
STRING_BASE = {r=0.6392157, g=7/85, b=7/85},
SLOT_BASE = {r=0.75, g=1, b=0.5},
USER_BASE = {r=1, g=0.5, b=1},
DUMMY = {r=1, g=0, b=1},
}
local primitive_class = {
bool="boolean",
char="char",
String="string",
byte="unsigned_integer",
ushort="unsigned_integer",
uint="unsigned_integer",
ulong="unsigned_integer",
sbyte="signed_integer",
short="signed_integer",
int="signed_integer",
long="signed_integer",
float="floating_point",
double="floating_point",
decimal="floating_point",
}
local primitive_class_rank = {
byte=0,
sbyte=0,
float=0,
ushort=1,
short=1,
double=1,
uint=2,
int=2,
ulong=3,
long=3,
}
local max_primitive_class_rank = {
boolean=0,
string=0,
char=0,
floating_point=1,
signed_integer=3,
unsigned_integer=3,
}
local primitive_class_base_color = {
signed_integer=colors.SIGNED_INT_BASE,
unsigned_integer=colors.UNSIGNED_INT_BASE,
floating_point=colors.FLOAT_BASE,
}
function p.get_impulse_color(type_str)
if type_str == "Continuation" then return colors.CONTINUATION
elseif type_str == "Call" then return colors.SYNC_FLOW
elseif type_str == "AsyncCall" then return colors.ASYNC_FLOW
elseif type_str == "SyncResumption" then return colors.SYNC_RESUMPTION
elseif type_str == "AsyncResumption" then return colors.ASYNC_RESUMPTION
end
end
function p.get_type_color(type_str, rec_limit)
if rec_limit == 0 then error("Recursion limit reached")
elseif rec_limit == nil then rec_limit = 4
end
local btype = get_base_type(type_str)
if btype == "bool" then return colors.BOOL_BASE
elseif btype == "char" then return colors.CHAR_BASE
elseif btype == "String" then return colors.STRING_BASE
elseif btype == "Dummy" then return colors.DUMMY
elseif btype == "color" then return colors.COLOR_BASE
elseif btype == "colorX" then return colors.COLORX_BASE
elseif btype == "decimal" then return colors.DECIMAL_BASE
elseif btype == "Slot" then return colors.SLOT_BASE
elseif btype == "User" then return colors.USER_BASE
elseif p.get_impulse_color(btype) then return p.get_impulse_color(btype)
end
local prim_cls = primitive_class[btype]
if prim_cls ~= nil then
local rank = primitive_class_rank[btype]
local max_rank = max_primitive_class_rank[prim_cls]
local rank_lerp = inverse_lerp(0, max_rank, rank)
local base_color = primitive_class_base_color[prim_cls]
if base_color == nul then error("Invalid primitive class: "..prim_cls) end
return lerp_primitive_color(base_color, rank_lerp)
end
-- Vector or Matrix (eg. float3, float3x3)
if btype:match("^%a+%d$") ~= nil or btype:match("^%a+%dx%d$") ~= nil then
local vec_btype = btype:match("^%a+")
if primitive_class[vec_btype] ~= nil then
return p.get_type_color(vec_btype, rec_limit - 1)
end
end
-- Quaternion
if btype:match("^%a+Q$") ~= nil then
local q_btype = btype:sub(1,-2)
local rank = nil
if q_btype == "float" then rank = 0
elseif q_btype == "double" then rank = 1
end
if rank ~= nil then
return lerp_primitive_color(colors.QUATERNION_BASE, rank)
end
end
-- Nullable
if btype == "Nullable" then
local i = string.find(type_str, "<", 0, true)
local last = type_str:sub(-1)
if i == nil or last ~= ">" then error("Invalid Nullable") end
local color = p.get_type_color(type_str:sub(i + 1, -2), rec_limit - 1)
color.a = (color.a or 1) * 0.5
return color
end
return p.color_from_crc(checksum.crc32(btype))
end
function p.color_from_crc(value)
local h = bit32.band(value, 0xff) / 255
local lerp_s = bit32.band(bit32.rshift(value, 8), 0xff) / 255
local lerp_v = bit32.band(bit32.rshift(value, 16), 0xff) / 255
local s = lerp_unclamped(0.25, 0.75, lerp_s)
local v = lerp_unclamped(0.25, 1.00, lerp_v)
return hsv_to_rgb(h, s, v)
end
function p.get_impulse_color_css(frame)
local color = p.get_impulse_color(frame.args[1])
return color and color_to_css(color)
end
function p.get_type_color_css(frame)
local color = p.get_type_color(frame.args[1])
return color_to_css(color, 1.5)
end
function p.get_color_css(frame)
local text = frame.args[1]
local factor = 1
local color = p.get_impulse_color(text)
if color == nil then
factor, color = 1.5, p.get_type_color(text)
end
return color_to_css(color, factor)
end
function color_to_css(color, factor)
local mult = 255 * (factor or 1)
return ("rgba(%s,%s,%s,%s)"):format(color.r*mult, color.g*mult, color.b*mult, color.a or 1)
end
function get_base_type(type_str)
local i = type_str:find("<", 0, true)
if i == nil then
return type_str
else
return type_str:sub(1, i - 1)
end
end
function lerp_primitive_color(base, rank)
return lerp_color({r=0,g=0,b=0}, base, 0.75 + rank * 0.25)
end
function lerp_color(a, b, lerp)
if lerp <= 0 then return a end
if lerp >= 1 then return b end
return lerp_unclamped_color(a, b, lerp)
end
function inverse_lerp(a, b, lerp)
return (lerp - a) / (b - a)
end
function lerp_unclamped(a, b, lerp)
return a + (b - a) * lerp
end
function lerp_unclamped_color(a, b, lerp)
local prel = 1 - lerp
local alpha = 1
if a.a ~= nil and b.a ~= nil then
alpha = a.a * prel + b.a * lerp
end
return {
r=a.r * prel + b.r * lerp,
g=a.g * prel + b.g * lerp,
b=a.b * prel + b.b * lerp,
a=alpha,
}
end
function num_repeat(v, length)
return v - math.floor(v / length) * length
end
function hsv_to_rgb(h, s, v)
local gain = math.max(v, 1)
v = v / gain
local vs = v * s
local f = vs * (1 - math.abs(num_repeat(h * 6, 2) - 1))
local light = v - vs
local r,g,b = 0,0,0
local switch_on = math.floor(num_repeat(h, 1) / (1/6))
if switch_on == 0 then r,g,b = vs,f,0
elseif switch_on == 1 then r,g,b = f,vs,0
elseif switch_on == 2 then r,g,b = 0,vs,f
elseif switch_on == 3 then r,g,b = 0,f,vs
elseif switch_on == 4 then r,g,b = f,0,vs
elseif switch_on == 5 then r,g,b = vs,0,f
end
return {
r=(r+light)*gain,
g=(g+light)*gain,
b=(b+light)*gain
}
end
return p