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%)"); + } } }