Wildlife and nature: animals, beehives, bushes, flowers, rocks, logs
Deploy web Studio / deploy (push) Successful in 1m5s
Deploy web Studio / deploy (push) Successful in 1m5s
New stdlib module wildlife.rhai: - Animals (new palette category): Deer (stag with antlers, does, fawns), Wild boar, Fox, Elephant (bull with tusks, cow, calf), Rabbits. Low-poly solids from faces (loft, tube), parts drawn back to front by depth, faces edged in their own shade (class beast). Herds scatter round a spot, each animal its own sprite facing about the object's rotation. Grazing animals switch between two head poses (iso-pose2 groups), elephants curl their trunks, rabbits hop. - Birds and Butterflies as flocks: swim() takes a shape (bird, fly, bee); wings flap out of step. Fish output unchanged. - Beehives on stands with supers, lids, entrances and bees flying round; Bush (round, flowering, berries); Flowers (bed in rows or meadow); Rocks; Log or stump. g-meadow and g-soil ground materials. - polyline() takes 3D points as documented (antlers). Theme colours for dark and light, viewer CSS for poses, flapping, hopping. examples/country: elephants in a paddock with a cut corner, deer at a forest edge, boar, fox, rabbits, an apiary by a meadow, a pond, a lane, fence and hedgerow at angles; Country demo in the web Studio and deploy. MCP guidance, README, SCRIPTING, ROADMAP. Tests: Rust 78, Studio 122; existing examples draw the same (only the embedded theme CSS grew).
This commit is contained in:
1 parent
96d472625f
commit
e840d36000
22 files changed
+1211
-16
No files matched your search
@@ -470,6 +470,7 @@ pub fn register_sprite(e: &mut Engine) {
|
||||
.get("cls")
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or_else(|| "koi".into()),
|
||||
shape: m.get("shape").map(|v| v.to_string()).unwrap_or_default(),
|
||||
});
|
||||
}
|
||||
s.0.borrow_mut().swim(n(&x)?, n(&y)?, n(&z)?, fish);
|
||||
|
||||
@@ -90,13 +90,31 @@ pub fn polys3(d: &Dynamic) -> RR<Vec<Vec<P3>>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// One line or several, of plan points `[x, y]` (put at height z) or 3D points.
|
||||
/// One line or several, of plan points `[x, y]` (put at height z) or 3D
|
||||
/// points `[x, y, z]` (antlers, branches).
|
||||
pub fn polys3_or_flat(d: &Dynamic, z: f64) -> RR<Vec<Vec<P3>>> {
|
||||
polys(d).map(|ls| {
|
||||
ls.into_iter()
|
||||
.map(|l| l.into_iter().map(|p| [p[0], p[1], z]).collect())
|
||||
.collect()
|
||||
})
|
||||
let a = arr(d, "polyline")?;
|
||||
let lines: Vec<&Dynamic> = match a.first() {
|
||||
None => return Ok(vec![]),
|
||||
Some(f) if is_point(f) => vec![d],
|
||||
_ => a.iter().collect(),
|
||||
};
|
||||
lines
|
||||
.into_iter()
|
||||
.map(|l| {
|
||||
arr(l, "polyline")?
|
||||
.iter()
|
||||
.map(|q| {
|
||||
let n = q.read_lock::<Array>().map(|v| v.len()).unwrap_or(0);
|
||||
if n >= 3 {
|
||||
p3(q)
|
||||
} else {
|
||||
p2(q).map(|p| [p[0], p[1], z])
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// `[[a, b, c, d], ...]` (segments, opening lists)
|
||||
|
||||
@@ -9,6 +9,7 @@ pub const MODULES: &[(&str, &str)] = &[
|
||||
("isokit/util", include_str!("../stdlib/util.rhai")),
|
||||
("isokit/ground", include_str!("../stdlib/ground.rhai")),
|
||||
("isokit/streets", include_str!("../stdlib/streets.rhai")),
|
||||
("isokit/wildlife", include_str!("../stdlib/wildlife.rhai")),
|
||||
("isokit/nature", include_str!("../stdlib/nature.rhai")),
|
||||
("isokit/building", include_str!("../stdlib/building.rhai")),
|
||||
("isokit/vehicles", include_str!("../stdlib/vehicles.rhai")),
|
||||
|
||||
@@ -858,6 +858,101 @@ fn piece(id: &str, kind: &str, a: [f64; 2], b: [f64; 2], props: Value) -> Object
|
||||
o
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn wildlife_and_garden_prefabs_draw_from_every_side() {
|
||||
let r = Runtime::new(&Bundle::default()).unwrap();
|
||||
let doc = Doc {
|
||||
objects: vec![
|
||||
obj("deer", "deer", -18.0, 8.0, json!({"n": 5, "r": 4})),
|
||||
obj(
|
||||
"stag",
|
||||
"deer",
|
||||
-8.0,
|
||||
12.0,
|
||||
json!({"n": 1, "mix": "stag", "graze": false}),
|
||||
),
|
||||
obj("boar", "boar", -18.0, -6.0, json!({"n": 3})),
|
||||
obj("fox", "fox", -8.0, -2.0, json!({})),
|
||||
obj(
|
||||
"el",
|
||||
"elephant",
|
||||
6.0,
|
||||
10.0,
|
||||
json!({"n": 3, "mix": "herd", "r": 5}),
|
||||
),
|
||||
obj("rab", "rabbits", -4.0, -12.0, json!({"n": 4})),
|
||||
obj("birds", "birds", 0.0, 0.0, json!({"n": 7})),
|
||||
obj("bf", "butterflies", 10.0, -12.0, json!({"n": 5})),
|
||||
obj("hives", "beehive", 16.0, -6.0, json!({"n": 4})),
|
||||
obj("bed", "flowers", 10.0, -12.0, json!({"w": 5, "d": 3})),
|
||||
obj(
|
||||
"mead",
|
||||
"flowers",
|
||||
20.0,
|
||||
-14.0,
|
||||
json!({"w": 8, "d": 5, "kind": "meadow"}),
|
||||
),
|
||||
obj("b1", "bush", 3.0, -4.0, json!({})),
|
||||
obj("b2", "bush", 5.0, -6.0, json!({"kind": "berries"})),
|
||||
obj("rk", "rocks", 22.0, 6.0, json!({"n": 4})),
|
||||
obj("lg", "log", 14.0, 4.0, json!({})),
|
||||
obj("st", "log", 18.0, 2.0, json!({"kind": "stump"})),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
for view in 0..4u8 {
|
||||
let b = r.build("site", view, &cfg(), &doc).unwrap();
|
||||
assert!(b.scene.warnings.is_empty(), "{:?}", b.scene.warnings);
|
||||
let out = render(&b.scene, view, &RenderOpts::default());
|
||||
assert!(!out.html.contains("NaN"));
|
||||
// each animal its own sprite: five deer, three boar, one fox, three elephants, four rabbits
|
||||
for (id, n) in [
|
||||
("deer", 5),
|
||||
("stag", 1),
|
||||
("boar", 3),
|
||||
("fox", 1),
|
||||
("el", 3),
|
||||
("rab", 4),
|
||||
] {
|
||||
assert_eq!(sprites_of(&b, id), n, "{id}");
|
||||
}
|
||||
// grazing animals carry two head poses taking turns, the first shown unanimated;
|
||||
// the standing stag and the rabbits have none
|
||||
assert_eq!(
|
||||
out.html.matches("iso-a iso-pose2 iso-pose0").count(),
|
||||
5 + 3 + 1 + 3
|
||||
);
|
||||
assert_eq!(out.html.matches("iso-a iso-pose2\"").count(), 5 + 3 + 1 + 3);
|
||||
assert_eq!(out.html.matches("iso-a iso-hop").count(), 4);
|
||||
// birds flap, butterflies flutter, bees fly round the hives
|
||||
assert_eq!(out.html.matches("iso-a iso-flap").count(), 7);
|
||||
assert_eq!(out.html.matches("iso-a iso-flutter").count(), 5);
|
||||
assert_eq!(out.html.matches("class=\"iso-bee iso-bee\"").count(), 4 * 8);
|
||||
// animal faces are edged in their own shade; antlers on the one stag
|
||||
assert!(out.html.contains("iso-deer iso-beast"));
|
||||
assert!(out.html.contains("iso-antler"));
|
||||
}
|
||||
let b = r.build("site", 0, &cfg(), &doc).unwrap();
|
||||
// a stag stands about 1.5 m at the head, antlers up to 2.1 m; it fits its sort box
|
||||
let st = b.boxes["stag"];
|
||||
assert!(st[5] > 1.9 && st[5] < 2.3, "{st:?}");
|
||||
// an elephant bull: about 3 m tall at the head
|
||||
let el = b.boxes["el"];
|
||||
assert!(el[5] > 2.6 && el[5] < 3.3, "{el:?}");
|
||||
let out = render(&b.scene, 0, &RenderOpts::default());
|
||||
let row = |id: &str, k: &str| {
|
||||
out.info[id]
|
||||
.rows
|
||||
.iter()
|
||||
.find(|r| r.0 == k)
|
||||
.map(|r| r.1.clone())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
assert_eq!(row("deer", "Herd"), "1 stag, 3 does, 1 fawn");
|
||||
assert_eq!(row("el", "Herd"), "1 bull, 1 cow, 1 calf");
|
||||
assert_eq!(row("hives", "Apiary"), "4 hives, 2 supers each");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diagonal_walls_fences_and_hedges_mitre_and_sort() {
|
||||
let r = Runtime::new(&Bundle::default()).unwrap();
|
||||
|
||||
Reference in new issue
Block a user