public void DrawPlot( Rect rect, int target = 0, string label = "", bool positiveOnly = false, bool negativeOnly = false )
{
// set sign
int sign = negativeOnly ? -1 : 1;
// subset chapters
List<Chapter> chapters = _chaptersShown.Where( chapter => !positiveOnly || chapter.pages[periodShown].Any( i => i > 0 ) )
.Where( chapter => !negativeOnly || chapter.pages[periodShown].Any( i => i < 0 ) )
.ToList();
// get out early if no chapters.
if ( chapters.Count == 0 )
{
GUI.DrawTexture( rect.ContractedBy(Utilities.Margin), Resources.SlightlyDarkBackground );
Utilities.Label( rect, "FM.HistoryNoChapters".Translate(), null, TextAnchor.MiddleCenter, color: Color.grey );
return;
}
// stuff we need
Rect plot = rect.ContractedBy( Utilities.Margin );
plot.xMin += _yAxisMargin;
// maximum of all chapters.
int max = CeilToPrecision( Math.Max( chapters.Select( c => c.Max( periodShown, !negativeOnly ) ).Max(), target ) * 1.2f );
// size, and pixels per node.
float w = plot.width;
float h = plot.height;
float wu = w / Size; // width per section
float hu = h / max; // height per count
int bi = max / ( Breaks + 1 ); // count per break
float bu = hu * bi; // height per break
// plot the line(s)
GUI.DrawTexture( plot, Resources.SlightlyDarkBackground );
GUI.BeginGroup( plot );
foreach ( Chapter chapter in chapters )
{
chapter.Plot( periodShown, plot.AtZero(), wu, hu, sign );
}
// handle mouseover events
if ( Mouse.IsOver( plot.AtZero() ) )
{
// very conveniently this is the position within the current group.
Vector2 pos = Event.current.mousePosition;
Vector2 upos = new Vector2(pos.x / wu, (plot.height - pos.y) / hu);
// get distances
float[] distances = chapters.Select( c => Math.Abs( c.ValueAt( periodShown, (int)upos.x, sign ) - upos.y ) ).ToArray();
// get the minimum index
float min = int.MaxValue;
int minIndex = 0;
for ( int i = 0; i < distances.Count(); i++ )
{
if ( distances[i] < min )
{
minIndex = i;
min = distances[i];
}
}
// closest line
Chapter closest = chapters[minIndex];
// do minimum stuff.
Vector2 realpos = new Vector2( pos.x, plot.height - closest.ValueAt( periodShown, (int)upos.x, sign ) * hu );
Rect blipRect = new Rect(realpos.x - Utilities.SmallIconSize / 2f, realpos.y - Utilities.SmallIconSize / 2f, Utilities.SmallIconSize, Utilities.SmallIconSize );
GUI.color = closest.lineColor;
GUI.DrawTexture( blipRect, Resources.StageB );
GUI.color = DefaultLineColor;
// get orientation of tooltip
Vector2 tippos = realpos + new Vector2( Utilities.Margin, Utilities.Margin );
string tip = chapters[minIndex].label + ": " + FormatCount( chapters[minIndex].ValueAt( periodShown, (int)upos.x, sign ));
Vector2 tipsize = Text.CalcSize( tip );
bool up = false, left = false;
if ( tippos.x + tipsize.x > plot.width )
{
left = true;
tippos.x -= tipsize.x + 2 * + Utilities.Margin;
}
if ( tippos.y + tipsize.y > plot.height )
{
up = true;
tippos.y -= tipsize.y + 2 * Utilities.Margin;
}
TextAnchor anchor = TextAnchor.UpperLeft;
if (up && left) anchor = TextAnchor.LowerRight;
if ( up && !left ) anchor = TextAnchor.LowerLeft;
if ( !up && left ) anchor = TextAnchor.UpperRight;
Rect tooltipRect = new Rect( tippos.x, tippos.y, tipsize.x, tipsize.y );
Utilities.Label( tooltipRect, tip, anchor: anchor, font: GameFont.Tiny );
}
// draw target line
if ( DrawTargetLine )
{
GUI.color = Color.gray;
for ( int i = 0; i < plot.width / DashLength; i += 2 )
{
Widgets.DrawLineHorizontal( i * DashLength, plot.height - target * hu, DashLength );
}
}
// draw legend
int lineCount = _chapters.Count;
if ( AllowTogglingLegend && lineCount > 1 && DrawInlineLegend )
{
float rowHeight = 20f;
float lineLength = 30f;
float labelWidth = 100f;
Vector2 cur = Vector2.zero;
foreach ( Chapter chapter in _chapters )
{
GUI.color = chapter.lineColor;
Widgets.DrawLineHorizontal(cur.x, cur.y + rowHeight / 2f, lineLength);
cur.x += lineLength;
Utilities.Label( ref cur, labelWidth, rowHeight, chapter.label, font: GameFont.Tiny );
cur.x = 0f;
}
GUI.color = Color.white;
}
GUI.EndGroup();
// plot axis
GUI.BeginGroup( rect );
Text.Anchor = TextAnchor.MiddleRight;
Text.Font = GameFont.Tiny;
// draw ticks + labels
for ( int i = 1; i < Breaks + 1; i++ )
{
Widgets.DrawLineHorizontal( _yAxisMargin + Margin / 2, plot.height - i * bu, Margin );
Rect labRect = new Rect( 0f, plot.height - i * bu - 4f, _yAxisMargin, 20f );
Widgets.Label( labRect, FormatCount( i * bi ) );
}
Text.Font = GameFont.Small;
Text.Anchor = TextAnchor.UpperLeft;
GUI.color = Color.white;
rect = rect.AtZero(); // ugh, I'm tired, just work.
// period / variables picker
if ( DrawOptions )
{
Rect switchRect = new Rect( rect.xMax - Utilities.SmallIconSize - Utilities.Margin,
rect.yMin + Utilities.Margin, Utilities.SmallIconSize,
Utilities.SmallIconSize );
Widgets.DrawHighlightIfMouseover( switchRect );
if ( Widgets.ButtonImage( switchRect, Resources.Cog ) )
{
List<FloatMenuOption> options =
periods.Select(
p =>
new FloatMenuOption( "FM.HistoryPeriod".Translate() + ": " + p.ToString(),
delegate { periodShown = p; } ) ).ToList();
if ( AllowTogglingLegend && _chapters.Count > 1 ) // add option to show/hide legend if appropriate.
{
options.Add( new FloatMenuOption( "FM.HistoryShowHideLegend".Translate(),
delegate { DrawInlineLegend = !DrawInlineLegend; } ) );
}
Find.WindowStack.Add( new FloatMenu( options ) );
}
}
GUI.EndGroup();
}