2026-01-11 20:07:55 -05:00
|
|
|
#pragma once
|
|
|
|
|
#include "Common.h"
|
|
|
|
|
#include "Python.h"
|
|
|
|
|
#include <libtcod.h>
|
|
|
|
|
|
|
|
|
|
// Forward declaration
|
|
|
|
|
class PyHeightMap;
|
|
|
|
|
|
|
|
|
|
// Python object structure
|
|
|
|
|
typedef struct {
|
|
|
|
|
PyObject_HEAD
|
|
|
|
|
TCOD_heightmap_t* heightmap; // libtcod heightmap pointer
|
|
|
|
|
} PyHeightMapObject;
|
|
|
|
|
|
|
|
|
|
class PyHeightMap
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
// Python type interface
|
|
|
|
|
static PyObject* pynew(PyTypeObject* type, PyObject* args, PyObject* kwds);
|
|
|
|
|
static int init(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static void dealloc(PyHeightMapObject* self);
|
|
|
|
|
static PyObject* repr(PyObject* obj);
|
|
|
|
|
|
|
|
|
|
// Properties
|
|
|
|
|
static PyObject* get_size(PyHeightMapObject* self, void* closure);
|
|
|
|
|
|
2026-01-12 20:56:39 -05:00
|
|
|
// Scalar operations (all return self for chaining, support region parameters)
|
|
|
|
|
static PyObject* fill(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
2026-01-11 20:07:55 -05:00
|
|
|
static PyObject* clear(PyHeightMapObject* self, PyObject* Py_UNUSED(args));
|
2026-01-12 20:56:39 -05:00
|
|
|
static PyObject* add_constant(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* scale(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
2026-01-11 20:07:55 -05:00
|
|
|
static PyObject* clamp(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* normalize(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
|
2026-01-11 20:42:33 -05:00
|
|
|
// Query methods (#196)
|
2026-01-11 21:43:44 -05:00
|
|
|
static PyObject* get(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* get_interpolated(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* get_slope(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
2026-01-11 20:42:33 -05:00
|
|
|
static PyObject* get_normal(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* min_max(PyHeightMapObject* self, PyObject* Py_UNUSED(args));
|
|
|
|
|
static PyObject* count_in_range(PyHeightMapObject* self, PyObject* args);
|
|
|
|
|
|
2026-01-11 21:49:28 -05:00
|
|
|
// Threshold operations (#197) - return NEW HeightMaps
|
|
|
|
|
static PyObject* threshold(PyHeightMapObject* self, PyObject* args);
|
|
|
|
|
static PyObject* threshold_binary(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* inverse(PyHeightMapObject* self, PyObject* Py_UNUSED(args));
|
|
|
|
|
|
HeightMap: add terrain generation methods (closes #195)
Add seven terrain generation methods wrapping libtcod heightmap functions:
- add_hill(center, radius, height): Add smooth hill
- dig_hill(center, radius, depth): Dig crater (use negative depth)
- add_voronoi(num_points, coefficients, seed): Voronoi-based features
- mid_point_displacement(roughness, seed): Diamond-square terrain
- rain_erosion(drops, erosion, sedimentation, seed): Erosion simulation
- dig_bezier(points, start_radius, end_radius, start_depth, end_depth): Carve paths
- smooth(iterations): Average neighboring cells
All methods return self for chaining. Includes 24 unit tests.
Note: dig_hill and dig_bezier use libtcod's "dig" semantics - use negative
depth values to actually dig below current terrain level.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:00:08 -05:00
|
|
|
// Terrain generation methods (#195) - mutate self, return self for chaining
|
|
|
|
|
static PyObject* add_hill(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* dig_hill(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* add_voronoi(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* mid_point_displacement(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* rain_erosion(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* dig_bezier(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* smooth(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
2026-01-19 14:10:07 -05:00
|
|
|
|
|
|
|
|
// Correct convolution methods (using new libtcod functions)
|
|
|
|
|
static PyObject* sparse_kernel(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* sparse_kernel_from(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
2026-01-23 20:48:46 -05:00
|
|
|
// NOTE: gradients waiting for jmccardle:feature/heightmap-gradients to be merged into libtcod:main
|
|
|
|
|
// static PyObject* gradients(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
HeightMap: add terrain generation methods (closes #195)
Add seven terrain generation methods wrapping libtcod heightmap functions:
- add_hill(center, radius, height): Add smooth hill
- dig_hill(center, radius, depth): Dig crater (use negative depth)
- add_voronoi(num_points, coefficients, seed): Voronoi-based features
- mid_point_displacement(roughness, seed): Diamond-square terrain
- rain_erosion(drops, erosion, sedimentation, seed): Erosion simulation
- dig_bezier(points, start_radius, end_radius, start_depth, end_depth): Carve paths
- smooth(iterations): Average neighboring cells
All methods return self for chaining. Includes 24 unit tests.
Note: dig_hill and dig_bezier use libtcod's "dig" semantics - use negative
depth values to actually dig below current terrain level.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 22:00:08 -05:00
|
|
|
|
2026-01-11 21:43:44 -05:00
|
|
|
// Subscript support for hmap[x, y] syntax
|
|
|
|
|
static PyObject* subscript(PyHeightMapObject* self, PyObject* key);
|
2026-01-19 14:10:07 -05:00
|
|
|
static int subscript_assign(PyHeightMapObject* self, PyObject* key, PyObject* value);
|
2026-01-11 21:43:44 -05:00
|
|
|
|
2026-01-12 20:56:39 -05:00
|
|
|
// Combination operations (#194) - mutate self, return self for chaining, support region parameters
|
|
|
|
|
static PyObject* add(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* subtract(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* multiply(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* lerp(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* copy_from(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* hmap_max(PyHeightMapObject* self, PyObject* args, PyObject* kwds); // 'max' conflicts with macro
|
|
|
|
|
static PyObject* hmap_min(PyHeightMapObject* self, PyObject* args, PyObject* kwds); // 'min' conflicts with macro
|
2026-01-12 19:01:20 -05:00
|
|
|
|
|
|
|
|
// Direct source sampling (#209) - sample from NoiseSource/BSP directly
|
|
|
|
|
static PyObject* add_noise(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* multiply_noise(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* add_bsp(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
static PyObject* multiply_bsp(PyHeightMapObject* self, PyObject* args, PyObject* kwds);
|
|
|
|
|
|
2026-01-11 21:43:44 -05:00
|
|
|
// Mapping methods for subscript support
|
|
|
|
|
static PyMappingMethods mapping_methods;
|
|
|
|
|
|
2026-01-11 20:07:55 -05:00
|
|
|
// Method and property definitions
|
|
|
|
|
static PyMethodDef methods[];
|
|
|
|
|
static PyGetSetDef getsetters[];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
namespace mcrfpydef {
|
2026-02-16 20:58:09 -05:00
|
|
|
inline PyTypeObject PyHeightMapType = {
|
2026-01-11 20:07:55 -05:00
|
|
|
.ob_base = {.ob_base = {.ob_refcnt = 1, .ob_type = NULL}, .ob_size = 0},
|
|
|
|
|
.tp_name = "mcrfpy.HeightMap",
|
|
|
|
|
.tp_basicsize = sizeof(PyHeightMapObject),
|
|
|
|
|
.tp_itemsize = 0,
|
|
|
|
|
.tp_dealloc = (destructor)PyHeightMap::dealloc,
|
|
|
|
|
.tp_repr = PyHeightMap::repr,
|
2026-01-11 21:43:44 -05:00
|
|
|
.tp_as_mapping = &PyHeightMap::mapping_methods, // hmap[x, y] subscript
|
2026-01-11 20:07:55 -05:00
|
|
|
.tp_flags = Py_TPFLAGS_DEFAULT,
|
|
|
|
|
.tp_doc = PyDoc_STR(
|
|
|
|
|
"HeightMap(size: tuple[int, int], fill: float = 0.0)\n\n"
|
|
|
|
|
"A 2D grid of float values for procedural generation.\n\n"
|
|
|
|
|
"HeightMap is the universal canvas for procedural generation. It stores "
|
|
|
|
|
"float values that can be manipulated, combined, and applied to Grid and "
|
|
|
|
|
"Layer objects.\n\n"
|
|
|
|
|
"Args:\n"
|
|
|
|
|
" size: (width, height) dimensions of the heightmap. Immutable after creation.\n"
|
|
|
|
|
" fill: Initial value for all cells. Default 0.0.\n\n"
|
|
|
|
|
"Example:\n"
|
|
|
|
|
" hmap = mcrfpy.HeightMap((100, 100))\n"
|
|
|
|
|
" hmap.fill(0.5).scale(2.0).clamp(0.0, 1.0)\n"
|
2026-01-11 21:43:44 -05:00
|
|
|
" value = hmap[5, 5] # Subscript shorthand for get()\n"
|
2026-01-11 20:07:55 -05:00
|
|
|
),
|
|
|
|
|
.tp_methods = nullptr, // Set in McRFPy_API.cpp before PyType_Ready
|
|
|
|
|
.tp_getset = nullptr, // Set in McRFPy_API.cpp before PyType_Ready
|
|
|
|
|
.tp_init = (initproc)PyHeightMap::init,
|
|
|
|
|
.tp_new = PyHeightMap::pynew,
|
|
|
|
|
};
|
|
|
|
|
}
|