Editor/DynamicsGraphWidget.cs
using System;
using System.Collections.Generic;
using Andicraft.SecondOrderDynamics;
using Editor;
using Sandbox;

public class DynamicsGraphWidget : Widget
{
	private SerializedObject _targetObject;
	public DynamicsGraphWidget(Widget parent, SerializedObject baseProp) : base(parent, false)
	{
		// Create a Column Layout
		Layout = Layout.Column();
		// Give it some Margins/Spacing
		Layout.Margin = 4;
		Layout.Spacing = 4;
		
		// Apply some CSS styling
		ToolTip = "Five second preview of the current dynamics settings";

		var f = new Frame( this ) { HorizontalSizeMode = SizeMode.Expand, Size = new Vector2( 100 ) };
		Layout.Add(f);
		_targetObject = baseProp;
	}
	
	protected override void OnPaint()
	{
		Paint.SetBrushAndPen( new Color( 0, 0, 0, .2f ), new Color( 0, 0, 0, .2f ) );
		var borderRect = ContentRect;
		borderRect.Left += 1;
		Paint.DrawRect( borderRect );
		
		_targetObject.TryGetProperty( "Frequency", out var f );
		_targetObject.TryGetProperty( "Damping", out var d );
		_targetObject.TryGetProperty( "Response", out var r );

		var fd = new FloatDynamics( f.GetValue<float>(), d.GetValue<float>(), r.GetValue<float>(), 1);
		
		Paint.SetPen( Color.Red );
		Paint.ClearBrush();


		var linePts = new List<Vector2>();
		var baseY = ContentRect.Center.y;
		var waveHeight = ContentRect.Height * .5f;
		
		for ( int i = 0; i < 60*5; i++ )
		{
			var x = (ContentRect.Width / 60f*5f) * (i / 60f * 5f);
			var y = Math.Clamp(baseY + fd.Update( 1 / 60f, 0 ) * waveHeight, ContentRect.Top, ContentRect.Bottom);
			linePts.Add( new Vector2( x, y ) );
		}
		Paint.DrawLine(linePts);
	}
}