Motion analysis review round 4: unwrap only followers within half a turn, found pairs in a set, key values kept in full
This commit is contained in:
1 parent
c34e607f5b
commit
3f81495514
5 files changed
+34
-6
No files matched your search
@@ -238,6 +238,8 @@ impl ContactGeometry {
|
||||
/// adds up the cost, refused past `budget`.
|
||||
pub(super) fn hits(&self, s: &Session, work: &mut usize, budget: usize, all: bool) -> Result<Vec<(Member, Member, f64)>> {
|
||||
let mut out: Vec<(Member, Member, f64)> = Vec::new();
|
||||
// The member pairs found so far (lower id first).
|
||||
let mut found: BTreeSet<(u64, u64)> = BTreeSet::new();
|
||||
let meshes: Vec<Mesh> = self
|
||||
.bodies
|
||||
.iter()
|
||||
@@ -251,7 +253,7 @@ impl ContactGeometry {
|
||||
let boxes: Vec<Aabb3> = meshes.iter().map(Mesh::bounds).collect();
|
||||
for (a, b, oa, ob) in &self.pairs {
|
||||
// Members already found colliding need no more of their bodies tested.
|
||||
if out.iter().any(|(x, y, _)| (x.0, y.0) == (oa.0, ob.0) || (x.0, y.0) == (ob.0, oa.0)) {
|
||||
if found.contains(&(oa.0.min(ob.0), oa.0.max(ob.0))) {
|
||||
continue;
|
||||
}
|
||||
let (Some(ma), Some(mb), Some(ba), Some(bb)) = (meshes.get(*a), meshes.get(*b), self.bodies.get(*a), self.bodies.get(*b)) else {
|
||||
@@ -280,6 +282,7 @@ impl ContactGeometry {
|
||||
return Err(limit("motion exceeds contact sweep collision budget; simplify the contact set or use smaller increments"));
|
||||
}
|
||||
if let Some(depth) = super::mesh_overlap(ma, mb, 1e-4 * ba.size.max(bb.size).max(1.0)) {
|
||||
found.insert((oa.0.min(ob.0), oa.0.max(ob.0)));
|
||||
out.push((oa.clone(), ob.clone(), depth));
|
||||
if !all {
|
||||
break;
|
||||
|
||||
@@ -212,9 +212,10 @@ pub(super) fn plot(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let unit = if angle { "deg" } else { "mm" };
|
||||
if angle {
|
||||
v.iter_mut().for_each(|x| *x = x.to_degrees());
|
||||
// Keyed values are as the study sets them; loops can hand back a follower's
|
||||
// angle a turn round.
|
||||
if !given.contains(&(*id, *i)) {
|
||||
// Keyed values are as the study sets them, and values beyond half a turn
|
||||
// (through motion links) already run on; a loop can hand back a follower's
|
||||
// angle within half a turn, a turn round.
|
||||
if !given.contains(&(*id, *i)) && v.iter().all(|x| x.abs() <= 180.0 + 1e-9) {
|
||||
unwrap_degrees(v);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_geom::Vec3;
|
||||
|
||||
use super::{four_bar, occ, rocker_pin, run, slider_crank};
|
||||
use super::{four_bar, occ, part, pin, rocker_pin, run, slider_crank};
|
||||
use crate::Session;
|
||||
|
||||
/// A joint's series in a plot, by joint name.
|
||||
@@ -100,6 +100,20 @@ fn slider_crank_piston_acceleration_matches_its_analytic_form() {
|
||||
assert!(nums(&crank["velocity"]).iter().all(|v| (v - 1.0).abs() < 1e-9));
|
||||
}
|
||||
|
||||
/// A dial linked 2:1 to the crank turns on past a turn: its values are not taken a turn round,
|
||||
/// even half a turn a sample.
|
||||
#[test]
|
||||
fn a_linked_value_past_half_a_turn_runs_on() {
|
||||
let (mut s, _, _) = four_bar();
|
||||
part(&mut s, "Dial", [80.0, 0.0, -5.0]);
|
||||
pin(&mut s, "revolute", "Dial", "Ground", "Dial", Vec3::new(80.0, 0.0, 0.0), Vec3::Z);
|
||||
run(&mut s, "joint.motion_link", json!({"a": "Crank", "b": "Dial", "ratio": 2}));
|
||||
run(&mut s, "motion.study", json!({"name": "Turn", "steps": 2, "keys": [{"joint": "Crank", "points": [[0, 0], [2, 360]]}]}));
|
||||
let r = run(&mut s, "motion.plot", json!({"study": "Turn", "joints": ["Dial"], "samples": 3}));
|
||||
let v: Vec<f64> = nums(&series(&r, "Dial")["value"]).iter().map(|x| x.abs()).collect();
|
||||
assert!((v[1] - 360.0).abs() < 1e-6 && (v[2] - 720.0).abs() < 1e-6, "{r}");
|
||||
}
|
||||
|
||||
/// Cubes A (grounded, x 0…10) and B (x 20…30) on a slider along x, B 10 mm away at value 10.
|
||||
fn rail() -> Session {
|
||||
let mut s = Session::default();
|
||||
|
||||
Reference in new issue
Block a user