public static void Main(string[] args) { using (IDbConnection con = OpenDatabaseConnection()) { var relations = GetRelations(con).ToList(); var knownAnime = GetAnime(con).ToDictionary(a => a.AnimeId); var relationsPerAnime = relations.Select(r => new { From = r.AnimeId, To = r.RelatedAnimeId }) .Union(relations.Select(r => new { From = r.RelatedAnimeId, To = r.AnimeId })) .ToLookup(r => r.From, r => r.To); var animeGroupCalculator = AutoAnimeGroupCalculator.CreateFromServerSettings(con); AutoGroupExclude relExclude = ServerSettings.GetAutoGroupSeriesRelationExclusions(); List <HashSet <int> > subGraphs = BuildSubGraphs(relationsPerAnime); WriteLine("digraph Shoko_Anime_Relations {"); WriteLine("\tnode [fontsize=11, fillcolor=powderblue, style=filled];"); WriteLine("\tgraph [nodesep=1.5, ranksep=6, overlap=false, splines=spline, style=dashed, smoothing=power_dist];"); int sgCount = 0; // Render each subgraph foreach (var subgraph in subGraphs) { Write("\tsubgraph cluster_"); Write(sgCount); WriteLine(" {"); foreach (int animeId in subgraph) { Write("\t\t\""); Write(animeId); Write("\" [fillcolor="); if (knownAnime.TryGetValue(animeId, out var anime)) { switch (anime.AnimeType) { case AnimeType.Movie: Write("palegreen"); break; case AnimeType.OVA: Write("tan"); break; case AnimeType.TVSeries: Write("lightblue"); break; case AnimeType.TVSpecial: Write("cornsilk"); break; case AnimeType.Other: Write("lightpink"); break; case AnimeType.Web: Write("pink"); break; } if (animeGroupCalculator.GetGroupAnimeId(animeId) == animeId) { Write(", penwidth=2, shape=doubleoctagon"); } string title = anime.MainTitle; if (title.Length > MaxTitleLength) { title = title.Substring(0, MaxTitleLength - 3) + "..."; } title = WordWrap(title, WordWrapLength); Write(", label=<"); Write(anime.AnimeId); if (anime.AirDate != null) { Write(" <i>("); Write(anime.AirDate.Value.ToString("MMM yyyy")); Write(")</i>"); } Write("<br/><b>"); Write(HttpUtility.HtmlEncode(title).Replace("\n", "<br/>")); Write("</b>>"); } else // Write colour for missing series { Write("tomato, shape=box"); } Write(", target=_blank, href=\"https://anidb.net/a"); Write(animeId); WriteLine("\"];"); } WriteLine("\t}"); sgCount++; } // Render all edges WriteLine(); WriteLine("\t#Edges"); foreach (var edge in relations) { Write("\t\""); Write(edge.AnimeId); Write("\" -> \""); Write(edge.RelatedAnimeId); Write("\" [label=\""); Write(edge.RelationType); Write("\""); // If the relation is meant to be exlcuded when calculating groups, then we'll colour it differently if (Enum.TryParse(edge.RelationType.Replace(" ", string.Empty), true, out AutoGroupExclude relType) && (relType & relExclude) > 0) { Write(", color=grey, style=dashed"); } WriteLine("];"); } WriteLine("}"); } }