using OpenTK.Wpf;
using OpenTK.Graphics.OpenGL;
using System.Windows.Input;
using UserControl = System.Windows.Controls.UserControl;
namespace OperationControl.Controls
{
///
/// Interaction logic for Bbox3DViewer.xaml
///
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 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 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
}
}