Add standalone core library with kimath and sexpr (Phase 2 Step 1)
Create standalone build system for KiCad's core computation libraries that compiles without wxWidgets, targeting both native and WebAssembly. Key additions: - wx_shim.h: ~250 lines replacing wx utilities with standard C++ - wxString class with Format() method - wxFFile for file I/O - wxASSERT, wxCHECK, wxFAIL_MSG macros - wxLog stubs - Stub headers: wx/debug.h, wx/log.h, wx/string.h, wx/file.h, etc. - config.h, advanced_config.h: Platform and triangulation config - CMakeLists.txt: Builds kimath, sexpr, clipper2, rtree Build results: - Native: libkimath.a (1.4MB), libsexpr.a, test passes - WASM: test_kimath.wasm (867KB), runs in Node.js Test verifies: - VECTOR2I, SEG, SHAPE_POLY_SET geometry operations - S-expression parsing of .kicad_pcb format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
f1526d8c24
commit
92faf7c082
17 changed files with 1068 additions and 0 deletions
3
core/.gitignore
vendored
Normal file
3
core/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Build directories
|
||||
build/
|
||||
build-wasm/
|
||||
177
core/CMakeLists.txt
Normal file
177
core/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
project(kicad_core VERSION 1.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Path to KiCad source (submodule)
|
||||
set(KICAD_SOURCE "${CMAKE_SOURCE_DIR}/../kicad" CACHE PATH "Path to KiCad source")
|
||||
|
||||
# Verify KiCad source exists
|
||||
if(NOT EXISTS "${KICAD_SOURCE}/libs/kimath")
|
||||
message(FATAL_ERROR "KiCad source not found at ${KICAD_SOURCE}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Using KiCad source at: ${KICAD_SOURCE}")
|
||||
|
||||
# =============================================================================
|
||||
# Options
|
||||
# =============================================================================
|
||||
option(KICAD_CORE_VERBOSE "Enable verbose logging in core library" OFF)
|
||||
|
||||
# =============================================================================
|
||||
# Clipper2 library (for polygon boolean operations)
|
||||
# =============================================================================
|
||||
set(CLIPPER2_DIR "${KICAD_SOURCE}/thirdparty/clipper2")
|
||||
|
||||
add_library(clipper2 STATIC
|
||||
${CLIPPER2_DIR}/Clipper2Lib/src/clipper.engine.cpp
|
||||
${CLIPPER2_DIR}/Clipper2Lib/src/clipper.offset.cpp
|
||||
${CLIPPER2_DIR}/Clipper2Lib/src/clipper.rectclip.cpp
|
||||
)
|
||||
|
||||
target_include_directories(clipper2 PUBLIC
|
||||
${CLIPPER2_DIR}/Clipper2Lib/include
|
||||
)
|
||||
|
||||
target_compile_definitions(clipper2 PUBLIC USINGZ)
|
||||
|
||||
# =============================================================================
|
||||
# RTree library (header-only spatial index)
|
||||
# =============================================================================
|
||||
add_library(rtree INTERFACE)
|
||||
target_include_directories(rtree INTERFACE
|
||||
${KICAD_SOURCE}/thirdparty/rtree
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# Core utilities library (minimal version without wx_stl_compat)
|
||||
# =============================================================================
|
||||
add_library(kicad_core_utils STATIC
|
||||
${KICAD_SOURCE}/libs/core/base64.cpp
|
||||
${KICAD_SOURCE}/libs/core/observable.cpp
|
||||
${KICAD_SOURCE}/libs/core/profile.cpp
|
||||
# Skip utf8.cpp and wx_stl_compat.cpp - they have heavy wx dependencies
|
||||
)
|
||||
|
||||
target_include_directories(kicad_core_utils PUBLIC
|
||||
${KICAD_SOURCE}/libs/core/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include # For wx_shim.h
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# S-expression library (parser for .kicad_pcb files)
|
||||
# =============================================================================
|
||||
add_library(sexpr STATIC
|
||||
${KICAD_SOURCE}/libs/sexpr/sexpr.cpp
|
||||
${KICAD_SOURCE}/libs/sexpr/sexpr_parser.cpp
|
||||
)
|
||||
|
||||
target_include_directories(sexpr PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include # wx_shim.h - FIRST!
|
||||
${KICAD_SOURCE}/libs/sexpr/include
|
||||
${KICAD_SOURCE}/libs/core/include
|
||||
)
|
||||
|
||||
target_link_libraries(sexpr PUBLIC kicad_core_utils)
|
||||
|
||||
# =============================================================================
|
||||
# KiMath library (geometry and math)
|
||||
# =============================================================================
|
||||
set(KIMATH_SRCS
|
||||
${KICAD_SOURCE}/libs/kimath/src/bezier_curves.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/convert_basic_shapes_to_polygon.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/md5_hash.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/transform.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/trigo.cpp
|
||||
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/corner_operations.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/distribute.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/eda_angle.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/ellipse.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/circle.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/convex_hull.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/direction_45.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/geometry_utils.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/half_line.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/intersection.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/line.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/nearest.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/oval.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/roundrect.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/seg.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_arc.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_collisions.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_compound.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_file_io.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_line_chain.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_nearest_points.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_poly_set.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_rect.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_segment.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/vector_utils.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/shape_utils.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/geometry/vertex_set.cpp
|
||||
|
||||
${KICAD_SOURCE}/libs/kimath/src/math/vector2.cpp
|
||||
${KICAD_SOURCE}/libs/kimath/src/math/util.cpp
|
||||
)
|
||||
|
||||
add_library(kimath STATIC ${KIMATH_SRCS})
|
||||
|
||||
target_include_directories(kimath PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include # wx_shim.h and wx/ stubs - FIRST!
|
||||
${KICAD_SOURCE}/libs/kimath/include
|
||||
${KICAD_SOURCE}/libs/core/include
|
||||
${KICAD_SOURCE}/include # Main KiCad includes (units, etc.)
|
||||
)
|
||||
|
||||
target_link_libraries(kimath PUBLIC
|
||||
kicad_core_utils
|
||||
clipper2
|
||||
rtree
|
||||
)
|
||||
|
||||
# Define KICAD_CORE_ONLY to enable any conditional compilation
|
||||
target_compile_definitions(kimath PRIVATE
|
||||
KICAD_CORE_ONLY=1
|
||||
)
|
||||
|
||||
if(KICAD_CORE_VERBOSE)
|
||||
target_compile_definitions(kimath PRIVATE KICAD_CORE_VERBOSE=1)
|
||||
endif()
|
||||
|
||||
# =============================================================================
|
||||
# Combined kicad_core library
|
||||
# =============================================================================
|
||||
add_library(kicad_core INTERFACE)
|
||||
target_link_libraries(kicad_core INTERFACE
|
||||
kimath
|
||||
sexpr
|
||||
kicad_core_utils
|
||||
clipper2
|
||||
rtree
|
||||
)
|
||||
|
||||
# =============================================================================
|
||||
# Test executable (optional)
|
||||
# =============================================================================
|
||||
option(BUILD_TESTS "Build test executables" ON)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_executable(test_kimath
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/test_kimath.cpp
|
||||
)
|
||||
target_link_libraries(test_kimath PRIVATE kimath sexpr)
|
||||
endif()
|
||||
|
||||
# =============================================================================
|
||||
# Summary
|
||||
# =============================================================================
|
||||
message(STATUS "")
|
||||
message(STATUS "kicad_core configuration:")
|
||||
message(STATUS " KiCad source: ${KICAD_SOURCE}")
|
||||
message(STATUS " Verbose logging: ${KICAD_CORE_VERBOSE}")
|
||||
message(STATUS " Build tests: ${BUILD_TESTS}")
|
||||
message(STATUS "")
|
||||
70
core/include/advanced_config.h
Normal file
70
core/include/advanced_config.h
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* advanced_config.h - Stub for kicad-core standalone build
|
||||
*
|
||||
* Provides default configuration values without wx dependencies
|
||||
*/
|
||||
|
||||
#ifndef KICAD_CORE_ADVANCED_CONFIG_H
|
||||
#define KICAD_CORE_ADVANCED_CONFIG_H
|
||||
|
||||
// Stub ADVANCED_CFG class with default values
|
||||
class ADVANCED_CFG {
|
||||
public:
|
||||
// Triangulation settings (defaults from KiCad)
|
||||
int m_TriangulateSimplificationLevel = 50;
|
||||
double m_TriangulateMinimumArea = 1000.0;
|
||||
|
||||
// Other settings that might be accessed
|
||||
bool m_EnableLibWithText = false;
|
||||
bool m_EnableEeschemaPrintCairo = false;
|
||||
int m_UpdateUIEventInterval = 0;
|
||||
double m_DrawArcAccuracy = 0.005;
|
||||
double m_DrawArcCenterMaxAngle = 50.0;
|
||||
int m_MaxUndoItems = 0;
|
||||
double m_MinPlotPenWidth = 0.0;
|
||||
int m_3DRT_BevelExtentFactor = 1;
|
||||
int m_3DRT_BevelHeight_um = 30;
|
||||
bool m_ShowRepairSchematic = false;
|
||||
bool m_ShowPropertiesPanel = true;
|
||||
bool m_ShowEventCounters = false;
|
||||
bool m_AllowManualCanvasScale = false;
|
||||
double m_CanvasScale = 1.0;
|
||||
bool m_CompactSave = false;
|
||||
int m_CoroutineStackSize = 0;
|
||||
int m_DrawBoundingBoxes = 0;
|
||||
bool m_ShowPcbnewExportNetlist = false;
|
||||
bool m_Skip3DModelFileCache = false;
|
||||
bool m_Skip3DModelMemoryCache = false;
|
||||
bool m_HideVersionFromTitle = false;
|
||||
bool m_TraceMasks = false;
|
||||
bool m_ShowRouterDebugGraphics = false;
|
||||
bool m_ExtraZoneDisplayModes = false;
|
||||
double m_MinClrDistance = 0.0;
|
||||
bool m_DebugZoneFiller = false;
|
||||
bool m_DebugPDFWriter = false;
|
||||
int m_HotkeysDumper = 0;
|
||||
bool m_DrawTriangulationOutlines = false;
|
||||
bool m_StrokeTriangulation = false;
|
||||
bool m_ExtraClearance = false;
|
||||
double m_SmallDrillMarkSize = 0.0;
|
||||
int m_HoleijWallThickness = 0;
|
||||
int m_MaxTangentAngleDeviation = 1;
|
||||
int m_MaxClearanceDistanceFactor = 2;
|
||||
int m_ViaijFillMinRatio = 0;
|
||||
bool m_RealtimeConnectivity = true;
|
||||
bool m_EnableCacheFriendlyFracture = true;
|
||||
double m_FontErrorSize = 0.0;
|
||||
double m_OcctVerbosity = 0.0;
|
||||
bool m_Use3DConnexionDriver = false;
|
||||
bool m_IncrementalConnectivity = true;
|
||||
|
||||
static ADVANCED_CFG& GetCfg() {
|
||||
static ADVANCED_CFG instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
private:
|
||||
ADVANCED_CFG() = default;
|
||||
};
|
||||
|
||||
#endif // KICAD_CORE_ADVANCED_CONFIG_H
|
||||
31
core/include/config.h
Normal file
31
core/include/config.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* config.h - Minimal config for kicad-core standalone build
|
||||
*
|
||||
* This replaces the auto-generated config.h from KiCad's CMake
|
||||
*/
|
||||
|
||||
#ifndef KICAD_CORE_CONFIG_H
|
||||
#define KICAD_CORE_CONFIG_H
|
||||
|
||||
// Platform detection for timing functions
|
||||
#if defined(_WIN32)
|
||||
// Windows uses GetSystemTimeAsFileTime
|
||||
#elif defined(__APPLE__) || defined(__linux__) || defined(__unix__)
|
||||
#define HAVE_CLOCK_GETTIME 1
|
||||
#else
|
||||
#define HAVE_GETTIMEOFDAY_FUNC 1
|
||||
#endif
|
||||
|
||||
// Version info
|
||||
#define KICAD_MAJOR_VERSION 8
|
||||
#define KICAD_MINOR_VERSION 0
|
||||
#define KICAD_PATCH_VERSION 0
|
||||
#define KICAD_VERSION_FULL "8.0.0-wasm"
|
||||
|
||||
// Feature flags - all disabled for core-only build
|
||||
#define KICAD_USE_CURL 0
|
||||
#define KICAD_USE_GIT 0
|
||||
#define KICAD_USE_OCC 0
|
||||
#define KICAD_USE_NGSPICE 0
|
||||
|
||||
#endif // KICAD_CORE_CONFIG_H
|
||||
19
core/include/string_utils.h
Normal file
19
core/include/string_utils.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* string_utils.h - Minimal stub for kicad-core
|
||||
*
|
||||
* The sexpr library only uses From_UTF8() which is defined in wx_shim.h
|
||||
*/
|
||||
|
||||
#ifndef KICAD_CORE_STRING_UTILS_H
|
||||
#define KICAD_CORE_STRING_UTILS_H
|
||||
|
||||
#include "wx_shim.h"
|
||||
|
||||
// From_UTF8 is already defined in wx_shim.h
|
||||
|
||||
// Additional string utilities that might be needed
|
||||
inline std::string To_UTF8(const wxString& str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
#endif // KICAD_CORE_STRING_UTILS_H
|
||||
27
core/include/wx/confbase.h
Normal file
27
core/include/wx/confbase.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Stub wx/confbase.h - minimal config base for kicad-core
|
||||
#ifndef _WX_CONFBASE_H_
|
||||
#define _WX_CONFBASE_H_
|
||||
|
||||
#include "wx_shim.h"
|
||||
|
||||
// Minimal wxConfigBase stub
|
||||
class wxConfigBase {
|
||||
public:
|
||||
virtual ~wxConfigBase() = default;
|
||||
|
||||
// Minimal interface - returns false/empty for everything
|
||||
virtual bool Read(const wxString& key, wxString* str) const { return false; }
|
||||
virtual bool Read(const wxString& key, long* val) const { return false; }
|
||||
virtual bool Read(const wxString& key, double* val) const { return false; }
|
||||
virtual bool Read(const wxString& key, bool* val) const { return false; }
|
||||
|
||||
virtual bool Write(const wxString& key, const wxString& value) { return false; }
|
||||
virtual bool Write(const wxString& key, long value) { return false; }
|
||||
virtual bool Write(const wxString& key, double value) { return false; }
|
||||
virtual bool Write(const wxString& key, bool value) { return false; }
|
||||
|
||||
static wxConfigBase* Get(bool createOnDemand = true) { return nullptr; }
|
||||
static wxConfigBase* Set(wxConfigBase* config) { return nullptr; }
|
||||
};
|
||||
|
||||
#endif // _WX_CONFBASE_H_
|
||||
5
core/include/wx/debug.h
Normal file
5
core/include/wx/debug.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Stub wx/debug.h - redirects to wx_shim.h
|
||||
#ifndef _WX_DEBUG_H_
|
||||
#define _WX_DEBUG_H_
|
||||
#include "wx_shim.h"
|
||||
#endif
|
||||
5
core/include/wx/ffile.h
Normal file
5
core/include/wx/ffile.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Stub wx/ffile.h - redirects to wx_shim.h
|
||||
#ifndef _WX_FFILE_H_
|
||||
#define _WX_FFILE_H_
|
||||
#include "wx_shim.h"
|
||||
#endif
|
||||
5
core/include/wx/file.h
Normal file
5
core/include/wx/file.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Stub wx/file.h - redirects to wx_shim.h
|
||||
#ifndef _WX_FILE_H_
|
||||
#define _WX_FILE_H_
|
||||
#include "wx_shim.h"
|
||||
#endif
|
||||
5
core/include/wx/log.h
Normal file
5
core/include/wx/log.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Stub wx/log.h - redirects to wx_shim.h
|
||||
#ifndef _WX_LOG_H_
|
||||
#define _WX_LOG_H_
|
||||
#include "wx_shim.h"
|
||||
#endif
|
||||
5
core/include/wx/string.h
Normal file
5
core/include/wx/string.h
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// Stub wx/string.h - redirects to wx_shim.h
|
||||
#ifndef _WX_STRING_H_
|
||||
#define _WX_STRING_H_
|
||||
#include "wx_shim.h"
|
||||
#endif
|
||||
264
core/include/wx_shim.h
Normal file
264
core/include/wx_shim.h
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
/*
|
||||
* wx_shim.h - Minimal wxWidgets compatibility layer for kicad-core
|
||||
*
|
||||
* Provides drop-in replacements for wx utility macros/functions used in
|
||||
* KiCad's core computation libraries. This is NOT a port of wxWidgets -
|
||||
* just ~100 lines of standard C++ that replaces debug/logging utilities.
|
||||
*
|
||||
* Used macros in kimath:
|
||||
* - wxASSERT, wxASSERT_MSG
|
||||
* - wxCHECK, wxCHECK_MSG, wxCHECK2_MSG, wxCHECK_RET
|
||||
* - wxFAIL_MSG
|
||||
* - wxLogTrace, wxLogDebug, wxLogWarning
|
||||
* - wxString, wxString::Format
|
||||
* - wxT()
|
||||
*/
|
||||
|
||||
#ifndef KICAD_WX_SHIM_H
|
||||
#define KICAD_WX_SHIM_H
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <sstream>
|
||||
|
||||
// =============================================================================
|
||||
// Assertions
|
||||
// =============================================================================
|
||||
|
||||
#define wxASSERT(cond) assert(cond)
|
||||
#define wxASSERT_MSG(cond, msg) assert(cond) // msg ignored in release
|
||||
|
||||
// wxCHECK variants - check condition, return if false
|
||||
#define wxCHECK(cond, ret) do { if(!(cond)) return ret; } while(0)
|
||||
#define wxCHECK_MSG(cond, ret, msg) do { if(!(cond)) return ret; } while(0)
|
||||
#define wxCHECK_RET(cond, msg) do { if(!(cond)) return; } while(0)
|
||||
#define wxCHECK2(cond, op) do { if(!(cond)) { op; } } while(0)
|
||||
#define wxCHECK2_MSG(cond, op, msg) do { if(!(cond)) { op; } } while(0)
|
||||
|
||||
#define wxFAIL_MSG(msg) assert(false) // msg ignored in standalone build
|
||||
|
||||
// =============================================================================
|
||||
// Logging - mostly no-ops for core library
|
||||
// =============================================================================
|
||||
|
||||
// Trace logging - typically disabled in release builds anyway
|
||||
#define wxLogTrace(...) ((void)0)
|
||||
#define wxLogDebug(...) ((void)0)
|
||||
|
||||
// Warnings - optionally print to stderr
|
||||
#ifdef KICAD_CORE_VERBOSE
|
||||
#define wxLogWarning(fmt, ...) fprintf(stderr, "Warning: " fmt "\n", ##__VA_ARGS__)
|
||||
#else
|
||||
#define wxLogWarning(...) ((void)0)
|
||||
#endif
|
||||
|
||||
// Variable argument version
|
||||
inline void wxVLogWarning(const char* format, va_list args) {
|
||||
#ifdef KICAD_CORE_VERBOSE
|
||||
vfprintf(stderr, format, args);
|
||||
fprintf(stderr, "\n");
|
||||
#else
|
||||
(void)format;
|
||||
(void)args;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Log level checking - always returns false (logging disabled)
|
||||
namespace wxLog {
|
||||
inline bool IsLevelEnabled(int, const std::string&) { return false; }
|
||||
inline void EnableLogging(bool enable = true) { (void)enable; }
|
||||
inline bool IsLoggingEnabled() { return false; }
|
||||
}
|
||||
|
||||
// Log level constants
|
||||
constexpr int wxLOG_Debug = 0;
|
||||
|
||||
// Log component macro
|
||||
#ifndef wxLOG_COMPONENT
|
||||
#define wxLOG_COMPONENT "kicad-core"
|
||||
#endif
|
||||
|
||||
// =============================================================================
|
||||
// String utilities
|
||||
// =============================================================================
|
||||
|
||||
// wxChar type
|
||||
using wxChar = char;
|
||||
|
||||
// wxT() macro - pass through (we're always using UTF-8)
|
||||
#define wxT(x) x
|
||||
|
||||
// wxASCII_STR for older wx compatibility
|
||||
#define wxASCII_STR(s) std::string(s)
|
||||
|
||||
// Helper to convert args for snprintf - strings need c_str()
|
||||
template<typename T>
|
||||
struct FormatArg {
|
||||
static auto convert(const T& arg) { return arg; }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct FormatArg<std::string> {
|
||||
static const char* convert(const std::string& arg) { return arg.c_str(); }
|
||||
};
|
||||
|
||||
// Forward declaration for wxString specialization
|
||||
class wxString;
|
||||
|
||||
template<>
|
||||
struct FormatArg<wxString> {
|
||||
static const char* convert(const wxString& arg);
|
||||
};
|
||||
|
||||
// wxString class with static Format method
|
||||
class wxString : public std::string {
|
||||
public:
|
||||
// Inherit constructors
|
||||
using std::string::string;
|
||||
|
||||
// Additional constructors for compatibility
|
||||
wxString() : std::string() {}
|
||||
wxString(const std::string& s) : std::string(s) {}
|
||||
wxString(const char* s) : std::string(s ? s : "") {}
|
||||
|
||||
// Static Format method - converts string args to c_str()
|
||||
template<typename... Args>
|
||||
static wxString Format(const char* fmt, Args... args) {
|
||||
char buf[2048];
|
||||
snprintf(buf, sizeof(buf), fmt, FormatArg<Args>::convert(args)...);
|
||||
return wxString(buf);
|
||||
}
|
||||
|
||||
static wxString Format(const wxString& fmt) {
|
||||
return fmt;
|
||||
}
|
||||
|
||||
// FromAscii static method
|
||||
static wxString FromAscii(const char* s) {
|
||||
return wxString(s ? s : "");
|
||||
}
|
||||
|
||||
// c_str() for compatibility (inherited from std::string but explicit)
|
||||
const char* c_str() const { return std::string::c_str(); }
|
||||
|
||||
// RemoveLast - removes last n characters
|
||||
void RemoveLast(size_t n = 1) {
|
||||
if (n <= size()) {
|
||||
resize(size() - n);
|
||||
} else {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Truncate - truncate to given length
|
||||
void Truncate(size_t len) {
|
||||
if (len < size()) {
|
||||
resize(len);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define the wxString FormatArg specialization now that wxString is defined
|
||||
inline const char* FormatArg<wxString>::convert(const wxString& arg) {
|
||||
return arg.c_str();
|
||||
}
|
||||
|
||||
// Empty string constant
|
||||
inline const wxString wxEmptyString = wxString("");
|
||||
|
||||
// =============================================================================
|
||||
// File I/O utilities
|
||||
// =============================================================================
|
||||
|
||||
#include <fstream>
|
||||
|
||||
// wxFFile - File wrapper class
|
||||
class wxFFile {
|
||||
FILE* m_fp = nullptr;
|
||||
bool m_close = false;
|
||||
|
||||
public:
|
||||
wxFFile() = default;
|
||||
wxFFile(const wxString& filename, const char* mode = "r") {
|
||||
Open(filename, mode);
|
||||
}
|
||||
~wxFFile() { Close(); }
|
||||
|
||||
bool Open(const wxString& filename, const char* mode = "r") {
|
||||
Close();
|
||||
m_fp = fopen(filename.c_str(), mode);
|
||||
m_close = (m_fp != nullptr);
|
||||
return m_fp != nullptr;
|
||||
}
|
||||
|
||||
bool IsOpened() const { return m_fp != nullptr; }
|
||||
|
||||
void Close() {
|
||||
if (m_fp && m_close) {
|
||||
fclose(m_fp);
|
||||
}
|
||||
m_fp = nullptr;
|
||||
m_close = false;
|
||||
}
|
||||
|
||||
size_t Read(void* buffer, size_t count) {
|
||||
if (!m_fp) return 0;
|
||||
return fread(buffer, 1, count, m_fp);
|
||||
}
|
||||
|
||||
size_t Write(const void* buffer, size_t count) {
|
||||
if (!m_fp) return 0;
|
||||
return fwrite(buffer, 1, count, m_fp);
|
||||
}
|
||||
|
||||
bool ReadAll(wxString* str) {
|
||||
if (!m_fp || !str) return false;
|
||||
// Get file size
|
||||
long pos = ftell(m_fp);
|
||||
fseek(m_fp, 0, SEEK_END);
|
||||
long size = ftell(m_fp);
|
||||
fseek(m_fp, pos, SEEK_SET);
|
||||
// Read content
|
||||
str->resize(size);
|
||||
size_t read = fread(&(*str)[0], 1, size, m_fp);
|
||||
str->resize(read);
|
||||
return read > 0;
|
||||
}
|
||||
|
||||
size_t Length() const {
|
||||
if (!m_fp) return 0;
|
||||
long pos = ftell(m_fp);
|
||||
fseek(m_fp, 0, SEEK_END);
|
||||
long size = ftell(m_fp);
|
||||
fseek(m_fp, pos, SEEK_SET);
|
||||
return static_cast<size_t>(size);
|
||||
}
|
||||
|
||||
bool Eof() const {
|
||||
return m_fp ? feof(m_fp) != 0 : true;
|
||||
}
|
||||
|
||||
bool Seek(long offset, int origin = SEEK_SET) {
|
||||
return m_fp ? fseek(m_fp, offset, origin) == 0 : false;
|
||||
}
|
||||
|
||||
long Tell() const {
|
||||
return m_fp ? ftell(m_fp) : -1;
|
||||
}
|
||||
};
|
||||
|
||||
// From_UTF8 - passthrough for UTF-8 strings
|
||||
inline wxString From_UTF8(const char* s) {
|
||||
return wxString(s ? s : "");
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Replacement for wx headers
|
||||
// =============================================================================
|
||||
|
||||
// Empty stubs for wx includes - these get included but do nothing
|
||||
// Create empty headers in core/include/wx/
|
||||
|
||||
#endif // KICAD_WX_SHIM_H
|
||||
57
core/src/test_kimath.cpp
Normal file
57
core/src/test_kimath.cpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* test_kimath.cpp - Test kimath and sexpr without wxWidgets
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <math/vector2d.h>
|
||||
#include <geometry/seg.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
#include <sexpr/sexpr.h>
|
||||
#include <sexpr/sexpr_parser.h>
|
||||
|
||||
int main() {
|
||||
std::cout << "Testing kimath standalone build...\n";
|
||||
|
||||
// Test VECTOR2I
|
||||
VECTOR2I p1(0, 0);
|
||||
VECTOR2I p2(100, 100);
|
||||
std::cout << "Created VECTOR2I: (" << p1.x << "," << p1.y << ") and ("
|
||||
<< p2.x << "," << p2.y << ")\n";
|
||||
|
||||
// Test SEG
|
||||
SEG segment(p1, p2);
|
||||
int length = segment.Length();
|
||||
std::cout << "SEG length: " << length << "\n";
|
||||
|
||||
// Test SHAPE_POLY_SET
|
||||
SHAPE_POLY_SET poly;
|
||||
poly.NewOutline();
|
||||
poly.Append(0, 0);
|
||||
poly.Append(1000, 0);
|
||||
poly.Append(1000, 1000);
|
||||
poly.Append(0, 1000);
|
||||
|
||||
std::cout << "Created polygon with " << poly.OutlineCount() << " outline(s)\n";
|
||||
std::cout << "Polygon area: " << poly.Area() << "\n";
|
||||
|
||||
// Test S-expression parser
|
||||
std::cout << "\nTesting S-expression parser...\n";
|
||||
std::string test_sexpr = "(kicad_pcb (version 20231014) (generator \"test\") (layer F.Cu front copper))";
|
||||
|
||||
SEXPR::PARSER parser;
|
||||
std::unique_ptr<SEXPR::SEXPR> parsed = parser.Parse(test_sexpr);
|
||||
|
||||
if (parsed && parsed->IsList()) {
|
||||
const SEXPR::SEXPR_VECTOR* list = parsed->GetChildren();
|
||||
std::cout << "Parsed S-expr with " << list->size() << " elements\n";
|
||||
if (!list->empty() && (*list)[0]->IsSymbol()) {
|
||||
std::cout << "Root element: " << (*list)[0]->GetSymbol() << "\n";
|
||||
}
|
||||
} else {
|
||||
std::cout << "Failed to parse S-expression\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::cout << "\nkimath + sexpr standalone build: SUCCESS!\n";
|
||||
return 0;
|
||||
}
|
||||
395
docs/02-PHASE2-CORE-EXTRACTION.md
Normal file
395
docs/02-PHASE2-CORE-EXTRACTION.md
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
# Phase 2: Core Library Extraction
|
||||
|
||||
## Overview
|
||||
|
||||
Extract KiCad's core computation code into a standalone library that compiles to both native and WebAssembly. This enables running the native wxWidgets GUI while delegating computation to a WASM module.
|
||||
|
||||
## Architecture: Native GUI + WASM Core
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ Native GUI (wxWidgets) │
|
||||
│ - PCB Editor canvas, menus, dialogs │
|
||||
│ - File dialogs, clipboard │
|
||||
│ - User interaction │
|
||||
└─────────────────────┬───────────────────────────────────┘
|
||||
│ S-expression serialization
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ WASM Bridge │
|
||||
│ - Wasmtime/wasm3 runtime (future) │
|
||||
│ - Serialize board → S-expr → WASM │
|
||||
│ - Deserialize results ← S-expr ← WASM │
|
||||
└─────────────────────┬───────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ kicad_core.wasm │
|
||||
│ - libs/kimath (geometry) │
|
||||
│ - libs/sexpr (parsing) │
|
||||
│ - Board data model │
|
||||
│ - DRC engine │
|
||||
│ - Router (Push & Shove) │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Why This Works: wxWidgets is NOT Required for Core
|
||||
|
||||
**Important**: The core libs don't use wxWidgets for GUI - they just use utility macros that happen to come from wx. We provide drop-in replacements via a ~50 line shim header:
|
||||
|
||||
| Library | wx Usage | Count | Shim Solution |
|
||||
|---------|----------|-------|---------------|
|
||||
| libs/kimath | wxASSERT, wxLogTrace | ~34 | Macros → assert/no-op |
|
||||
| libs/sexpr | wxFFile, wxString | ~4 | Classes → std equivalents |
|
||||
| libs/core | wxString | ~17 | typedef → std::string |
|
||||
|
||||
### wx_shim.h
|
||||
|
||||
```cpp
|
||||
// core/include/wx_shim.h
|
||||
#ifndef KICAD_WX_SHIM_H
|
||||
#define KICAD_WX_SHIM_H
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
|
||||
// Assertions - just use standard assert
|
||||
#define wxASSERT(x) assert(x)
|
||||
#define wxASSERT_MSG(x, msg) assert((x) && (msg))
|
||||
#define wxCHECK(x, ret) do { if(!(x)) return ret; } while(0)
|
||||
#define wxCHECK_MSG(x, ret, msg) do { if(!(x)) return ret; } while(0)
|
||||
#define wxFAIL_MSG(msg) assert(false && (msg))
|
||||
|
||||
// Logging - no-op or stderr
|
||||
#define wxLogTrace(...) ((void)0)
|
||||
#define wxLogDebug(...) ((void)0)
|
||||
#define wxLogWarning(...) fprintf(stderr, __VA_ARGS__)
|
||||
|
||||
// String - just use std::string
|
||||
using wxString = std::string;
|
||||
using wxChar = char;
|
||||
|
||||
// File I/O - use standard C++
|
||||
#include <fstream>
|
||||
class wxFFile {
|
||||
std::ifstream m_file;
|
||||
public:
|
||||
bool Open(const std::string& name) { m_file.open(name); return m_file.is_open(); }
|
||||
bool IsOpened() const { return m_file.is_open(); }
|
||||
size_t Read(void* buf, size_t count) { m_file.read((char*)buf, count); return m_file.gcount(); }
|
||||
bool Eof() const { return m_file.eof(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
kicad-wasm/
|
||||
├── core/
|
||||
│ ├── CMakeLists.txt # Standalone build without wxWidgets
|
||||
│ ├── include/
|
||||
│ │ ├── wx_shim.h # wx compatibility layer (~50 lines)
|
||||
│ │ └── kicad_core_api.h # C API for WASM
|
||||
│ ├── src/
|
||||
│ │ └── api.cpp # API implementation
|
||||
│ └── wasm/
|
||||
│ └── CMakeLists.txt # Emscripten-specific settings
|
||||
├── test/
|
||||
│ ├── test_geometry.cpp # Test kimath without wx
|
||||
│ ├── test_board_io.cpp # Test board load/save
|
||||
│ └── test.kicad_pcb # Sample board file
|
||||
```
|
||||
|
||||
## Implementation Steps
|
||||
|
||||
### Step 1: Foundation + Shim Layer
|
||||
|
||||
- Create `core/` directory structure
|
||||
- Create `wx_shim.h` with standard C++ replacements
|
||||
- Create CMakeLists.txt that builds kimath, core, sexpr
|
||||
- **Test**: Compiles without wxWidgets
|
||||
|
||||
### Step 2: Board Model Extraction
|
||||
|
||||
- Identify minimal BOARD dependencies
|
||||
- Create C API: `kicad_load_board()`, `kicad_save_board()`
|
||||
- Handle S-expression serialization
|
||||
- **Test**: Load a .kicad_pcb file via API
|
||||
|
||||
### Step 3: DRC Engine Extraction
|
||||
|
||||
- Extract DRC_ENGINE and test providers
|
||||
- Create C API: `kicad_drc_run()` returns violations as JSON
|
||||
- **Test**: Run DRC on test board
|
||||
|
||||
### Step 4: Router Extraction
|
||||
|
||||
- Extract PNS::ROUTER and supporting classes
|
||||
- Create minimal ROUTER_IFACE implementation
|
||||
- Create C API: `kicad_router_start()`, `_move()`, `_commit()`
|
||||
- **Test**: Route traces via API
|
||||
|
||||
### Step 5: Emscripten Build
|
||||
|
||||
- Set up emsdk toolchain
|
||||
- Build `kicad_core.wasm`
|
||||
- Create JavaScript bindings
|
||||
- **Test**: Load board in Node.js, run DRC, route traces
|
||||
|
||||
### Step 6: Browser Demo (Future)
|
||||
|
||||
- Simple HTML page with file upload
|
||||
- Load .kicad_pcb, display stats
|
||||
- Run DRC, show violations
|
||||
|
||||
## C API Design
|
||||
|
||||
### Board I/O
|
||||
|
||||
```cpp
|
||||
extern "C" {
|
||||
// Load board from S-expression string
|
||||
void* kicad_load_board(const char* sexpr_data, size_t len);
|
||||
|
||||
// Serialize board to S-expression
|
||||
char* kicad_save_board(void* board);
|
||||
|
||||
// Free memory
|
||||
void kicad_free_board(void* board);
|
||||
void kicad_free_string(char* str);
|
||||
|
||||
// Query operations
|
||||
int kicad_get_track_count(void* board);
|
||||
int kicad_get_footprint_count(void* board);
|
||||
}
|
||||
```
|
||||
|
||||
### DRC Engine
|
||||
|
||||
```cpp
|
||||
extern "C" {
|
||||
// Initialize DRC with rules
|
||||
void* kicad_drc_create(void* board, const char* rules_sexpr);
|
||||
|
||||
// Run DRC, returns JSON array of violations
|
||||
char* kicad_drc_run(void* drc_engine);
|
||||
|
||||
// Query specific clearance
|
||||
int kicad_drc_query_clearance(void* drc, int item_a, int item_b);
|
||||
|
||||
void kicad_drc_free(void* drc);
|
||||
}
|
||||
```
|
||||
|
||||
### Router
|
||||
|
||||
```cpp
|
||||
extern "C" {
|
||||
// Create router with board data
|
||||
void* kicad_router_create(void* board);
|
||||
|
||||
// Start routing from point
|
||||
int kicad_router_start(void* router, int x, int y, int layer);
|
||||
|
||||
// Move to point, returns preview geometry as S-expr
|
||||
char* kicad_router_move(void* router, int x, int y);
|
||||
|
||||
// Commit route
|
||||
char* kicad_router_commit(void* router);
|
||||
|
||||
void kicad_router_free(void* router);
|
||||
}
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
### Emscripten CMake Settings
|
||||
|
||||
```cmake
|
||||
if(EMSCRIPTEN)
|
||||
set_target_properties(kicad_core PROPERTIES
|
||||
LINK_FLAGS "-s EXPORTED_FUNCTIONS='[_kicad_load_board,_kicad_save_board,...]' \
|
||||
-s EXPORTED_RUNTIME_METHODS='[ccall,cwrap,UTF8ToString]' \
|
||||
-s MODULARIZE=1 \
|
||||
-s EXPORT_NAME='KicadCore' \
|
||||
-s ALLOW_MEMORY_GROWTH=1"
|
||||
)
|
||||
endif()
|
||||
```
|
||||
|
||||
## Key KiCad Source Files
|
||||
|
||||
**Libraries to include (via shim, no modification):**
|
||||
- `kicad/libs/kimath/src/**/*.cpp` - 17.5k lines, geometry
|
||||
- `kicad/libs/core/*.cpp` - 870 lines, utilities
|
||||
- `kicad/libs/sexpr/*.cpp` - 734 lines, parser
|
||||
- `kicad/pcbnew/board*.cpp` - Board data model
|
||||
- `kicad/pcbnew/pcb_io/kicad_sexpr/*.cpp` - S-expr I/O
|
||||
- `kicad/pcbnew/drc/*.cpp` - DRC engine
|
||||
- `kicad/pcbnew/router/pns_*.cpp` - Router
|
||||
|
||||
**Key headers:**
|
||||
- `kicad/libs/kimath/include/geometry/shape_poly_set.h` - Polygon ops
|
||||
- `kicad/pcbnew/board.h` - BOARD class (1510 lines)
|
||||
- `kicad/pcbnew/drc/drc_engine.h` - DRC entry point
|
||||
- `kicad/pcbnew/router/pns_router.h` - Router entry point
|
||||
|
||||
## Progress
|
||||
|
||||
### ✅ Step 1: Foundation + Shim Layer (COMPLETE)
|
||||
|
||||
**Date**: 2025-11-26
|
||||
|
||||
Successfully compiled kimath standalone without wxWidgets:
|
||||
|
||||
```
|
||||
core/
|
||||
├── include/
|
||||
│ ├── wx_shim.h # ~170 lines (more than expected, but still minimal)
|
||||
│ ├── config.h # Platform configuration
|
||||
│ ├── advanced_config.h # Default values for triangulation etc.
|
||||
│ └── wx/ # Stub wx headers
|
||||
│ ├── debug.h
|
||||
│ ├── log.h
|
||||
│ ├── string.h
|
||||
│ └── confbase.h
|
||||
├── src/
|
||||
│ └── test_kimath.cpp
|
||||
├── CMakeLists.txt
|
||||
└── build/
|
||||
├── libkimath.a # 1.4 MB static library
|
||||
├── libclipper2.a
|
||||
├── libkicad_core_utils.a
|
||||
└── test_kimath # Working test executable
|
||||
```
|
||||
|
||||
**Key learnings:**
|
||||
- wx_shim.h needed to be ~170 lines, not ~50, due to:
|
||||
- `wxString::Format()` with varargs required a proper class with template Format method
|
||||
- `FormatArg<T>` template needed to convert string args to `c_str()` for snprintf
|
||||
- `wxLog::EnableLogging()` used in polygon_triangulation.h
|
||||
- `wxString::RemoveLast()` used for string manipulation
|
||||
- `ADVANCED_CFG` class needed for triangulation settings
|
||||
- C++20 required (not C++17) due to KiCad's use of concepts
|
||||
- Build order: Our `core/include/` must come FIRST in include paths
|
||||
|
||||
**Test output:**
|
||||
```
|
||||
Testing kimath standalone build...
|
||||
Created VECTOR2I: (0,0) and (100,100)
|
||||
SEG length: 141
|
||||
Created polygon with 1 outline(s)
|
||||
Polygon area: 1e+06
|
||||
kimath standalone build: SUCCESS!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ Step 5 (partial): Emscripten Build (COMPLETE)
|
||||
|
||||
**Date**: 2025-11-26
|
||||
|
||||
Successfully compiled kimath to WebAssembly:
|
||||
|
||||
```bash
|
||||
$ emcmake cmake .. && emmake make
|
||||
$ node test_kimath.js
|
||||
|
||||
Testing kimath standalone build...
|
||||
Created VECTOR2I: (0,0) and (100,100)
|
||||
SEG length: 141
|
||||
Created polygon with 1 outline(s)
|
||||
Polygon area: 1e+06
|
||||
kimath standalone build: SUCCESS!
|
||||
```
|
||||
|
||||
**Build artifacts:**
|
||||
```
|
||||
build-wasm/
|
||||
├── test_kimath.wasm # 845KB - WASM module
|
||||
├── test_kimath.js # 154KB - JS glue code
|
||||
├── libkimath.a # 5.1MB - Static WASM library
|
||||
├── libclipper2.a # 1.2MB
|
||||
└── libkicad_core_utils.a # 106KB
|
||||
```
|
||||
|
||||
**Key findings:**
|
||||
- No code changes needed between native and WASM builds
|
||||
- Same wx_shim.h works for both targets
|
||||
- WASM module runs identically to native in Node.js
|
||||
|
||||
---
|
||||
|
||||
### ✅ Step 2 (partial): S-expression Parser (COMPLETE)
|
||||
|
||||
**Date**: 2025-11-26
|
||||
|
||||
Added libs/sexpr to the standalone build:
|
||||
|
||||
**Additional stubs needed:**
|
||||
- `wx/file.h`, `wx/ffile.h` - file I/O stubs
|
||||
- `wxFFile` class in wx_shim.h (~80 lines)
|
||||
- `string_utils.h` - minimal stub for `From_UTF8()`
|
||||
|
||||
**Test output (both native and WASM):**
|
||||
```
|
||||
Testing S-expression parser...
|
||||
Parsed S-expr with 4 elements
|
||||
Root element: kicad_pcb
|
||||
```
|
||||
|
||||
**WASM sizes:**
|
||||
```
|
||||
test_kimath.wasm - 867KB (geometry + sexpr parser)
|
||||
libkimath.a - 5.1MB
|
||||
libsexpr.a - 159KB
|
||||
libclipper2.a - 1.2MB
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- [x] wx_shim.h provides all needed wx replacements
|
||||
- [x] libs/kimath compiles with shim (no wxWidgets linked)
|
||||
- [x] kicad_core.wasm builds with Emscripten (kimath portion)
|
||||
- [x] libs/sexpr compiles with shim (added wxFFile, string_utils stubs)
|
||||
- [x] S-expression parser can parse .kicad_pcb format strings
|
||||
- [ ] S-expression parser can load .kicad_pcb from string
|
||||
- [ ] Board data model extracts cleanly
|
||||
- [ ] C API wrapper builds as native static library
|
||||
- [ ] kicad_core.wasm builds with Emscripten
|
||||
- [ ] Node.js can load a .kicad_pcb file via WASM
|
||||
- [ ] DRC runs in WASM, outputs violation list
|
||||
- [ ] Router API works in WASM
|
||||
|
||||
## First Concrete Step
|
||||
|
||||
Start with Step 1: Create `core/` directory with `wx_shim.h` and attempt to compile just `libs/kimath` standalone. This proves the shim approach works before tackling the larger board model.
|
||||
|
||||
## Dependencies
|
||||
|
||||
**Must include in WASM build:**
|
||||
- Clipper2 library (polygon boolean operations) - pure C++
|
||||
- RTree (spatial indexing) - header-only
|
||||
|
||||
**Not needed:**
|
||||
- wxWidgets (replaced by shim)
|
||||
- Boost (only header-only templates used)
|
||||
- OpenCASCADE, ngspice, curl, libgit2 (already disabled in Phase 1)
|
||||
Loading…
Reference in a new issue