pcbjam/tests/wasm-app/standalone/graphicsctx/graphicsctx_test.cpp
Viktor Vaczi f71136b50a Add Phase 3 test coverage: validators, ownerdrawn, popup, graphicsctx, xml, wasmedge
New test apps:
- validators: wxTextValidator, wxIntegerValidator, wxFloatingPointValidator, custom validators
- ownerdrawn: wxOwnerDrawnComboBox for custom dropdown rendering (layer/font/icon combos)
- popup: wxPopupTransientWindow for transient popups (status, palette, color picker)
- graphicsctx: wxGraphicsContext for vector graphics (partial WASM support)
- xml: wxXmlDocument for XML parsing (used 665 times in KiCad)
- wasmedge: WASM edge cases (file system, threading, fonts, clipboard, memory)

Updates:
- 35 test apps, 246 tests passing (~99% coverage)
- Document WASM limitations and future TODOs in WHATWORKS.md
- Add baseline screenshots for all new tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 18:54:40 +01:00

354 lines
12 KiB
C++

// wxGraphicsContext Test - Vector graphics like KiCad's anti-aliased rendering
// Tests: wxGraphicsContext, paths, gradients, transforms, comparison with wxDC
#include "wx/wx.h"
#include "wx/graphics.h"
#include "wx/dcbuffer.h"
class GraphicsPanel : public wxPanel
{
public:
GraphicsPanel(wxWindow* parent, bool useGraphicsContext)
: wxPanel(parent, wxID_ANY), m_useGC(useGraphicsContext)
{
SetBackgroundStyle(wxBG_STYLE_PAINT);
Bind(wxEVT_PAINT, &GraphicsPanel::OnPaint, this);
}
void SetDrawMode(int mode) { m_drawMode = mode; Refresh(); }
void SetUseGC(bool use) { m_useGC = use; Refresh(); }
private:
void OnPaint(wxPaintEvent& event)
{
wxAutoBufferedPaintDC dc(this);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
if (m_useGC)
{
wxGraphicsContext* gc = wxGraphicsContext::Create(dc);
if (gc)
{
DrawWithGraphicsContext(gc);
delete gc;
}
else
{
dc.DrawText("wxGraphicsContext not available!", 10, 10);
}
}
else
{
DrawWithDC(dc);
}
}
void DrawWithGraphicsContext(wxGraphicsContext* gc)
{
wxSize size = GetClientSize();
switch (m_drawMode)
{
case 0: DrawBasicShapes(gc, size); break;
case 1: DrawPaths(gc, size); break;
case 2: DrawGradients(gc, size); break;
case 3: DrawTransforms(gc, size); break;
case 4: DrawAntiAliased(gc, size); break;
}
}
void DrawBasicShapes(wxGraphicsContext* gc, const wxSize& size)
{
// Title
gc->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), *wxBLACK);
gc->DrawText("Basic Shapes (wxGraphicsContext)", 10, 10);
// Rectangle
gc->SetBrush(gc->CreateBrush(wxBrush(*wxRED)));
gc->SetPen(gc->CreatePen(wxPen(*wxBLACK, 2)));
gc->DrawRectangle(30, 50, 80, 60);
// Ellipse
gc->SetBrush(gc->CreateBrush(wxBrush(*wxBLUE)));
gc->DrawEllipse(140, 50, 80, 60);
// Rounded rectangle
gc->SetBrush(gc->CreateBrush(wxBrush(*wxGREEN)));
gc->DrawRoundedRectangle(250, 50, 80, 60, 15);
// Lines
gc->SetPen(gc->CreatePen(wxPen(*wxRED, 3)));
gc->StrokeLine(30, 140, 110, 180);
gc->SetPen(gc->CreatePen(wxPen(*wxBLUE, 3)));
gc->StrokeLine(140, 140, 220, 180);
gc->SetPen(gc->CreatePen(wxPen(*wxGREEN, 3)));
gc->StrokeLine(250, 140, 330, 180);
}
void DrawPaths(wxGraphicsContext* gc, const wxSize& size)
{
gc->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), *wxBLACK);
gc->DrawText("Path Drawing (wxGraphicsPath)", 10, 10);
// Star shape using path
wxGraphicsPath path = gc->CreatePath();
double cx = 80, cy = 100;
double outerR = 40, innerR = 20;
int points = 5;
for (int i = 0; i < points * 2; i++)
{
double r = (i % 2 == 0) ? outerR : innerR;
double angle = i * M_PI / points - M_PI / 2;
double x = cx + r * cos(angle);
double y = cy + r * sin(angle);
if (i == 0)
path.MoveToPoint(x, y);
else
path.AddLineToPoint(x, y);
}
path.CloseSubpath();
gc->SetBrush(gc->CreateBrush(wxBrush(*wxYELLOW)));
gc->SetPen(gc->CreatePen(wxPen(*wxBLACK, 2)));
gc->FillPath(path);
gc->StrokePath(path);
// Bezier curve
wxGraphicsPath bezier = gc->CreatePath();
bezier.MoveToPoint(150, 60);
bezier.AddCurveToPoint(180, 40, 220, 140, 280, 120);
gc->SetPen(gc->CreatePen(wxPen(*wxRED, 3)));
gc->StrokePath(bezier);
// Arc
wxGraphicsPath arc = gc->CreatePath();
arc.AddArc(200, 180, 40, 0, M_PI * 1.5, true);
gc->SetPen(gc->CreatePen(wxPen(*wxBLUE, 3)));
gc->StrokePath(arc);
}
void DrawGradients(wxGraphicsContext* gc, const wxSize& size)
{
gc->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), *wxBLACK);
gc->DrawText("Gradient Fills", 10, 10);
// Linear gradient
wxGraphicsBrush linGrad = gc->CreateLinearGradientBrush(
30, 50, 130, 110, *wxRED, *wxBLUE);
gc->SetBrush(linGrad);
gc->SetPen(gc->CreatePen(wxPen(*wxBLACK, 1)));
gc->DrawRectangle(30, 50, 100, 60);
// Another linear gradient
wxGraphicsBrush linGrad2 = gc->CreateLinearGradientBrush(
150, 50, 150, 110, *wxGREEN, *wxYELLOW);
gc->SetBrush(linGrad2);
gc->DrawRectangle(150, 50, 100, 60);
// Radial gradient
wxGraphicsBrush radGrad = gc->CreateRadialGradientBrush(
300, 80, 300, 80, 50, *wxWHITE, *wxBLUE);
gc->SetBrush(radGrad);
gc->DrawEllipse(250, 50, 100, 60);
// Gradient with custom stops
wxGraphicsGradientStops stops;
stops.Add(*wxRED, 0.0f);
stops.Add(*wxYELLOW, 0.5f);
stops.Add(*wxGREEN, 1.0f);
wxGraphicsBrush multiGrad = gc->CreateLinearGradientBrush(
30, 140, 350, 140, stops);
gc->SetBrush(multiGrad);
gc->DrawRectangle(30, 140, 320, 40);
}
void DrawTransforms(wxGraphicsContext* gc, const wxSize& size)
{
gc->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), *wxBLACK);
gc->DrawText("Transforms (rotate, scale, translate)", 10, 10);
gc->SetBrush(gc->CreateBrush(wxBrush(*wxBLUE)));
gc->SetPen(gc->CreatePen(wxPen(*wxBLACK, 2)));
// Original rectangle
gc->PushState();
gc->DrawRectangle(50, 60, 60, 40);
gc->PopState();
// Rotated rectangle
gc->PushState();
gc->Translate(180, 80);
gc->Rotate(M_PI / 6); // 30 degrees
gc->SetBrush(gc->CreateBrush(wxBrush(*wxRED)));
gc->DrawRectangle(-30, -20, 60, 40);
gc->PopState();
// Scaled rectangle
gc->PushState();
gc->Translate(280, 80);
gc->Scale(1.5, 0.75);
gc->SetBrush(gc->CreateBrush(wxBrush(*wxGREEN)));
gc->DrawRectangle(-30, -20, 60, 40);
gc->PopState();
// Multiple transforms
gc->PushState();
gc->Translate(120, 160);
gc->Rotate(M_PI / 4); // 45 degrees
gc->Scale(1.2, 1.2);
gc->SetBrush(gc->CreateBrush(wxBrush(wxColour(255, 128, 0))));
gc->DrawRectangle(-25, -25, 50, 50);
gc->PopState();
}
void DrawAntiAliased(wxGraphicsContext* gc, const wxSize& size)
{
gc->SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), *wxBLACK);
gc->DrawText("Anti-Aliased Rendering (vs wxDC)", 10, 10);
// Draw thin diagonal lines (shows anti-aliasing)
for (int i = 0; i < 5; i++)
{
gc->SetPen(gc->CreatePen(wxPen(*wxBLACK, 1)));
gc->StrokeLine(30 + i * 20, 50, 70 + i * 20, 120);
}
// Draw circles (shows smooth curves)
gc->SetBrush(gc->CreateBrush(wxBrush(wxColour(255, 200, 200))));
gc->SetPen(gc->CreatePen(wxPen(*wxRED, 2)));
gc->DrawEllipse(150, 50, 70, 70);
// Text rendering
gc->SetFont(wxFont(16, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL), *wxBLUE);
gc->DrawText("Smooth Text", 240, 70);
// Rotated text
gc->PushState();
gc->Translate(100, 180);
gc->Rotate(-M_PI / 12);
gc->SetFont(wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), wxColour(0, 128, 0));
gc->DrawText("Rotated Text", 0, 0);
gc->PopState();
}
void DrawWithDC(wxDC& dc)
{
// Simple DC drawing for comparison
dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc.DrawText("Basic Shapes (wxDC - no anti-aliasing)", 10, 10);
dc.SetBrush(*wxRED_BRUSH);
dc.SetPen(*wxBLACK_PEN);
dc.DrawRectangle(30, 50, 80, 60);
dc.SetBrush(*wxBLUE_BRUSH);
dc.DrawEllipse(140, 50, 80, 60);
dc.SetBrush(*wxGREEN_BRUSH);
dc.DrawRoundedRectangle(250, 50, 80, 60, 15);
}
bool m_useGC = true;
int m_drawMode = 0;
};
class GraphicsCtxFrame : public wxFrame
{
public:
GraphicsCtxFrame() : wxFrame(nullptr, wxID_ANY, "wxGraphicsContext Test",
wxDefaultPosition, wxSize(800, 600))
{
wxPanel* mainPanel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Description
wxStaticText* desc = new wxStaticText(mainPanel, wxID_ANY,
"KiCad uses wxGraphicsContext for anti-aliased vector graphics.\n"
"Tests: Paths, gradients, transforms, anti-aliasing comparison.");
mainSizer->Add(desc, 0, wxALL, 5);
// Controls
wxBoxSizer* ctrlSizer = new wxBoxSizer(wxHORIZONTAL);
ctrlSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Draw Mode:"), 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
wxChoice* modeChoice = new wxChoice(mainPanel, wxID_ANY);
modeChoice->Append("Basic Shapes");
modeChoice->Append("Paths (star, bezier, arc)");
modeChoice->Append("Gradients");
modeChoice->Append("Transforms");
modeChoice->Append("Anti-Aliasing Demo");
modeChoice->SetSelection(0);
modeChoice->Bind(wxEVT_CHOICE, &GraphicsCtxFrame::OnModeChanged, this);
ctrlSizer->Add(modeChoice, 0, wxRIGHT, 20);
m_gcCheck = new wxCheckBox(mainPanel, wxID_ANY, "Use wxGraphicsContext");
m_gcCheck->SetValue(true);
m_gcCheck->Bind(wxEVT_CHECKBOX, &GraphicsCtxFrame::OnGCToggle, this);
ctrlSizer->Add(m_gcCheck, 0, wxALIGN_CENTER_VERTICAL);
mainSizer->Add(ctrlSizer, 0, wxALL, 5);
// Graphics panel
m_graphicsPanel = new GraphicsPanel(mainPanel, true);
m_graphicsPanel->SetMinSize(wxSize(-1, 250));
mainSizer->Add(m_graphicsPanel, 1, wxEXPAND | wxALL, 5);
// Event log
mainSizer->Add(new wxStaticText(mainPanel, wxID_ANY, "Event Log"), 0, wxLEFT | wxTOP, 5);
m_log = new wxTextCtrl(mainPanel, wxID_ANY, "", wxDefaultPosition, wxSize(-1, 100),
wxTE_MULTILINE | wxTE_READONLY);
mainSizer->Add(m_log, 0, wxEXPAND | wxALL, 5);
mainPanel->SetSizer(mainSizer);
CreateStatusBar();
SetStatusText("Graphics context test app started");
Log("Graphics context test app started");
Log("wxGraphicsContext provides anti-aliased vector graphics");
}
private:
void OnModeChanged(wxCommandEvent& event)
{
wxChoice* choice = dynamic_cast<wxChoice*>(event.GetEventObject());
int mode = choice->GetSelection();
m_graphicsPanel->SetDrawMode(mode);
Log(wxString::Format("Draw mode changed to: %s", choice->GetStringSelection()));
}
void OnGCToggle(wxCommandEvent& event)
{
bool useGC = m_gcCheck->IsChecked();
m_graphicsPanel->SetUseGC(useGC);
Log(wxString::Format("Using %s", useGC ? "wxGraphicsContext" : "wxDC"));
}
void Log(const wxString& msg)
{
m_log->AppendText(msg + "\n");
}
GraphicsPanel* m_graphicsPanel;
wxCheckBox* m_gcCheck;
wxTextCtrl* m_log;
};
class GraphicsCtxApp : public wxApp
{
public:
virtual bool OnInit() override
{
GraphicsCtxFrame* frame = new GraphicsCtxFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(GraphicsCtxApp);