BSP: add Binary Space Partitioning for procedural dungeon generation

Implements #202, #203, #204, #205; partially implements #206:
- BSP class: core tree structure with bounds, split_once, split_recursive, clear
- BSPNode class: lightweight node reference with bounds, level, is_leaf,
  split_horizontal, split_position; navigation via left/right/parent/sibling;
  contains() and center() methods
- Traversal enum: PRE_ORDER, IN_ORDER, POST_ORDER, LEVEL_ORDER, INVERTED_LEVEL_ORDER
- BSP iteration: leaves() for leaf nodes only, traverse(order) for all nodes
- BSP query: find(pos) returns deepest node containing position
- BSP.to_heightmap(): converts BSP to HeightMap with select, shrink, value options

Note: #206's BSPMap subclass deferred - to_heightmap returns plain HeightMap.
The HeightMap already has all necessary operations (inverse, threshold, etc.)
for procedural generation workflows.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-01-12 07:02:54 -05:00
commit 8699bba9e6
5 changed files with 1703 additions and 0 deletions

View file

@ -22,6 +22,7 @@
#include "PyMouse.h"
#include "UIGridPathfinding.h" // AStarPath and DijkstraMap types
#include "PyHeightMap.h" // Procedural generation heightmap (#193)
#include "PyBSP.h" // Procedural generation BSP (#202-206)
#include "McRogueFaceVersion.h"
#include "GameEngine.h"
#include "ImGuiConsole.h"
@ -418,6 +419,7 @@ PyObject* PyInit_mcrfpy()
/*procedural generation (#192)*/
&mcrfpydef::PyHeightMapType,
&mcrfpydef::PyBSPType,
nullptr};
@ -434,6 +436,10 @@ PyObject* PyInit_mcrfpy()
/*pathfinding iterator - returned by AStarPath.__iter__() but not directly instantiable*/
&mcrfpydef::PyAStarPathIterType,
/*BSP internal types - returned by BSP methods but not directly instantiable*/
&mcrfpydef::PyBSPNodeType,
&mcrfpydef::PyBSPIterType,
nullptr};
// Set up PyWindowType methods and getsetters before PyType_Ready
@ -448,6 +454,12 @@ PyObject* PyInit_mcrfpy()
mcrfpydef::PyHeightMapType.tp_methods = PyHeightMap::methods;
mcrfpydef::PyHeightMapType.tp_getset = PyHeightMap::getsetters;
// Set up PyBSPType and BSPNode methods and getsetters (#202-206)
mcrfpydef::PyBSPType.tp_methods = PyBSP::methods;
mcrfpydef::PyBSPType.tp_getset = PyBSP::getsetters;
mcrfpydef::PyBSPNodeType.tp_methods = PyBSPNode::methods;
mcrfpydef::PyBSPNodeType.tp_getset = PyBSPNode::getsetters;
// Set up weakref support for all types that need it
PyTimerType.tp_weaklistoffset = offsetof(PyTimerObject, weakreflist);
PyUIFrameType.tp_weaklistoffset = offsetof(PyUIFrameObject, weakreflist);
@ -555,6 +567,13 @@ PyObject* PyInit_mcrfpy()
PyErr_Clear();
}
// Add Traversal enum class for BSP traversal (uses Python's IntEnum)
PyObject* traversal_class = PyTraversal::create_enum_class(m);
if (!traversal_class) {
// If enum creation fails, continue without it (non-fatal)
PyErr_Clear();
}
// Add Key enum class for keyboard input
PyObject* key_class = PyKey::create_enum_class(m);
if (!key_class) {