diff --git a/cinera/cinera.c b/cinera/cinera.c
index 9d81bee..61c4383 100644
--- a/cinera/cinera.c
+++ b/cinera/cinera.c
@@ -23,7 +23,7 @@ typedef struct
version CINERA_APP_VERSION = {
.Major = 0,
.Minor = 10,
- .Patch = 28
+ .Patch = 29
};
#define __USE_XOPEN2K8 // NOTE(matt): O_NOFOLLOW
@@ -4172,6 +4172,68 @@ LogEdit(edit_type_id EditType, string Lineage, string EntryID, string *EntryTitl
}
}
+#define CINERA_HSL_TRANSPARENT_HUE 65535
+
+typedef struct
+{
+ unsigned int Hue:16;
+ unsigned int Saturation:8;
+ unsigned int Lightness:8;
+} hsl_colour;
+
+hsl_colour
+CharToColour(char Char)
+{
+ hsl_colour Colour;
+ if(Char >= 'a' && Char <= 'z')
+ {
+ Colour.Hue = (((float)Char - 'a') / ('z' - 'a') * 360);
+ Colour.Saturation = (((float)Char - 'a') / ('z' - 'a') * 26 + 74);
+ }
+ else if(Char >= 'A' && Char <= 'Z')
+ {
+ Colour.Hue = (((float)Char - 'A') / ('Z' - 'A') * 360);
+ Colour.Saturation = (((float)Char - 'A') / ('Z' - 'A') * 26 + 74);
+ }
+ else if(Char >= '0' && Char <= '9')
+ {
+ Colour.Hue = (((float)Char - '0') / ('9' - '0') * 360);
+ Colour.Saturation = (((float)Char - '0') / ('9' - '0') * 26 + 74);
+ }
+ else
+ {
+ Colour.Hue = 180;
+ Colour.Saturation = 50;
+ }
+
+ return Colour;
+}
+
+void
+StringToColourHash(hsl_colour *Colour, string String)
+{
+ Colour->Hue = 0;
+ Colour->Saturation = 0;
+ Colour->Lightness = 74;
+
+ for(int i = 0; i < String.Length; ++i)
+ {
+ Colour->Hue += CharToColour(String.Base[i]).Hue;
+ Colour->Saturation += CharToColour(String.Base[i]).Saturation;
+ }
+
+ Colour->Hue = Colour->Hue % 360;
+ Colour->Saturation = Colour->Saturation % 26 + 74;
+}
+
+bool
+IsValidHSLColour(hsl_colour Colour)
+{
+ return (Colour.Hue >= 0 && Colour.Hue < 360)
+ && (Colour.Saturation >= 0 && Colour.Saturation <= 100)
+ && (Colour.Lightness >= 0 && Colour.Lightness <= 100);
+}
+
#define SLASH 1
#define NULLTERM 1
typedef struct
@@ -4326,6 +4388,7 @@ typedef struct
{
string Marker;
string WrittenText;
+ hsl_colour Colour;
} category_info;
#define CopyString(Dest, DestSize, Format, ...) CopyString_(__LINE__, (Dest), (DestSize), (Format), ##__VA_ARGS__)
@@ -5481,58 +5544,6 @@ TimecodeToDottedSeconds(v4 Timecode)
return (float)Timecode.Hours * SECONDS_PER_HOUR + (float)Timecode.Minutes * SECONDS_PER_MINUTE + (float)Timecode.Seconds + (float)Timecode.Milliseconds / 1000;
}
-typedef struct
-{
- unsigned int Hue:16;
- unsigned int Saturation:8;
- unsigned int Lightness:8;
-} hsl_colour;
-
-hsl_colour
-CharToColour(char Char)
-{
- hsl_colour Colour;
- if(Char >= 'a' && Char <= 'z')
- {
- Colour.Hue = (((float)Char - 'a') / ('z' - 'a') * 360);
- Colour.Saturation = (((float)Char - 'a') / ('z' - 'a') * 26 + 74);
- }
- else if(Char >= 'A' && Char <= 'Z')
- {
- Colour.Hue = (((float)Char - 'A') / ('Z' - 'A') * 360);
- Colour.Saturation = (((float)Char - 'A') / ('Z' - 'A') * 26 + 74);
- }
- else if(Char >= '0' && Char <= '9')
- {
- Colour.Hue = (((float)Char - '0') / ('9' - '0') * 360);
- Colour.Saturation = (((float)Char - '0') / ('9' - '0') * 26 + 74);
- }
- else
- {
- Colour.Hue = 180;
- Colour.Saturation = 50;
- }
-
- return Colour;
-}
-
-void
-StringToColourHash(hsl_colour *Colour, string String)
-{
- Colour->Hue = 0;
- Colour->Saturation = 0;
- Colour->Lightness = 74;
-
- for(int i = 0; i < String.Length; ++i)
- {
- Colour->Hue += CharToColour(String.Base[i]).Hue;
- Colour->Saturation += CharToColour(String.Base[i]).Saturation;
- }
-
- Colour->Hue = Colour->Hue % 360;
- Colour->Saturation = Colour->Saturation % 26 + 74;
-}
-
char *
SanitisePunctuation(char *String)
{
@@ -8102,7 +8113,7 @@ BuildCredits(string HMMLFilepath, buffer *CreditsMenu, HMML_VideoMetaData *Metad
}
void
-InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_info) *LocalTopics, _memory_book(category_info) *GlobalMedia, _memory_book(category_info) *LocalMedia, string Marker)
+InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_info) *LocalTopics, _memory_book(category_info) *GlobalMedia, _memory_book(category_info) *LocalMedia, string Marker, hsl_colour *Colour)
{
medium *Medium = GetMediumFromProject(CurrentProject, Marker);
@@ -8203,11 +8214,15 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
{
category_info *Src = GetPlaceInBook(LocalTopics, CategoryCount - 1);
category_info *Dest = GetPlaceInBook(LocalTopics, CategoryCount);
- Dest->Marker = Src->Marker;
+ *Dest = *Src;
}
category_info *New = GetPlaceInBook(LocalTopics, CategoryCount);
New->Marker = Marker;
+ if(Colour)
+ {
+ New->Colour = *Colour;
+ }
break;
}
}
@@ -8218,6 +8233,10 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
{
category_info *New = GetPlaceInBook(LocalTopics, TopicIndex);
New->Marker = Marker;
+ if(Colour)
+ {
+ New->Colour = *Colour;
+ }
}
bool MadeGlobalSpace = FALSE;
@@ -8241,11 +8260,15 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
{
category_info *Src = GetPlaceInBook(GlobalTopics, CategoryCount - 1);
category_info *Dest = GetPlaceInBook(GlobalTopics, CategoryCount);
- Dest->Marker = Src->Marker;
+ *Dest = *Src;
}
category_info *New = GetPlaceInBook(GlobalTopics, CategoryCount);
New->Marker = Marker;
+ if(Colour)
+ {
+ New->Colour = *Colour;
+ }
break;
}
}
@@ -8257,6 +8280,10 @@ InsertCategory(_memory_book(category_info) *GlobalTopics, _memory_book(category_
{
category_info *New = GetPlaceInBook(GlobalTopics, TopicIndex);
New->Marker = Marker;
+ if(Colour)
+ {
+ New->Colour = *Colour;
+ }
}
}
}
@@ -8348,9 +8375,18 @@ BuildCategoryIcons(buffer *CategoryIcons, _memory_book(category_info) *LocalTopi
CopyString(SanitisedMarker, sizeof(SanitisedMarker), "%.*s", (int)This->Marker.Length, This->Marker.Base);
SanitisePunctuation(SanitisedMarker);
- CopyStringToBuffer(CategoryIcons, "
",
+ CopyStringToBuffer(CategoryIcons,
+ "Marker.Length, This->Marker.Base,
SanitisedMarker);
+ if(IsValidHSLColour(This->Colour))
+ {
+ CopyStringToBuffer(CategoryIcons,
+ " data-hue=\"%u\" data-saturation=\"%u%%\"",
+ This->Colour.Hue, This->Colour.Saturation);
+ }
+ CopyStringToBuffer(CategoryIcons,
+ ">
");
}
}
@@ -8630,7 +8666,7 @@ BuildQuote(memory_book *Strings, quote_info *Info, string Speaker, int ID, bool
}
rc
-GenerateTopicColours(neighbourhood *N, string Topic)
+GenerateTopicColours(neighbourhood *N, string Topic, hsl_colour *Dest)
{
rc Result = RC_SUCCESS;
// NOTE(matt): Stack-string
@@ -8641,6 +8677,15 @@ GenerateTopicColours(neighbourhood *N, string Topic)
medium *Medium = GetMediumFromProject(CurrentProject, Topic);
if(!Medium)
{
+ if(StringsMatch(Topic, Wrap0("nullTopic")))
+ {
+ Dest->Hue = CINERA_HSL_TRANSPARENT_HUE;
+ }
+ else
+ {
+ StringToColourHash(Dest, Topic);
+ }
+
file Topics = {};
Topics.Path = 0;
Topics.Buffer.ID = BID_TOPICS;
@@ -8706,10 +8751,8 @@ GenerateTopicColours(neighbourhood *N, string Topic)
}
else
{
- hsl_colour Colour;
- StringToColourHash(&Colour, Topic);
WriteToFile(Topics.Handle, ".category.%s { border: 1px solid hsl(%d, %d%%, %d%%); background: hsl(%d, %d%%, %d%%); }\n",
- SanitisedTopic, Colour.Hue, Colour.Saturation, Colour.Lightness, Colour.Hue, Colour.Saturation, Colour.Lightness);
+ SanitisedTopic, Dest->Hue, Dest->Saturation, Dest->Lightness, Dest->Hue, Dest->Saturation, Dest->Lightness);
}
#if DEBUG_MEM
@@ -10899,7 +10942,7 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
{
*HasFilterMenu = TRUE;
}
- InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("authored"));
+ InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("authored"), NULL);
hsl_colour AuthorColour;
StringToColourHash(&AuthorColour, Author);
// TODO(matt): That EDITION_NETWORK site database API-polling stuff
@@ -10924,14 +10967,15 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
HMML_MarkerType Type = Timestamp->markers[MarkerIndex].type;
if(Type == HMML_CATEGORY)
{
- Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker));
+ hsl_colour TopicColour = {};
+ Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
if(Result == RC_SUCCESS)
{
if(!*HasFilterMenu)
{
*HasFilterMenu = TRUE;
}
- InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker));
+ InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
CopyStringToBuffer(&IndexBuffers->Text, "%.*s", (int)StringLength(Readable), InPtr);
}
else
@@ -11162,14 +11206,15 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
while(MarkerIndex < Timestamp->marker_count)
{
- Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker));
+ hsl_colour TopicColour = {};
+ Result = GenerateTopicColours(N, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
if(Result == RC_SUCCESS)
{
if(!*HasFilterMenu)
{
*HasFilterMenu = TRUE;
}
- InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker));
+ InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0(Timestamp->markers[MarkerIndex].marker), &TopicColour);
++MarkerIndex;
}
else
@@ -11182,10 +11227,11 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
{
if(LocalTopics.ItemCount == 0)
{
- Result = GenerateTopicColours(N, Wrap0("nullTopic"));
+ hsl_colour TopicColour = {};
+ Result = GenerateTopicColours(N, Wrap0("nullTopic"), &TopicColour);
if(Result == RC_SUCCESS)
{
- InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("nullTopic"));
+ InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, Wrap0("nullTopic"), &TopicColour);
}
}
@@ -11193,7 +11239,7 @@ ProcessTimestamp(buffers *CollationBuffers, neighbourhood *N, string Filepath, m
{
if(LocalMedia.ItemCount == 0)
{
- InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, DefaultMedium->ID);
+ InsertCategory(Topics, &LocalTopics, Media, &LocalMedia, DefaultMedium->ID, NULL);
}
BuildTimestampClass(&IndexBuffers->Class, &LocalTopics, &LocalMedia, DefaultMedium->ID);
@@ -11991,13 +12037,21 @@ HMMLToBuffers(buffers *CollationBuffers, template *BespokeTemplate, string BaseF
bool NullTopic = StringsMatch(This->Marker, Wrap0("nullTopic"));
CopyStringToBuffer(&MenuBuffers.FilterTopics,
- " \n"
- " %.*s\n"
- "
\n",
+ " \n"
+ " Colour))
+ {
+ CopyStringToBuffer(&MenuBuffers.FilterTopics,
+ " data-hue=\"%u\" data-saturation=\"%u%%\"",
+ This->Colour.Hue, This->Colour.Saturation);
+ }
+ CopyStringToBuffer(&MenuBuffers.FilterTopics,
+ ">%.*s\n"
+ "
\n",
NullTopic ? (int)sizeof("(null topic)")-1 : (int)This->Marker.Length,
NullTopic ? "(null topic)" : This->Marker.Base);
}
diff --git a/cinera/cinera_pre.js b/cinera/cinera_pre.js
index 5b23b3f..78aba63 100644
--- a/cinera/cinera_pre.js
+++ b/cinera/cinera_pre.js
@@ -551,35 +551,6 @@ BindHelp(Button, DocumentationContainer)
})
}
-function RGBtoHSL(colour)
-{
- var rgb = colour.slice(4, -1).split(", ");
- var red = rgb[0];
- var green = rgb[1];
- var blue = rgb[2];
- var min = Math.min(red, green, blue);
- var max = Math.max(red, green, blue);
- var chroma = max - min;
- var hue = 0;
- if(max == red)
- {
- hue = ((green - blue) / chroma) % 6;
- }
- else if(max == green)
- {
- hue = ((blue - red) / chroma) + 2;
- }
- else if(max == blue)
- {
- hue = ((red - green) / chroma) + 4;
- }
-
- var saturation = chroma / 255 * 100;
- hue = (hue * 60) < 0 ? 360 + (hue * 60) : (hue * 60);
-
- return [hue, saturation]
-}
-
function getBackgroundColourRGB(element) {
var Colour = getComputedStyle(element).getPropertyValue("background-color");
var depth = 0;
@@ -610,28 +581,34 @@ function setTextLightness(textElement)
{
var textHue = textElement.getAttribute("data-hue");
var textSaturation = textElement.getAttribute("data-saturation");
- if(getBackgroundBrightness(textElement.parentNode) < 127)
+ if(textHue && textSaturation)
{
- textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
- }
- else
- {
- textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
+ if(getBackgroundBrightness(textElement.parentNode) < 127)
+ {
+ textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 76%)");
+ }
+ else
+ {
+ textElement.style.color = ("hsl(" + textHue + ", " + textSaturation + ", 24%)");
+ }
}
}
function setDotLightness(topicDot)
{
- var Hue = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[0];
- var Saturation = RGBtoHSL(getComputedStyle(topicDot).getPropertyValue("background-color"))[1];
- if(getBackgroundBrightness(topicDot.parentNode) < 127)
+ var dotHue = topicDot.getAttribute("data-hue");
+ var dotSaturation = topicDot.getAttribute("data-saturation");
+ if(dotHue && dotSaturation)
{
- topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
- topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 76%)");
- }
- else
- {
- topicDot.style.backgroundColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
- topicDot.style.borderColor = ("hsl(" + Hue + ", " + Saturation + "%, 47%)");
+ if(getBackgroundBrightness(topicDot.parentNode) < 127)
+ {
+ topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
+ topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 76%)");
+ }
+ else
+ {
+ topicDot.style.backgroundColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
+ topicDot.style.borderColor = ("hsl(" + dotHue + ", " + dotSaturation + ", 47%)");
+ }
}
}