Added UI selection parts. Now the text is more useful.

This commit is contained in:
That_One_Nerd 2024-04-08 09:15:01 -04:00
parent 470a70a97a
commit 41732d01e7
12 changed files with 211 additions and 31 deletions

View File

@ -238,19 +238,8 @@ public partial class GraphForm : Form
{
if (ables[i].ShouldSelectGraphable(this, graphMousePos, 2.5))
{
Float2 selectedPoint = ables[i].GetSelectedPoint(this, graphMousePos);
GraphUiCircle select = new(selectedPoint);
Int2 textPos = GraphSpaceToScreenSpace(select.center);
textPos.y -= (int)(DpiFloat * 32 / 192);
string content = $"({selectedPoint.x:0.00}, {selectedPoint.y:0.00})";
SizeF textSize = g.MeasureString(content, textFont);
g.FillRectangle(background, new Rectangle(textPos.x, textPos.y,
(int)textSize.Width, (int)textSize.Height));
g.DrawString(content, textFont, graphPens[i].Brush, new Point(textPos.x, textPos.y));
select.Render(this, g, graphPens[i]);
IEnumerable<IGraphPart> selectionParts = ables[i].GetSelectionItemsToRender(this, graphMousePos);
foreach (IGraphPart selPart in selectionParts) selPart.Render(this, g, graphPens[i]);
}
}
}

View File

@ -33,6 +33,7 @@ public partial class ViewCacheForm : Form
foreach (Graphable able in refForm.Graphables)
{
long thisBytes = able.GetCacheBytes();
if (thisBytes == 0) continue;
CachePie.Values.Add((able.Color, thisBytes));
totalBytes += thisBytes;

View File

@ -37,5 +37,5 @@ public abstract class Graphable
public virtual void Preload(Float2 xRange, Float2 yRange, double step) { }
public virtual bool ShouldSelectGraphable(in GraphForm graph, Float2 graphMousePos, double factor) => false;
public virtual Float2 GetSelectedPoint(in GraphForm graph, Float2 graphMousePos) => default;
public virtual IEnumerable<IGraphPart> GetSelectionItemsToRender(in GraphForm graph, Float2 graphMousePos) => [];
}

View File

@ -2,6 +2,7 @@
using Graphing.Parts;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace Graphing.Graphables;
@ -52,6 +53,81 @@ public class ColumnTable : Graphable
return items;
}
public override bool ShouldSelectGraphable(in GraphForm graph, Float2 graphMousePos, double factor)
{
// Get closest value to mouse pos.
double closestDist = double.PositiveInfinity, closestX = 0, closestY = 0;
foreach (KeyValuePair<double, double> points in tableXY)
{
double dist = Math.Abs(points.Key - graphMousePos.x);
if (dist < closestDist)
{
closestDist = dist;
closestX = points.Key;
closestY = points.Value;
}
}
Int2 screenMousePos = graph.GraphSpaceToScreenSpace(graphMousePos);
Int2 minBox = graph.GraphSpaceToScreenSpace(new(closestX - width / 2, 0)),
maxBox = graph.GraphSpaceToScreenSpace(new(closestX + width / 2, closestY));
int distX, distY;
if (screenMousePos.x < minBox.x) distX = minBox.x - screenMousePos.x; // On left side.
else if (screenMousePos.x > maxBox.x) distX = screenMousePos.x - maxBox.x; // On right side.
else distX = 0; // Inside.
if (closestY > 0)
{
if (screenMousePos.y > minBox.y) distY = screenMousePos.y - minBox.y; // Underneath.
else if (screenMousePos.y < maxBox.y) distY = maxBox.y - screenMousePos.y; // Above.
else distY = 0; // Inside.
}
else
{
if (screenMousePos.y < minBox.y) distY = minBox.y - screenMousePos.y; // Underneath.
else if (screenMousePos.y > maxBox.y) distY = screenMousePos.y - maxBox.y; // Above.
else distY = 0; // Inside.
}
int totalDist = (int)Math.Sqrt(distX * distX + distY * distY);
return totalDist < 50 * factor * graph.DpiFloat / 192;
}
public override IEnumerable<IGraphPart> GetSelectionItemsToRender(in GraphForm graph, Float2 graphMousePos)
{
// Get closest value to mouse pos.
double closestDist = double.PositiveInfinity, closestX = 0, closestY = 0;
foreach (KeyValuePair<double, double> points in tableXY)
{
double dist = Math.Abs(points.Key - graphMousePos.x);
if (dist < closestDist)
{
closestDist = dist;
closestX = points.Key;
closestY = points.Value;
}
}
Float2 textPoint = new(closestX, closestY);
Int2 offset;
ContentAlignment alignment;
if (textPoint.y >= 0)
{
offset = new(0, -5);
alignment = ContentAlignment.BottomCenter;
}
else
{
offset = new(0, 5);
alignment = ContentAlignment.TopCenter;
}
return
[
new GraphUiText($"{closestY:0.00}", textPoint, alignment, offsetPix: offset)
];
}
// Nothing to preload, everything is already cached.
public override void Preload(Float2 xRange, Float2 yRange, double step) { }
}

View File

@ -3,6 +3,7 @@ using Graphing.Forms;
using Graphing.Parts;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace Graphing.Graphables;
@ -148,8 +149,15 @@ public class Equation : Graphable, IIntegrable, IDerivable, ITranslatableXY, ICo
double totalDist = Math.Sqrt(dist.x * dist.x + dist.y * dist.y);
return totalDist <= allowedDist;
}
public override Float2 GetSelectedPoint(in GraphForm graph, Float2 graphMousePos) =>
new(graphMousePos.x, GetFromCache(graphMousePos.x, 1e-3));
public override IEnumerable<IGraphPart> GetSelectionItemsToRender(in GraphForm graph, Float2 graphMousePos)
{
Float2 point = new(graphMousePos.x, GetFromCache(graphMousePos.x, 1e-3));
return
[
new GraphUiText($"({point.x:0.00}, {point.y:0.00})", point, ContentAlignment.BottomLeft),
new GraphUiCircle(point),
];
}
public override void Preload(Float2 xRange, Float2 yRange, double step)
{

View File

@ -1,7 +1,9 @@
using Graphing.Abstract;
using Graphing.Forms;
using Graphing.Parts;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace Graphing.Graphables;
@ -42,16 +44,50 @@ public class EquationDifference : Graphable, ITranslatableX, IConvertEquation
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
{
Float2 pA = new(Position, points.x),
pB = new(Position, points.y),
pC = new(Position, (points.x + points.y) / 2);
return [new GraphUiText($"{points.x - points.y:0.00}", pC),
new GraphUiCircle(pA), new GraphUiCircle(pB), new GraphLine(pA, pB)];
pB = new(Position, points.y);
return
[
new GraphUiCircle(pA),
new GraphUiCircle(pB),
new GraphLine(pA, pB)
];
}
public double DistanceAtPoint(double x) => equA.GetValueAt(x) - equB.GetValueAt(x);
public override Graphable ShallowCopy() => new EquationDifference(Position, equA, equB);
public override bool ShouldSelectGraphable(in GraphForm graph, Float2 graphMousePos, double factor)
{
Float2 nearestPoint = new(Position, graphMousePos.y);
double upper = double.Max(points.x, points.y),
lower = double.Min(points.x, points.y);
if (nearestPoint.y > upper) nearestPoint.y = upper;
else if (nearestPoint.y < lower) nearestPoint.y = lower;
Int2 nearestPixelPoint = graph.GraphSpaceToScreenSpace(nearestPoint);
Int2 screenMousePos = graph.GraphSpaceToScreenSpace(graphMousePos);
Int2 diff = new(screenMousePos.x - nearestPixelPoint.x,
screenMousePos.y - nearestPixelPoint.y);
int dist = (int)Math.Sqrt(diff.x * diff.x + diff.y * diff.y);
return dist < 50 * factor * graph.DpiFloat / 192;
}
public override IEnumerable<IGraphPart> GetSelectionItemsToRender(in GraphForm graph, Float2 graphMousePos)
{
Float2 nearestPoint = new(Position, graphMousePos.y);
double upper = double.Max(points.x, points.y),
lower = double.Min(points.x, points.y);
if (nearestPoint.y > upper) nearestPoint.y = upper;
else if (nearestPoint.y < lower) nearestPoint.y = lower;
return
[
new GraphUiText($"Δ = {points.x - points.y:0.000}", nearestPoint, ContentAlignment.MiddleLeft, offsetPix: new Int2(15, 0)),
new GraphUiCircle(nearestPoint)
];
}
public Equation ToEquation() => new(DistanceAtPoint)
{
Color = Color,

View File

@ -234,6 +234,6 @@ public class IntegralEquation : Graphable, IIntegrable, IDerivable
double totalDist = Math.Sqrt(dist.x * dist.x + dist.y * dist.y);
return totalDist <= allowedDist;
}
public override Float2 GetSelectedPoint(in GraphForm graph, Float2 graphMousePos) =>
new(graphMousePos.x, IntegralAtPoint(graphMousePos.x));
public override IEnumerable<IGraphPart> GetSelectionItemsToRender(in GraphForm graph, Float2 graphMousePos) =>
[new GraphUiCircle(new(graphMousePos.x, IntegralAtPoint(graphMousePos.x)))];
}

View File

@ -2,6 +2,7 @@
using Graphing.Parts;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace Graphing.Graphables;
@ -101,7 +102,7 @@ public class SlopeField : Graphable
double totalDist = Math.Sqrt(dist.x * dist.x + dist.y * dist.y);
return totalDist <= allowedDist;
}
public override Float2 GetSelectedPoint(in GraphForm graph, Float2 graphMousePos)
public override IEnumerable<IGraphPart> GetSelectionItemsToRender(in GraphForm graph, Float2 graphMousePos)
{
Float2 nearestPos = new(Math.Round(graphMousePos.x * detail) / detail,
Math.Round(graphMousePos.y * detail) / detail);
@ -114,7 +115,11 @@ public class SlopeField : Graphable
lineY = slope * (lineX - nearestPos.x) + nearestPos.y;
Float2 point = new(lineX, lineY);
return point;
return
[
new GraphUiText($"M = {slope:0.000}", point, ContentAlignment.BottomLeft),
new GraphUiCircle(point)
];
}
public override void Preload(Float2 xRange, Float2 yRange, double step)

View File

@ -3,6 +3,7 @@ using Graphing.Forms;
using Graphing.Parts;
using System;
using System.Collections.Generic;
using System.Drawing;
namespace Graphing.Graphables;
@ -58,7 +59,11 @@ public class TangentLine : Graphable, IConvertEquation, ITranslatableX
public override IEnumerable<IGraphPart> GetItemsToRender(in GraphForm graph)
{
Float2 point = new(Position, currentSlope.y);
return [MakeSlopeLine(), new GraphUiCircle(point)];
return
[
MakeSlopeLine(),
new GraphUiCircle(point)
];
}
protected GraphLine MakeSlopeLine()
{
@ -107,7 +112,7 @@ public class TangentLine : Graphable, IConvertEquation, ITranslatableX
double totalDist = Math.Sqrt(dist.x * dist.x + dist.y * dist.y);
return totalDist <= allowedDist;
}
public override Float2 GetSelectedPoint(in GraphForm graph, Float2 graphMousePos)
public override IEnumerable<IGraphPart> GetSelectionItemsToRender(in GraphForm graph, Float2 graphMousePos)
{
GraphLine line = MakeSlopeLine();
@ -115,7 +120,15 @@ public class TangentLine : Graphable, IConvertEquation, ITranslatableX
Math.Min(line.a.x, line.b.x),
Math.Max(line.a.x, line.b.x)),
lineY = currentSlope.x * (lineX - Position) + currentSlope.y;
return new Float2(lineX, lineY);
double slope = currentSlope.x;
Float2 point = new(lineX, lineY);
return
[
new GraphUiText($"M = {slope:0.000}", point, ContentAlignment.BottomLeft),
new GraphUiCircle(new(lineX, lineY))
];
}
public override void Preload(Float2 xRange, Float2 yRange, double step)

View File

@ -45,7 +45,9 @@ public record struct GraphRectangle : IGraphPart
start.y - end.y);
if (size.x == 0 || size.y == 0) return;
Color initialColor = pen.Color;
pen.Color = Color.FromArgb((int)(opacity * 255), pen.Color);
g.FillRectangle(pen.Brush, new Rectangle(start.x, end.y, size.x, size.y));
pen.Color = initialColor;
}
}

View File

@ -9,16 +9,22 @@ public record struct GraphUiText : IGraphPart
public Float2 position;
public bool background;
public ContentAlignment alignment;
public Int2 offsetPix;
private readonly Font font;
private readonly Brush? backgroundBrush;
public GraphUiText(string text, Float2 position, bool background = true)
public GraphUiText(string text, Float2 position, ContentAlignment alignment,
bool background = true, Int2? offsetPix = null)
{
font = new Font("Segoe UI", 8, FontStyle.Bold);
this.text = text;
this.position = position;
this.background = background;
this.alignment = alignment;
this.offsetPix = offsetPix ?? new();
if (background) backgroundBrush = new SolidBrush(GraphForm.BackgroundColor);
}
@ -26,10 +32,53 @@ public record struct GraphUiText : IGraphPart
public readonly void Render(in GraphForm form, in Graphics g, in Pen p)
{
Int2 posScreen = form.GraphSpaceToScreenSpace(position);
posScreen.y -= (int)(form.DpiFloat * font.Size * 2 / 192);
SizeF size = g.MeasureString(text, font);
// Adjust X position based on alignment.
switch (alignment)
{
case ContentAlignment.TopLeft or
ContentAlignment.MiddleLeft or
ContentAlignment.BottomLeft: break; // Nothing to offset.
case ContentAlignment.TopCenter or
ContentAlignment.MiddleCenter or
ContentAlignment.BottomCenter:
posScreen.x -= (int)(size.Width / 2);
break;
case ContentAlignment.TopRight or
ContentAlignment.MiddleRight or
ContentAlignment.BottomRight:
posScreen.x -= (int)size.Width;
break;
}
// Adjust Y position based on alignment.
switch (alignment)
{
case ContentAlignment.TopLeft or
ContentAlignment.TopCenter or
ContentAlignment.TopRight: break; // Nothing to offset.
case ContentAlignment.MiddleLeft or
ContentAlignment.MiddleCenter or
ContentAlignment.MiddleRight:
posScreen.y -= (int)(size.Height / 2);
break;
case ContentAlignment.BottomLeft or
ContentAlignment.BottomCenter or
ContentAlignment.BottomRight:
posScreen.y -= (int)size.Height;
break;
}
posScreen.x += (int)(offsetPix.x * form.DpiFloat / 192);
posScreen.y += (int)(offsetPix.y * form.DpiFloat / 192);
if (background)
{
SizeF size = g.MeasureString(text, font);
g.FillRectangle(backgroundBrush!, new Rectangle(posScreen.x, posScreen.y,
(int)size.Width, (int)size.Height));
}

View File

@ -20,7 +20,8 @@ internal static class Program
equB = new(Math.Cos);
EquationDifference diff = new(2, equA, equB);
ParametricEquation equC = new(0, 20, t => 0.0375 * t * Math.Cos(t), t => 0.0625 * t * Math.Sin(t) + 3);
graph.Graph(equA, equB, diff, equC, equA.ToColumnTable(-1, 1, 2));
TangentLine tanA = new(2, 2, equA);
graph.Graph(equA, equB, diff, equC, equB.ToColumnTable(-3, 3, 2), tanA);
Application.Run(graph);
}