feat(webgl): Wire up WEBGL_GAL test harness with proper canvas setup
- Add wasm_stubs.cpp with WASM-specific stub implementations for COLOR4D::BLACK/WHITE, GLU tesselator, PGM_BASE, and other KiCad dependencies - Update Makefile to include KiCad sources (GAL base class, display options, HiDPI canvas) and test scenarios - Fix kiglew.h to define GLEW guard (__glew_h__) preventing conflicts with Emscripten's GLEW header - Update test HTML to create canvas before module load (MODULARIZE requires passing canvas in Module config, not preRun) - Update Playwright tests to find canvas elements correctly - Add #window-container for wxWidgets GL canvas support Build produces 8.4MB WASM with full WEBGL_GAL implementation. Tests pass but rendering still shows dark output (debugging in progress). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
c381fdb85c
commit
c23bc974be
7 changed files with 747 additions and 180 deletions
|
|
@ -1,60 +1,84 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en-us">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>GAL WebGL Test</title>
|
<title>GAL WebGL Test</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px;
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
background: #1a1a26;
|
background: #1a1a26;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
#canvas-container {
|
|
||||||
display: inline-block;
|
/* wxWidgets window styling */
|
||||||
border: 2px solid #444;
|
.window {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: black;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
#canvas {
|
|
||||||
display: block;
|
.window-canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
#controls {
|
|
||||||
margin-top: 20px;
|
/* GL canvas styling */
|
||||||
|
.gl-canvas {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Controls overlay */
|
||||||
|
#controls-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 1000;
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
#status {
|
#status {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding: 10px;
|
padding: 5px;
|
||||||
background: #2a2a3a;
|
background: #2a2a3a;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
button {
|
|
||||||
padding: 8px 16px;
|
button, select {
|
||||||
margin-right: 10px;
|
padding: 5px 10px;
|
||||||
cursor: pointer;
|
margin-right: 5px;
|
||||||
}
|
|
||||||
select {
|
|
||||||
padding: 8px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>GAL WebGL Test</h1>
|
<!-- wxWidgets main window container -->
|
||||||
|
<div id="main-window"></div>
|
||||||
|
|
||||||
<div id="canvas-container">
|
<!-- wxWidgets window container (for popups and GL canvases) -->
|
||||||
<canvas id="canvas" width="800" height="600"></canvas>
|
<div id="window-container"></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="controls">
|
<!-- Controls overlay -->
|
||||||
|
<div id="controls-overlay">
|
||||||
<select id="scenario-select">
|
<select id="scenario-select">
|
||||||
<option value="-1">Select scenario...</option>
|
<option value="-1">Select scenario...</option>
|
||||||
</select>
|
</select>
|
||||||
<button id="run-btn" disabled>Run Scenario</button>
|
<button id="run-btn" disabled>Run</button>
|
||||||
<button id="run-all-btn" disabled>Run All</button>
|
<button id="run-all-btn" disabled>All</button>
|
||||||
</div>
|
<div id="status">Loading WASM...</div>
|
||||||
|
|
||||||
<div id="status">
|
|
||||||
<div id="status-text">Loading WASM module...</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -90,7 +114,7 @@
|
||||||
'transform-api' // 27
|
'transform-api' // 27
|
||||||
];
|
];
|
||||||
|
|
||||||
let Module = null;
|
var wasmModule = null;
|
||||||
|
|
||||||
// Populate scenario dropdown
|
// Populate scenario dropdown
|
||||||
function populateScenarios() {
|
function populateScenarios() {
|
||||||
|
|
@ -105,49 +129,101 @@
|
||||||
|
|
||||||
// Run a single scenario
|
// Run a single scenario
|
||||||
function runScenario(index) {
|
function runScenario(index) {
|
||||||
if (!Module) return;
|
if (!wasmModule) return;
|
||||||
|
|
||||||
const result = Module.ccall('runScenario', 'number', ['number'], [index]);
|
const result = wasmModule.ccall('runScenario', 'number', ['number'], [index]);
|
||||||
if (result === 0) {
|
if (result === 0) {
|
||||||
setStatus(`Rendered scenario ${index}: ${SCENARIO_NAMES[index]}`);
|
setStatus(`Rendered: ${SCENARIO_NAMES[index]}`);
|
||||||
} else {
|
} else {
|
||||||
setStatus(`ERROR: Failed to render scenario ${index}`);
|
setStatus(`ERROR: Failed scenario ${index}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run all scenarios (for automated testing)
|
// Run all scenarios
|
||||||
async function runAllScenarios() {
|
async function runAllScenarios() {
|
||||||
if (!Module) return;
|
if (!wasmModule) return;
|
||||||
|
|
||||||
const total = Module.ccall('getTotalScenarios', 'number', [], []);
|
const total = wasmModule.ccall('getTotalScenarios', 'number', [], []);
|
||||||
setStatus(`Running all ${total} scenarios...`);
|
setStatus(`Running all ${total} scenarios...`);
|
||||||
|
|
||||||
for (let i = 0; i < total; i++) {
|
for (let i = 0; i < total; i++) {
|
||||||
runScenario(i);
|
runScenario(i);
|
||||||
// Small delay to allow rendering
|
|
||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(`Completed all ${total} scenarios`);
|
setStatus(`Completed all ${total} scenarios`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update status display
|
// Update status
|
||||||
function setStatus(text) {
|
function setStatus(text) {
|
||||||
document.getElementById('status-text').textContent = text;
|
document.getElementById('status').textContent = text;
|
||||||
console.log('[GAL Test]', text);
|
console.log('[GAL Test]', text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the GL canvas for screenshots
|
||||||
|
function getGLCanvas() {
|
||||||
|
// wxGLCanvas creates canvases with class 'gl-canvas' or id 'glcanvas-N'
|
||||||
|
const glCanvases = document.querySelectorAll('.gl-canvas');
|
||||||
|
if (glCanvases.length > 0) {
|
||||||
|
return glCanvases[0];
|
||||||
|
}
|
||||||
|
// Fallback to any canvas in window-container
|
||||||
|
const containerCanvases = document.querySelectorAll('#window-container canvas');
|
||||||
|
if (containerCanvases.length > 0) {
|
||||||
|
return containerCanvases[0];
|
||||||
|
}
|
||||||
|
// Fallback to main canvas
|
||||||
|
return document.getElementById('canvas');
|
||||||
|
}
|
||||||
|
|
||||||
// Export for Playwright
|
// Export for Playwright
|
||||||
window.galTest = {
|
window.galTest = {
|
||||||
runScenario,
|
runScenario,
|
||||||
runAllScenarios,
|
runAllScenarios,
|
||||||
getScenarioName: (index) => SCENARIO_NAMES[index],
|
getScenarioName: (index) => SCENARIO_NAMES[index],
|
||||||
getTotalScenarios: () => SCENARIO_NAMES.length
|
getTotalScenarios: () => SCENARIO_NAMES.length,
|
||||||
|
getGLCanvas: getGLCanvas
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create canvas immediately (before module loads)
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.id = 'canvas';
|
||||||
|
canvas.style.display = 'block';
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.top = '0';
|
||||||
|
canvas.width = 800;
|
||||||
|
canvas.height = 600;
|
||||||
|
canvas.style.width = '800px';
|
||||||
|
canvas.style.height = '600px';
|
||||||
|
canvas.oncontextmenu = function(e) { e.preventDefault(); };
|
||||||
|
canvas.addEventListener("webglcontextlost", function(e) {
|
||||||
|
setStatus('WebGL context lost!');
|
||||||
|
e.preventDefault();
|
||||||
|
}, false);
|
||||||
|
document.getElementById('main-window').appendChild(canvas);
|
||||||
|
|
||||||
|
// Module configuration for MODULARIZE
|
||||||
|
var Module = {
|
||||||
|
canvas: canvas, // Pass the canvas we created
|
||||||
|
print: function(text) {
|
||||||
|
console.log(text);
|
||||||
|
},
|
||||||
|
printErr: function(text) {
|
||||||
|
console.error(text);
|
||||||
|
},
|
||||||
|
setStatus: function(text) {
|
||||||
|
setStatus(text || 'Ready');
|
||||||
|
},
|
||||||
|
onRuntimeInitialized: function() {
|
||||||
|
setStatus('WASM initialized');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup UI after module loads
|
// Setup UI after module loads
|
||||||
function onModuleReady() {
|
function onModuleReady(module) {
|
||||||
setStatus('WASM module loaded. Ready for testing.');
|
wasmModule = module;
|
||||||
|
setStatus('Ready. Select a scenario.');
|
||||||
|
|
||||||
document.getElementById('run-btn').disabled = false;
|
document.getElementById('run-btn').disabled = false;
|
||||||
document.getElementById('run-all-btn').disabled = false;
|
document.getElementById('run-all-btn').disabled = false;
|
||||||
|
|
@ -169,22 +245,13 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dispatch custom event for Playwright
|
// Dispatch ready event for Playwright
|
||||||
window.dispatchEvent(new CustomEvent('gal-test-ready'));
|
window.dispatchEvent(new CustomEvent('gal-test-ready'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
populateScenarios();
|
populateScenarios();
|
||||||
|
|
||||||
// Load WASM module
|
|
||||||
createGALTest().then(module => {
|
|
||||||
Module = module;
|
|
||||||
onModuleReady();
|
|
||||||
}).catch(err => {
|
|
||||||
setStatus('ERROR: Failed to load WASM module: ' + err.message);
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<script src="gal_webgl_test.js"></script>
|
<script src="gal_webgl_test.js" onload="createGALTest(Module).then(onModuleReady).catch(e => setStatus('Load error: ' + e.message))"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -96,9 +96,38 @@ test.describe('GAL WebGL Regression Tests', () => {
|
||||||
// Wait for rendering to complete
|
// Wait for rendering to complete
|
||||||
await page.waitForTimeout(100);
|
await page.waitForTimeout(100);
|
||||||
|
|
||||||
// Get the canvas element and take a screenshot of just the canvas
|
// Debug: list all canvases on the page
|
||||||
const canvas = await page.locator('#canvas');
|
const canvasInfo = await page.evaluate(() => {
|
||||||
await expect(canvas).toBeVisible();
|
const canvases = document.querySelectorAll('canvas');
|
||||||
|
const windowContainer = document.getElementById('window-container');
|
||||||
|
return {
|
||||||
|
canvases: Array.from(canvases).map(c => ({
|
||||||
|
id: c.id,
|
||||||
|
className: c.className,
|
||||||
|
width: c.width,
|
||||||
|
height: c.height,
|
||||||
|
display: window.getComputedStyle(c).display,
|
||||||
|
parentId: c.parentElement?.id
|
||||||
|
})),
|
||||||
|
windowContainerChildren: windowContainer?.children.length || 0,
|
||||||
|
allElements: document.querySelectorAll('#window-container *').length
|
||||||
|
};
|
||||||
|
});
|
||||||
|
console.log('Canvas debug:', JSON.stringify(canvasInfo, null, 2));
|
||||||
|
|
||||||
|
// Get the canvas element
|
||||||
|
// First try GL canvas, fall back to main canvas
|
||||||
|
let canvas = page.locator('.gl-canvas').first();
|
||||||
|
if (!(await canvas.count())) {
|
||||||
|
canvas = page.locator('#canvas');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If still no canvas, use first available
|
||||||
|
if (!(await canvas.count())) {
|
||||||
|
canvas = page.locator('canvas').first();
|
||||||
|
}
|
||||||
|
|
||||||
|
await expect(canvas).toBeVisible({ timeout: 5000 });
|
||||||
|
|
||||||
// Screenshot the canvas (matching native 800x600 output)
|
// Screenshot the canvas (matching native 800x600 output)
|
||||||
const screenshotPath = path.join(OUTPUT_DIR, `gal-${scenarioName}.png`);
|
const screenshotPath = path.join(OUTPUT_DIR, `gal-${scenarioName}.png`);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,9 @@ SYSROOT = $(PROJECT_ROOT)/build-wasm/sysroot
|
||||||
|
|
||||||
# KiCad include paths
|
# KiCad include paths
|
||||||
# Note: -I../native provides stub files (config.h, kicad_stubs.h, etc.)
|
# Note: -I../native provides stub files (config.h, kicad_stubs.h, etc.)
|
||||||
|
# Note: -I../scenarios provides shared test scenarios (gal_test_scenarios.h)
|
||||||
KICAD_INCLUDES = -I../native \
|
KICAD_INCLUDES = -I../native \
|
||||||
|
-I../scenarios \
|
||||||
-Igenerated \
|
-Igenerated \
|
||||||
-I$(KICAD_ROOT)/include \
|
-I$(KICAD_ROOT)/include \
|
||||||
-I$(KICAD_ROOT)/include/gal \
|
-I$(KICAD_ROOT)/include/gal \
|
||||||
|
|
@ -76,7 +78,18 @@ JS = $(TOOLS_ROOT)/wx.js
|
||||||
HTML_TEMPLATE = $(TOOLS_ROOT)/template.html
|
HTML_TEMPLATE = $(TOOLS_ROOT)/template.html
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
MAIN_SRCS = gal_webgl_test.cpp
|
MAIN_SRCS = gal_webgl_test.cpp wasm_stubs.cpp
|
||||||
|
|
||||||
|
# KiCad sources needed for linking (not in WEBGL_SRCS)
|
||||||
|
KICAD_SRCS = $(KICAD_ROOT)/common/gal/hidpi_gl_canvas.cpp \
|
||||||
|
$(KICAD_ROOT)/common/gal/graphics_abstraction_layer.cpp \
|
||||||
|
$(KICAD_ROOT)/common/gal/gal_display_options.cpp
|
||||||
|
|
||||||
|
# Test scenario sources (shared with native test)
|
||||||
|
SCENARIO_SRCS = ../scenarios/gal_test_scenarios.cpp
|
||||||
|
|
||||||
|
# GLU tesselator - stub (no actual implementation needed for basic shapes)
|
||||||
|
GLU_SRCS =
|
||||||
|
|
||||||
# Generated shader sources (from generate_shaders.py)
|
# Generated shader sources (from generate_shaders.py)
|
||||||
SHADER_SRCS = generated/glsl_kicad_frag.cpp \
|
SHADER_SRCS = generated/glsl_kicad_frag.cpp \
|
||||||
|
|
@ -107,7 +120,7 @@ WEBGL_SRCS = webgl/webgl_gal.cpp \
|
||||||
webgl/gl_resources.cpp \
|
webgl/gl_resources.cpp \
|
||||||
webgl/webgl_antialiasing.cpp
|
webgl/webgl_antialiasing.cpp
|
||||||
|
|
||||||
SRCS = $(MAIN_SRCS) $(SHADER_SRCS) $(WEBGL_SRCS)
|
SRCS = $(MAIN_SRCS) $(KICAD_SRCS) $(SCENARIO_SRCS) $(GLU_SRCS) $(SHADER_SRCS) $(WEBGL_SRCS)
|
||||||
OBJS = $(SRCS:.cpp=.o)
|
OBJS = $(SRCS:.cpp=.o)
|
||||||
|
|
||||||
# Target
|
# Target
|
||||||
|
|
|
||||||
|
|
@ -1,87 +1,70 @@
|
||||||
/**
|
/**
|
||||||
* GAL WebGL Test - WASM Entry Point
|
* GAL WebGL Test - WASM Entry Point
|
||||||
*
|
*
|
||||||
* This is a test harness that renders GAL test scenarios using WEBGL_GAL
|
* This test harness renders GAL test scenarios using WEBGL_GAL
|
||||||
* and allows Playwright to capture screenshots for comparison against native.
|
* and allows Playwright to capture screenshots for comparison against native.
|
||||||
*
|
|
||||||
* Phase 1: Stub that initializes WebGL and renders a test pattern
|
|
||||||
* Phase 2: Full WEBGL_GAL implementation with all scenarios
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// Include kiglew.h first to set GLEW guard before anything includes <GL/glew.h>
|
||||||
|
#include "webgl/kiglew.h"
|
||||||
|
|
||||||
|
#include <wx/wx.h>
|
||||||
|
#include <wx/glcanvas.h>
|
||||||
|
|
||||||
#include <emscripten.h>
|
#include <emscripten.h>
|
||||||
#include <emscripten/html5.h>
|
#include <emscripten/html5.h>
|
||||||
#include <GLES3/gl3.h>
|
|
||||||
|
// KiCad GAL headers
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
#include <math/vector2d.h>
|
||||||
|
#include "webgl/webgl_gal.h"
|
||||||
|
|
||||||
|
// Test scenarios (shared with native test)
|
||||||
|
#include "gal_test_scenarios.h"
|
||||||
|
|
||||||
|
// Stubs
|
||||||
|
#include "kicad_stubs.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
// Canvas dimensions (matching native test)
|
// Canvas dimensions (matching native test)
|
||||||
static const int CANVAS_WIDTH = 800;
|
static const int CANVAS_WIDTH = 800;
|
||||||
static const int CANVAS_HEIGHT = 600;
|
static const int CANVAS_HEIGHT = 600;
|
||||||
|
|
||||||
// Current scenario index
|
// Global state
|
||||||
static int g_currentScenario = -1;
|
static int g_currentScenario = -1;
|
||||||
static int g_totalScenarios = 28;
|
static int g_totalScenarios = 28;
|
||||||
|
static KIGFX::WEBGL_GAL* g_gal = nullptr;
|
||||||
// WebGL context
|
static wxFrame* g_frame = nullptr;
|
||||||
static EMSCRIPTEN_WEBGL_CONTEXT_HANDLE g_glContext = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize WebGL context
|
* Render the current scenario
|
||||||
*/
|
*/
|
||||||
bool initWebGL() {
|
void renderCurrentScenario() {
|
||||||
EmscriptenWebGLContextAttributes attrs;
|
if (!g_gal || g_currentScenario < 0) {
|
||||||
emscripten_webgl_init_context_attributes(&attrs);
|
return;
|
||||||
|
|
||||||
attrs.majorVersion = 2; // WebGL 2.0
|
|
||||||
attrs.minorVersion = 0;
|
|
||||||
attrs.alpha = true;
|
|
||||||
attrs.depth = true;
|
|
||||||
attrs.stencil = true;
|
|
||||||
attrs.antialias = false; // We handle AA ourselves
|
|
||||||
attrs.premultipliedAlpha = false;
|
|
||||||
attrs.preserveDrawingBuffer = true; // Needed for screenshots
|
|
||||||
|
|
||||||
g_glContext = emscripten_webgl_create_context("#canvas", &attrs);
|
|
||||||
if (g_glContext <= 0) {
|
|
||||||
printf("ERROR: Failed to create WebGL 2.0 context: %d\n", g_glContext);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
emscripten_webgl_make_context_current(g_glContext);
|
printf("Rendering scenario %d: %s\n", g_currentScenario,
|
||||||
|
GALTest::GetScenarioName(g_currentScenario));
|
||||||
|
|
||||||
printf("WebGL 2.0 context created successfully\n");
|
// Begin drawing
|
||||||
printf(" GL_VENDOR: %s\n", glGetString(GL_VENDOR));
|
g_gal->BeginDrawing();
|
||||||
printf(" GL_RENDERER: %s\n", glGetString(GL_RENDERER));
|
|
||||||
printf(" GL_VERSION: %s\n", glGetString(GL_VERSION));
|
|
||||||
|
|
||||||
return true;
|
// Clear the screen
|
||||||
|
g_gal->ClearScreen();
|
||||||
|
|
||||||
|
// Render the scenario
|
||||||
|
GALTest::RenderScenario(g_gal, g_currentScenario, CANVAS_WIDTH, CANVAS_HEIGHT);
|
||||||
|
|
||||||
|
// End drawing and present
|
||||||
|
g_gal->EndDrawing();
|
||||||
|
|
||||||
|
printf("Scenario %d rendered\n", g_currentScenario);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a test pattern (stub for Phase 1)
|
* C API for JavaScript calls
|
||||||
* In Phase 2, this will be replaced with actual WEBGL_GAL rendering
|
|
||||||
*/
|
|
||||||
void renderTestPattern(int scenarioIndex) {
|
|
||||||
// Set viewport
|
|
||||||
glViewport(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
|
||||||
|
|
||||||
// Clear with KiCad-like dark background
|
|
||||||
glClearColor(0.102f, 0.102f, 0.149f, 1.0f); // RGB(26, 26, 38)
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
// TODO Phase 2: Replace with actual WEBGL_GAL rendering
|
|
||||||
// For now, just render a different colored rectangle for each scenario
|
|
||||||
// to verify the pipeline works
|
|
||||||
|
|
||||||
// This is a placeholder - in Phase 2 we'll call:
|
|
||||||
// GALTest::RenderScenario(webglGal, scenarioIndex, CANVAS_WIDTH, CANVAS_HEIGHT);
|
|
||||||
|
|
||||||
printf("Rendered scenario %d (stub)\n", scenarioIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run a specific scenario
|
|
||||||
* Called from JavaScript: Module.ccall('runScenario', 'number', ['number'], [index])
|
|
||||||
*/
|
*/
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
||||||
|
|
@ -94,7 +77,7 @@ int runScenario(int scenarioIndex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
g_currentScenario = scenarioIndex;
|
g_currentScenario = scenarioIndex;
|
||||||
renderTestPattern(scenarioIndex);
|
renderCurrentScenario();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -122,24 +105,84 @@ int getCanvasHeight() {
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main entry point
|
* wxWidgets Application
|
||||||
*/
|
*/
|
||||||
int main() {
|
class GALTestApp : public wxApp {
|
||||||
printf("GAL WebGL Test - Phase 1 Stub\n");
|
public:
|
||||||
printf("============================\n\n");
|
virtual bool OnInit() override;
|
||||||
|
};
|
||||||
|
|
||||||
// Set canvas size
|
wxIMPLEMENT_APP_NO_MAIN(GALTestApp);
|
||||||
emscripten_set_canvas_element_size("#canvas", CANVAS_WIDTH, CANVAS_HEIGHT);
|
|
||||||
|
|
||||||
// Initialize WebGL
|
bool GALTestApp::OnInit() {
|
||||||
if (!initWebGL()) {
|
printf("GAL WebGL Test - Initializing wxWidgets\n");
|
||||||
printf("Failed to initialize WebGL\n");
|
|
||||||
return 1;
|
// Create a frame (invisible, just hosts the GL canvas)
|
||||||
|
g_frame = new wxFrame(nullptr, wxID_ANY, "GAL WebGL Test",
|
||||||
|
wxDefaultPosition, wxSize(CANVAS_WIDTH, CANVAS_HEIGHT));
|
||||||
|
|
||||||
|
// Create display options
|
||||||
|
KIGFX::GAL_DISPLAY_OPTIONS displayOptions;
|
||||||
|
KIGFX::VC_SETTINGS vcSettings;
|
||||||
|
|
||||||
|
printf("Creating WEBGL_GAL instance...\n");
|
||||||
|
|
||||||
|
try {
|
||||||
|
g_gal = new KIGFX::WEBGL_GAL(
|
||||||
|
vcSettings,
|
||||||
|
displayOptions,
|
||||||
|
g_frame, // parent window
|
||||||
|
nullptr, // mouse listener
|
||||||
|
nullptr, // paint listener
|
||||||
|
"GAL WebGL Test" // name
|
||||||
|
);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
printf("ERROR: Failed to create WEBGL_GAL: %s\n", e.what());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printf("WEBGL_GAL created successfully\n");
|
||||||
|
|
||||||
|
// Set up the GAL
|
||||||
|
g_gal->SetScreenSize(VECTOR2I(CANVAS_WIDTH, CANVAS_HEIGHT));
|
||||||
|
g_gal->ResizeScreen(CANVAS_WIDTH, CANVAS_HEIGHT);
|
||||||
|
|
||||||
|
// Set default world coordinates (matching native test)
|
||||||
|
double worldScale = 1.0 / 10000.0; // Convert nm to screen units
|
||||||
|
g_gal->SetWorldUnitLength(worldScale);
|
||||||
|
g_gal->SetScreenDPI(96);
|
||||||
|
|
||||||
|
// Set world boundaries
|
||||||
|
VECTOR2D worldSize(CANVAS_WIDTH / worldScale, CANVAS_HEIGHT / worldScale);
|
||||||
|
BOX2D worldBounds(VECTOR2D(0, 0), worldSize);
|
||||||
|
g_gal->SetWorldScreenMatrix(g_gal->GetWorldScreenMatrix());
|
||||||
|
|
||||||
|
// Initialize the compositor
|
||||||
|
g_gal->BeginDrawing();
|
||||||
|
g_gal->ClearScreen();
|
||||||
|
g_gal->EndDrawing();
|
||||||
|
|
||||||
printf("\nReady for scenarios. Total: %d\n", g_totalScenarios);
|
printf("\nReady for scenarios. Total: %d\n", g_totalScenarios);
|
||||||
printf("Call runScenario(index) from JavaScript to render.\n");
|
printf("Call runScenario(index) from JavaScript to render.\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main entry point
|
||||||
|
*/
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
printf("GAL WebGL Test\n");
|
||||||
|
printf("==============\n\n");
|
||||||
|
|
||||||
|
// Initialize wxWidgets
|
||||||
|
wxEntryStart(argc, argv);
|
||||||
|
|
||||||
|
if (!wxTheApp->OnInit()) {
|
||||||
|
printf("ERROR: wxApp initialization failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't exit - keep runtime alive for JavaScript calls
|
// Don't exit - keep runtime alive for JavaScript calls
|
||||||
emscripten_exit_with_live_runtime();
|
emscripten_exit_with_live_runtime();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,84 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html lang="en-us">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title>GAL WebGL Test</title>
|
<title>GAL WebGL Test</title>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 20px;
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
background: #1a1a26;
|
background: #1a1a26;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
#canvas-container {
|
|
||||||
display: inline-block;
|
/* wxWidgets window styling */
|
||||||
border: 2px solid #444;
|
.window {
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: black;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
}
|
}
|
||||||
#canvas {
|
|
||||||
display: block;
|
.window-canvas {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
#controls {
|
|
||||||
margin-top: 20px;
|
/* GL canvas styling */
|
||||||
|
.gl-canvas {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 100;
|
||||||
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Controls overlay */
|
||||||
|
#controls-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 10px;
|
||||||
|
left: 10px;
|
||||||
|
z-index: 1000;
|
||||||
|
background: rgba(0,0,0,0.8);
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
#status {
|
#status {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding: 10px;
|
padding: 5px;
|
||||||
background: #2a2a3a;
|
background: #2a2a3a;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
button {
|
|
||||||
padding: 8px 16px;
|
button, select {
|
||||||
margin-right: 10px;
|
padding: 5px 10px;
|
||||||
cursor: pointer;
|
margin-right: 5px;
|
||||||
}
|
|
||||||
select {
|
|
||||||
padding: 8px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>GAL WebGL Test</h1>
|
<!-- wxWidgets main window container -->
|
||||||
|
<div id="main-window"></div>
|
||||||
|
|
||||||
<div id="canvas-container">
|
<!-- wxWidgets window container (for popups and GL canvases) -->
|
||||||
<canvas id="canvas" width="800" height="600"></canvas>
|
<div id="window-container"></div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="controls">
|
<!-- Controls overlay -->
|
||||||
|
<div id="controls-overlay">
|
||||||
<select id="scenario-select">
|
<select id="scenario-select">
|
||||||
<option value="-1">Select scenario...</option>
|
<option value="-1">Select scenario...</option>
|
||||||
</select>
|
</select>
|
||||||
<button id="run-btn" disabled>Run Scenario</button>
|
<button id="run-btn" disabled>Run</button>
|
||||||
<button id="run-all-btn" disabled>Run All</button>
|
<button id="run-all-btn" disabled>All</button>
|
||||||
</div>
|
<div id="status">Loading WASM...</div>
|
||||||
|
|
||||||
<div id="status">
|
|
||||||
<div id="status-text">Loading WASM module...</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -90,7 +114,7 @@
|
||||||
'transform-api' // 27
|
'transform-api' // 27
|
||||||
];
|
];
|
||||||
|
|
||||||
let Module = null;
|
var wasmModule = null;
|
||||||
|
|
||||||
// Populate scenario dropdown
|
// Populate scenario dropdown
|
||||||
function populateScenarios() {
|
function populateScenarios() {
|
||||||
|
|
@ -105,49 +129,101 @@
|
||||||
|
|
||||||
// Run a single scenario
|
// Run a single scenario
|
||||||
function runScenario(index) {
|
function runScenario(index) {
|
||||||
if (!Module) return;
|
if (!wasmModule) return;
|
||||||
|
|
||||||
const result = Module.ccall('runScenario', 'number', ['number'], [index]);
|
const result = wasmModule.ccall('runScenario', 'number', ['number'], [index]);
|
||||||
if (result === 0) {
|
if (result === 0) {
|
||||||
setStatus(`Rendered scenario ${index}: ${SCENARIO_NAMES[index]}`);
|
setStatus(`Rendered: ${SCENARIO_NAMES[index]}`);
|
||||||
} else {
|
} else {
|
||||||
setStatus(`ERROR: Failed to render scenario ${index}`);
|
setStatus(`ERROR: Failed scenario ${index}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run all scenarios (for automated testing)
|
// Run all scenarios
|
||||||
async function runAllScenarios() {
|
async function runAllScenarios() {
|
||||||
if (!Module) return;
|
if (!wasmModule) return;
|
||||||
|
|
||||||
const total = Module.ccall('getTotalScenarios', 'number', [], []);
|
const total = wasmModule.ccall('getTotalScenarios', 'number', [], []);
|
||||||
setStatus(`Running all ${total} scenarios...`);
|
setStatus(`Running all ${total} scenarios...`);
|
||||||
|
|
||||||
for (let i = 0; i < total; i++) {
|
for (let i = 0; i < total; i++) {
|
||||||
runScenario(i);
|
runScenario(i);
|
||||||
// Small delay to allow rendering
|
|
||||||
await new Promise(r => setTimeout(r, 100));
|
await new Promise(r => setTimeout(r, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatus(`Completed all ${total} scenarios`);
|
setStatus(`Completed all ${total} scenarios`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update status display
|
// Update status
|
||||||
function setStatus(text) {
|
function setStatus(text) {
|
||||||
document.getElementById('status-text').textContent = text;
|
document.getElementById('status').textContent = text;
|
||||||
console.log('[GAL Test]', text);
|
console.log('[GAL Test]', text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the GL canvas for screenshots
|
||||||
|
function getGLCanvas() {
|
||||||
|
// wxGLCanvas creates canvases with class 'gl-canvas' or id 'glcanvas-N'
|
||||||
|
const glCanvases = document.querySelectorAll('.gl-canvas');
|
||||||
|
if (glCanvases.length > 0) {
|
||||||
|
return glCanvases[0];
|
||||||
|
}
|
||||||
|
// Fallback to any canvas in window-container
|
||||||
|
const containerCanvases = document.querySelectorAll('#window-container canvas');
|
||||||
|
if (containerCanvases.length > 0) {
|
||||||
|
return containerCanvases[0];
|
||||||
|
}
|
||||||
|
// Fallback to main canvas
|
||||||
|
return document.getElementById('canvas');
|
||||||
|
}
|
||||||
|
|
||||||
// Export for Playwright
|
// Export for Playwright
|
||||||
window.galTest = {
|
window.galTest = {
|
||||||
runScenario,
|
runScenario,
|
||||||
runAllScenarios,
|
runAllScenarios,
|
||||||
getScenarioName: (index) => SCENARIO_NAMES[index],
|
getScenarioName: (index) => SCENARIO_NAMES[index],
|
||||||
getTotalScenarios: () => SCENARIO_NAMES.length
|
getTotalScenarios: () => SCENARIO_NAMES.length,
|
||||||
|
getGLCanvas: getGLCanvas
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create canvas immediately (before module loads)
|
||||||
|
var canvas = document.createElement('canvas');
|
||||||
|
canvas.id = 'canvas';
|
||||||
|
canvas.style.display = 'block';
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.top = '0';
|
||||||
|
canvas.width = 800;
|
||||||
|
canvas.height = 600;
|
||||||
|
canvas.style.width = '800px';
|
||||||
|
canvas.style.height = '600px';
|
||||||
|
canvas.oncontextmenu = function(e) { e.preventDefault(); };
|
||||||
|
canvas.addEventListener("webglcontextlost", function(e) {
|
||||||
|
setStatus('WebGL context lost!');
|
||||||
|
e.preventDefault();
|
||||||
|
}, false);
|
||||||
|
document.getElementById('main-window').appendChild(canvas);
|
||||||
|
|
||||||
|
// Module configuration for MODULARIZE
|
||||||
|
var Module = {
|
||||||
|
canvas: canvas, // Pass the canvas we created
|
||||||
|
print: function(text) {
|
||||||
|
console.log(text);
|
||||||
|
},
|
||||||
|
printErr: function(text) {
|
||||||
|
console.error(text);
|
||||||
|
},
|
||||||
|
setStatus: function(text) {
|
||||||
|
setStatus(text || 'Ready');
|
||||||
|
},
|
||||||
|
onRuntimeInitialized: function() {
|
||||||
|
setStatus('WASM initialized');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup UI after module loads
|
// Setup UI after module loads
|
||||||
function onModuleReady() {
|
function onModuleReady(module) {
|
||||||
setStatus('WASM module loaded. Ready for testing.');
|
wasmModule = module;
|
||||||
|
setStatus('Ready. Select a scenario.');
|
||||||
|
|
||||||
document.getElementById('run-btn').disabled = false;
|
document.getElementById('run-btn').disabled = false;
|
||||||
document.getElementById('run-all-btn').disabled = false;
|
document.getElementById('run-all-btn').disabled = false;
|
||||||
|
|
@ -169,22 +245,13 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Dispatch custom event for Playwright
|
// Dispatch ready event for Playwright
|
||||||
window.dispatchEvent(new CustomEvent('gal-test-ready'));
|
window.dispatchEvent(new CustomEvent('gal-test-ready'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
populateScenarios();
|
populateScenarios();
|
||||||
|
|
||||||
// Load WASM module
|
|
||||||
createGALTest().then(module => {
|
|
||||||
Module = module;
|
|
||||||
onModuleReady();
|
|
||||||
}).catch(err => {
|
|
||||||
setStatus('ERROR: Failed to load WASM module: ' + err.message);
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<script src="gal_webgl_test.js"></script>
|
<script src="gal_webgl_test.js" onload="createGALTest(Module).then(onModuleReady).catch(e => setStatus('Load error: ' + e.message))"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
342
tests/gal-regression/wasm/wasm_stubs.cpp
Normal file
342
tests/gal-regression/wasm/wasm_stubs.cpp
Normal file
|
|
@ -0,0 +1,342 @@
|
||||||
|
/**
|
||||||
|
* WASM stubs for GAL WebGL test
|
||||||
|
*
|
||||||
|
* Provides implementations for symbols needed by WEBGL_GAL but not
|
||||||
|
* provided by the minimal test harness.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Include kiglew.h first to prevent GLEW conflicts
|
||||||
|
#include "webgl/kiglew.h"
|
||||||
|
|
||||||
|
#include <wx/wx.h>
|
||||||
|
#include <wx/snglinst.h>
|
||||||
|
|
||||||
|
// Forward declarations to avoid including heavy headers
|
||||||
|
namespace KIGFX { class GAL; }
|
||||||
|
|
||||||
|
// Include minimal headers
|
||||||
|
#include <gal/color4d.h>
|
||||||
|
#include <kiid.h>
|
||||||
|
#include <gal/hidpi_gl_canvas.h>
|
||||||
|
#include <gal/graphics_abstraction_layer.h>
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// COLOR4D static constants
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
namespace KIGFX {
|
||||||
|
|
||||||
|
const COLOR4D COLOR4D::BLACK( 0, 0, 0, 1 );
|
||||||
|
const COLOR4D COLOR4D::WHITE( 1, 1, 1, 1 );
|
||||||
|
const COLOR4D COLOR4D::UNSPECIFIED( 0, 0, 0, 0 );
|
||||||
|
const COLOR4D COLOR4D::CLEAR( 0, 0, 0, 0 );
|
||||||
|
|
||||||
|
} // namespace KIGFX
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// KIID
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
KIID niluuid;
|
||||||
|
|
||||||
|
// Note: GAL class is provided by graphics_abstraction_layer.cpp
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// GLU tesselator stubs (needed for polygon rendering)
|
||||||
|
// These are no-op stubs - polygons won't render correctly until
|
||||||
|
// we add a real tesselator implementation
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
struct GLUtesselator {};
|
||||||
|
|
||||||
|
GLUtesselator* gluNewTess() { return new GLUtesselator(); }
|
||||||
|
void gluDeleteTess(GLUtesselator* tess) { delete tess; }
|
||||||
|
void gluTessProperty(GLUtesselator*, GLenum, GLdouble) {}
|
||||||
|
void gluTessCallback(GLUtesselator*, GLenum, void(*)()) {}
|
||||||
|
void gluTessBeginPolygon(GLUtesselator*, void*) {}
|
||||||
|
void gluTessEndPolygon(GLUtesselator*) {}
|
||||||
|
void gluTessBeginContour(GLUtesselator*) {}
|
||||||
|
void gluTessEndContour(GLUtesselator*) {}
|
||||||
|
void gluTessVertex(GLUtesselator*, GLdouble*, void*) {}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// Additional stubs from kicad_stubs.cpp that are needed
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
#include <core/observable.h>
|
||||||
|
|
||||||
|
namespace UTIL {
|
||||||
|
namespace DETAIL {
|
||||||
|
|
||||||
|
OBSERVABLE_BASE::OBSERVABLE_BASE() {}
|
||||||
|
OBSERVABLE_BASE::~OBSERVABLE_BASE() {}
|
||||||
|
void OBSERVABLE_BASE::on_observers_empty() {}
|
||||||
|
void OBSERVABLE_BASE::enter_iteration() {}
|
||||||
|
void OBSERVABLE_BASE::leave_iteration() {}
|
||||||
|
void OBSERVABLE_BASE::add_observer(void*) {}
|
||||||
|
void OBSERVABLE_BASE::remove_observer(void*) {}
|
||||||
|
|
||||||
|
} // namespace DETAIL
|
||||||
|
} // namespace UTIL
|
||||||
|
|
||||||
|
// UTIL::LINK stubs
|
||||||
|
namespace UTIL {
|
||||||
|
LINK::LINK() : token_(nullptr), observer_(nullptr) {}
|
||||||
|
LINK::LINK(std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token, void* observer)
|
||||||
|
: token_(token), observer_(observer) {}
|
||||||
|
LINK::LINK(LINK&& other) : token_(std::move(other.token_)), observer_(other.observer_) {
|
||||||
|
other.observer_ = nullptr;
|
||||||
|
}
|
||||||
|
LINK::~LINK() {}
|
||||||
|
LINK& LINK::operator=(LINK&& other) {
|
||||||
|
token_ = std::move(other.token_);
|
||||||
|
observer_ = other.observer_;
|
||||||
|
other.observer_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
void LINK::reset() { token_.reset(); observer_ = nullptr; }
|
||||||
|
LINK::operator bool() const { return token_ != nullptr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI dialog stubs
|
||||||
|
void DisplayError(wxWindow*, const wxString&) {}
|
||||||
|
void DisplayErrorMessage(wxWindow*, const wxString&, const wxString&) {}
|
||||||
|
|
||||||
|
// OpenGL info stub
|
||||||
|
void SetOpenGLInfo(const char*, const char*, const char*) {}
|
||||||
|
|
||||||
|
// Math logging stub
|
||||||
|
void kimathLogOverflow(double, const char*) {}
|
||||||
|
|
||||||
|
// Arc geometry stub
|
||||||
|
#include <geometry/eda_angle.h>
|
||||||
|
int GetArcToSegmentCount(int aRadius, int aArcError, const EDA_ANGLE& aArcAngle) {
|
||||||
|
double degrees = std::abs(aArcAngle.AsDegrees());
|
||||||
|
return std::max(1, (int)(degrees / 10.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// DPI scaling stub
|
||||||
|
#include <dpi_scaling.h>
|
||||||
|
double DPI_SCALING::GetDefaultScaleFactor() { return 1.0; }
|
||||||
|
|
||||||
|
// Cursor store stub
|
||||||
|
#include <gal/cursors.h>
|
||||||
|
const WX_CURSOR_TYPE CURSOR_STORE::GetCursor(KICURSOR aCursor, bool aHiDPI) {
|
||||||
|
return wxCursor(wxCURSOR_ARROW);
|
||||||
|
}
|
||||||
|
|
||||||
|
// VC_SETTINGS::Reset stub
|
||||||
|
#include <view/view_controls.h>
|
||||||
|
namespace KIGFX {
|
||||||
|
void VC_SETTINGS::Reset() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// KIID constructors
|
||||||
|
KIID::KIID() {}
|
||||||
|
KIID::KIID(int aValue) {}
|
||||||
|
|
||||||
|
// UTF8 stubs
|
||||||
|
#include <core/utf8.h>
|
||||||
|
UTF8::UTF8(const wxString& s) : m_s(s.ToUTF8().data()) {}
|
||||||
|
int UTF8::uni_forward(const unsigned char* aSequence, unsigned* aResult) {
|
||||||
|
if (aSequence && *aSequence) {
|
||||||
|
if (aResult) *aResult = (unsigned)*aSequence;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (aResult) *aResult = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEXT_ATTRIBUTES constructor
|
||||||
|
#include <font/text_attributes.h>
|
||||||
|
TEXT_ATTRIBUTES::TEXT_ATTRIBUTES(KIFONT::FONT* aFont) {}
|
||||||
|
|
||||||
|
// KIFONT stubs
|
||||||
|
#include <font/font.h>
|
||||||
|
|
||||||
|
namespace KIFONT {
|
||||||
|
|
||||||
|
FONT* FONT::GetFont(const wxString&, bool, bool, const std::vector<wxString>*, bool) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const METRICS& METRICS::Default() {
|
||||||
|
static METRICS m;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OUTLINE_GLYPH::Triangulate(std::function<void(const VECTOR2I&, const VECTOR2I&, const VECTOR2I&)>) const {}
|
||||||
|
|
||||||
|
void FONT::Draw(KIGFX::GAL*, const wxString&, const VECTOR2I&, const VECTOR2I&,
|
||||||
|
const TEXT_ATTRIBUTES&, const METRICS&) const {}
|
||||||
|
|
||||||
|
// STROKE_GLYPH stubs
|
||||||
|
STROKE_GLYPH::STROKE_GLYPH( const STROKE_GLYPH& aGlyph )
|
||||||
|
{
|
||||||
|
reserve( aGlyph.size() );
|
||||||
|
for( const std::vector<VECTOR2D>& pointList : aGlyph )
|
||||||
|
push_back( pointList );
|
||||||
|
m_boundingBox = aGlyph.m_boundingBox;
|
||||||
|
m_penIsDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void STROKE_GLYPH::AddPoint( const VECTOR2D& aPoint )
|
||||||
|
{
|
||||||
|
if( !m_penIsDown )
|
||||||
|
{
|
||||||
|
emplace_back();
|
||||||
|
back().reserve( 16 );
|
||||||
|
m_penIsDown = true;
|
||||||
|
}
|
||||||
|
back().push_back( aPoint );
|
||||||
|
}
|
||||||
|
|
||||||
|
void STROKE_GLYPH::RaisePen()
|
||||||
|
{
|
||||||
|
m_penIsDown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void STROKE_GLYPH::Finalize()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace KIFONT
|
||||||
|
|
||||||
|
// SHAPE_POLY_SET stub
|
||||||
|
#include <geometry/shape_poly_set.h>
|
||||||
|
bool SHAPE_POLY_SET::IsTriangulationUpToDate() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bezier curve stub
|
||||||
|
#include <bezier_curves.h>
|
||||||
|
void BEZIER_POLY::GetPoly(std::vector<VECTOR2D>& aOutput, double aMinSegLen) {
|
||||||
|
if (!m_ctrlPts.empty()) {
|
||||||
|
aOutput.push_back(m_ctrlPts.front());
|
||||||
|
aOutput.push_back(m_ctrlPts.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BITMAP_BASE stubs
|
||||||
|
#include <bitmap_base.h>
|
||||||
|
|
||||||
|
BITMAP_BASE::BITMAP_BASE( const VECTOR2I& pos )
|
||||||
|
{
|
||||||
|
m_scale = 1.0;
|
||||||
|
m_imageType = wxBITMAP_TYPE_PNG;
|
||||||
|
m_bitmap = nullptr;
|
||||||
|
m_image = nullptr;
|
||||||
|
m_originalImage = nullptr;
|
||||||
|
m_ppi = 91;
|
||||||
|
m_pixelSizeIu = 254000.0 / m_ppi;
|
||||||
|
m_isMirroredX = false;
|
||||||
|
m_isMirroredY = false;
|
||||||
|
m_rotation = ANGLE_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BITMAP_BASE::SetImage( const wxImage& aImage )
|
||||||
|
{
|
||||||
|
delete m_image;
|
||||||
|
delete m_originalImage;
|
||||||
|
delete m_bitmap;
|
||||||
|
|
||||||
|
m_image = new wxImage( aImage );
|
||||||
|
m_originalImage = new wxImage( aImage );
|
||||||
|
m_bitmap = new wxBitmap( *m_image );
|
||||||
|
m_imageType = wxBITMAP_TYPE_PNG;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// PGM_BASE and related stubs (minimal for GL_CONTEXT_MANAGER)
|
||||||
|
//=============================================================================
|
||||||
|
|
||||||
|
// Forward-declared types
|
||||||
|
class SETTINGS_MANAGER {};
|
||||||
|
class LIBRARY_MANAGER {};
|
||||||
|
class BACKGROUND_JOBS_MONITOR {};
|
||||||
|
class NOTIFICATIONS_MANAGER {};
|
||||||
|
|
||||||
|
#include <pgm_base.h>
|
||||||
|
#include <advanced_config.h>
|
||||||
|
#include <settings/environment.h>
|
||||||
|
|
||||||
|
// Minimal PGM_BASE subclass
|
||||||
|
class PGM_BASE_TEST : public PGM_BASE {
|
||||||
|
public:
|
||||||
|
PGM_BASE_TEST() : PGM_BASE() {}
|
||||||
|
void MacOpenFile(const wxString& aFileName) override {}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PGM_BASE_TEST* s_pgmInstance = nullptr;
|
||||||
|
|
||||||
|
PGM_BASE& Pgm() {
|
||||||
|
if (!s_pgmInstance) {
|
||||||
|
s_pgmInstance = new PGM_BASE_TEST();
|
||||||
|
}
|
||||||
|
return *s_pgmInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ADVANCED_CFG& ADVANCED_CFG::GetCfg() {
|
||||||
|
static ADVANCED_CFG instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ADVANCED_CFG::ADVANCED_CFG() {
|
||||||
|
m_ScreenDPI = 91;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetBuildVersion() {
|
||||||
|
return "8.0.0-wasm-test";
|
||||||
|
}
|
||||||
|
|
||||||
|
wxApp& PGM_BASE::App() {
|
||||||
|
return *wxTheApp;
|
||||||
|
}
|
||||||
|
|
||||||
|
COMMON_SETTINGS* PGM_BASE::GetCommonSettings() const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wxString& PGM_BASE::GetExecutablePath() const {
|
||||||
|
static wxString empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
ENV_VAR_MAP& PGM_BASE::GetLocalEnvVariables() const {
|
||||||
|
static ENV_VAR_MAP empty;
|
||||||
|
return empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PGM_BASE::SetLanguage(wxString&, bool) { return false; }
|
||||||
|
const wxString& PGM_BASE::GetTextEditor(bool) { static wxString s; return s; }
|
||||||
|
void PGM_BASE::SetTextEditor(const wxString&) {}
|
||||||
|
wxString PGM_BASE::GetLanguageTag() { return wxString(); }
|
||||||
|
void PGM_BASE::SetLanguagePath() {}
|
||||||
|
void PGM_BASE::ReadPdfBrowserInfos() {}
|
||||||
|
bool PGM_BASE::SetLocalEnvVariable(const wxString&, const wxString&) { return false; }
|
||||||
|
void PGM_BASE::SetLocalEnvVariables() {}
|
||||||
|
void PGM_BASE::WritePdfBrowserInfos() {}
|
||||||
|
void PGM_BASE::SetLanguageIdentifier(int) {}
|
||||||
|
const wxString PGM_BASE::AskUserForPreferredEditor(const wxString&) { return wxString(); }
|
||||||
|
|
||||||
|
PGM_BASE::PGM_BASE() {
|
||||||
|
m_singleton.Init();
|
||||||
|
}
|
||||||
|
PGM_BASE::~PGM_BASE() {}
|
||||||
|
|
||||||
|
// Singleton init
|
||||||
|
#include <singleton.h>
|
||||||
|
#include "webgl/gl_context_mgr.h"
|
||||||
|
|
||||||
|
void KICAD_SINGLETON::Init() {
|
||||||
|
m_GLContextManager = new GL_CONTEXT_MANAGER();
|
||||||
|
}
|
||||||
|
KICAD_SINGLETON::~KICAD_SINGLETON() {
|
||||||
|
delete m_GLContextManager;
|
||||||
|
m_GLContextManager = nullptr;
|
||||||
|
}
|
||||||
|
|
@ -32,6 +32,12 @@
|
||||||
#include <wx/platform.h>
|
#include <wx/platform.h>
|
||||||
|
|
||||||
#if defined( __EMSCRIPTEN__ )
|
#if defined( __EMSCRIPTEN__ )
|
||||||
|
// Prevent real GLEW header from being included (Emscripten has one too)
|
||||||
|
// We provide our own compatibility stubs below
|
||||||
|
#ifndef __glew_h__
|
||||||
|
#define __glew_h__
|
||||||
|
#endif
|
||||||
|
|
||||||
// WebGL2/GLES3: Modern shader functions (glUseProgram, etc.)
|
// WebGL2/GLES3: Modern shader functions (glUseProgram, etc.)
|
||||||
#include <GLES3/gl3.h>
|
#include <GLES3/gl3.h>
|
||||||
// Legacy GL emulation: glMatrixMode, glColor4d, glBegin/glEnd, etc.
|
// Legacy GL emulation: glMatrixMode, glColor4d, glBegin/glEnd, etc.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue