@model CharacterArcViewModel @{ ViewData["Title"] = $"{Model.Character.CharacterName} Arc"; var arcScenes = Model.ArcPoints .OrderBy(point => point.GlobalSceneIndex) .Select(point => new { sceneId = point.SceneID, axisLabel = $"Ch {point.ChapterNumber:g}.{point.SceneNumber:g}", chapterLabel = $"Chapter {point.ChapterNumber:g}", sceneNumber = point.SceneNumber.ToString("g"), sceneTitle = point.SceneTitle, bookTitle = point.BookTitle, characterAppears = true, characterPresence = point.IsPov ? "POV scene" : point.PresenceTypeName ?? "Appears", timelineUrl = Url.Action("Edit", "Scenes", new { id = point.SceneID }) }) .ToList(); var relationshipIntensityPoints = Model.RelationshipEvents .Where(item => item.Intensity.HasValue) .GroupBy(item => item.SceneID) .Select(group => new { sceneId = group.Key, value = Convert.ToInt32(Math.Round(group.Average(item => item.Intensity!.Value))) }); var arcGraphMetrics = new[] { new { metricName = "Overall Intensity", points = Model.ArcPoints.Where(point => point.OverallIntensity.HasValue).Select(point => new { sceneId = point.SceneID, value = point.OverallIntensity!.Value }) }, new { metricName = "Emotional Weight", points = Model.ArcPoints.Where(point => point.EmotionalWeight.HasValue).Select(point => new { sceneId = point.SceneID, value = point.EmotionalWeight!.Value }) }, new { metricName = "Tension", points = Model.ArcPoints.Where(point => point.Tension.HasValue).Select(point => new { sceneId = point.SceneID, value = point.Tension!.Value }) }, new { metricName = "Romance", points = Model.ArcPoints.Where(point => point.Romance.HasValue).Select(point => new { sceneId = point.SceneID, value = point.Romance!.Value }) }, new { metricName = "Action", points = Model.ArcPoints.Where(point => point.Action.HasValue).Select(point => new { sceneId = point.SceneID, value = point.Action!.Value }) }, new { metricName = "Darkness", points = Model.ArcPoints.Where(point => point.Darkness.HasValue).Select(point => new { sceneId = point.SceneID, value = point.Darkness!.Value }) }, new { metricName = "Hope / Lightness", points = Model.ArcPoints.Where(point => point.HopeLightness.HasValue).Select(point => new { sceneId = point.SceneID, value = point.HopeLightness!.Value }) }, new { metricName = "Relationship Intensity", points = relationshipIntensityPoints } } .Where(metric => metric.points.Any()) .ToList(); var arcGraphPayload = new { defaultMetrics = new[] { "Overall Intensity", "Emotional Weight", "Tension" }, scenes = arcScenes, metrics = arcGraphMetrics }; }

Character Arc

@Model.Character.CharacterName

Appearances, emotional shape, relationship changes, knowledge gains, and advisory notes.

Knowledge timeline Show on timeline

Arc Shape

Character Metric Shape

@arcGraphMetrics.Count metric@(arcGraphMetrics.Count == 1 ? "" : "s")
@if (!arcGraphMetrics.Any() || !arcScenes.Any()) {

Add scene metrics to scenes where @Model.Character.CharacterName appears to see their emotional and story shape.

} else {

Compare this character's scene metrics across appearances in narrative order. Click a point to open the scene.

Character arc chart could not load. The scene markers are still listed below.

}

Appearance Frequency

@Model.ArcPoints.Count

@Model.ArcPoints.Count(x => x.IsPov) POV scene@(Model.ArcPoints.Count(x => x.IsPov) == 1 ? "" : "s")

Knowledge Gains

@Model.KnowledgeEvents.Count

Relationship Beats

@Model.RelationshipEvents.Count

Scene Markers

@foreach (var point in Model.ArcPoints) {

@point.BookTitle / Ch @point.ChapterNumber / Scene @point.SceneNumber: @point.SceneTitle

@if (point.IsPov) { POV }
Emotional @DisplayValue(point.EmotionalWeight) Tension @DisplayValue(point.Tension) Romance @DisplayValue(point.Romance) Action @DisplayValue(point.Action) @(point.RoleInSceneTypeName ?? "No role")
@if (!string.IsNullOrWhiteSpace(point.EmotionalState) || !string.IsNullOrWhiteSpace(point.KeyNotes)) {

@point.EmotionalState @point.KeyNotes

}
}
@functions { private static string DisplayValue(int? value) => value?.ToString() ?? "-"; }