add axis inversion + terrace step fix + manual size input store

This commit is contained in:
vld_voiculescu
2026-08-08 17:24:02 +03:00
parent 10165e88d1
commit b11baccb54
5 changed files with 172 additions and 74 deletions
+50 -34
View File
@@ -274,22 +274,27 @@ static void insert_floor_ceil_brush( scene::Node& entity,
static void insert_wall_brush( scene::Node& entity,
double x_bl, double x_tl, double x_br, double x_tr,
double gy, double gmx_y, double gz, double gmx_z,
double outer_x, double limit_x,
double gu, double gmx_u, double gz, double gmx_z,
double outer, double limit,
const char* top_tex, const char* caulk,
bool is_left, bool alt_dir ){
bool is_left, bool alt_dir, Axis axis ){
// Wall geometry is floor/ceiling rotated 90°.
// Coordinate mapping: floor(fx,fy,fz) → world(fz, fx, fy)
// gy→fx, gz→fy, gmx_y→fmx, gmx_z→fmy, x_*→fz_*, outer_x/limit_x→fmin_z/fsolid_top
// Left wall: inner surface faces +X (like floor), cap at outer_x (min_z side).
// Right wall: inner surface faces −X (like ceiling), cap at outer_x (solid_top side).
// For a Y-running tunnel (walls at fixed X, left/right), coordinate mapping:
// floor(fx,fy,fz) → world(fz, fx, fy) — gu→fx, gz→fy, gmx_u→fmx, gmx_z→fmy,
// x_*→fz_*, outer/limit→fmin_z/fsolid_top. This is a proper 90° rotation, so
// face winding carries over unchanged.
// For an X-running tunnel (walls at fixed Y, near/far), the same swap
// (world(fx, fz, fy)) is a mirror image of the above rather than a rotation,
// so v1/v2 are swapped per face to keep normals pointing outward.
// Left/near wall: inner surface faces away from outer (min_z side).
// Right/far wall: inner surface faces away from outer (solid_top side).
_QERFaceData face;
const bool is_ceiling = !is_left;
const double min_z = is_left ? outer_x : limit_x;
const double solid_top = is_left ? limit_x : outer_x;
const double min_z = is_left ? outer : limit;
const double solid_top = is_left ? limit : outer;
// Aliases to match insert_floor_ceil_brush variable names exactly.
const double x = gy, y = gz, mx = gmx_y, my = gmx_z;
const double x = gu, y = gz, mx = gmx_u, my = gmx_z;
const double z_bl = x_bl, z_tl = x_tl, z_br = x_br, z_tr = x_tr;
// fw: emit a face using floor coordinate order but with world axes permuted.
@@ -297,7 +302,10 @@ static void insert_wall_brush( scene::Node& entity,
double fx1, double fy1, double fz1,
double fx2, double fy2, double fz2,
const char* shader ){
fill_face( face, fz0, fx0, fy0, fz1, fx1, fy1, fz2, fx2, fy2, shader );
if ( axis == Axis::Y )
fill_face( face, fz0, fx0, fy0, fz1, fx1, fy1, fz2, fx2, fy2, shader );
else
fill_face( face, fx0, fz0, fy0, fx2, fz2, fy2, fx1, fz1, fy1, shader );
};
if ( !alt_dir ) {
@@ -366,7 +374,7 @@ static void insert_wall_brush( scene::Node& entity,
void build_tunnel_brushes( const BrushData& target, double step_x, double step_y,
const TunnelMaps& maps, const char* top_texture,
double cave_height, double slope_height ){
double cave_height, double slope_height, Axis axis ){
// Undo is started by the caller so deletion + generation form one step.
scene::Node& floor_entity = create_func_group();
scene::Node& ceil_entity = create_func_group();
@@ -420,48 +428,56 @@ void build_tunnel_brushes( const BrushData& target, double step_x, double step_y
}
}
// Walls — must match the range generated by terrain_engine.
// Walls — must match the range generated by terrain_engine. Grid runs along
// Y (left/right walls at fixed X) for a Y-running tunnel, or along X
// (near/far walls at fixed Y) for an X-running tunnel.
// slope_height may be negative (downward slope), so clamp accordingly.
double wall_min_z = target.max_z + std::min( 0.0, slope_height );
double wall_max_z = wall_min_z + cave_height + std::abs( slope_height );
double step_z = maps.step_z;
double outer_left = target.min_x - step_x;
double outer_right = target.max_x + step_x;
double limit_x = ( target.min_x + target.max_x ) / 2.0;
int gy_index = 0;
for ( double gy = target.min_y; gy < target.max_y - 0.01; gy += step_y, ++gy_index ) {
const bool along_y = ( axis == Axis::Y );
const double along_min = along_y ? target.min_y : target.min_x;
const double along_max = along_y ? target.max_y : target.max_x;
const double along_step = along_y ? step_y : step_x;
const double outer_lo = along_y ? target.min_x - step_x : target.min_y - step_y;
const double outer_hi = along_y ? target.max_x + step_x : target.max_y + step_y;
const double limit = along_y ? ( target.min_x + target.max_x ) / 2.0
: ( target.min_y + target.max_y ) / 2.0;
int gu_index = 0;
for ( double gu = along_min; gu < along_max - 0.01; gu += along_step, ++gu_index ) {
int gz_index = 0;
for ( double gz = wall_min_z; gz < wall_max_z - 0.01; gz += step_z, ++gz_index ) {
double gmx_y = gy + step_y < target.max_y ? gy + step_y : target.max_y;
double gmx_z = gz + step_z < wall_max_z ? gz + step_z : wall_max_z;
double gmx_u = gu + along_step < along_max ? gu + along_step : along_max;
double gmx_z = gz + step_z < wall_max_z ? gz + step_z : wall_max_z;
auto safe_wall = [&]( const HeightMap& m, double kx, double ky, double fallback ) -> double {
auto it = m.find( { r2( kx ), r2( ky ) } );
auto safe_wall = [&]( const HeightMap& m, double ku, double kz, double fallback ) -> double {
auto it = m.find( { r2( ku ), r2( kz ) } );
if ( it == m.end() ) {
globalErrorStream() << "TerrainGenerator: wall map missing key ("
<< kx << ", " << ky << ") - using fallback\n";
<< ku << ", " << kz << ") - using fallback\n";
return fallback;
}
return it->second;
};
double lx_bl = safe_wall( maps.left_wall_map, gy, gz, limit_x );
double lx_tl = safe_wall( maps.left_wall_map, gy, gmx_z, limit_x );
double lx_br = safe_wall( maps.left_wall_map, gmx_y, gz, limit_x );
double lx_tr = safe_wall( maps.left_wall_map, gmx_y, gmx_z, limit_x );
double lx_bl = safe_wall( maps.left_wall_map, gu, gz, limit );
double lx_tl = safe_wall( maps.left_wall_map, gu, gmx_z, limit );
double lx_br = safe_wall( maps.left_wall_map, gmx_u, gz, limit );
double lx_tr = safe_wall( maps.left_wall_map, gmx_u, gmx_z, limit );
double rx_bl = safe_wall( maps.right_wall_map, gy, gz, limit_x );
double rx_tl = safe_wall( maps.right_wall_map, gy, gmx_z, limit_x );
double rx_br = safe_wall( maps.right_wall_map, gmx_y, gz, limit_x );
double rx_tr = safe_wall( maps.right_wall_map, gmx_y, gmx_z, limit_x );
double rx_bl = safe_wall( maps.right_wall_map, gu, gz, limit );
double rx_tl = safe_wall( maps.right_wall_map, gu, gmx_z, limit );
double rx_br = safe_wall( maps.right_wall_map, gmx_u, gz, limit );
double rx_tr = safe_wall( maps.right_wall_map, gmx_u, gmx_z, limit );
bool alt_dir = ( ( gy_index + gz_index ) % 2 ) != 0;
bool alt_dir = ( ( gu_index + gz_index ) % 2 ) != 0;
insert_wall_brush( lwall_entity, lx_bl, lx_tl, lx_br, lx_tr,
gy, gmx_y, gz, gmx_z, outer_left, limit_x, top_texture, caulk, true, alt_dir );
gu, gmx_u, gz, gmx_z, outer_lo, limit, top_texture, caulk, true, alt_dir, axis );
insert_wall_brush( rwall_entity, rx_bl, rx_tl, rx_br, rx_tr,
gy, gmx_y, gz, gmx_z, outer_right, limit_x, top_texture, caulk, false, alt_dir );
gu, gmx_u, gz, gmx_z, outer_hi, limit, top_texture, caulk, false, alt_dir, axis );
}
}
+1 -1
View File
@@ -11,4 +11,4 @@ void build_terrain_brushes( const BrushData& target, double step_x, double step_
void build_tunnel_brushes( const BrushData& target, double step_x, double step_y,
const TunnelMaps& maps, const char* top_texture,
double cave_height, double slope_height );
double cave_height, double slope_height, Axis axis );
+61 -27
View File
@@ -67,7 +67,8 @@ void adjust_bounds_to_fit_grid( BrushData& target, double step_x, double step_y
HeightMap generate_height_map( const BrushData& target, double step_x, double step_y,
ShapeType shape_type, double shape_height,
double variance, double frequency,
NoiseType noise_type, double terrace_step ){
NoiseType noise_type, double terrace_step,
Axis axis ){
HeightMap height_map;
double seed_x = random_double() * 10000.0;
@@ -78,6 +79,9 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
double nx = target.width_x > 0 ? ( x - target.min_x ) / target.width_x : 0.0;
double ny = target.length_y > 0 ? ( y - target.min_y ) / target.length_y : 0.0;
// Position along the axis Slope / Ridge / Valley run along.
double na = ( axis == Axis::X ) ? nx : ny;
double center_dist = std::min( 1.0, std::sqrt(
( nx - 0.5 ) * ( nx - 0.5 ) + ( ny - 0.5 ) * ( ny - 0.5 ) ) / 0.5 );
@@ -92,12 +96,12 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
break;
}
case ShapeType::Ridge: {
double dist = std::min( 1.0, std::abs( nx - 0.5 ) / 0.5 );
double dist = std::min( 1.0, std::abs( na - 0.5 ) / 0.5 );
base_z = shape_height * 0.5 * ( 1.0 + std::cos( dist * std::numbers::pi ) );
break;
}
case ShapeType::Slope: {
base_z = shape_height * nx;
base_z = shape_height * na;
break;
}
case ShapeType::Volcano: {
@@ -108,7 +112,7 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
break;
}
case ShapeType::Valley: {
double dist = std::min( 1.0, std::abs( nx - 0.5 ) / 0.5 );
double dist = std::min( 1.0, std::abs( na - 0.5 ) / 0.5 );
base_z = shape_height * 0.5 * ( 1.0 - std::cos( dist * std::numbers::pi ) );
break;
}
@@ -116,6 +120,14 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
break;
}
// Terrace the clean shape height first, before noise is mixed in, so
// terrace bands stay flat strips instead of being broken up by
// per-vertex noise jitter. Noise is then layered on top as surface
// detail on the (possibly stepped) base height.
double shape_z = target.max_z + base_z;
if ( shape_type != ShapeType::Flat && terrace_step > 0.0 )
shape_z = std::floor( shape_z / terrace_step ) * terrace_step;
double noise_z = 0.0;
if ( variance > 0.0 ) {
if ( noise_type == NoiseType::Random ) {
@@ -127,9 +139,7 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
}
}
double final_z = target.max_z + base_z + noise_z;
if ( shape_type != ShapeType::Flat && terrace_step > 0.0 )
final_z = std::floor( final_z / terrace_step ) * terrace_step;
double final_z = shape_z + noise_z;
height_map[{ round2( x ), round2( y ) }] = std::round( final_z );
}
@@ -145,7 +155,8 @@ HeightMap generate_height_map( const BrushData& target, double step_x, double st
TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x, double step_y,
double cave_height, double slope_height,
double variance, double frequency,
NoiseType noise_type, double terrace_step ){
NoiseType noise_type, double terrace_step,
Axis axis ){
TunnelMaps result;
double seed_floor_x = random_double() * 10000.0;
@@ -155,15 +166,37 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
double seed_wall_l = random_double() * 10000.0;
double seed_wall_r = random_double() * 10000.0;
double center_x = ( target.min_x + target.max_x ) / 2.0;
double half_width = target.width_x / 2.0;
// Cross-section (rounding) axis: X for a Y-running tunnel, Y for an
// X-running tunnel. Reproduces the original formulas exactly when axis == Y.
const bool along_y = ( axis == Axis::Y );
const double center_cs = along_y ? ( target.min_x + target.max_x ) / 2.0
: ( target.min_y + target.max_y ) / 2.0;
const double half_cs = along_y ? target.width_x / 2.0
: target.length_y / 2.0;
// Floor and ceiling
for ( double x = target.min_x; x <= target.max_x + 0.01; x += step_x ) {
for ( double y = target.min_y; y <= target.max_y + 0.01; y += step_y ) {
double t = half_width > 0 ? std::min( 1.0, std::abs( x - center_x ) / half_width ) : 0.0;
double cs_pos = along_y ? x : y;
double t = half_cs > 0 ? std::min( 1.0, std::abs( cs_pos - center_cs ) / half_cs ) : 0.0;
double blend = 1.0 - std::sqrt( std::max( 0.0, 1.0 - t * t ) );
double along_pos = along_y
? ( target.length_y > 0 ? ( y - target.min_y ) / target.length_y : 0.0 )
: ( target.width_x > 0 ? ( x - target.min_x ) / target.width_x : 0.0 );
double base_z = target.max_z + slope_height * along_pos;
double floor_base = base_z + blend * ( cave_height * 0.25 );
double ceil_base = base_z + cave_height - blend * ( cave_height * 0.25 );
// Terrace the clean floor/ceiling first, before noise is mixed in, so
// terrace bands stay flat strips instead of being broken up by
// per-vertex noise jitter (see the standard-terrain heightmap for the
// same reasoning).
if ( terrace_step > 0.0 ) {
floor_base = std::floor( floor_base / terrace_step ) * terrace_step;
ceil_base = std::ceil( ceil_base / terrace_step ) * terrace_step;
}
double floor_noise = 0.0, ceil_noise = 0.0;
if ( variance > 0.0 ) {
if ( noise_type == NoiseType::Random ) {
@@ -177,10 +210,8 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
}
}
double ny = target.length_y > 0 ? ( y - target.min_y ) / target.length_y : 0.0;
double base_z = target.max_z + slope_height * ny;
double floor_z = base_z + blend * ( cave_height * 0.25 ) + floor_noise;
double ceil_z = base_z + cave_height - blend * ( cave_height * 0.25 ) - ceil_noise;
double floor_z = floor_base + floor_noise;
double ceil_z = ceil_base - ceil_noise;
if ( floor_z > ceil_z ) {
double mid = ( floor_z + ceil_z ) / 2.0;
@@ -188,11 +219,6 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
ceil_z = mid;
}
if ( terrace_step > 0.0 ) {
floor_z = std::floor( floor_z / terrace_step ) * terrace_step;
ceil_z = std::ceil( ceil_z / terrace_step ) * terrace_step;
}
result.floor_map[ { round2( x ), round2( y ) }] = std::round( floor_z );
result.ceiling_map[ { round2( x ), round2( y ) }] = std::round( ceil_z );
}
@@ -207,10 +233,18 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
double wall_max_z = wall_min_z + total_wall_height;
result.step_z = step_z;
// Left and right walls — grid over (Y, Z)
for ( double y = target.min_y; y <= target.max_y + 0.01; y += step_y ) {
// Walls — grid over (along-tunnel coordinate, Z). For a Y-running tunnel
// this is (Y, Z) with walls at fixed X (left/right); for an X-running
// tunnel it's (X, Z) with walls at fixed Y (near/far).
double along_min = along_y ? target.min_y : target.min_x;
double along_max = along_y ? target.max_y : target.max_x;
double along_step = along_y ? step_y : step_x;
double wall_lo = along_y ? target.min_x : target.min_y;
double wall_hi = along_y ? target.max_x : target.max_y;
for ( double u = along_min; u <= along_max + 0.01; u += along_step ) {
for ( double z = wall_min_z; z <= wall_max_z + 0.01; z += step_z ) {
double ry = round2( y );
double ru = round2( u );
double rz = round2( z );
double wall_noise = 0.0;
@@ -219,10 +253,10 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
wall_noise = random_double() * variance;
} else {
wall_noise = std::abs( sample_noise( noise_type,
( y + seed_wall_l ) * frequency, ( z + seed_wall_l ) * frequency ) ) * variance;
( u + seed_wall_l ) * frequency, ( z + seed_wall_l ) * frequency ) ) * variance;
}
}
result.left_wall_map[{ ry, rz }] = std::round( target.min_x + wall_noise );
result.left_wall_map[{ ru, rz }] = std::round( wall_lo + wall_noise );
wall_noise = 0.0;
if ( variance > 0.0 ) {
@@ -230,10 +264,10 @@ TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x,
wall_noise = random_double() * variance;
} else {
wall_noise = std::abs( sample_noise( noise_type,
( y + seed_wall_r ) * frequency, ( z + seed_wall_r ) * frequency ) ) * variance;
( u + seed_wall_r ) * frequency, ( z + seed_wall_r ) * frequency ) ) * variance;
}
}
result.right_wall_map[{ ry, rz }] = std::round( target.max_x - wall_noise );
result.right_wall_map[{ ru, rz }] = std::round( wall_hi - wall_noise );
}
}
+11 -2
View File
@@ -36,6 +36,13 @@ enum class NoiseType {
Random = 2
};
// Direction Slope / Ridge / Valley run along. Ignored by radially symmetric
// shapes (Hill, Crater, Volcano) and by Tunnel/SlopeTunnel, which run along Y.
enum class Axis {
X = 0,
Y = 1
};
BrushData make_manual_brush_data( double width, double length, double height );
void adjust_bounds_to_fit_grid( BrushData& target, double step_x, double step_y );
@@ -43,9 +50,11 @@ void adjust_bounds_to_fit_grid( BrushData& target, double step_x, double step_y
HeightMap generate_height_map( const BrushData& target, double step_x, double step_y,
ShapeType shape_type, double shape_height,
double variance, double frequency,
NoiseType noise_type, double terrace_step );
NoiseType noise_type, double terrace_step,
Axis axis );
TunnelMaps generate_tunnel_height_maps( const BrushData& target, double step_x, double step_y,
double cave_height, double slope_height,
double variance, double frequency,
NoiseType noise_type, double terrace_step );
NoiseType noise_type, double terrace_step,
Axis axis );
+49 -10
View File
@@ -140,14 +140,28 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
form->addRow( "Length (Y):", sel_l_label );
form->addRow( "Height (Z):", sel_h_label );
// Manual size inputs (shown in Manual Size mode)
auto *manual_w_spin = new SpinBox( 64, 131072, 1024, 0, 64 );
auto *manual_l_spin = new SpinBox( 64, 131072, 1024, 0, 64 );
auto *manual_h_spin = new SpinBox( 64, 131072, 64, 0, 64 );
// Manual size inputs (shown in Manual Size mode). Remembers the last
// values used across dialog open/close within this editor session, so
// repeated manual-size generation doesn't reset to hardcoded defaults
// every time the dialog is reopened.
constexpr double DEFAULT_MANUAL_W = 1024.0, DEFAULT_MANUAL_L = 1024.0, DEFAULT_MANUAL_H = 64.0;
static double s_manual_w = DEFAULT_MANUAL_W, s_manual_l = DEFAULT_MANUAL_L, s_manual_h = DEFAULT_MANUAL_H;
auto *manual_w_spin = new SpinBox( 64, 131072, s_manual_w, 0, 64 );
auto *manual_l_spin = new SpinBox( 64, 131072, s_manual_l, 0, 64 );
auto *manual_h_spin = new SpinBox( 64, 131072, s_manual_h, 0, 64 );
form->addRow( "Width (X):", manual_w_spin );
form->addRow( "Length (Y):", manual_l_spin );
form->addRow( "Height (Z):", manual_h_spin );
// Reset the remembered manual size back to the original hardcoded default.
auto *manual_reset_btn = new QPushButton( "Reset to Default" );
QObject::connect( manual_reset_btn, &QPushButton::clicked, [&](){
manual_w_spin->setValue( DEFAULT_MANUAL_W );
manual_l_spin->setValue( DEFAULT_MANUAL_L );
manual_h_spin->setValue( DEFAULT_MANUAL_H );
} );
form->addRow( "", manual_reset_btn );
// Sub-square size: preset combo + Advanced checkbox in one row
auto *sq_widget = new QWidget;
auto *sq_hbox = new QHBoxLayout( sq_widget );
@@ -181,6 +195,16 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
shape_combo->setCurrentIndex( 0 ); // Flat
form->addRow( "Base Shape:", shape_combo );
// Axis — meaningful for the directional shapes (Slope, Ridge, Valley) and
// for tunnels (which way the tunnel runs). Radially symmetric shapes
// (Hill, Crater, Volcano) ignore it. Default differs per shape (see
// update_shape below) to match each shape's original hardcoded axis.
auto *axis_combo = new ComboBox;
axis_combo->addItem( "X Axis", (int)Axis::X );
axis_combo->addItem( "Y Axis", (int)Axis::Y );
axis_combo->setCurrentIndex( 0 );
form->addRow( "Axis:", axis_combo );
// Shape height — editable spinbox (manual mode or non-slope shapes).
// For Slope / Slope Tunnel in Use Selection mode, replaced by a
// read-only label derived from the selection brush's Z extent.
@@ -369,6 +393,7 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
const double step_x = advanced ? step_x_spin->value() : sq_combo->currentData().toInt();
const double step_y = advanced ? step_y_spin->value() : sq_combo->currentData().toInt();
const ShapeType shape = (ShapeType)shape_combo->currentData().toInt();
const Axis axis = (Axis)axis_combo->currentData().toInt();
const NoiseType noise = (NoiseType)noise_combo->currentData().toInt();
const double tun_height = tunnel_height_spin->value();
const double variance = variance_spin->value();
@@ -507,12 +532,12 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
const double cave_height = ( shape == ShapeType::SlopeTunnel ) ? tun_height : shape_height;
const double slope_height = ( shape == ShapeType::SlopeTunnel ) ? shape_height : 0;
const double tunnel_terrace = ( shape == ShapeType::SlopeTunnel ) ? terrace : 0.0;
auto maps = generate_tunnel_height_maps( target, step_x, step_y, cave_height, slope_height, variance, frequency, noise, tunnel_terrace );
build_tunnel_brushes( target, step_x, step_y, maps, texture, cave_height, slope_height );
auto maps = generate_tunnel_height_maps( target, step_x, step_y, cave_height, slope_height, variance, frequency, noise, tunnel_terrace, axis );
build_tunnel_brushes( target, step_x, step_y, maps, texture, cave_height, slope_height, axis );
}
else {
bool split_diagonally = ( variance > 0 || shape != ShapeType::Flat );
auto height_map = generate_height_map( target, step_x, step_y, shape, shape_height, variance, frequency, noise, terrace );
auto height_map = generate_height_map( target, step_x, step_y, shape, shape_height, variance, frequency, noise, terrace, axis );
build_terrain_brushes( target, step_x, step_y, height_map, texture, split_diagonally );
}
@@ -576,9 +601,10 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
set_row_visible( sel_w_label, use_sel );
set_row_visible( sel_l_label, use_sel );
set_row_visible( sel_h_label, use_sel );
set_row_visible( manual_w_spin, !use_sel );
set_row_visible( manual_l_spin, !use_sel );
set_row_visible( manual_h_spin, !use_sel );
set_row_visible( manual_w_spin, !use_sel );
set_row_visible( manual_l_spin, !use_sel );
set_row_visible( manual_h_spin, !use_sel );
set_row_visible( manual_reset_btn, !use_sel );
};
// Advanced sub-square toggle
@@ -593,7 +619,17 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
const ShapeType st = (ShapeType)shape_combo->itemData( idx ).toInt();
const bool is_flat = ( st == ShapeType::Flat );
const bool is_slope_tunnel = ( st == ShapeType::SlopeTunnel );
const bool is_tunnel_shape = ( st == ShapeType::Tunnel || st == ShapeType::SlopeTunnel );
const bool has_axis = ( st == ShapeType::Slope || st == ShapeType::Ridge || st == ShapeType::Valley
|| is_tunnel_shape );
set_row_visible( axis_combo, has_axis );
if ( has_axis ) {
// Match each shape's original hardcoded axis: Slope/Ridge/Valley
// ran along X, tunnels ran along Y. Re-applied on every shape
// switch, same as the slope-height auto-fill below.
axis_combo->setCurrentIndex( is_tunnel_shape ? (int)Axis::Y : (int)Axis::X );
}
set_row_visible( shape_height_spin, !is_flat );
if ( !is_flat ) {
if ( auto *lbl = qobject_cast<QLabel*>( form->labelForField( shape_height_spin ) ) )
@@ -640,6 +676,9 @@ void dispatch( const char* command, float* vMin, float* vMax, bool bSingleBrush
QObject::connect( &dialog, &QDialog::finished, &loop, &QEventLoop::quit );
loop.exec();
}
s_manual_w = manual_w_spin->value();
s_manual_l = manual_l_spin->value();
s_manual_h = manual_h_spin->value();
s_active_dialog = nullptr;
}
}