add visualization and report
This commit is contained in:
parent
661b55ace6
commit
3a4a5f6021
282
Lab4/Algorithm.cs
Normal file
282
Lab4/Algorithm.cs
Normal file
@ -0,0 +1,282 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lab4
|
||||
{
|
||||
public class Algorithm
|
||||
{
|
||||
/*
|
||||
Faile places_data.xlsx pateikta informacija apie lankytinas vietas (1 lentelė). Tikslas: kaip galima pigesnio maršruto sudarymas kai,
|
||||
|
||||
* priimama, kad kelionės tarp vietų kaina lygi kvadratinei šakniai iš kelionės atstumo;
|
||||
* kelionės pradžios ir pabaigos vieta sutampa (su grįžimu atgal);
|
||||
* ta pati vieta negali būti aplankyta daugiau nei vieną;
|
||||
* Reikia aplankyti bet kurias 150 vietų iš pateikto sąrašo.
|
||||
*/
|
||||
|
||||
public int pathLength = 150;
|
||||
public int batchSize = 40;
|
||||
public float mutationPickUniqueChance = 0.005f;
|
||||
public float mutationSwapChance = 0.01f;
|
||||
public float mutationPickLocalChance = 0.005f;
|
||||
public float localAreaRadius = 70000.000f;
|
||||
public int seed;
|
||||
public int iteration = 0;
|
||||
public SortedList<double, List<int>> paths;
|
||||
public List<List<int>> localPlaces;
|
||||
|
||||
List<Place> places;
|
||||
Random rand;
|
||||
|
||||
public Algorithm(List<Place> places, int seed)
|
||||
{
|
||||
this.places = places;
|
||||
this.seed = seed;
|
||||
rand = new Random(seed);
|
||||
paths = GenerateInitialPaths(rand, places, pathLength, batchSize);
|
||||
localPlaces = ComputeLocalNodes(places, localAreaRadius);
|
||||
}
|
||||
|
||||
public Algorithm(List<Place> places) : this(places, (int)DateTime.Now.Ticks) { }
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
rand = new Random(seed);
|
||||
paths = GenerateInitialPaths(rand, places, pathLength, batchSize);
|
||||
localPlaces = ComputeLocalNodes(places, localAreaRadius);
|
||||
iteration = 0;
|
||||
}
|
||||
|
||||
public class Place
|
||||
{
|
||||
public string name;
|
||||
public float x;
|
||||
public float y;
|
||||
public Place(string name, float x, float y)
|
||||
{
|
||||
this.name = name;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
public static double GetDistance(float x0, float y0, float x1, float y1)
|
||||
{
|
||||
float dx = x0 - x1;
|
||||
float dy = y0 - y1;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
public static double GetDistance(Place A, Place B)
|
||||
{
|
||||
return GetDistance(A.x, A.y, B.x, B.y);
|
||||
}
|
||||
|
||||
public static double GetTravalCost(Place A, Place B)
|
||||
{
|
||||
return Math.Sqrt(GetDistance(A, B));
|
||||
}
|
||||
|
||||
public static List<Place> ReadPlacesFromTSV(string filename)
|
||||
{
|
||||
List<Place> cities = new List<Place>();
|
||||
foreach (var line in File.ReadLines(filename))
|
||||
{
|
||||
string[] parts = line.Split("\t");
|
||||
|
||||
string name = parts[0];
|
||||
float x = float.Parse(parts[2]);
|
||||
float y = float.Parse(parts[3]);
|
||||
cities.Add(new Place(name, x, y));
|
||||
}
|
||||
return cities;
|
||||
}
|
||||
|
||||
// Warning 'PickUnqiueNode' could get stuck in an infinite loop, if there are not enough places
|
||||
public static int PickUnqiueNode(Random rand, List<int> path, int placesCount)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int num = rand.Next(0, placesCount);
|
||||
if (!path.Contains(num))
|
||||
{
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<List<int>> ComputeLocalNodes(List<Place> places, float localRadius)
|
||||
{
|
||||
var locals = new List<List<int>>(places.Count);
|
||||
for (int i = 0; i < places.Count; i++)
|
||||
{
|
||||
var nodeLocals = new List<int>();
|
||||
for (int j = 0; j < places.Count; j++)
|
||||
{
|
||||
if (i == j) continue;
|
||||
|
||||
if (GetDistance(places[i], places[j]) < localRadius)
|
||||
{
|
||||
nodeLocals.Add(j);
|
||||
}
|
||||
}
|
||||
locals.Add(nodeLocals);
|
||||
}
|
||||
return locals;
|
||||
}
|
||||
|
||||
public static int PickUniqueLocalNode(Random rand, List<int> path, List<int> localPlaces)
|
||||
{
|
||||
if (localPlaces.Count == 0) return -1;
|
||||
|
||||
int retryCount = 5;
|
||||
for (int i = 0; i < retryCount; i++)
|
||||
{
|
||||
int idx = rand.Next(0, localPlaces.Count);
|
||||
int place = localPlaces[idx];
|
||||
if (!path.Contains(place))
|
||||
{
|
||||
return place;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static List<int> GenerateRandomPath(Random rand, int size, int placesCount)
|
||||
{
|
||||
List<int> path = new List<int>(size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
path.Add(PickUnqiueNode(rand, path, placesCount));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static double GetPathCost(List<int> path, List<Place> places)
|
||||
{
|
||||
double cost = 0;
|
||||
for (int i = 0; i < path.Count - 1; i++)
|
||||
{
|
||||
int from = path[i];
|
||||
int to = path[i + 1];
|
||||
cost += GetTravalCost(places[from], places[to]);
|
||||
}
|
||||
cost += GetTravalCost(places[path.First()], places[path.Last()]);
|
||||
return cost;
|
||||
}
|
||||
|
||||
public static List<int> SplicePaths(Random rand, List<int> A, List<int> B)
|
||||
{
|
||||
Debug.Assert(A.Count == B.Count);
|
||||
|
||||
List<int> spliced = new List<int>(A.Count);
|
||||
for (int i = 0; i < A.Count - 1; i++)
|
||||
{
|
||||
spliced[i] = rand.Next(0, 2) == 0 ? A[i] : B[i];
|
||||
}
|
||||
return spliced;
|
||||
}
|
||||
|
||||
public static SortedList<double, List<int>> GenerateInitialPaths(Random rand, List<Place> places, int pathLength, int pathCount)
|
||||
{
|
||||
var paths = new SortedList<double, List<int>>();
|
||||
for (int i = 0; i < pathCount; i++)
|
||||
{
|
||||
var path = GenerateRandomPath(rand, pathLength, places.Count);
|
||||
var cost = GetPathCost(path, places);
|
||||
paths.Add(cost, path);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
public void SetLocalRadius(float radius)
|
||||
{
|
||||
localAreaRadius = radius;
|
||||
localPlaces = ComputeLocalNodes(places, localAreaRadius);
|
||||
}
|
||||
|
||||
public void MutatePath(List<int> path)
|
||||
{
|
||||
for (int i = 0; i < path.Count; i++)
|
||||
{
|
||||
if (rand.NextSingle() < mutationPickUniqueChance)
|
||||
{
|
||||
path[i] = PickUnqiueNode(rand, path, places.Count);
|
||||
}
|
||||
if (rand.NextSingle() < mutationSwapChance)
|
||||
{
|
||||
int idx1 = rand.Next(0, path.Count);
|
||||
int idx2 = rand.Next(0, path.Count);
|
||||
|
||||
(path[idx2], path[idx1]) = (path[idx1], path[idx2]);
|
||||
}
|
||||
if (rand.NextSingle() < mutationPickLocalChance)
|
||||
{
|
||||
int localNode = PickUniqueLocalNode(rand, path, localPlaces[i]);
|
||||
if (localNode != -1)
|
||||
{
|
||||
path[i] = localNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void IterateSolution()
|
||||
{
|
||||
double bestScore = paths.First().Key;
|
||||
double worstScore = paths.Last().Key;
|
||||
|
||||
var removedPaths = new List<double>();
|
||||
var parentPool = new List<List<int>>();
|
||||
foreach (var entry in paths)
|
||||
{
|
||||
double probability = (entry.Key - bestScore) / (worstScore - bestScore);
|
||||
if (rand.NextSingle() >= probability)
|
||||
{
|
||||
parentPool.Add(entry.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
removedPaths.Add(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in removedPaths)
|
||||
{
|
||||
paths.Remove(key);
|
||||
}
|
||||
|
||||
while (batchSize > paths.Count)
|
||||
{
|
||||
var parentIdx = rand.Next(0, parentPool.Count);
|
||||
var parent = parentPool[parentIdx];
|
||||
var child = new List<int>(parent);
|
||||
MutatePath(child);
|
||||
var cost = GetPathCost(child, places);
|
||||
if (!paths.ContainsKey(cost))
|
||||
{
|
||||
paths.Add(cost, child);
|
||||
}
|
||||
}
|
||||
|
||||
iteration++;
|
||||
}
|
||||
|
||||
public void IterateSolutionFor(float seconds)
|
||||
{
|
||||
var stopwatch = new Stopwatch();
|
||||
stopwatch.Start();
|
||||
while (stopwatch.ElapsedMilliseconds < seconds * 1000)
|
||||
{
|
||||
IterateSolution();
|
||||
}
|
||||
stopwatch.Stop();
|
||||
}
|
||||
|
||||
public Tuple<double, List<int>> GetBestPath()
|
||||
{
|
||||
var entry = paths.First();
|
||||
return Tuple.Create(entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
}
|
BIN
Lab4/Ataskaita.docx
Normal file
BIN
Lab4/Ataskaita.docx
Normal file
Binary file not shown.
94
Lab4/EditorWindow.cs
Normal file
94
Lab4/EditorWindow.cs
Normal file
@ -0,0 +1,94 @@
|
||||
using System.Numerics;
|
||||
using ImGuiNET;
|
||||
using Lab4;
|
||||
using Raylib_cs;
|
||||
|
||||
namespace Lab4
|
||||
{
|
||||
class EditorWindow
|
||||
{
|
||||
private bool showDemoWindow = false;
|
||||
public ImFontPtr font1;
|
||||
|
||||
MainWindow win;
|
||||
|
||||
public EditorWindow(MainWindow win)
|
||||
{
|
||||
this.win = win;
|
||||
}
|
||||
|
||||
public void Update(float dt)
|
||||
{
|
||||
ImGui.PushFont(font1);
|
||||
|
||||
if (showDemoWindow)
|
||||
{
|
||||
// Normally user code doesn't need/want to call this because positions are saved in .ini file anyway.
|
||||
// Here we just want to make the demo initial state a bit more friendly!
|
||||
ImGui.SetNextWindowPos(new Vector2(650, 20), ImGuiCond.FirstUseEver);
|
||||
ImGui.ShowDemoWindow(ref showDemoWindow);
|
||||
}
|
||||
|
||||
if (ImGui.Begin("Editor", ImGuiWindowFlags.None))
|
||||
{
|
||||
var algo = win.algo;
|
||||
|
||||
var mouse = win.GetMousePosition();
|
||||
if (win.isIterationRunning)
|
||||
{
|
||||
if (ImGui.Button(win.iterationPaused ? "Continue" : "Pause"))
|
||||
{
|
||||
win.iterationPaused = !win.iterationPaused;
|
||||
}
|
||||
if (ImGui.Button("Stop"))
|
||||
{
|
||||
win.Stop();
|
||||
}
|
||||
} else
|
||||
{
|
||||
if (ImGui.Button("Start"))
|
||||
{
|
||||
win.Start();
|
||||
}
|
||||
}
|
||||
ImGui.Text($"Mouse: {mouse.X:f3} {mouse.Y:f3}");
|
||||
ImGui.Text($"Iteration: {algo.iteration}");
|
||||
ImGui.Text($"Runtime: {win.interationTime:f3}s");
|
||||
|
||||
ImGuiInputTextFlags inputFlags = 0;
|
||||
if (win.isIterationRunning)
|
||||
{
|
||||
uint bg = ImGui.GetColorU32(ImGuiCol.FrameBg);
|
||||
ImGui.PushStyleColor(ImGuiCol.FrameBg, 0x44000000 | (bg & 0x00FFFFFF));
|
||||
ImGui.PushStyleColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled));
|
||||
inputFlags = ImGuiInputTextFlags.ReadOnly;
|
||||
}
|
||||
|
||||
ImGui.InputInt("Seed", ref algo.seed, 1, 1, ImGuiInputTextFlags.CharsDecimal | inputFlags);
|
||||
ImGui.InputInt("Batch size", ref algo.batchSize, 1, 1, ImGuiInputTextFlags.CharsDecimal | inputFlags);
|
||||
|
||||
if (win.isIterationRunning)
|
||||
{
|
||||
ImGui.PopStyleColor();
|
||||
ImGui.PopStyleColor();
|
||||
}
|
||||
|
||||
var localRadius = algo.localAreaRadius;
|
||||
if (ImGui.InputFloat("Local area radius", ref localRadius))
|
||||
{
|
||||
Console.WriteLine(localRadius);
|
||||
win.algo.SetLocalRadius(localRadius);
|
||||
}
|
||||
ImGui.SliderFloat("Mutation swap chance", ref algo.mutationSwapChance, 0, 0.3f);
|
||||
ImGui.SliderFloat("Mutation repick chance", ref algo.mutationPickUniqueChance, 0, 0.3f);
|
||||
ImGui.SliderFloat("Mutation repick local chance", ref algo.mutationPickLocalChance, 0, 0.3f);
|
||||
|
||||
ImGui.Text($"Best path score: {win.bestScore}");
|
||||
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
}
|
||||
}
|
323
Lab4/ImguiController.cs
Normal file
323
Lab4/ImguiController.cs
Normal file
@ -0,0 +1,323 @@
|
||||
using ImGuiNET;
|
||||
using Raylib_cs;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Lab4
|
||||
{
|
||||
public class ImguiController : IDisposable
|
||||
{
|
||||
IntPtr context;
|
||||
Texture2D fontTexture;
|
||||
Vector2 scaleFactor = Vector2.One;
|
||||
|
||||
public ImguiController()
|
||||
{
|
||||
context = ImGui.CreateContext();
|
||||
ImGui.SetCurrentContext(context);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
ImGui.DestroyContext(context);
|
||||
Raylib.UnloadTexture(fontTexture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a texture and loads the font data from ImGui.
|
||||
/// </summary>
|
||||
public void Load(int width, int height)
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
io.Fonts.AddFontDefault();
|
||||
|
||||
Resize(width, height);
|
||||
LoadFontTexture();
|
||||
SetupInput();
|
||||
ImGui.NewFrame();
|
||||
}
|
||||
|
||||
unsafe void LoadFontTexture()
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
// Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders.
|
||||
// If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
|
||||
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out int width, out int height);
|
||||
|
||||
// Upload texture to graphics system
|
||||
Image image = new Image
|
||||
{
|
||||
data = pixels,
|
||||
width = width,
|
||||
height = height,
|
||||
mipmaps = 1,
|
||||
format = PixelFormat.PIXELFORMAT_UNCOMPRESSED_R8G8B8A8,
|
||||
};
|
||||
fontTexture = Raylib.LoadTextureFromImage(image);
|
||||
|
||||
// Store texture id in imgui font
|
||||
io.Fonts.SetTexID(new IntPtr(fontTexture.id));
|
||||
|
||||
// Clears font data on the CPU side
|
||||
io.Fonts.ClearTexData();
|
||||
}
|
||||
|
||||
void SetupInput()
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
// Setup config flags
|
||||
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard;
|
||||
|
||||
// Setup back-end capabilities flags
|
||||
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
|
||||
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
|
||||
|
||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
|
||||
io.KeyMap[(int)ImGuiKey.Tab] = (int)KeyboardKey.KEY_TAB;
|
||||
io.KeyMap[(int)ImGuiKey.LeftArrow] = (int)KeyboardKey.KEY_LEFT;
|
||||
io.KeyMap[(int)ImGuiKey.RightArrow] = (int)KeyboardKey.KEY_RIGHT;
|
||||
io.KeyMap[(int)ImGuiKey.UpArrow] = (int)KeyboardKey.KEY_UP;
|
||||
io.KeyMap[(int)ImGuiKey.DownArrow] = (int)KeyboardKey.KEY_DOWN;
|
||||
io.KeyMap[(int)ImGuiKey.PageUp] = (int)KeyboardKey.KEY_PAGE_UP;
|
||||
io.KeyMap[(int)ImGuiKey.PageDown] = (int)KeyboardKey.KEY_PAGE_DOWN;
|
||||
io.KeyMap[(int)ImGuiKey.Home] = (int)KeyboardKey.KEY_HOME;
|
||||
io.KeyMap[(int)ImGuiKey.End] = (int)KeyboardKey.KEY_END;
|
||||
io.KeyMap[(int)ImGuiKey.Insert] = (int)KeyboardKey.KEY_INSERT;
|
||||
io.KeyMap[(int)ImGuiKey.Delete] = (int)KeyboardKey.KEY_DELETE;
|
||||
io.KeyMap[(int)ImGuiKey.Backspace] = (int)KeyboardKey.KEY_BACKSPACE;
|
||||
io.KeyMap[(int)ImGuiKey.Space] = (int)KeyboardKey.KEY_SPACE;
|
||||
io.KeyMap[(int)ImGuiKey.Enter] = (int)KeyboardKey.KEY_ENTER;
|
||||
io.KeyMap[(int)ImGuiKey.Escape] = (int)KeyboardKey.KEY_ESCAPE;
|
||||
io.KeyMap[(int)ImGuiKey.A] = (int)KeyboardKey.KEY_A;
|
||||
io.KeyMap[(int)ImGuiKey.C] = (int)KeyboardKey.KEY_C;
|
||||
io.KeyMap[(int)ImGuiKey.V] = (int)KeyboardKey.KEY_V;
|
||||
io.KeyMap[(int)ImGuiKey.X] = (int)KeyboardKey.KEY_X;
|
||||
io.KeyMap[(int)ImGuiKey.Y] = (int)KeyboardKey.KEY_Y;
|
||||
io.KeyMap[(int)ImGuiKey.Z] = (int)KeyboardKey.KEY_Z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update imgui internals(input, frameData)
|
||||
/// </summary>
|
||||
/// <param name="dt"></param>
|
||||
public void Update(float dt)
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
io.DisplayFramebufferScale = Vector2.One;
|
||||
io.DeltaTime = dt;
|
||||
|
||||
UpdateKeyboard();
|
||||
UpdateMouse();
|
||||
|
||||
if (Raylib.IsWindowResized())
|
||||
{
|
||||
Resize(Raylib.GetScreenWidth(), Raylib.GetScreenHeight());
|
||||
}
|
||||
|
||||
ImGui.NewFrame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resize imgui display
|
||||
/// </summary>
|
||||
public void Resize(int width, int height)
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
io.DisplaySize = new Vector2(width, height) / scaleFactor;
|
||||
}
|
||||
|
||||
void UpdateKeyboard()
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
// Modifiers are not reliable across systems
|
||||
io.KeyCtrl = io.KeysDown[(int)KeyboardKey.KEY_LEFT_CONTROL] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_CONTROL];
|
||||
io.KeyShift = io.KeysDown[(int)KeyboardKey.KEY_LEFT_SHIFT] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_SHIFT];
|
||||
io.KeyAlt = io.KeysDown[(int)KeyboardKey.KEY_LEFT_ALT] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_ALT];
|
||||
io.KeySuper = io.KeysDown[(int)KeyboardKey.KEY_LEFT_SUPER] || io.KeysDown[(int)KeyboardKey.KEY_RIGHT_SUPER];
|
||||
|
||||
// Key states
|
||||
for (int i = (int)KeyboardKey.KEY_SPACE; i < (int)KeyboardKey.KEY_KB_MENU + 1; i++)
|
||||
{
|
||||
io.KeysDown[i] = Raylib.IsKeyDown((KeyboardKey)i);
|
||||
}
|
||||
|
||||
// Key input
|
||||
int keyPressed = Raylib.GetCharPressed();
|
||||
if (keyPressed != 0)
|
||||
{
|
||||
io.AddInputCharacter((uint)keyPressed);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMouse()
|
||||
{
|
||||
ImGuiIOPtr io = ImGui.GetIO();
|
||||
|
||||
// Store button states
|
||||
for (int i = 0; i < io.MouseDown.Count; i++)
|
||||
{
|
||||
io.MouseDown[i] = Raylib.IsMouseButtonDown((MouseButton)i);
|
||||
}
|
||||
|
||||
// Mouse scroll
|
||||
io.MouseWheel += Raylib.GetMouseWheelMove();
|
||||
|
||||
// Mouse position
|
||||
Vector2 mousePosition = io.MousePos;
|
||||
bool focused = Raylib.IsWindowFocused();
|
||||
|
||||
if (focused)
|
||||
{
|
||||
if (io.WantSetMousePos)
|
||||
{
|
||||
Raylib.SetMousePosition((int)mousePosition.X, (int)mousePosition.Y);
|
||||
}
|
||||
else
|
||||
{
|
||||
io.MousePos = Raylib.GetMousePosition();
|
||||
}
|
||||
}
|
||||
|
||||
// Mouse cursor state
|
||||
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) == 0 || Raylib.IsCursorHidden())
|
||||
{
|
||||
ImGuiMouseCursor cursor = ImGui.GetMouseCursor();
|
||||
if (cursor == ImGuiMouseCursor.None || io.MouseDrawCursor)
|
||||
{
|
||||
Raylib.HideCursor();
|
||||
}
|
||||
else
|
||||
{
|
||||
Raylib.ShowCursor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the geometry as set up by ImGui and sends it to the graphics device
|
||||
/// </summary>
|
||||
public void Draw()
|
||||
{
|
||||
ImGui.Render();
|
||||
RenderCommandLists(ImGui.GetDrawData());
|
||||
}
|
||||
|
||||
// Returns a Color struct from hexadecimal value
|
||||
Color GetColor(uint hexValue)
|
||||
{
|
||||
Color color;
|
||||
|
||||
color.r = (byte)(hexValue & 0xFF);
|
||||
color.g = (byte)((hexValue >> 8) & 0xFF);
|
||||
color.b = (byte)((hexValue >> 16) & 0xFF);
|
||||
color.a = (byte)((hexValue >> 24) & 0xFF);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void DrawTriangleVertex(ImDrawVertPtr idxVert)
|
||||
{
|
||||
Color c = GetColor(idxVert.col);
|
||||
Rlgl.rlColor4ub(c.r, c.g, c.b, c.a);
|
||||
Rlgl.rlTexCoord2f(idxVert.uv.X, idxVert.uv.Y);
|
||||
Rlgl.rlVertex2f(idxVert.pos.X, idxVert.pos.Y);
|
||||
}
|
||||
|
||||
// Draw the imgui triangle data
|
||||
void DrawTriangles(uint count, int idxOffset, int vtxOffset, ImVector<ushort> idxBuffer, ImPtrVector<ImDrawVertPtr> idxVert, IntPtr textureId)
|
||||
{
|
||||
ushort index = 0;
|
||||
ImDrawVertPtr vertex;
|
||||
|
||||
if (Rlgl.rlCheckRenderBatchLimit((int)count * 3))
|
||||
{
|
||||
Rlgl.rlDrawRenderBatchActive();
|
||||
}
|
||||
|
||||
Rlgl.rlBegin(DrawMode.TRIANGLES);
|
||||
Rlgl.rlSetTexture((uint)textureId);
|
||||
|
||||
for (int i = 0; i <= (count - 3); i += 3)
|
||||
{
|
||||
index = idxBuffer[idxOffset + i];
|
||||
vertex = idxVert[vtxOffset + index];
|
||||
DrawTriangleVertex(vertex);
|
||||
|
||||
index = idxBuffer[idxOffset + i + 1];
|
||||
vertex = idxVert[vtxOffset + index];
|
||||
DrawTriangleVertex(vertex);
|
||||
|
||||
index = idxBuffer[idxOffset + i + 2];
|
||||
vertex = idxVert[vtxOffset + index];
|
||||
DrawTriangleVertex(vertex);
|
||||
}
|
||||
Rlgl.rlEnd();
|
||||
}
|
||||
|
||||
void RenderCommandLists(ImDrawDataPtr data)
|
||||
{
|
||||
// Scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
|
||||
int fbWidth = (int)(data.DisplaySize.X * data.FramebufferScale.X);
|
||||
int fbHeight = (int)(data.DisplaySize.Y * data.FramebufferScale.Y);
|
||||
|
||||
// Avoid rendering if display is minimized or if the command list is empty
|
||||
if (fbWidth <= 0 || fbHeight <= 0 || data.CmdListsCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Rlgl.rlDrawRenderBatchActive();
|
||||
Rlgl.rlDisableBackfaceCulling();
|
||||
Rlgl.rlEnableScissorTest();
|
||||
|
||||
data.ScaleClipRects(ImGui.GetIO().DisplayFramebufferScale);
|
||||
|
||||
for (int n = 0; n < data.CmdListsCount; n++)
|
||||
{
|
||||
int idxOffset = 0;
|
||||
ImDrawListPtr cmdList = data.CmdListsRange[n];
|
||||
|
||||
// Vertex buffer and index buffer generated by DearImGui
|
||||
ImPtrVector<ImDrawVertPtr> vtxBuffer = cmdList.VtxBuffer;
|
||||
ImVector<ushort> idxBuffer = cmdList.IdxBuffer;
|
||||
|
||||
for (int cmdi = 0; cmdi < cmdList.CmdBuffer.Size; cmdi++)
|
||||
{
|
||||
ImDrawCmdPtr pcmd = cmdList.CmdBuffer[cmdi];
|
||||
|
||||
// Scissor rect
|
||||
Vector2 pos = data.DisplayPos;
|
||||
int rectX = (int)((pcmd.ClipRect.X - pos.X) * data.FramebufferScale.X);
|
||||
int rectY = (int)((pcmd.ClipRect.Y - pos.Y) * data.FramebufferScale.Y);
|
||||
int rectW = (int)((pcmd.ClipRect.Z - rectX) * data.FramebufferScale.Y);
|
||||
int rectH = (int)((pcmd.ClipRect.W - rectY) * data.FramebufferScale.Y);
|
||||
Rlgl.rlScissor(rectX, Raylib.GetScreenHeight() - (rectY + rectH), rectW, rectH);
|
||||
|
||||
if (pcmd.UserCallback != IntPtr.Zero)
|
||||
{
|
||||
// pcmd.UserCallback(cmdList, pcmd);
|
||||
idxOffset += (int)pcmd.ElemCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawTriangles(pcmd.ElemCount, idxOffset, (int)pcmd.VtxOffset, idxBuffer, vtxBuffer, pcmd.TextureId);
|
||||
idxOffset += (int)pcmd.ElemCount;
|
||||
Rlgl.rlDrawRenderBatchActive();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rlgl.rlSetTexture(0);
|
||||
Rlgl.rlDisableScissorTest();
|
||||
Rlgl.rlEnableBackfaceCulling();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
@ -21,6 +22,8 @@
|
||||
<None Update="places.tsv">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<PackageReference Include="Raylib-cs" Version="4.5.0" />
|
||||
<PackageReference Include="ImGui.NET" Version="1.78.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
198
Lab4/MainWindow.cs
Normal file
198
Lab4/MainWindow.cs
Normal file
@ -0,0 +1,198 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Raylib_cs;
|
||||
using static Lab4.Algorithm;
|
||||
|
||||
namespace Lab4
|
||||
{
|
||||
public class MainWindow
|
||||
{
|
||||
public Algorithm algo;
|
||||
List<Place> places;
|
||||
public int screenWidth;
|
||||
public int screenHeight;
|
||||
|
||||
public float interationTime = 0;
|
||||
Task? iterationTask;
|
||||
|
||||
public List<int>? bestPath;
|
||||
public double bestScore = 0;
|
||||
public bool isIterationRunning = false;
|
||||
|
||||
public bool iterationPaused = false;
|
||||
|
||||
RaylibDrawContext ctx;
|
||||
|
||||
ScoreHistWindow scoreHist;
|
||||
|
||||
public MainWindow(int screenWidth, int screenHeight, List<Place> places)
|
||||
{
|
||||
this.places = places;
|
||||
algo = new Algorithm(places);
|
||||
scoreHist = new ScoreHistWindow(this);
|
||||
|
||||
this.screenWidth = screenWidth;
|
||||
this.screenHeight = screenHeight;
|
||||
|
||||
{
|
||||
ctx = new RaylibDrawContext();
|
||||
|
||||
const int screenMargin = 50;
|
||||
|
||||
var (minX, maxX, minY, maxY) = GetCoordinateBounds(places);
|
||||
var rangeX = maxX - minX;
|
||||
var rangeY = maxY - minY;
|
||||
float scale = Math.Min((screenWidth - screenMargin * 2) / rangeX, (screenHeight - screenMargin * 2) / rangeY);
|
||||
ctx.Scale(scale, scale);
|
||||
ctx.Translate(-screenMargin, -screenMargin);
|
||||
ctx.Translate(minX * scale, minY * scale);
|
||||
}
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
algo.Reset();
|
||||
scoreHist.Reset();
|
||||
isIterationRunning = true;
|
||||
iterationPaused = false;
|
||||
interationTime = 0;
|
||||
iterationTask = new Task(() => {
|
||||
while (isIterationRunning)
|
||||
{
|
||||
if (!iterationPaused)
|
||||
{
|
||||
algo.IterateSolution();
|
||||
var best = algo.GetBestPath();
|
||||
bestScore = best.Item1;
|
||||
bestPath = best.Item2;
|
||||
}
|
||||
}
|
||||
});
|
||||
iterationTask.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
isIterationRunning = false;
|
||||
}
|
||||
|
||||
public static Tuple<float, float, float, float> GetCoordinateBounds(List<Place> places)
|
||||
{
|
||||
float minX = places[0].x;
|
||||
float maxX = places[0].x;
|
||||
float minY = places[0].y;
|
||||
float maxY = places[0].y;
|
||||
|
||||
for (int i = 1; i < places.Count; i++)
|
||||
{
|
||||
minX = Math.Min(minX, places[i].x);
|
||||
maxX = Math.Max(maxX, places[i].x);
|
||||
minY = Math.Min(minY, places[i].y);
|
||||
maxY = Math.Max(maxY, places[i].y);
|
||||
}
|
||||
|
||||
return Tuple.Create(minX, maxX, minY, maxY);
|
||||
}
|
||||
|
||||
public static void DrawPath(RaylibDrawContext ctx, List<int> path, List<Place> places)
|
||||
{
|
||||
Color color = Color.GREEN;
|
||||
for (int i = 0; i < path.Count - 1; i++)
|
||||
{
|
||||
var place0 = places[path[i + 0]];
|
||||
var place1 = places[path[i + 1]];
|
||||
ctx.DrawLine(place0.x, place0.y, place1.x, place1.y, color);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector2 GetMousePosition()
|
||||
{
|
||||
return ctx.TransformToLocalSpace(Raylib.GetMousePosition());
|
||||
}
|
||||
|
||||
public Place? GetNearestPlaceToMouse()
|
||||
{
|
||||
if (places.Count == 0) return null;
|
||||
|
||||
var mouse = GetMousePosition();
|
||||
var mearest = places.First();
|
||||
double nearestDistance = GetDistance(mearest.x, mearest.y, mouse.X, mouse.Y);
|
||||
foreach (var place in places)
|
||||
{
|
||||
var distance = GetDistance(place.x, place.y, mouse.X, mouse.Y);
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
nearestDistance = distance;
|
||||
mearest = place;
|
||||
}
|
||||
}
|
||||
return mearest;
|
||||
}
|
||||
|
||||
public unsafe void Run()
|
||||
{
|
||||
Raylib.SetTraceLogCallback(&Logging.LogConsole);
|
||||
Raylib.SetConfigFlags(ConfigFlags.FLAG_VSYNC_HINT | ConfigFlags.FLAG_WINDOW_RESIZABLE);
|
||||
Raylib.InitWindow(this.screenWidth, this.screenHeight, "IP Užduotis");
|
||||
Raylib.SetTargetFPS(60);
|
||||
|
||||
Raylib.InitAudioDevice();
|
||||
|
||||
ImguiController controller = new ImguiController();
|
||||
EditorWindow editor = new EditorWindow(this);
|
||||
|
||||
controller.Load(screenWidth, screenHeight);
|
||||
while (!Raylib.WindowShouldClose())
|
||||
{
|
||||
// Update
|
||||
float dt = Raylib.GetFrameTime();
|
||||
controller.Update(dt);
|
||||
editor.Update(dt);
|
||||
scoreHist.Update(dt);
|
||||
if (isIterationRunning && !iterationPaused)
|
||||
{
|
||||
interationTime += dt;
|
||||
}
|
||||
|
||||
|
||||
// Draw
|
||||
Raylib.BeginDrawing();
|
||||
Raylib.ClearBackground(Color.RAYWHITE);
|
||||
|
||||
foreach (var place in places)
|
||||
{
|
||||
ctx.DrawCircle(place.x, place.y, 2000, Color.RED);
|
||||
}
|
||||
|
||||
if (bestPath != null)
|
||||
{
|
||||
DrawPath(ctx, bestPath, places);
|
||||
}
|
||||
|
||||
/*
|
||||
var nearest = GetNearestPlaceToMouse();
|
||||
if (nearest != null)
|
||||
{
|
||||
var mouse = GetMousePosition();
|
||||
ctx.DrawLine(nearest.x, nearest.y, mouse.X, mouse.Y, Color.BLUE);
|
||||
ctx.DrawCircleLines(nearest.x, nearest.y, algo.localAreaRadius, Color.BLUE);
|
||||
}
|
||||
*/
|
||||
|
||||
controller.Draw();
|
||||
|
||||
Raylib.EndDrawing();
|
||||
}
|
||||
|
||||
// De-Initialization
|
||||
controller.Dispose();
|
||||
Raylib.CloseAudioDevice();
|
||||
Raylib.CloseWindow();
|
||||
Stop();
|
||||
}
|
||||
}
|
||||
}
|
194
Lab4/Program.cs
194
Lab4/Program.cs
@ -1,197 +1,27 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using Microsoft.VisualBasic;
|
||||
using Raylib_cs;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Lab4
|
||||
{
|
||||
class Place
|
||||
{
|
||||
public string name;
|
||||
public float x;
|
||||
public float y;
|
||||
public Place(string name, float x, float y)
|
||||
{
|
||||
this.name = name;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
internal class Program
|
||||
{
|
||||
public static double GetDistance(Place A, Place B)
|
||||
static unsafe void Main(string[] args)
|
||||
{
|
||||
float dx = A.x - B.x;
|
||||
float dy = A.y - B.y;
|
||||
return Math.Sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
var places = Algorithm.ReadPlacesFromTSV("places.tsv");
|
||||
|
||||
public static double GetTravalCost(Place A, Place B)
|
||||
{
|
||||
return Math.Sqrt(GetDistance(A, B));
|
||||
}
|
||||
var win = new MainWindow(1280, 720, places);
|
||||
win.Run();
|
||||
|
||||
public static List<Place> ReadPlacesFromTSV(string filename)
|
||||
{
|
||||
List<Place> cities = new List<Place>();
|
||||
foreach (var line in File.ReadLines(filename))
|
||||
{
|
||||
string[] parts = line.Split("\t");
|
||||
|
||||
string name = parts[0];
|
||||
float x = float.Parse(parts[2]);
|
||||
float y = float.Parse(parts[3]);
|
||||
cities.Add(new Place(name, x, y));
|
||||
}
|
||||
return cities;
|
||||
}
|
||||
|
||||
// Warning 'PickUnqiueNode' could get stuck in an infinite loop, if there are not enough places
|
||||
public static int PickUnqiueNode(Random rand, List<int> path, int placesCount)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int num = rand.Next(0, placesCount);
|
||||
if (!path.Contains(num))
|
||||
{
|
||||
return num;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static List<int> GenerateRandomPath(Random rand, int size, int placesCount)
|
||||
{
|
||||
List<int> path = new List<int>(size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
path.Add(PickUnqiueNode(rand, path, placesCount));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
public static double GetPathCost(List<int> path, List<Place> places)
|
||||
{
|
||||
double cost = 0;
|
||||
for (int i = 0; i < path.Count-1; i++)
|
||||
{
|
||||
int from = path[i];
|
||||
int to = path[i+1];
|
||||
cost += GetTravalCost(places[from], places[to]);
|
||||
}
|
||||
cost += GetTravalCost(places[path.First()], places[path.Last()]);
|
||||
return cost;
|
||||
}
|
||||
|
||||
public static List<int> SplicePaths(Random rand, List<int> A, List<int> B)
|
||||
{
|
||||
Debug.Assert(A.Count == B.Count);
|
||||
|
||||
List<int> spliced = new List<int>(A.Count);
|
||||
for (int i = 0; i < A.Count-1; i++)
|
||||
{
|
||||
spliced[i] = rand.Next(0, 2) == 0 ? A[i] : B[i];
|
||||
}
|
||||
return spliced;
|
||||
}
|
||||
|
||||
public static void MutatePath(Random rand, List<int> path, float mutationPickUniqueChance, float mutationSwapChance, int placesCount)
|
||||
{
|
||||
for (int i = 0; i < path.Count; i++)
|
||||
{
|
||||
if (rand.NextSingle() < mutationPickUniqueChance) {
|
||||
path[i] = PickUnqiueNode(rand, path, placesCount);
|
||||
}
|
||||
if (rand.NextSingle() < mutationSwapChance) {
|
||||
int idx1 = rand.Next(0, path.Count);
|
||||
int idx2 = rand.Next(0, path.Count);
|
||||
|
||||
(path[idx2], path[idx1]) = (path[idx1], path[idx2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
/*
|
||||
Faile places_data.xlsx pateikta informacija apie lankytinas vietas (1 lentelė). Tikslas: kaip galima pigesnio maršruto sudarymas kai,
|
||||
|
||||
* priimama, kad kelionės tarp vietų kaina lygi kvadratinei šakniai iš kelionės atstumo;
|
||||
* kelionės pradžios ir pabaigos vieta sutampa (su grįžimu atgal);
|
||||
* ta pati vieta negali būti aplankyta daugiau nei vieną;
|
||||
* Reikia aplankyti bet kurias 150 vietų iš pateikto sąrašo.
|
||||
*/
|
||||
|
||||
int pathLength = 150;
|
||||
int batchSize = 50;
|
||||
int timeLimitMs = 60 * 1000;
|
||||
float mutationPickUniqueChance = 0.05f;
|
||||
float mutationSwapChance = 0.05f;
|
||||
|
||||
int seed = (int)DateTime.Now.Ticks;
|
||||
Random rand = new Random(seed);
|
||||
|
||||
var places = ReadPlacesFromTSV("places.tsv");
|
||||
|
||||
var bestPaths = new SortedList<double, List<int>>();
|
||||
for (int i = 0; i < batchSize; i++)
|
||||
{
|
||||
var path = GenerateRandomPath(rand, pathLength, places.Count);
|
||||
var cost = GetPathCost(path, places);
|
||||
bestPaths.Add(cost, path);
|
||||
}
|
||||
|
||||
var stopwatch = new Stopwatch();
|
||||
int iteration = 0;
|
||||
stopwatch.Start();
|
||||
while (stopwatch.ElapsedMilliseconds < timeLimitMs)
|
||||
{
|
||||
double bestScore = bestPaths.First().Key;
|
||||
double worstScore = bestPaths.Last().Key;
|
||||
|
||||
if (iteration % 200 == 0)
|
||||
{
|
||||
//Console.WriteLine("At {0}, best '{1}', worst '{2}'", iteration, bestScore, worstScore);
|
||||
}
|
||||
|
||||
var removedPaths = new List<double>();
|
||||
var parentPool = new List<List<int>>();
|
||||
foreach (var entry in bestPaths)
|
||||
{
|
||||
double probability = (entry.Key - bestScore) / (worstScore - bestScore);
|
||||
if (rand.NextSingle() >= probability) {
|
||||
parentPool.Add(entry.Value);
|
||||
} else {
|
||||
removedPaths.Add(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in removedPaths)
|
||||
{
|
||||
bestPaths.Remove(key);
|
||||
}
|
||||
|
||||
while (batchSize > bestPaths.Count)
|
||||
{
|
||||
var parentIdx = rand.Next(0, parentPool.Count);
|
||||
var parent = parentPool[parentIdx];
|
||||
var child = new List<int>(parent);
|
||||
MutatePath(rand, child, mutationPickUniqueChance, mutationSwapChance, places.Count);
|
||||
var cost = GetPathCost(child, places);
|
||||
if (!bestPaths.ContainsKey(cost)) {
|
||||
bestPaths.Add(cost, child);
|
||||
}
|
||||
}
|
||||
|
||||
iteration++;
|
||||
}
|
||||
stopwatch.Stop();
|
||||
|
||||
Console.WriteLine("Best after {0} iterations is {1}", iteration, bestPaths.First().Key);
|
||||
Console.WriteLine("Time taken: {0}ms", stopwatch.ElapsedMilliseconds);
|
||||
Console.WriteLine("Seed: {0}", seed);
|
||||
//var algo = new Algorithm(places);
|
||||
//algo.IterateSolutionFor(1);
|
||||
//Console.WriteLine("Best after {0} iterations is {1}", algo.iteration, algo.GetBestPath().Item1);
|
||||
//Console.WriteLine("Seed: {0}", algo.seed);
|
||||
|
||||
// Best after 484012 iterations is 30826.444176719036
|
||||
// Time taken: 60000ms
|
||||
// Seed: -1477536140
|
||||
}
|
||||
}
|
||||
|
||||
}
|
98
Lab4/RaylibDrawContext.cs
Normal file
98
Lab4/RaylibDrawContext.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using Raylib_cs;
|
||||
using System.Numerics;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml;
|
||||
|
||||
namespace Lab4
|
||||
{
|
||||
public class RaylibDrawContext
|
||||
{
|
||||
class DrawFrame
|
||||
{
|
||||
public float ox = 0;
|
||||
public float oy = 0;
|
||||
public float sx = 1;
|
||||
public float sy = 1;
|
||||
public DrawFrame(float ox, float oy, float sx, float sy)
|
||||
{
|
||||
this.ox = ox;
|
||||
this.oy = oy;
|
||||
this.sx = sx;
|
||||
this.sy = sy;
|
||||
}
|
||||
}
|
||||
|
||||
public float ox = 0;
|
||||
public float oy = 0;
|
||||
public float sx = 1;
|
||||
public float sy = 1;
|
||||
|
||||
Stack<DrawFrame> stack;
|
||||
|
||||
public RaylibDrawContext()
|
||||
{
|
||||
stack = new Stack<DrawFrame>();
|
||||
}
|
||||
|
||||
public void Translate(float offsetX, float offsetY)
|
||||
{
|
||||
ox += offsetX / sx;
|
||||
oy += offsetY / sy;
|
||||
}
|
||||
|
||||
public void Scale(float scaleX, float scaleY)
|
||||
{
|
||||
sx *= scaleX;
|
||||
sy *= scaleY;
|
||||
}
|
||||
|
||||
public void Push()
|
||||
{
|
||||
stack.Push(new DrawFrame(ox, oy, sx, sy));
|
||||
}
|
||||
|
||||
public void Pop()
|
||||
{
|
||||
var frame = stack.Pop();
|
||||
ox = frame.ox;
|
||||
oy = frame.oy;
|
||||
sx = frame.sx;
|
||||
sy = frame.sy;
|
||||
}
|
||||
|
||||
public Vector2 TransformToLocalSpace(Vector2 pos)
|
||||
{
|
||||
return new Vector2(
|
||||
(pos.X / sx) + ox,
|
||||
(pos.Y / sy) + oy
|
||||
);
|
||||
}
|
||||
|
||||
public void DrawCircle(float centerX, float centerY, float radius, Color color)
|
||||
{
|
||||
int x = (int)((centerX - ox) * sx);
|
||||
int y = (int)((centerY - oy) * sy);
|
||||
float rh = radius * sx;
|
||||
float rv = radius * sy;
|
||||
Raylib.DrawEllipse(x, y, rh, rv, color);
|
||||
}
|
||||
|
||||
public void DrawCircleLines(float centerX, float centerY, float radius, Color color)
|
||||
{
|
||||
int x = (int)((centerX - ox) * sx);
|
||||
int y = (int)((centerY - oy) * sy);
|
||||
float rh = radius * sx;
|
||||
float rv = radius * sy;
|
||||
Raylib.DrawEllipseLines(x, y, rh, rv, color);
|
||||
}
|
||||
|
||||
public void DrawLine(float startPosX, float startPosY, float endPosX, float endPosY, Color color)
|
||||
{
|
||||
int x0 = (int)((startPosX - ox) * sx);
|
||||
int y0 = (int)((startPosY - oy) * sy);
|
||||
int x1 = (int)((endPosX - ox) * sx);
|
||||
int y1 = (int)((endPosY - oy) * sy);
|
||||
Raylib.DrawLine(x0, y0, x1, y1, color);
|
||||
}
|
||||
}
|
||||
}
|
59
Lab4/ScoreHistWindow.cs
Normal file
59
Lab4/ScoreHistWindow.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using ImGuiNET;
|
||||
|
||||
namespace Lab4
|
||||
{
|
||||
internal class ScoreHistWindow
|
||||
{
|
||||
public ImFontPtr font1;
|
||||
MainWindow win;
|
||||
|
||||
float[] dataPoints;
|
||||
|
||||
float timer = 0;
|
||||
float lastSampleTime = -1;
|
||||
float sampleInterval = 0.1f;
|
||||
|
||||
public ScoreHistWindow(MainWindow win)
|
||||
{
|
||||
this.win = win;
|
||||
dataPoints = new float[100];
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
dataPoints = new float[100];
|
||||
timer = 0;
|
||||
lastSampleTime = -1;
|
||||
}
|
||||
|
||||
public unsafe void Update(float dt)
|
||||
{
|
||||
if (win.isIterationRunning && !win.iterationPaused)
|
||||
{
|
||||
timer += dt;
|
||||
if (timer - lastSampleTime > sampleInterval)
|
||||
{
|
||||
for (int i = 0; i < dataPoints.Length-1; i++)
|
||||
{
|
||||
dataPoints[i] = dataPoints[i + 1];
|
||||
}
|
||||
|
||||
dataPoints[dataPoints.Length - 1] = (float)win.bestScore;
|
||||
|
||||
lastSampleTime = timer;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui.PushFont(font1);
|
||||
|
||||
if (ImGui.Begin("Score histogram", ImGuiWindowFlags.None))
|
||||
{
|
||||
ImGui.PlotLines("", ref dataPoints[0], dataPoints.Length, 0, null, 10_000f, 50_000f, new System.Numerics.Vector2(0, 180.0f));
|
||||
ImGui.Spacing();
|
||||
ImGui.End();
|
||||
}
|
||||
|
||||
ImGui.PopFont();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user