276 lines
10 KiB
C#
276 lines
10 KiB
C#
using OpenTK.Wpf;
|
|
using OpenTK.Graphics.OpenGL;
|
|
using System.Windows.Input;
|
|
using UserControl = System.Windows.Controls.UserControl;
|
|
|
|
namespace OperationControl.Controls
|
|
{
|
|
/// <summary>
|
|
/// Interaction logic for Bbox3DViewer.xaml
|
|
/// </summary>
|
|
public partial class Bbox3DViewer : UserControl
|
|
{
|
|
// ======= estado de câmera/viewport =======
|
|
float zoom = 1.0f;
|
|
float cameraDist = 12.0f;
|
|
float rotX = 25f, rotY = 20f;
|
|
float panX = 0f, panY = 0f;
|
|
System.Windows.Point lastMouse;
|
|
bool dragLeft, dragMid;
|
|
|
|
// ======= parâmetros de cena =======
|
|
readonly List<AgroBase.Models.LivoxBboxModel> boxes = new();
|
|
public float GridHalf { get; set; } = 12f;
|
|
public float RobotHalfW { get; set; } = 0.30f;
|
|
public float RobotL { get; set; } = 0.60f;
|
|
|
|
public Bbox3DViewer()
|
|
{
|
|
InitializeComponent();
|
|
|
|
// inicia contexto GL compatível com fixed pipeline (2.1)
|
|
var settings = new GLWpfControlSettings
|
|
{
|
|
MajorVersion = 2,
|
|
MinorVersion = 1,
|
|
RenderContinuously = false, // redesenha sob demanda
|
|
// para 60 fps contínuo, use true
|
|
};
|
|
GLHost.Start(settings);
|
|
|
|
// eventos do ciclo de vida
|
|
GLHost.Ready += OnGLReady; // 1x
|
|
GLHost.Render += OnGLRender; // quando precisar
|
|
GLHost.SizeChanged += (_, __) => Invalidate(); // ajusta viewport
|
|
|
|
// interações mouse
|
|
GLHost.MouseWheel += OnWheel;
|
|
GLHost.MouseDown += OnMouseDown;
|
|
GLHost.MouseUp += OnMouseUp;
|
|
GLHost.MouseMove += OnMouseMove;
|
|
}
|
|
|
|
// ======= API pública =======
|
|
public void SetBboxes(IEnumerable<AgroBase.Models.LivoxBboxModel> list)
|
|
{
|
|
boxes.Clear();
|
|
if (list != null) boxes.AddRange(list);
|
|
Invalidate();
|
|
}
|
|
|
|
public void Invalidate() => GLHost.InvalidateVisual(); // dispara Render
|
|
|
|
// ======= GL =======
|
|
void OnGLReady()
|
|
{
|
|
GL.ClearColor(System.Drawing.Color.FromArgb(10, 12, 18));
|
|
GL.Enable(EnableCap.DepthTest);
|
|
UpdateProjection();
|
|
}
|
|
|
|
void UpdateProjection()
|
|
{
|
|
if (GLHost.ActualHeight <= 0) return;
|
|
|
|
GL.Viewport(0, 0, (int)GLHost.ActualWidth, (int)GLHost.ActualHeight);
|
|
GL.MatrixMode(MatrixMode.Projection);
|
|
GL.LoadIdentity();
|
|
|
|
float aspect = (float)GLHost.ActualWidth / Math.Max(1f, (float)GLHost.ActualHeight);
|
|
// perspectiva igual ao WinForms
|
|
GL.Frustum(-aspect * zoom, aspect * zoom, -1.0 * zoom, 1.0 * zoom, 0.5, 200.0);
|
|
}
|
|
|
|
void OnGLRender(TimeSpan _)
|
|
{
|
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
|
|
|
// view
|
|
GL.MatrixMode(MatrixMode.Modelview);
|
|
GL.LoadIdentity();
|
|
GL.Translate(panX, panY, -cameraDist);
|
|
GL.Rotate(rotX, 1, 0, 0);
|
|
GL.Rotate(rotY, 0, 1, 0);
|
|
|
|
// chão + eixos
|
|
DrawGroundGrid(GridHalf, 1f);
|
|
DrawAxes(2.0f);
|
|
|
|
// robô na origem (nariz para +X)
|
|
DrawRobotGlyph();
|
|
|
|
// bboxes
|
|
foreach (var b in boxes)
|
|
DrawBox((float)b.cx, (float)b.cy, (float)b.cz, (float)b.d, (float)b.w, (float)b.h);
|
|
}
|
|
|
|
// ======= desenho =======
|
|
void DrawGroundGrid(float half, float step)
|
|
{
|
|
GL.Disable(EnableCap.Lighting);
|
|
GL.LineWidth(1f);
|
|
GL.Begin(PrimitiveType.Lines);
|
|
for (float x = -half; x <= half; x += step)
|
|
{
|
|
bool major = Math.Abs(x % 5f) < 1e-3;
|
|
var c = major ? System.Drawing.Color.FromArgb(90, 220, 220, 220) :
|
|
System.Drawing.Color.FromArgb(40, 180, 180, 180);
|
|
GL.Color3(c);
|
|
GL.Vertex3(x, -0.001, -half);
|
|
GL.Vertex3(x, -0.001, +half);
|
|
}
|
|
for (float y = -half; y <= half; y += step)
|
|
{
|
|
bool major = Math.Abs(y % 5f) < 1e-3;
|
|
var c = major ? System.Drawing.Color.FromArgb(90, 220, 220, 220) :
|
|
System.Drawing.Color.FromArgb(40, 180, 180, 180);
|
|
GL.Color3(c);
|
|
GL.Vertex3(-half, -0.001, y);
|
|
GL.Vertex3(+half, -0.001, y);
|
|
}
|
|
GL.End();
|
|
|
|
// eixos no chão
|
|
GL.LineWidth(2f);
|
|
GL.Begin(PrimitiveType.Lines);
|
|
GL.Color3(System.Drawing.Color.FromArgb(160, 80, 180, 255)); // X
|
|
GL.Vertex3(0, 0, 0); GL.Vertex3(3, 0, 0);
|
|
GL.Color3(System.Drawing.Color.FromArgb(160, 80, 255, 180)); // Y
|
|
GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, 3);
|
|
GL.End();
|
|
}
|
|
|
|
void DrawAxes(float len)
|
|
{
|
|
GL.LineWidth(2f);
|
|
GL.Begin(PrimitiveType.Lines);
|
|
GL.Color3(System.Drawing.Color.Red); GL.Vertex3(0, 0, 0); GL.Vertex3(len, 0, 0); // X
|
|
GL.Color3(System.Drawing.Color.Green); GL.Vertex3(0, 0, 0); GL.Vertex3(0, 0, len); // Y
|
|
GL.Color3(System.Drawing.Color.Blue); GL.Vertex3(0, 0, 0); GL.Vertex3(0, len, 0); // Z
|
|
GL.End();
|
|
}
|
|
|
|
void DrawRobotGlyph()
|
|
{
|
|
float hw = RobotHalfW, L = RobotL;
|
|
|
|
// corpo
|
|
GL.Color4(System.Drawing.Color.FromArgb(70, 0, 255, 255));
|
|
GL.Begin(PrimitiveType.Quads);
|
|
GL.Vertex3(-0.1, 0.0, -hw); GL.Vertex3(L, 0.0, -hw);
|
|
GL.Vertex3(L, 0.0, +hw); GL.Vertex3(-0.1, 0.0, +hw);
|
|
GL.End();
|
|
|
|
// borda
|
|
GL.Color3(System.Drawing.Color.Cyan);
|
|
GL.LineWidth(2f);
|
|
GL.Begin(PrimitiveType.LineLoop);
|
|
GL.Vertex3(-0.1, 0.0, -hw); GL.Vertex3(L, 0.0, -hw);
|
|
GL.Vertex3(L, 0.0, +hw); GL.Vertex3(-0.1, 0.0, +hw);
|
|
GL.End();
|
|
|
|
// seta frontal
|
|
GL.Begin(PrimitiveType.Triangles);
|
|
GL.Color3(System.Drawing.Color.Cyan);
|
|
GL.Vertex3(L + 0.10, 0.0, 0.0);
|
|
GL.Vertex3(L - 0.10, 0.0, -0.10);
|
|
GL.Vertex3(L - 0.10, 0.0, +0.10);
|
|
GL.End();
|
|
}
|
|
|
|
void DrawBox(float cx, float cy, float cz, float dx, float dy, float dz)
|
|
{
|
|
float x0 = cx - dx / 2f, x1 = cx + dx / 2f;
|
|
float y0 = cy - dy / 2f, y1 = cy + dy / 2f;
|
|
float z0 = cz - dz / 2f, z1 = cz + dz / 2f;
|
|
|
|
// faces translúcidas
|
|
GL.Enable(EnableCap.Blend);
|
|
GL.BlendFunc((BlendingFactor)BlendingFactorSrc.SrcAlpha, (BlendingFactor)BlendingFactorDest.OneMinusSrcAlpha);
|
|
GL.Color4(System.Drawing.Color.FromArgb(50, 255, 180, 0));
|
|
GL.Begin(PrimitiveType.Quads);
|
|
// +X
|
|
GL.Vertex3(x1, z0, y0); GL.Vertex3(x1, z0, y1); GL.Vertex3(x1, z1, y1); GL.Vertex3(x1, z1, y0);
|
|
// -X
|
|
GL.Vertex3(x0, z0, y1); GL.Vertex3(x0, z0, y0); GL.Vertex3(x0, z1, y0); GL.Vertex3(x0, z1, y1);
|
|
// +Y
|
|
GL.Vertex3(x0, z0, y1); GL.Vertex3(x1, z0, y1); GL.Vertex3(x1, z1, y1); GL.Vertex3(x0, z1, y1);
|
|
// -Y
|
|
GL.Vertex3(x1, z0, y0); GL.Vertex3(x0, z0, y0); GL.Vertex3(x0, z1, y0); GL.Vertex3(x1, z1, y0);
|
|
// +Z (topo)
|
|
GL.Vertex3(x0, z1, y0); GL.Vertex3(x1, z1, y0); GL.Vertex3(x1, z1, y1); GL.Vertex3(x0, z1, y1);
|
|
// -Z (base)
|
|
GL.Vertex3(x0, z0, y1); GL.Vertex3(x1, z0, y1); GL.Vertex3(x1, z0, y0); GL.Vertex3(x0, z0, y0);
|
|
GL.End();
|
|
GL.Disable(EnableCap.Blend);
|
|
|
|
// wireframe
|
|
GL.Color3(System.Drawing.Color.Orange);
|
|
GL.LineWidth(2f);
|
|
GL.Begin(PrimitiveType.Lines);
|
|
// base z0
|
|
Line(x0, z0, y0, x1, z0, y0); Line(x1, z0, y0, x1, z0, y1);
|
|
Line(x1, z0, y1, x0, z0, y1); Line(x0, z0, y1, x0, z0, y0);
|
|
// topo z1
|
|
Line(x0, z1, y0, x1, z1, y0); Line(x1, z1, y0, x1, z1, y1);
|
|
Line(x1, z1, y1, x0, z1, y1); Line(x0, z1, y1, x0, z1, y0);
|
|
// colunas
|
|
Line(x0, z0, y0, x0, z1, y0); Line(x1, z0, y0, x1, z1, y0);
|
|
Line(x1, z0, y1, x1, z1, y1); Line(x0, z0, y1, x0, z1, y1);
|
|
GL.End();
|
|
}
|
|
void Line(float x0, float z0, float y0, float x1, float z1, float y1)
|
|
{ GL.Vertex3(x0, z0, y0); GL.Vertex3(x1, z1, y1); }
|
|
|
|
// ======= Interações =======
|
|
void OnWheel(object s, MouseWheelEventArgs e)
|
|
{
|
|
zoom *= (e.Delta > 0) ? 0.9f : 1.1f;
|
|
zoom = Math.Clamp(zoom, 0.2f, 4f);
|
|
UpdateProjection();
|
|
Invalidate();
|
|
}
|
|
void OnMouseDown(object s, MouseButtonEventArgs e)
|
|
{
|
|
GLHost.Focus();
|
|
lastMouse = e.GetPosition(GLHost);
|
|
if (e.ChangedButton == MouseButton.Left) dragLeft = true;
|
|
if (e.ChangedButton == MouseButton.Middle) dragMid = true;
|
|
Mouse.Capture(GLHost);
|
|
}
|
|
void OnMouseUp(object s, MouseButtonEventArgs e)
|
|
{
|
|
if (e.ChangedButton == MouseButton.Left) dragLeft = false;
|
|
if (e.ChangedButton == MouseButton.Middle) dragMid = false;
|
|
if (!dragLeft && !dragMid) Mouse.Capture(null);
|
|
}
|
|
void OnMouseMove(object s, System.Windows.Input.MouseEventArgs e)
|
|
{
|
|
var p = e.GetPosition(GLHost);
|
|
if (dragLeft)
|
|
{
|
|
float dx = (float)(p.X - lastMouse.X);
|
|
float dy = (float)(p.Y - lastMouse.Y);
|
|
rotY += dx * 0.5f; rotX += dy * 0.5f;
|
|
rotX = MathF.Max(-89f, MathF.Min(89f, rotX));
|
|
lastMouse = p; Invalidate();
|
|
}
|
|
else if (dragMid)
|
|
{
|
|
float dx = (float)(p.X - lastMouse.X);
|
|
float dy = (float)(p.Y - lastMouse.Y);
|
|
panX += dx * 0.01f; panY -= dy * 0.01f;
|
|
lastMouse = p; Invalidate();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Modelo igual ao seu (ajuste namespace/props se preciso)
|
|
public class LivoxBboxModel
|
|
{
|
|
public double cx, cy, cz; // centro
|
|
public double d, w, h; // dimensões (X,Y,Z) -> usei d=wX
|
|
}
|
|
}
|
|
|