Initial project setup for KiCad WebAssembly port

- Add KiCad fork as git submodule
- Create project structure (patches, stubs, cmake, scripts, core, docs)
- Add knowledge base and implementation plan documentation
- Add README with project overview and goals

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Viktor Vaczi 2025-11-26 10:46:44 +01:00
commit 2d7c26feac
7 changed files with 1405 additions and 0 deletions

29
.gitignore vendored Normal file
View file

@ -0,0 +1,29 @@
# Build directories
build/
build-*/
cmake-build-*/
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# Compiled objects
*.o
*.obj
*.a
*.so
*.dylib
*.dll
# WebAssembly output
*.wasm
*.js.map
# macOS
.DS_Store
# Emscripten cache
.emscripten_cache/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "kicad"]
path = kicad
url = git@github.com:VV-EE/kicad-source-mirror.git

55
README.md Normal file
View file

@ -0,0 +1,55 @@
# KiCad WebAssembly Port
Experimental project to run KiCad's core logic in WebAssembly.
## Project Structure
```
kicad-wasm/
├── kicad/ # KiCad source (git submodule)
├── patches/ # Patches to make deps optional
├── stubs/ # Stub headers for disabled deps
├── cmake/ # CMake modules
├── scripts/ # Build and maintenance scripts
├── core/ # Extracted core library for Wasm
└── docs/ # Knowledge base and plans
```
## Goals
1. **Phase 1**: Build KiCad with optional deps disabled (curl, git, OCC, ngspice)
2. **Phase 2**: Extract core computation code as standalone library
3. **Phase 3**: Compile core to WebAssembly
4. **Phase 4**: Run native GUI with Wasm worker backend
5. **Phase 5**: Browser-based UI
## Documentation
- [Implementation Plan](docs/IMPLEMENTATION_PLAN.md)
- [Knowledge Base (Summary)](docs/KNOWLEDGE_BASE.md)
- [Knowledge Base (Full)](docs/KNOWLEDGE_BASE_FULL.md)
## Design Decisions
- **Memory**: Serialize/deserialize on every operation (proof of concept)
- **Threading**: Web Workers (browser-first design)
- **Updates**: Full board re-serialization
## Getting Started
```bash
# Clone with submodules
git clone --recursive <repo-url>
# Apply patches
./scripts/prepare.sh
# Build with optional deps disabled
mkdir build && cd build
cmake .. -DKICAD_USE_CURL=OFF -DKICAD_USE_GIT=OFF -DKICAD_USE_OCC=OFF
make -j$(nproc)
```
## License
KiCad is GPL-3.0. This wrapper/tooling follows the same license.

335
docs/IMPLEMENTATION_PLAN.md Normal file
View file

@ -0,0 +1,335 @@
# KiCad Wasm Port - Implementation Plan
## Goal
Run KiCad with native wxWidgets GUI, but with core logic (file parsing, geometry, DRC, routing) executing in a WebAssembly module. This validates the Wasm build before tackling the browser UI.
---
## Phase 1: Minimal Build with Stubs
**Objective**: Get KiCad compiling with optional deps disabled via preprocessor guards.
### 1.1 Create Project Structure
```
kicad-wasm/
├── kicad/ # Git submodule → upstream KiCad
├── patches/
│ └── 0001-optional-deps.patch
├── stubs/
│ └── include/
│ ├── git2.h # Minimal stub headers
│ └── curl/curl.h
├── cmake/
│ └── KicadWasmOptions.cmake
├── CMakeLists.txt
└── scripts/
├── prepare.sh # Apply patches
└── update-kicad.sh # Update to new KiCad version
```
### 1.2 Create the Patch
Add CMake options to `CMakeLists.txt` (after line 107):
```cmake
option( KICAD_USE_CURL "Enable network features" ON )
option( KICAD_USE_GIT "Enable git integration" ON )
option( KICAD_USE_OCC "Enable OpenCASCADE for STEP" ON )
option( KICAD_USE_NGSPICE "Enable SPICE simulation" ON )
option( KICAD_USE_DATABASE "Enable database libraries" ON )
```
Wrap `find_package` calls conditionally:
```cmake
if( KICAD_USE_CURL )
find_package( CURL REQUIRED )
add_compile_definitions( KICAD_USE_CURL )
else()
add_library( CURL::libcurl INTERFACE IMPORTED )
endif()
```
### 1.3 Create Stub Headers
Minimal headers that let code compile without the actual libraries:
```cpp
// stubs/include/git2.h
#pragma once
typedef struct git_repository git_repository;
// Stub functions return error codes
```
### 1.4 Add Preprocessor Guards to Source
Wrap implementations in `common/git/*.cpp`, `common/kicad_curl/*.cpp`:
```cpp
#ifdef KICAD_USE_GIT
// actual implementation
#else
// return error or throw "feature disabled"
#endif
```
### 1.5 Verify Native Build
```bash
cd kicad-wasm
./scripts/prepare.sh
mkdir build && cd build
cmake .. -DKICAD_USE_CURL=OFF -DKICAD_USE_GIT=OFF -DKICAD_USE_OCC=OFF
make -j$(nproc)
```
Confirm KiCad launches and can open/edit PCB files (without git/network/STEP features).
---
## Phase 2: Extract Core Library
**Objective**: Build core computation code as a standalone library that can be compiled to both native and Wasm.
### 2.1 Identify Core Components
Create `kicad-wasm/core/CMakeLists.txt` targeting:
| Component | Source Location | Notes |
|-----------|-----------------|-------|
| Math/Geometry | `libs/kimath/` | Pure C++, no deps |
| Core utilities | `libs/core/` | Pure C++ |
| KiCad file parser | `common/io/kicad/` | Needs minimal deps |
| S-expression parser | `libs/sexpr/` | Pure C++ |
| Board data model | `pcbnew/board*.cpp` | Extract carefully |
### 2.2 Define API Boundary
Create a C API for the core (easier Wasm interop than C++):
```cpp
// kicad-wasm/core/include/kicad_core_api.h
extern "C" {
// File operations
void* kicad_load_pcb(const char* data, size_t len);
void kicad_free_pcb(void* board);
char* kicad_serialize_pcb(void* board);
// Query
int kicad_get_track_count(void* board);
// Modification
void kicad_add_track(void* board, /* params */);
}
```
### 2.3 Build Core as Static Library
```cmake
# kicad-wasm/core/CMakeLists.txt
add_library(kicad_core STATIC
${KIMATH_SOURCES}
${SEXPR_SOURCES}
${IO_KICAD_SOURCES}
api/kicad_core_api.cpp
)
target_compile_definitions(kicad_core PRIVATE
KICAD_CORE_ONLY=1
)
```
---
## Phase 3: Compile Core to WebAssembly
**Objective**: Build `kicad_core.wasm` using Emscripten.
### 3.1 Emscripten Build
```bash
source /path/to/emsdk/emsdk_env.sh
cd kicad-wasm
mkdir build-wasm && cd build-wasm
emcmake cmake ../core \
-DCMAKE_BUILD_TYPE=Release \
-DKICAD_WASM_BUILD=ON
emmake make
```
### 3.2 Export Functions
```cmake
# For Emscripten
if(EMSCRIPTEN)
set_target_properties(kicad_core PROPERTIES
LINK_FLAGS "-s EXPORTED_FUNCTIONS='[_kicad_load_pcb,_kicad_free_pcb,...]' \
-s EXPORTED_RUNTIME_METHODS='[ccall,cwrap]' \
-s MODULARIZE=1 \
-s EXPORT_NAME='KicadCore'"
)
endif()
```
### 3.3 Test Wasm Module
Create a simple test that loads a `.kicad_pcb` file:
```javascript
// test/test_core.mjs
import KicadCore from './kicad_core.js';
const core = await KicadCore();
const pcbData = fs.readFileSync('test.kicad_pcb', 'utf8');
const board = core.ccall('kicad_load_pcb', 'number', ['string', 'number'],
[pcbData, pcbData.length]);
console.log('Track count:', core.ccall('kicad_get_track_count', 'number', ['number'], [board]));
```
---
## Phase 4: Native App with Wasm Worker
**Objective**: Run native KiCad GUI but delegate core operations to the Wasm module.
### 4.1 Choose Wasm Runtime
Options for running Wasm in native app:
- **Wasmtime** - Rust-based, mature, good C API
- **wasm3** - Small, fast interpreter
- **WAMR** - WebAssembly Micro Runtime, lightweight
Recommend: **Wasmtime** for development (better debugging), **wasm3** for size.
### 4.2 Create Wasm Bridge
```cpp
// kicad-wasm/bridge/wasm_bridge.h
class WasmBridge {
public:
WasmBridge(const std::string& wasmPath);
BOARD* LoadPCB(const std::string& data);
std::string SerializePCB(BOARD* board);
void RunDRC(BOARD* board);
private:
wasmtime_instance_t* m_instance;
};
```
### 4.3 Integrate with KiCad
Modify KiCad to optionally use the Wasm bridge:
```cpp
// In pcbnew loading code
#ifdef USE_WASM_CORE
auto board = WasmBridge::Get().LoadPCB(fileContent);
#else
auto board = IO_KICAD::Load(filename);
#endif
```
### 4.4 Validate Correctness
1. Load same PCB file via native code and via Wasm bridge
2. Compare serialized output (should be identical)
3. Run DRC via both paths, compare results
4. Profile performance difference
---
## Phase 5: Incremental Migration
**Objective**: Move more functionality to Wasm core, validate stability.
### Priority Order
1. **File I/O** - parsing and serialization
2. **DRC engine** - computationally intensive, isolated
3. **Router** - Push & Shove algorithms
4. **ERC** - schematic checks
### Validation Strategy
For each migrated component:
1. Keep both native and Wasm implementations
2. Add flag to switch between them
3. Run test suite with both
4. Benchmark performance
5. Remove native implementation once confident
---
## Milestones
| Milestone | Deliverable | Validation |
|-----------|-------------|------------|
| M1 | KiCad builds with deps disabled | Opens PCB files, basic editing works |
| M2 | Core library extracts cleanly | Compiles as standalone static lib |
| M3 | Core compiles to Wasm | Passes unit tests via Node.js |
| M4 | Native app loads Wasm worker | Can load/save PCB via Wasm bridge |
| M5 | DRC runs in Wasm | Results match native DRC |
---
## Design Decisions
### Memory Management
**Decision**: Serialize/deserialize on every operation
- Proof of concept - simplicity over performance
- Clean interface between GUI and Wasm core
- Easy to debug
- Browser-friendly (no shared memory complexity)
- Can optimize later if needed
```
GUI Wasm Core
│ │
│──serialize(board)─────────────▶│
│ │──process
│◀─────────────serialize(board)──│
│ │
```
### Threading Model
**Decision**: Web Workers
- Target is browser, design for it from the start
- Each heavy operation (DRC, routing) runs in dedicated worker
- Clean message-passing interface
- No shared memory complexity
```javascript
// Main thread spawns workers for heavy operations
const drcWorker = new Worker('drc-worker.js');
drcWorker.postMessage({ board: serializedBoard });
drcWorker.onmessage = (e) => updateUI(e.data.violations);
```
### Incremental Updates
**Decision**: Re-serialize entire board
- Simple for proof of concept
- Board files typically <10MB, acceptable latency
- Can add delta operations later if performance requires it
---
## Next Steps
1. Fork KiCad repo, set up as submodule
2. Write the minimal patch for optional deps
3. Create stub headers
4. Verify native build with features disabled
5. Begin core library extraction

136
docs/KNOWLEDGE_BASE.md Normal file
View file

@ -0,0 +1,136 @@
# KiCad WebAssembly Port - Knowledge Base
## Architecture Overview
KiCad is a modular EDA suite with these main components:
- **Project Manager** (`kicad/`) - launches other tools
- **PCB Editor** (`pcbnew/`) - board layout
- **Schematic Editor** (`eeschema/`) - circuit design
- **3D Viewer** (`3d-viewer/`) - 3D visualization
Shared code lives in:
- `common/` - shared library (kicommon), includes GUI, I/O, git, curl
- `libs/core/` - utilities
- `libs/kimath/` - geometry and math (pure C++, no deps)
## Key Dependencies
| Dependency | Purpose | Wasm Strategy |
|------------|---------|---------------|
| **wxWidgets** | All GUI | Keep native initially; replace with web UI later |
| **OpenGL** | 2D rendering via GAL | WebGL (Emscripten handles this) |
| **Cairo** | Fallback 2D rendering | Compile with Emscripten or skip |
| **OpenCASCADE** | STEP import/export, 3D | Disable for MVP; huge (~40MB) |
| **libcurl** | PCM, update check, HTTP libs | Stub out; replace with Fetch API |
| **libgit2** | Version control integration | Stub out; optional feature |
| **ngspice** | Circuit simulation | Stub out; separate concern |
| **nanoodbc** | Database libraries | Stub out; enterprise feature |
| **Freetype/HarfBuzz** | Font rendering | Compile with Emscripten (works) |
| **Boost** | Locale, unit tests | Minimize; locale needed for nanoodbc only |
## Graphics Abstraction Layer (GAL)
Location: `common/gal/`, `include/gal/`
KiCad abstracts rendering through GAL with two backends:
- `OPENGL_GAL` (`common/gal/opengl/`) - primary, uses GLSL shaders
- `CAIRO_GAL` (`common/gal/cairo/`) - fallback, vector graphics
Key files:
- `include/gal/graphics_abstraction_layer.h` - base interface
- `common/gal/opengl/opengl_gal.cpp` - OpenGL implementation
- `common/gal/shaders/` - GLSL shaders (need ES conversion for WebGL)
For Wasm: OpenGL ES subset via Emscripten maps to WebGL. Shaders need `#version` removal and precision qualifiers.
## File I/O System
Location: `common/io/`, `include/io/`
Plugin-based architecture supporting multiple formats:
- KiCad native (`.kicad_pcb`, `.kicad_sch`)
- Eagle, Altium, CADSTAR, EasyEDA imports
Key class: `IO_BASE` in `include/io/io_base.h`
Parsers are mostly pure C++ - good candidates for Wasm core.
## Python Scripting
Location: `scripting/`, `pcbnew/python/`
Uses SWIG to generate CPython bindings (~3,500 lines of `.i` files).
For Wasm: SWIG bindings won't work. Options:
1. Use Emscripten's `embind` instead
2. Use Pyodide with custom FFI
3. Skip Python for MVP
## Optional Features to Disable
These have minimal impact on core editing functionality:
| Feature | CMake Area | Files |
|---------|-----------|-------|
| Git integration | `common/git/` | 15 handler files |
| Network (PCM, updates) | `common/kicad_curl/`, `common/http_lib/` | ~5 files |
| Database libraries | `common/database/` | 2-3 files |
| STEP/3D export | `pcbnew/exporters/step/`, `plugins/3d/oce/` | Isolated |
| SPICE simulation | `eeschema/sim/` | Isolated subsystem |
## Build System Notes
Main CMake: `CMakeLists.txt`
Currently all deps are REQUIRED (lines 820-892). No options exist to disable curl/git/OCC.
Libraries link in `common/CMakeLists.txt:316-343`:
```cmake
target_link_libraries( kicommon
CURL::libcurl
${LIBGIT2_LIBRARIES}
...
)
```
## IPC/Communication
`KIWAY` system (`include/kiway.h`, `include/kiway_holder.h`) handles inter-frame communication. Frames inherit from `KIWAY_PLAYER`.
For Wasm worker architecture: This could be adapted for message-passing between native GUI and Wasm worker.
## Potential Problem Areas
1. **Threading**: KiCad uses threads for DRC, rendering. Wasm has Web Workers but different threading model.
2. **File dialogs**: `wxFileDialog` throughout - needs abstraction for browser File API.
3. **Memory**: Large boards can use 1GB+. Wasm has 4GB limit, but browser tabs may have lower practical limits.
4. **Clipboard**: Native clipboard integration in multiple places.
5. **Printing**: Cairo-based printing system won't work in browser.
## Files of Interest for Core Extraction
Pure computation, no GUI deps - good Wasm candidates:
- `libs/kimath/` - all geometry code
- `libs/core/` - utilities
- `common/io/kicad/` - native format parser
- `pcbnew/router/` - Push & Shove routing algorithms
- `pcbnew/drc/` - Design Rule Check engine
- `eeschema/erc/` - Electrical Rule Check
## Reference Paths
| Component | Path |
|-----------|------|
| Main CMake | `CMakeLists.txt` |
| Common library | `common/CMakeLists.txt` |
| GAL system | `common/gal/`, `include/gal/` |
| File I/O | `common/io/` |
| PCB data model | `pcbnew/board.h`, `pcbnew/footprint.h` |
| Git integration | `common/git/` |
| Curl wrapper | `common/kicad_curl/` |
| Python bindings | `scripting/`, `pcbnew/python/swig/` |
| 3D/STEP | `pcbnew/exporters/step/`, `3d-viewer/` |

846
docs/KNOWLEDGE_BASE_FULL.md Normal file
View file

@ -0,0 +1,846 @@
# KiCad WebAssembly Port - Complete Knowledge Base
## Table of Contents
1. [Architecture Overview](#architecture-overview)
2. [Dependencies Deep Dive](#dependencies-deep-dive)
3. [Graphics System (GAL)](#graphics-system-gal)
4. [File I/O System](#file-io-system)
5. [Python Scripting](#python-scripting)
6. [Build System](#build-system)
7. [Features to Disable](#features-to-disable)
8. [Code Extraction Candidates](#code-extraction-candidates)
9. [Technical Challenges](#technical-challenges)
10. [Design Decisions](#design-decisions)
11. [File Reference](#file-reference)
---
## Architecture Overview
### Main Applications
| Application | Directory | Purpose |
|-------------|-----------|---------|
| `kicad` | `kicad/` | Project manager, launcher |
| `pcbnew` | `pcbnew/` | PCB layout editor |
| `eeschema` | `eeschema/` | Schematic capture |
| `gerbview` | `gerbview/` | Gerber file viewer |
| `cvpcb` | `cvpcb/` | Component-footprint association |
| `bitmap2component` | `bitmap2component/` | Image to footprint converter |
| `pcb_calculator` | `pcb_calculator/` | Engineering calculators |
| `pagelayout_editor` | `pagelayout_editor/` | Drawing sheet editor |
| `kicad-cli` | `kicad/cli/` | Command-line interface |
### Shared Libraries
| Library | Location | Purpose |
|---------|----------|---------|
| `kicommon` | `common/` | Shared UI, I/O, utilities (SHARED lib) |
| `kigal` | `common/gal/` | Graphics Abstraction Layer (SHARED lib) |
| `kimath` | `libs/kimath/` | Geometry and math (STATIC lib) |
| `core` | `libs/core/` | Base utilities (STATIC lib) |
| `sexpr` | `libs/sexpr/` | S-expression parser (STATIC lib) |
| `kiplatform` | `libs/kiplatform/` | Platform abstraction |
### Plugin Architecture (KIFACE)
Each major application is built as both:
- Standalone executable
- KIFACE module (`.kiface` on Linux/Mac, `.dll` on Windows)
This allows:
- Standalone operation
- Project manager integration via dynamic loading
Key files:
- `include/kiway.h` - KIWAY system for inter-module communication
- `include/kiway_holder.h` - Mixin for frames that participate in KIWAY
- `include/kiway_player.h` - Frame base class (`KIWAY_PLAYER : public wxFrame`)
### Frame Hierarchy
```
wxFrame
└── KIWAY_PLAYER (include/kiway_player.h)
└── EDA_BASE_FRAME (include/eda_base_frame.h)
├── PCB_BASE_FRAME (pcbnew/)
│ └── PCB_EDIT_FRAME
├── SCH_BASE_FRAME (eeschema/)
│ └── SCH_EDIT_FRAME
└── ...other frames
```
---
## Dependencies Deep Dive
### wxWidgets (GUI Framework)
**Version**: 3.2.0+
**Components used**: `gl aui adv html core net base propgrid xml stc richtext webview`
**Location**: Found via `find_package(wxWidgets)` in `CMakeLists.txt:1089`
KiCad requires GTK3 port on Linux. All GUI code depends on wxWidgets.
Key wxWidgets classes used:
- `wxFrame`, `wxDialog`, `wxPanel` - windows
- `wxGLCanvas` - OpenGL context
- `wxFileDialog` - file selection
- `wxAuiManager` - dockable panes
- `wxPropertyGrid` - property editors
**Wasm strategy**: Keep native initially. Eventually replace with web framework (React/Vue/Svelte).
### OpenGL / GLEW
**Purpose**: Hardware-accelerated 2D rendering via GAL
**Location**: `common/gal/opengl/`
Uses OpenGL 2.1+ with shaders. GLEW handles extension loading.
**Wasm strategy**: Emscripten maps OpenGL ES to WebGL automatically. Need to:
- Use OpenGL ES subset
- Convert shaders to GLSL ES (remove `#version`, add `precision` qualifiers)
### Cairo
**Version**: 1.12+
**Purpose**: Software 2D rendering fallback, printing, PDF export
**Location**: `common/gal/cairo/`
**Wasm strategy**: Can compile Cairo with Emscripten. Or skip for MVP (OpenGL-only).
### OpenCASCADE (OCC/OCCT)
**Version**: 7.5.0+
**Purpose**:
- STEP file import/export (mechanical CAD interchange)
- 3D model loading for component visualization
- Boolean operations on 3D geometry
**Files using OCC**:
- `pcbnew/exporters/step/step_pcb_model.cpp` - STEP export
- `pcbnew/exporters/step/exporter_step.cpp` - Export orchestration
- `plugins/3d/oce/loadmodel.cpp` - 3D model loading
- `plugins/3d/oce/oce.cpp` - Plugin entry point
**Headers imported** (from `step_pcb_model.cpp`):
```cpp
#include <IGESCAFControl_Reader.hxx>
#include <STEPCAFControl_Reader.hxx>
#include <STEPCAFControl_Writer.hxx>
#include <TopoDS.hxx>
#include <XCAFDoc_ShapeTool.hxx>
// ... ~30 more OCC headers
```
**Wasm strategy**: Disable for MVP. OCC is huge (~40MB compiled). Alternative: [opencascade.js](https://github.com/nicholaseasmith/opencascade.js) exists but experimental.
### libcurl
**Purpose**: HTTP requests for:
1. Plugin Content Manager (PCM) - `kicad/pcm/pcm.cpp`
2. Update checker - `kicad/update_manager.cpp`
3. HTTP component libraries - `common/http_lib/http_lib_connection.cpp`
**Wrapper**: `common/kicad_curl/kicad_curl_easy.cpp` (~400 lines)
**Key class**: `KICAD_CURL_EASY`
```cpp
class KICAD_CURL_EASY {
void SetURL(const std::string& url);
void Perform();
std::string GetBuffer();
};
```
**Wasm strategy**: Stub out. Replace with Emscripten Fetch API or JavaScript fetch via embind.
### libgit2
**Version**: 1.5+
**Purpose**: Built-in version control for projects
**Files** (`common/git/`):
| File | Purpose |
|------|---------|
| `git_clone_handler.cpp` | Clone repositories |
| `git_commit_handler.cpp` | Create commits |
| `git_push_handler.cpp` | Push to remote |
| `git_pull_handler.cpp` | Pull from remote |
| `git_branch_handler.cpp` | Branch management |
| `git_status_handler.cpp` | Status display |
| `git_revert_handler.cpp` | Revert changes |
| `kicad_git_common.cpp` | Common utilities |
| `kigit_pcb_merge.cpp` | Custom PCB merge driver |
**UI integration**:
- Project tree shows git status icons
- Menus for git operations
- Conflict resolution dialogs
**Wasm strategy**: Stub out entirely. Optional feature. Could use isomorphic-git in browser later.
### ngspice
**Purpose**: SPICE circuit simulation in eeschema
**Location**: `eeschema/sim/`
**Wasm strategy**: Stub out. Simulation is a separate concern. Could compile ngspice to Wasm later.
### nanoodbc (ODBC)
**Purpose**: Database Libraries feature - fetch component data from SQL databases
**Location**: `common/database/database_connection.cpp`
**What it does**: Connects to external databases (MySQL, PostgreSQL, SQLite, SQL Server) to fetch component information instead of using local `.kicad_sym` files.
**Enterprise feature** - most users don't use this.
**Wasm strategy**: Stub out. Would need REST API backend in browser.
### Boost
**Version**: 1.71.0+
**Components**: `locale`, `unit_test_framework`
`boost::locale` is used by nanoodbc for Unicode handling.
**Wasm strategy**: Minimize. If we disable database libraries, we may not need boost::locale.
### Freetype / HarfBuzz / Fontconfig
**Purpose**: Text rendering with outline fonts
**Versions**: Freetype 2.11.1+, HarfBuzz (any), Fontconfig (any)
**Location**: `common/font/`
**Wasm strategy**: Compile with Emscripten. These work. May need to bundle fonts or use browser fonts.
### Protobuf
**Purpose**: IPC API for external tool integration
**Location**: `api/`
**Wasm strategy**: Can compile Protobuf to Wasm. Or stub out IPC API for MVP.
---
## Graphics System (GAL)
### Architecture
```
┌─────────────────────────────────────────┐
│ VIEW (common/view/) │
│ Manages what's visible, handles zoom │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ PAINTER (include/gal/painter.h) │
│ Converts board objects to draw calls │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ GAL (graphics_abstraction_layer) │
│ Abstract interface for drawing │
└─────────────────────────────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ OPENGL_GAL │ │ CAIRO_GAL │
│ (Hardware accel)│ │ (Software/print)│
└─────────────────┘ └─────────────────┘
```
### GAL Base Class
**File**: `include/gal/graphics_abstraction_layer.h`
```cpp
namespace KIGFX {
class GAL : public GAL_DISPLAY_OPTIONS_OBSERVER {
// Primitives
virtual void DrawLine(const VECTOR2D& start, const VECTOR2D& end);
virtual void DrawCircle(const VECTOR2D& center, double radius);
virtual void DrawArc(const VECTOR2D& center, double radius, ...);
virtual void DrawRectangle(const VECTOR2D& start, const VECTOR2D& end);
virtual void DrawPolygon(const std::deque<VECTOR2D>& points);
// State
virtual void SetFillColor(const COLOR4D& color);
virtual void SetStrokeColor(const COLOR4D& color);
virtual void SetLineWidth(float width);
// Transformations
virtual void Transform(const MATRIX3x3D& matrix);
virtual void Translate(const VECTOR2D& translation);
virtual void Scale(const VECTOR2D& scale);
virtual void Rotate(double angle);
// Layers
virtual void SetLayerDepth(double depth);
};
}
```
### OpenGL GAL
**Files**:
- `include/gal/opengl/opengl_gal.h`
- `common/gal/opengl/opengl_gal.cpp`
- `common/gal/opengl/shader.cpp` - GLSL shader management
- `common/gal/opengl/vertex_manager.cpp` - Vertex buffer management
- `common/gal/opengl/gpu_manager.cpp` - GPU memory management
- `common/gal/opengl/cached_container.cpp` - Geometry caching
**Canvas**: `HIDPI_GL_CANVAS` wraps `wxGLCanvas`
**Shader files** (`common/gal/shaders/`):
| File | Purpose |
|------|---------|
| `kicad_vert.glsl` | Main vertex shader |
| `kicad_frag.glsl` | Main fragment shader |
| `smaa_base.glsl` | SMAA antialiasing base |
| `smaa_pass_1_frag.glsl` | SMAA edge detection |
| `smaa_pass_2_frag.glsl` | SMAA blending weights |
| `smaa_pass_3_frag.glsl` | SMAA neighborhood blending |
Shaders are embedded as C strings at build time.
**For WebGL**: Need to convert shaders:
```glsl
// Before (desktop GLSL)
#version 120
varying vec4 color;
// After (WebGL/GLSL ES)
precision mediump float;
varying vec4 color;
```
### Cairo GAL
**Files**:
- `include/gal/cairo/cairo_gal.h`
- `common/gal/cairo/cairo_gal.cpp`
- `common/gal/cairo/cairo_compositor.cpp` - Layer compositing
- `common/gal/cairo/cairo_print.cpp` - Printing support
Used for:
- Software rendering fallback
- Printing
- PDF/SVG export
### Draw Panel
**File**: `include/class_draw_panel_gal.h`
`EDA_DRAW_PANEL_GAL` wraps GAL and handles:
- Mouse events
- Keyboard events
- Tool dispatching
- View management
---
## File I/O System
### Architecture
Plugin-based system with base class `IO_BASE`:
**File**: `include/io/io_base.h`
```cpp
class IO_BASE {
struct IO_FILE_DESC {
wxString m_Description;
std::vector<std::string> m_FileExtensions;
bool m_CanRead;
bool m_CanWrite;
};
virtual std::vector<IO_FILE_DESC> GetFileDescriptors();
virtual void SetReporter(REPORTER* reporter);
virtual void SetProgressReporter(PROGRESS_REPORTER* reporter);
};
```
### Format Plugins
**KiCad Native** (`common/io/kicad/`):
- S-expression based format
- `.kicad_pcb`, `.kicad_sch`, `.kicad_sym`, `.kicad_mod`
**Import Plugins** (`common/io/`):
| Plugin | Location | Formats |
|--------|----------|---------|
| Eagle | `common/io/eagle/` | `.brd`, `.sch` |
| Altium | `common/io/altium/` | `.PcbDoc`, `.SchDoc` |
| CADSTAR | `common/io/cadstar/` | `.cpa`, `.csa` |
| EasyEDA | `common/io/easyeda/` | `.json` |
| EasyEDA Pro | `common/io/easyedapro/` | `.epro` |
### S-Expression Parser
**Location**: `libs/sexpr/`
KiCad native files use S-expressions:
```lisp
(kicad_pcb (version 20221018)
(generator pcbnew)
(layers
(0 "F.Cu" signal)
(31 "B.Cu" signal))
(footprint "Package_SO:SOIC-8"
(at 100 100)
(pad "1" smd rect (at -1.905 -2.475) (size 0.6 1.5))))
```
Parser is pure C++, good candidate for Wasm.
### PCB Data Model
**Key classes** (in `pcbnew/`):
| Class | File | Purpose |
|-------|------|---------|
| `BOARD` | `board.h` | Top-level PCB container |
| `FOOTPRINT` | `footprint.h` | Component footprint |
| `PAD` | `pad.h` | Footprint pad |
| `PCB_TRACK` | `pcb_track.h` | Trace segment |
| `PCB_VIA` | `pcb_track.h` | Via |
| `ZONE` | `zone.h` | Copper pour |
| `PCB_SHAPE` | `pcb_shape.h` | Graphical shape |
| `PCB_TEXT` | `pcb_text.h` | Text |
**Hierarchy**:
```
BOARD
├── FOOTPRINT[]
│ ├── PAD[]
│ ├── PCB_SHAPE[]
│ └── PCB_TEXT[]
├── PCB_TRACK[]
├── PCB_VIA[]
├── ZONE[]
├── PCB_SHAPE[]
└── PCB_TEXT[]
```
---
## Python Scripting
### SWIG Bindings
**Location**: `scripting/`, `common/swig/`, `pcbnew/python/swig/`
**Interface files** (`.i`):
| File | Lines | Purpose |
|------|-------|---------|
| `kicadplugins.i` | 695 | Plugin framework |
| `wx.i` | 348 | wxWidgets types |
| `board.i` | 201 | BOARD class |
| `board_item.i` | 222 | Base item class |
| `footprint.i` | 200 | FOOTPRINT class |
| `pad.i` | 128 | PAD class |
| `pcbnew.i` | 148 | Main pcbnew module |
| Others | ~1600 | Various classes |
| **Total** | ~3500 | |
### How SWIG Works
1. SWIG reads `.i` interface files
2. Generates C++ wrapper code for CPython
3. Wrapper compiled to shared library (`_pcbnew.so`)
4. Python imports the module
```python
import pcbnew
board = pcbnew.GetBoard()
for track in board.GetTracks():
print(track.GetStart(), track.GetEnd())
```
### Plugin Types
| Type | Purpose | Interface |
|------|---------|-----------|
| FootprintWizard | Generate footprints programmatically | `FootprintWizardPlugin` |
| ActionPlugin | Custom toolbar actions | `ActionPlugin` |
| FilePlugin | Custom file formats | `FilePlugin` |
### Wasm Strategy
SWIG generates CPython-specific code. Options for Wasm:
1. **Emscripten embind**: Rewrite bindings (~3500 lines to port)
```cpp
#include <emscripten/bind.h>
EMSCRIPTEN_BINDINGS(pcbnew) {
class_<BOARD>("Board")
.function("GetTracks", &BOARD::Tracks);
}
```
2. **Pyodide**: If we want Python in browser, use Pyodide with custom FFI
3. **Skip for MVP**: Python scripting is optional for basic editing
---
## Build System
### Main CMakeLists.txt Structure
```
CMakeLists.txt
├── Project setup (lines 1-100)
├── Options (lines 110-290)
│ ├── KICAD_SPICE_QA
│ ├── KICAD_USE_SENTRY
│ ├── KICAD_BUILD_I18N
│ ├── KICAD_BUILD_QA_TESTS
│ ├── KICAD_SCRIPTING_WXPYTHON
│ ├── KICAD_UPDATE_CHECK
│ └── ... more options
├── Compiler setup (lines 300-800)
├── Dependencies (lines 800-1200)
│ ├── find_package(ZLIB)
│ ├── find_package(CURL) # Line 825 - REQUIRED
│ ├── find_package(libgit2) # Line 842 - REQUIRED
│ ├── find_package(ngspice) # Line 877 - REQUIRED
│ ├── find_package(OCC) # Line 880 - FATAL if not found
│ └── ... more deps
├── wxWidgets setup (lines 1080-1140)
└── Subdirectories (lines 1250+)
```
### Common Library Build
**File**: `common/CMakeLists.txt`
```cmake
# KICOMMON_SRCS includes:
# - git/*.cpp (lines 75-95)
# - kicad_curl/*.cpp (lines 140-141)
# - database/*.cpp
# - All UI code
target_link_libraries(kicommon
CURL::libcurl # Line 325
${LIBGIT2_LIBRARIES} # Line 330
# ...
)
```
### Adding CMake Options for Optional Deps
Need to add after line 107:
```cmake
option( KICAD_USE_CURL "Enable network features" ON )
option( KICAD_USE_GIT "Enable git integration" ON )
option( KICAD_USE_OCC "Enable STEP/3D via OpenCASCADE" ON )
option( KICAD_USE_NGSPICE "Enable SPICE simulation" ON )
option( KICAD_USE_DATABASE "Enable database libraries" ON )
```
Then wrap find_package calls:
```cmake
if( KICAD_USE_CURL )
find_package( CURL REQUIRED )
add_compile_definitions( KICAD_USE_CURL )
else()
add_library( CURL::libcurl INTERFACE IMPORTED )
endif()
```
---
## Features to Disable
### Git Integration
**Files to stub** (`common/git/`):
- `git_add_to_index_handler.cpp`
- `git_branch_handler.cpp`
- `git_clone_handler.cpp`
- `git_commit_handler.cpp`
- `git_config_handler.cpp`
- `git_compare_handler.cpp`
- `git_init_handler.cpp`
- `git_pull_handler.cpp`
- `git_push_handler.cpp`
- `git_remove_from_index_handler.cpp`
- `git_remove_vcs_handler.cpp`
- `git_resolve_conflict_handler.cpp`
- `git_revert_handler.cpp`
- `git_status_handler.cpp`
- `git_switch_branch_handler.cpp`
- `git_sync_handler.cpp`
- `kicad_git_common.cpp`
- `git_backend.cpp`
- `libgit_backend.cpp`
- `project_git_utils.cpp`
**Stub header needed**: `stubs/include/git2.h`
### Network Features (curl)
**Files to stub** (`common/kicad_curl/`):
- `kicad_curl.cpp`
- `kicad_curl_easy.cpp`
**Files to stub** (`common/http_lib/`):
- `http_lib_connection.cpp`
**Files affected** (`kicad/`):
- `pcm/pcm.cpp` - Plugin Content Manager
- `pcm/pcm_task_manager.cpp`
- `update_manager.cpp`
**Stub header needed**: `stubs/include/curl/curl.h`
### OpenCASCADE (STEP/3D)
**Files to exclude**:
- `pcbnew/exporters/step/*.cpp`
- `plugins/3d/oce/*.cpp`
**Approach**: Don't build these targets rather than stubbing.
### SPICE Simulation
**Files affected**: `eeschema/sim/`
**Approach**: Disable simulator UI, don't build sim targets.
### Database Libraries
**Files to stub** (`common/database/`):
- `database_connection.cpp`
- `database_cache.cpp`
---
## Code Extraction Candidates
### Tier 1: Pure Computation (No Dependencies)
| Component | Location | Lines (approx) | Notes |
|-----------|----------|----------------|-------|
| Math library | `libs/kimath/src/` | ~5000 | Vectors, matrices, geometry |
| Core utilities | `libs/core/src/` | ~2000 | String utils, exceptions |
| S-expr parser | `libs/sexpr/` | ~1500 | Pure parsing |
### Tier 2: File I/O (Minimal Dependencies)
| Component | Location | Notes |
|-----------|----------|-------|
| KiCad PCB parser | `common/io/kicad/` | Needs kimath, sexpr |
| Board data model | `pcbnew/*.cpp` | Core classes only |
| Schematic parser | `common/io/eeschema/` | Needs kimath |
### Tier 3: Algorithms (May Need Adaptation)
| Component | Location | Notes |
|-----------|----------|-------|
| DRC engine | `pcbnew/drc/` | May use threading |
| Router | `pcbnew/router/` | Push & Shove |
| ERC | `eeschema/erc/` | Electrical checks |
| Connectivity | `pcbnew/connectivity/` | Net analysis |
---
## Technical Challenges
### Threading
**Current usage**:
- DRC runs checks in parallel
- Router uses threading for optimization
- Background jobs system
**Wasm limitation**: Single-threaded by default.
**Solutions**:
1. **Web Workers**: Spawn separate Wasm instances
2. **Wasm threads** (experimental): SharedArrayBuffer + pthreads
3. **Sequential fallback**: Slower but works
**Decision**: Use Web Workers. Design for async message-passing.
### Memory Management
**Challenge**: Need to share board state between native GUI and Wasm core.
**Options**:
1. **Serialize/deserialize**: Simple, proof of concept
2. **Shared memory**: Complex, requires careful synchronization
3. **Authoritative Wasm copy**: GUI requests views
**Decision**: Serialize/deserialize. Proof of concept, simplicity over performance.
### File Access
**Native**: Direct filesystem, `wxFileDialog`
**Browser**: No filesystem access without user interaction
**Solutions**:
- Emscripten virtual filesystem (MEMFS, IDBFS)
- File System Access API (Chrome)
- IndexedDB for persistence
- Drag & drop / file picker
### Clipboard
**Native**: `wxClipboard`, platform integration
**Browser**: Async Clipboard API (permissions required)
### Fonts
**Native**: System fonts via Fontconfig
**Browser**: Bundle fonts or use CSS fonts
---
## Design Decisions
### Memory Management
**Decision**: Serialize/deserialize on every operation
**Rationale**:
- Proof of concept phase
- Simplicity over performance
- Clean interface between GUI and core
- Easy to debug
- Browser-friendly (no shared memory complexity)
**Implementation**:
```cpp
// GUI → Wasm: Send operation + serialized state
std::string boardJson = SerializeBoard(board);
wasmCore.ApplyOperation(boardJson, operation);
std::string newBoardJson = wasmCore.GetBoardState();
board = DeserializeBoard(newBoardJson);
```
### Threading Model
**Decision**: Web Workers
**Rationale**:
- Target is browser
- Clean message-passing interface
- Each worker is isolated Wasm instance
- No shared memory complexity
**Implementation**:
```javascript
// Main thread
const worker = new Worker('kicad-core-worker.js');
worker.postMessage({ type: 'runDRC', board: boardData });
worker.onmessage = (e) => { handleDRCResults(e.data); };
// Worker
importScripts('kicad_core.js');
onmessage = async (e) => {
const core = await KicadCore();
if (e.data.type === 'runDRC') {
const results = core.runDRC(e.data.board);
postMessage(results);
}
};
```
### Incremental Updates
**Decision**: Re-serialize entire board (for now)
**Rationale**:
- Simple implementation
- Board files are typically <10MB
- Performance acceptable for proof of concept
- Can optimize later with deltas if needed
---
## File Reference
### Core Headers
| Purpose | File |
|---------|------|
| GAL interface | `include/gal/graphics_abstraction_layer.h` |
| OpenGL GAL | `include/gal/opengl/opengl_gal.h` |
| Cairo GAL | `include/gal/cairo/cairo_gal.h` |
| Draw panel | `include/class_draw_panel_gal.h` |
| Base frame | `include/eda_base_frame.h` |
| KIWAY | `include/kiway.h` |
| KIWAY holder | `include/kiway_holder.h` |
| I/O base | `include/io/io_base.h` |
| Board | `pcbnew/board.h` |
| Footprint | `pcbnew/footprint.h` |
| Track | `pcbnew/pcb_track.h` |
### Build Files
| Purpose | File |
|---------|------|
| Main build | `CMakeLists.txt` |
| Common lib | `common/CMakeLists.txt` |
| GAL lib | `common/gal/CMakeLists.txt` |
| PCBnew | `pcbnew/CMakeLists.txt` |
| Eeschema | `eeschema/CMakeLists.txt` |
| 3D viewer | `3d-viewer/CMakeLists.txt` |
### SWIG Bindings
| Purpose | File |
|---------|------|
| Main entry | `scripting/kicadplugins.i` |
| Common | `common/swig/kicad.i` |
| wxWidgets | `common/swig/wx.i` |
| Math | `common/swig/math.i` |
| Shapes | `common/swig/shape.i` |
| Board | `pcbnew/python/swig/board.i` |
| PCBnew main | `pcbnew/python/swig/pcbnew.i` |
### Git Integration
| Purpose | File |
|---------|------|
| Common class | `common/git/kicad_git_common.cpp` |
| Clone | `common/git/git_clone_handler.cpp` |
| Commit | `common/git/git_commit_handler.cpp` |
| Push | `common/git/git_push_handler.cpp` |
| Pull | `common/git/git_pull_handler.cpp` |
| Status | `common/git/git_status_handler.cpp` |
| PCB merge | `pcbnew/git/kigit_pcb_merge.cpp` |
### Curl/Network
| Purpose | File |
|---------|------|
| Curl wrapper | `common/kicad_curl/kicad_curl_easy.cpp` |
| Curl init | `common/kicad_curl/kicad_curl.cpp` |
| HTTP lib | `common/http_lib/http_lib_connection.cpp` |
| PCM | `kicad/pcm/pcm.cpp` |
| Updates | `kicad/update_manager.cpp` |
### OpenCASCADE/STEP
| Purpose | File |
|---------|------|
| STEP model | `pcbnew/exporters/step/step_pcb_model.cpp` |
| STEP export | `pcbnew/exporters/step/exporter_step.cpp` |
| OCC plugin | `plugins/3d/oce/oce.cpp` |
| Model loader | `plugins/3d/oce/loadmodel.cpp` |

1
kicad Submodule

@ -0,0 +1 @@
Subproject commit 4bfed3f1746e8cc0a7d942767770f56fa28b393c