better flat

This commit is contained in:
IsseW 2022-10-29 18:13:47 +02:00 committed by Isse
parent 6d2b39c254
commit 179c6dab8f
6 changed files with 195 additions and 145 deletions

View File

@ -189,10 +189,7 @@ impl Civs {
SiteKind::GiantTree => (12i32, 8.0), SiteKind::GiantTree => (12i32, 8.0),
SiteKind::Gnarling => (16i32, 10.0), SiteKind::Gnarling => (16i32, 10.0),
SiteKind::Citadel => (16i32, 0.0), SiteKind::Citadel => (16i32, 0.0),
SiteKind::Bridge(start, end) => { SiteKind::Bridge(_, _) => (0, 0.0),
let e = (end - start).map(|e| e.abs()).reduce_max();
((e + 1) / 2, ((e + 1) / 2) as f32 / 2.0)
},
}; };
let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind { let (raise, raise_dist, make_waypoint): (f32, i32, bool) = match &site.kind {
@ -1264,9 +1261,9 @@ fn walk_in_dir(
3.0 // + (1.0 - b_chunk.tree_density) * 20.0 // Prefer going through forests, for aesthetics 3.0 // + (1.0 - b_chunk.tree_density) * 20.0 // Prefer going through forests, for aesthetics
}; };
Some((a + dir, 1.0 + hill_cost + water_cost + wild_cost)) Some((a + dir, 1.0 + hill_cost + water_cost + wild_cost))
} else if dir.x == 0 || dir.y == 0 { } else if NEIGHBORS.iter().all(|p| get_bridge(a + p).is_none()) && (dir.x == 0 || dir.y == 0) {
(4..=5).find_map(|i| { (4..=5).find_map(|i| {
loc_suitable_for_walking(sim, a + dir * i).then(|| (a + dir * i, i as f32 * 30.0)) loc_suitable_for_walking(sim, a + dir * i).then(|| (a + dir * i, 400.0 + (i - 4) as f32 * 10.0))
}) })
} else { } else {
None None
@ -1276,13 +1273,10 @@ fn walk_in_dir(
/// Return true if a position is suitable for walking on /// Return true if a position is suitable for walking on
fn loc_suitable_for_walking(sim: &WorldSim, loc: Vec2<i32>) -> bool { fn loc_suitable_for_walking(sim: &WorldSim, loc: Vec2<i32>) -> bool {
if sim.get(loc).is_some() { if sim.get(loc).is_some() {
NEIGHBORS !NEIGHBORS.iter().any(|n| {
.iter() sim.get(loc + *n)
.find(|n| { .map_or(false, |chunk| chunk.river.near_water())
sim.get(loc + *n) })
.map_or(false, |chunk| chunk.river.near_water())
})
.is_none()
} else { } else {
false false
} }
@ -1626,7 +1620,7 @@ impl Site {
} }
pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) } pub fn is_castle(&self) -> bool { matches!(self.kind, SiteKind::Castle) }
pub fn is_bridge(&self) -> bool { matches!(self.kind, SiteKind::Bridge(_, _)) } pub fn is_bridge(&self) -> bool { matches!(self.kind, SiteKind::Bridge(_, _)) }
} }

View File

@ -1,7 +1,8 @@
use crate::{ use crate::{
column::ColumnSample, column::ColumnSample,
sim::{RiverKind, WorldSim}, sim::{RiverKind, WorldSim},
CONFIG, IndexRef, site::SiteKind, site::SiteKind,
IndexRef, CONFIG,
}; };
use common::{ use common::{
terrain::{ terrain::{
@ -118,17 +119,21 @@ pub fn sample_pos(
sample.river.river_kind, sample.river.river_kind,
sample.river.spline_derivative, sample.river.spline_derivative,
sample.path.0.is_way(), sample.path.0.is_way(),
sample.sites.iter().any(|site| match &index.sites.get(*site).kind { sample
SiteKind::Bridge(bridge) => if let Some(plot) = bridge.wpos_tile(TerrainChunkSize::center_wpos(pos)).plot { .sites
match bridge.plot(plot).kind { .iter()
crate::site2::PlotKind::Bridge(_) => true, .any(|site| match &index.sites.get(*site).kind {
_ => false SiteKind::Bridge(bridge) => {
} if let Some(plot) =
} else { bridge.wpos_tile(TerrainChunkSize::center_wpos(pos)).plot
false {
}, matches!(bridge.plot(plot).kind, crate::site2::PlotKind::Bridge(_))
_ => false, } else {
}), false
}
},
_ => false,
}),
) )
}) })
.unwrap_or(( .unwrap_or((

View File

@ -15,7 +15,7 @@ use common::{
vol::ReadVol, vol::ReadVol,
}; };
use num::cast::AsPrimitive; use num::cast::AsPrimitive;
use std::{cell::RefCell, sync::Arc, ops::RangeBounds}; use std::{cell::RefCell, ops::RangeBounds, sync::Arc};
use vek::*; use vek::*;
#[allow(dead_code)] #[allow(dead_code)]
@ -386,8 +386,7 @@ impl Fill {
Self::contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub)) Self::contains_at(tree, *prim, pos.map2(*vec, i32::saturating_sub))
}, },
Primitive::Scale(prim, vec) => { Primitive::Scale(prim, vec) => {
let center = let center = Self::get_bounds(tree, *prim).as_::<f32>().center();
Self::get_bounds(tree, *prim).as_::<f32>().center();
let fpos = pos.as_::<f32>(); let fpos = pos.as_::<f32>();
let spos = (center + ((fpos - center) / vec)) let spos = (center + ((fpos - center) / vec))
.map(|x| x.round()) .map(|x| x.round())
@ -779,9 +778,8 @@ impl Painter {
self.prim(Primitive::Cylinder(aabb.made_valid())) self.prim(Primitive::Cylinder(aabb.made_valid()))
} }
/// Returns a `PrimitiveRef` of the largest horizontal cylinder that fits in
/// Returns a `PrimitiveRef` of the largest horizontal cylinder that fits in the /// the provided Aabb.
/// provided Aabb.
pub fn horizontal_cylinder(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef { pub fn horizontal_cylinder(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef {
let aabr = Aabr::from(aabb); let aabr = Aabr::from(aabb);
let length = dir.select(aabr.size()); let length = dir.select(aabr.size());
@ -790,7 +788,8 @@ impl Painter {
min: (aabr.min - dir.abs().to_vec2() * height).with_z(aabb.min.z), min: (aabr.min - dir.abs().to_vec2() * height).with_z(aabb.min.z),
max: (dir.abs().select_with(aabr.min, aabr.max)).with_z(aabb.min.z + length), max: (dir.abs().select_with(aabr.min, aabr.max)).with_z(aabb.min.z + length),
}; };
self.cylinder(aabb).rotate_about((-dir.abs()).from_z_mat3(), aabr.min.with_z(aabb.min.z)) self.cylinder(aabb)
.rotate_about((-dir.abs()).from_z_mat3(), aabr.min.with_z(aabb.min.z))
} }
/// Returns a `PrimitiveRef` of a cylinder using a radius check where a /// Returns a `PrimitiveRef` of a cylinder using a radius check where a
@ -1072,40 +1071,21 @@ impl Painter {
/// |_____|/ /// |_____|/
/// ``` /// ```
pub fn vault(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef { pub fn vault(&self, aabb: Aabb<i32>, dir: Dir) -> PrimitiveRef {
let aabr = Aabr::from(aabb); let h = dir.orthogonal().select(Vec3::from(aabb.size()).xy());
let a = dir.select_aabr(aabr);
let b = (-dir).select_aabr(aabr);
let dmin = a.min(b);
let dmax = a.max(b);
let length = dmax - dmin;
let c = dir.rotated_cw().select_aabr(aabr); let mut prim = self.horizontal_cylinder(Aabb {
let d = dir.rotated_ccw().select_aabr(aabr); min: aabb.min.with_z(aabb.max.z - h),
let omin = c.min(d); max: aabb.max,
let omax = c.max(d); }, dir);
let diameter = omax - omin;
let radius = (diameter + 1) / 2;
let min = (a * dir.to_vec2() + c * dir.orthogonal().to_vec2()).with_z(aabb.max.z);
self.cylinder( if aabb.size().d < h {
Aabb { prim = prim.intersect(self.aabb(aabb));
min, }
max: (a * dir.to_vec2()
+ diameter * (-dir).to_vec2() self.aabb(Aabb {
+ d * dir.orthogonal().to_vec2())
.with_z(aabb.max.z + length),
}
.made_valid(),
)
.rotate_about(dir.abs().from_z_mat3(), min)
.without(self.aabb(Aabb {
min: aabb.min.with_z(aabb.min.z - radius),
max: aabb.max.with_z(aabb.min.z),
}))
.union(self.aabb(Aabb {
min: aabb.min, min: aabb.min,
max: aabb.max.with_z(aabb.max.z - radius), max: aabb.max.with_z(aabb.max.z - h / 2),
})) }).union(prim)
} }
/// Place aabbs around another aabb in a symmetric and distributed manner. /// Place aabbs around another aabb in a symmetric and distributed manner.
@ -1293,7 +1273,9 @@ impl<'a> PrimitiveTransform for PrimitiveRef<'a> {
self.painter.prim(Primitive::rotate_about(self, rot, point)) self.painter.prim(Primitive::rotate_about(self, rot, point))
} }
fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self { self.painter.prim(Primitive::scale(self, scale.as_())) } fn scale(self, scale: Vec3<impl AsPrimitive<f32>>) -> Self {
self.painter.prim(Primitive::scale(self, scale.as_()))
}
fn repeat(self, offset: Vec3<i32>, count: u32) -> Self { fn repeat(self, offset: Vec3<i32>, count: u32) -> Self {
self.painter.prim(Primitive::repeat(self, offset, count)) self.painter.prim(Primitive::repeat(self, offset, count))

View File

@ -1018,7 +1018,6 @@ impl Site {
max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width, max: start_tile.map2(end_tile, |a, b| a.max(b)) + 1 + orth * width,
}; };
let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile); let bridge = plot::Bridge::generate(land, index, &mut rng, &site, start_tile, end_tile);
let start_tile = site.wpos_tile_pos(bridge.start.xy()); let start_tile = site.wpos_tile_pos(bridge.start.xy());
@ -1044,7 +1043,7 @@ impl Site {
(-bridge.dir).select_aabr_with(start_aabr, aabr.center()), (-bridge.dir).select_aabr_with(start_aabr, aabr.center()),
2, 2,
); );
let plot = site.create_plot(Plot { let plot = site.create_plot(Plot {
kind: PlotKind::Bridge(bridge), kind: PlotKind::Bridge(bridge),
root_tile: start_tile, root_tile: start_tile,

View File

@ -25,16 +25,20 @@ struct HeightenedViaduct {
} }
impl HeightenedViaduct { impl HeightenedViaduct {
fn random(rng: &mut impl Rng) -> Self { fn random(rng: &mut impl Rng, height: i32) -> Self {
let vault_spacing = *[3, 4, 5, 7].choose(rng).unwrap();
Self { Self {
slope_inv: rng.gen_range(6..=8), slope_inv: rng.gen_range(6..=8),
bridge_start_offset: rng.gen_range(5..=12), bridge_start_offset: rng.gen_range({
vault_spacing: rng.gen_range(3..=4), let min = (5 - height / 3).max(0);
min..=(12 - height).max(min)
}),
vault_spacing,
vault_size: *[(3, 16), (1, 4), (1, 4), (1, 4), (5, 32), (5, 32)] vault_size: *[(3, 16), (1, 4), (1, 4), (1, 4), (5, 32), (5, 32)]
.choose(rng) .choose(rng)
.unwrap(), .unwrap(),
side_vault_size: *[(4, 5), (7, 10), (7, 10), (13, 20)].choose(rng).unwrap(), side_vault_size: *[(4, 5), (7, 10), (7, 10), (13, 20)].choose(rng).unwrap(),
holes: rng.gen_bool(0.5), holes: vault_spacing > 4 && rng.gen_bool(0.8),
} }
} }
} }
@ -53,25 +57,32 @@ impl BridgeKind {
start: Vec3<i32>, start: Vec3<i32>,
center: Vec3<i32>, center: Vec3<i32>,
end: Vec3<i32>, end: Vec3<i32>,
name: &str,
) -> BridgeKind { ) -> BridgeKind {
let len = (start.xy() - end.xy()).map(|e| e.abs()).reduce_max(); let len = (start.xy() - end.xy()).map(|e| e.abs()).reduce_max();
let height = end.z - start.z;
let down = start.z - center.z;
(0..=3) (0..=3)
.filter_map(|bridge| match bridge { .filter_map(|bridge| match bridge {
0 if end.z - start.z > 17 => Some(BridgeKind::Tower(match rng.gen_range(0..=2) { 0 if height >= 16 => Some(BridgeKind::Tower(match rng.gen_range(0..=2) {
0 => RoofKind::Crenelated, 0 => RoofKind::Crenelated,
_ => RoofKind::Hipped, _ => RoofKind::Hipped,
})), })),
1 if len < 40 => Some(BridgeKind::Short), 1 if len < 40 => Some(BridgeKind::Short),
2 if end.z - start.z < 5 && start.z - center.z < 16 => Some( 2 if height < 15 && down < 16 => Some(BridgeKind::HeightenedViaduct(
BridgeKind::HeightenedViaduct(HeightenedViaduct::random(rng)), HeightenedViaduct::random(rng, height),
), )),
3 if end.z - start.z < 9 && start.z - center.z > 10 => Some(BridgeKind::HangBridge), 3 if height < 9 && down > 10 => Some(BridgeKind::HangBridge),
_ => None, _ => None,
}) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_iter() .into_iter()
.choose(rng) .choose(rng)
.unwrap_or(BridgeKind::Flat) .unwrap_or_else(|| {
println!("Pick: {name}\n\tlen: {len}\n\theight: {height}\n\tdown: {down}\n");
BridgeKind::Flat
})
} }
fn width(&self) -> i32 { fn width(&self) -> i32 {
@ -190,59 +201,89 @@ fn render_short(bridge: &Bridge, painter: &Painter) {
fn render_flat(bridge: &Bridge, painter: &Painter) { fn render_flat(bridge: &Bridge, painter: &Painter) {
let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50))); let rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(50)));
let light_rock = Fill::Block(Block::new(BlockKind::Rock, Rgb::gray(130)));
let dz = bridge.end.z - bridge.start.z; let orth_dir = bridge.dir.orthogonal();
let orthogonal = bridge.dir.orthogonal().to_vec2();
let orthogonal = orth_dir.to_vec2();
let forward = bridge.dir.to_vec2(); let forward = bridge.dir.to_vec2();
let inset = 5;
let len = (bridge.end.xy() - bridge.start.xy()) let height = bridge.end.z - bridge.start.z;
.map(|e| e.abs())
.reduce_max();
let upset = bridge.end.z - bridge.start.z;
let size = tweak!(8); let bridge_width = bridge.width();
let hole = painter let side = orthogonal * bridge_width;
.cylinder(aabb(
bridge.center.with_z(bridge.end.z - 3 - bridge.width() * 2) - Vec2::broadcast(size), let aabr = Aabr {
bridge.center.with_z(bridge.end.z + 2) + Vec2::broadcast(size), min: bridge.start.xy() - side,
)) max: bridge.end.xy() + side,
.rotate_about( }
bridge.dir.orthogonal().from_z_mat3(), .made_valid();
bridge
.center let [ramp_aabr, aabr] = bridge.dir.split_aabr(aabr, height);
.as_()
.with_z(bridge.end.z as f32 - 1.5 - bridge.width() as f32), let ramp_prim = |ramp_aabr: Aabr<i32>, offset: i32| {
painter
.aabb(aabb(
ramp_aabr.min.with_z(bridge.start.z - 10 + offset),
ramp_aabr.max.with_z(bridge.start.z - 1 + offset),
))
.union(painter.ramp(
aabb(
ramp_aabr.min.with_z(bridge.start.z + offset),
ramp_aabr.max.with_z(bridge.end.z + offset),
),
height + 1,
bridge.dir,
))
};
ramp_prim(ramp_aabr, 1).fill(light_rock.clone());
let ramp_aabr = orth_dir.trim_aabr(ramp_aabr, 1);
ramp_prim(ramp_aabr, 5).clear();
ramp_prim(ramp_aabr, 0).fill(rock.clone());
let vault_width = 12;
let vault_offset = 5;
let bridge_thickness = 4;
let [vault, _] = bridge.dir.split_aabr(aabr, vault_width);
let len = bridge.dir.select(aabr.size());
let true_offset = vault_width + vault_offset;
let n = len / true_offset;
let p = len / n;
let holes = painter
.vault(
aabb(
vault.min.with_z(bridge.center.z - 20),
vault.max.with_z(bridge.end.z - bridge_thickness - 1),
),
orth_dir,
) )
.scale( .repeat((forward * p).with_z(0), n as u32);
bridge.dir.abs().to_vec3().as_()
* ((len as f32 - upset as f32 * tweak!(2.0)) / (size as f32 * 2.0) - 1.0)
+ 1.0,
);
painter painter
.ramp( .aabb(aabb(
aabb( aabr.min.with_z(bridge.center.z - 10),
bridge.start.with_z(bridge.start.z - inset) aabr.max.with_z(bridge.end.z + 1),
- orthogonal * bridge.width() ))
- forward * inset, .without(holes)
bridge.start.with_z(bridge.end.z) + orthogonal * bridge.width() + forward * dz, .fill(light_rock);
),
dz + inset + 1, let aabr = orth_dir.trim_aabr(aabr, 1);
bridge.dir, painter
) .aabb(aabb(
.union( aabr.min.with_z(bridge.end.z + 1),
painter aabr.max.with_z(bridge.end.z + 8),
.aabb(aabb( )).clear();
bridge.start.with_z(bridge.end.z - 3 - size) - orthogonal * bridge.width()
+ forward * dz, painter
bridge.start.with_z(bridge.end.z) .aabb(aabb(
+ orthogonal * bridge.width() aabr.min.with_z(bridge.end.z),
+ forward * forward * (bridge.end.xy() - bridge.start.xy()), aabr.max.with_z(bridge.end.z),
)) )).fill(rock);
.without(hole),
)
.fill(rock);
} }
fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &HeightenedViaduct) { fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &HeightenedViaduct) {
@ -262,12 +303,6 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
let bridge_start_z = bridge.end.z + data.bridge_start_offset; let bridge_start_z = bridge.end.z + data.bridge_start_offset;
let bridge_top = bridge_start_z + len / slope_inv / 2; let bridge_top = bridge_start_z + len / slope_inv / 2;
// painter.ramp(aabb(
// bridge.start - side,
// (bridge.start.xy() + side + forward * (bridge_start_z -
// bridge.start.z)).with_z(bridge_start_z), ), bridge_start_z -
// bridge.start.z, bridge.dir).fill(rock);
let bridge_width = bridge.width(); let bridge_width = bridge.width();
let side = orthogonal * bridge_width; let side = orthogonal * bridge_width;
@ -415,8 +450,8 @@ fn render_heightened_viaduct(bridge: &Bridge, painter: &Painter, data: &Heighten
} }
} }
bridge_prim(bridge_width).without(remove).fill(rock.clone()); bridge_prim(bridge_width).without(remove).fill(rock);
b.translate(-Vec3::unit_z()).fill(light_rock.clone()); b.translate(-Vec3::unit_z()).fill(light_rock);
br.translate(Vec3::unit_z() * 5) br.translate(Vec3::unit_z() * 5)
.without(br.translate(-Vec3::unit_z())) .without(br.translate(-Vec3::unit_z()))
@ -625,7 +660,7 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
(tower_aabr.min - 1).with_z(tower_end + 1), (tower_aabr.min - 1).with_z(tower_end + 1),
(tower_aabr.max + 1).with_z(tower_end + 2 + tower_size), (tower_aabr.max + 1).with_z(tower_end + 2 + tower_size),
)) ))
.fill(wood.clone()); .fill(wood);
}, },
} }
@ -650,14 +685,14 @@ fn render_tower(bridge: &Bridge, painter: &Painter, roof_kind: &RoofKind) {
painter painter
.vault( .vault(
aabb( aabb(
(start + offset - size).with_z(bridge.center.z - 10), (start + offset / 2 - size).with_z(bridge.center.z - 10),
(start + offset + size).with_z(bridge.end.z - 3), (start + offset / 2 + size).with_z(bridge.end.z - 3),
), ),
orth_dir, orth_dir,
) )
.repeat(offset.with_z(0), n as u32), .repeat(offset.with_z(0), n as u32),
) )
.fill(rock.clone()); .fill(rock);
painter painter
.aabb(aabb( .aabb(aabb(
@ -740,8 +775,10 @@ fn render_hang(bridge: &Bridge, painter: &Painter) {
let xsqr = (x * x) as f32; let xsqr = (x * x) as f32;
let hsqr = (h * h) as f32; let hsqr = (h * h) as f32;
let w = ((xsqr + (xsqr * (4.0 * hsqr + xsqr)).sqrt()) / 2.0).sqrt().ceil() + 1.0; let w = ((xsqr + (xsqr * (4.0 * hsqr + xsqr)).sqrt()) / 2.0)
.sqrt()
.ceil()
+ 1.0;
let bottom = top - (h - (hsqr - hsqr * x as f32 / w).sqrt().ceil() as i32); let bottom = top - (h - (hsqr - hsqr * x as f32 / w).sqrt().ceil() as i32);
@ -756,7 +793,10 @@ fn render_hang(bridge: &Bridge, painter: &Painter) {
), ),
orth_dir, orth_dir,
) )
.intersect(painter.aabb(aabb(aabr.min.with_z(bottom), aabr.max.with_z(bottom + h * 2)))); .intersect(painter.aabb(aabb(
aabr.min.with_z(bottom),
aabr.max.with_z(bottom + h * 2),
)));
cylinder.fill(wood.clone()); cylinder.fill(wood.clone());
@ -773,14 +813,34 @@ fn render_hang(bridge: &Bridge, painter: &Painter) {
.translate(Vec3::unit_z()) .translate(Vec3::unit_z())
.fill(Fill::Sprite(SpriteKind::Rope)); .fill(Fill::Sprite(SpriteKind::Rope));
edges.translate(Vec3::unit_z() * 2).fill(wood.clone()); edges.translate(Vec3::unit_z() * 2).fill(wood);
let column_height = 3; let column_height = 3;
let column_range = top..=top + column_height; let column_range = top..=top + column_height;
painter.column(bridge.dir.select_aabr_with(ramp_f, ramp_f.min), column_range.clone()).fill(rock.clone()); painter
painter.column(bridge.dir.select_aabr_with(ramp_f, ramp_f.max), column_range.clone()).fill(rock.clone()); .column(
painter.column((-bridge.dir).select_aabr_with(ramp_b, ramp_b.min), column_range.clone()).fill(rock.clone()); bridge.dir.select_aabr_with(ramp_f, ramp_f.min),
painter.column((-bridge.dir).select_aabr_with(ramp_b, ramp_b.max), column_range.clone()).fill(rock.clone()); column_range.clone(),
)
.fill(rock.clone());
painter
.column(
bridge.dir.select_aabr_with(ramp_f, ramp_f.max),
column_range.clone(),
)
.fill(rock.clone());
painter
.column(
(-bridge.dir).select_aabr_with(ramp_b, ramp_b.min),
column_range.clone(),
)
.fill(rock.clone());
painter
.column(
(-bridge.dir).select_aabr_with(ramp_b, ramp_b.max),
column_range,
)
.fill(rock);
} }
pub struct Bridge { pub struct Bridge {
@ -852,7 +912,7 @@ impl Bridge {
let center = (start.xy() + end.xy()) / 2; let center = (start.xy() + end.xy()) / 2;
let center = center.with_z(land.get_alt_approx(center) as i32); let center = center.with_z(land.get_alt_approx(center) as i32);
let bridge = BridgeKind::random(rng, start, center, end); let bridge = BridgeKind::random(rng, start, center, end, site.name());
Self { Self {
start, start,
end, end,

View File

@ -221,7 +221,10 @@ impl Dir {
} }
} }
pub fn split_aabr<T>(self, aabr: Aabr<T>, offset: T) -> [Aabr<T>; 2] where T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T> { pub fn split_aabr<T>(self, aabr: Aabr<T>, offset: T) -> [Aabr<T>; 2]
where
T: Copy + PartialOrd + Add<T, Output = T> + Sub<T, Output = T>,
{
match self { match self {
Dir::X => aabr.split_at_x(aabr.min.x + offset), Dir::X => aabr.split_at_x(aabr.min.x + offset),
Dir::Y => aabr.split_at_y(aabr.min.y + offset), Dir::Y => aabr.split_at_y(aabr.min.y + offset),
@ -236,6 +239,13 @@ impl Dir {
} }
} }
pub fn trim_aabr(self, aabr: Aabr<i32>, offset: i32) -> Aabr<i32> {
Aabr {
min: aabr.min + self.abs().to_vec2() * offset,
max: aabr.max - self.abs().to_vec2() * offset,
}
}
pub fn extend_aabr(self, aabr: Aabr<i32>, amount: i32) -> Aabr<i32> { pub fn extend_aabr(self, aabr: Aabr<i32>, amount: i32) -> Aabr<i32> {
match self { match self {
Dir::X => Aabr { Dir::X => Aabr {