Motion analysis review round 1: unwrap only followers and past half a turn, studies by id, refuse keys between steps, count mesh placement work, duration checked in the dialog, Browser joint pick charted
This commit is contained in:
1 parent
f5871608f4
commit
7119d60d9d
7 files changed
+94
-25
No files matched your search
@@ -155,6 +155,11 @@ impl ContactGeometry {
|
||||
self.pairs.retain(|(_, _, a, b)| keep(a.0, b.0));
|
||||
}
|
||||
|
||||
/// How many mesh points are placed for each check.
|
||||
pub(super) fn vertex_count(&self) -> usize {
|
||||
self.bodies.iter().map(|b| b.mesh.positions.len()).fold(0, usize::saturating_add)
|
||||
}
|
||||
|
||||
/// How many pairs of set members are checked against each other.
|
||||
pub(super) fn pair_count(&self) -> usize {
|
||||
self.pairs.iter().map(|(_, _, a, b)| (a.0, b.0)).collect::<BTreeSet<_>>().len()
|
||||
@@ -242,6 +247,8 @@ impl ContactGeometry {
|
||||
Ok(Mesh { positions, triangles: body.mesh.triangles.clone(), ..Mesh::default() })
|
||||
})
|
||||
.collect::<Result<_>>()?;
|
||||
// Each mesh's box once, not once per pair it is in.
|
||||
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)) {
|
||||
@@ -250,7 +257,9 @@ impl ContactGeometry {
|
||||
let (Some(ma), Some(mb), Some(ba), Some(bb)) = (meshes.get(*a), meshes.get(*b), self.bodies.get(*a), self.bodies.get(*b)) else {
|
||||
return Err(limit("invalid contact geometry pair"));
|
||||
};
|
||||
let (aabb, bbb) = (ma.bounds(), mb.bounds());
|
||||
let (Some(aabb), Some(bbb)) = (boxes.get(*a), boxes.get(*b)) else {
|
||||
return Err(limit("invalid contact geometry pair"));
|
||||
};
|
||||
if aabb.max.x < bbb.min.x
|
||||
|| bbb.max.x < aabb.min.x
|
||||
|| aabb.max.y < bbb.min.y
|
||||
|
||||
@@ -128,12 +128,14 @@ fn second_derivative(v: &[f64], h: f64) -> Vec<f64> {
|
||||
}
|
||||
|
||||
/// Angles in degrees made continuous: a jump of more than half a turn between samples is taken
|
||||
/// as the angle going round.
|
||||
/// as the angle going round (half a turn exactly is a real step).
|
||||
fn unwrap_degrees(v: &mut [f64]) {
|
||||
let (mut turns, mut prev) = (0.0, None::<f64>);
|
||||
for x in v.iter_mut() {
|
||||
let raw = *x;
|
||||
if let Some(p) = prev {
|
||||
if let Some(p) = prev
|
||||
&& (raw - p).abs() > 180.0
|
||||
{
|
||||
turns -= 360.0 * ((raw - p) / 360.0).round();
|
||||
}
|
||||
prev = Some(raw);
|
||||
@@ -175,6 +177,7 @@ pub(super) fn plot(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
};
|
||||
let series = plotted(s, p, cmd)?;
|
||||
let given = keyed(&m);
|
||||
let point = if p.get("point").is_some() || p.get("occurrence").is_some() { Some(traced_point(s, p, cmd)?) } else { None };
|
||||
let mut values: Vec<Vec<f64>> = vec![Vec::new(); series.len()];
|
||||
let mut path: Vec<Vec3> = Vec::new();
|
||||
@@ -208,7 +211,11 @@ 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());
|
||||
unwrap_degrees(v);
|
||||
// Keyed values are as the study sets them; loops can hand back a follower's
|
||||
// angle a turn round.
|
||||
if !given.contains(&(*id, *i)) {
|
||||
unwrap_degrees(v);
|
||||
}
|
||||
}
|
||||
json!({"joint": id, "name": j.map(|j| j.name.clone()), "index": i, "dof": j.and_then(|j| j.kind.dofs().get(*i).copied()),
|
||||
"unit": unit, "velocity_unit": format!("{unit}/{per}"), "acceleration_unit": format!("{unit}/{per}²"),
|
||||
@@ -347,6 +354,12 @@ pub(super) fn interference(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut found: BTreeMap<(u64, u64), Collision> = BTreeMap::new();
|
||||
let (mut work, mut checked, mut truncated) = (0usize, 0usize, false);
|
||||
let stop = play(s, &m, n, false, |t, k| {
|
||||
// Placing the meshes is work too, also where nothing comes near.
|
||||
work = work.saturating_add(geometry.vertex_count());
|
||||
if work > MAX_WORK {
|
||||
truncated = true;
|
||||
return Ok(false);
|
||||
}
|
||||
let hits: Vec<(Member, Member, f64)> = match geometry.hits(t, &mut work, MAX_WORK, true) {
|
||||
Ok(h) => h,
|
||||
Err(_) if work > MAX_WORK => {
|
||||
@@ -428,5 +441,9 @@ mod tests {
|
||||
let mut v = vec![170.0, 179.0, -172.0, -160.0, 175.0];
|
||||
unwrap_degrees(&mut v);
|
||||
assert_eq!(v, vec![170.0, 179.0, 188.0, 200.0, 175.0]);
|
||||
// Half a turn a sample is a step, not a wrap.
|
||||
let mut v = vec![0.0, 180.0, 360.0];
|
||||
unwrap_degrees(&mut v);
|
||||
assert_eq!(v, vec![0.0, 180.0, 360.0]);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user