Intersection edge nodes advance along the curve at fine display qualities (#1615)
Thanks @jyviko.
This commit is contained in:
1 parent
61678654c2
commit
f57a62deb7
4 files changed
+1243
-15
No files matched your search
@@ -288,3 +288,27 @@ fn cut_of_a_whole_face_beside_round_corners_after_a_chamfer() {
|
||||
let top = mesh.bounds().max.z;
|
||||
assert!((top - 15.0).abs() < 1e-6, "top at {top}");
|
||||
}
|
||||
|
||||
/// The display tessellation of loft bodies joined into a bike frame plans at every display
|
||||
/// quality and every refinement step. The edge nodes on an intersection of two lofts once
|
||||
/// stepped back along the curve (the edge's fit rang where truck's parameter changes speed, and
|
||||
/// took a leader point off the curve past a vertex), so a face's boundary crossed itself and
|
||||
/// the body fell back to the reference mesher, which drew a sheet across the down tube.
|
||||
#[test]
|
||||
fn joined_loft_frames_plan_their_display_at_every_quality() {
|
||||
use solidcraft_doc::display_quality::{DisplayQuality, MAX_REFINE};
|
||||
for (script, name) in [
|
||||
(include_str!("../../../examples/regressions/loft_join_bracket_hull_triangle.json"), "DT2"),
|
||||
(include_str!("../../../examples/regressions/front_triangle_hull_triangle.json"), "FrontTriangle"),
|
||||
] {
|
||||
let s = replay(script);
|
||||
let state = s.model.state();
|
||||
let body = &state.body(name).unwrap().body;
|
||||
for q in DisplayQuality::ALL {
|
||||
for k in 0..=MAX_REFINE {
|
||||
let plan = body.display_plan_angular(q.refined_chord(body.size(), k), q.refined_angle(k));
|
||||
assert!(plan.is_ok(), "{name} at {} + {k}: {:?}", q.id(), plan.err());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,13 @@
|
||||
//! each surface in turn from no starting guess: a nearest-point search per surface per point.
|
||||
//! Here the intersection is solved exactly at a set of leader parameters, and a cubic B-spline
|
||||
//! is fitted through those points together with both surfaces' parameters there (a smooth
|
||||
//! leader with its curves on both surfaces). Each point is then refined from the B-spline by
|
||||
//! Newton's method on the four unknowns (u1, v1, u2, v2): S1(u1, v1) = S2(u2, v2), held on the
|
||||
//! leader with its curves on both surfaces). The B-spline runs over the chord length of the
|
||||
//! solved points, never over the curve's own parameter: truck's parameter can change speed
|
||||
//! abruptly (a polyline leader with short and long spans), and a cubic interpolated over it
|
||||
//! rings there, stepping back along the curve (Lee, "Choosing nodes in parametric curve
|
||||
//! interpolation", CAD 21, 1989). The curve's parameter reaches the chord length through the
|
||||
//! piecewise-linear map between the seeds, which is monotone. Each point is then refined from
|
||||
//! the B-spline by Newton's method on the four unknowns (u1, v1, u2, v2): S1(u1, v1) = S2(u2, v2), held on the
|
||||
//! plane through the B-spline point normal to the B-spline (Barnhill & Kersey, "A marching
|
||||
//! method for parametric surface/surface intersection", CAGD 7, 1990; Patrikalakis & Maekawa,
|
||||
//! "Shape Interrogation for CAD/CAM", 2002, ch. 5). Every point is independent once seeded. Where the surfaces are close to
|
||||
@@ -34,9 +39,11 @@ const MIN_SIN: f64 = 0.05;
|
||||
pub(crate) struct Intersection {
|
||||
surfaces: [EvalSurface; 2],
|
||||
domains: [Domain; 2],
|
||||
/// The smooth leader over the curve's parameter: the intersection point (x, y, z) and both
|
||||
/// surfaces' parameters (u1, v1, u2, v2), periodic directions unwrapped along it.
|
||||
/// The smooth leader over the chord length of the seeds: the intersection point (x, y, z)
|
||||
/// and both surfaces' parameters (u1, v1, u2, v2), periodic directions unwrapped along it.
|
||||
leader: CurveN<7>,
|
||||
/// The curve's parameter at each seed and the chord length there, both increasing.
|
||||
seeds: Vec<(f64, f64)>,
|
||||
/// Convergence tolerance (mm).
|
||||
eps: f64,
|
||||
}
|
||||
@@ -59,8 +66,14 @@ impl Intersection {
|
||||
// The leader lies within the curve's tolerance of both surfaces.
|
||||
let accept = (ic.tolerance() * 100.0).max(1e-6);
|
||||
let scale = (0..=SEEDS).map(|k| leader(t0 + (t1 - t0) * k as f64 / SEEDS as f64).0.len()).fold(1.0, f64::max);
|
||||
let mut it = Intersection { surfaces, domains, leader: CurveN { degree: 1, knots: Vec::new(), ctrl: Vec::new() }, eps: scale * 1e-10 };
|
||||
let (mut params, mut points) = (Vec::with_capacity(SEEDS + 1), Vec::with_capacity(SEEDS + 1));
|
||||
let mut it = Intersection {
|
||||
surfaces,
|
||||
domains,
|
||||
leader: CurveN { degree: 1, knots: Vec::new(), ctrl: Vec::new() },
|
||||
seeds: Vec::new(),
|
||||
eps: scale * 1e-10,
|
||||
};
|
||||
let (mut params, mut points): (Vec<f64>, Vec<[f64; 7]>) = (Vec::with_capacity(SEEDS + 1), Vec::with_capacity(SEEDS + 1));
|
||||
let mut prev: Option<[f64; 4]> = None;
|
||||
for k in 0..=SEEDS {
|
||||
let t = t0 + (t1 - t0) * k as f64 / SEEDS as f64;
|
||||
@@ -77,25 +90,60 @@ impl Intersection {
|
||||
q[2 * i] = uv[0];
|
||||
q[2 * i + 1] = uv[1];
|
||||
}
|
||||
// Exactly on the intersection where Newton converges (near-tangent seeds keep the
|
||||
// leader point and its projections).
|
||||
let (p, q) = d.normalized().and_then(|dir| it.newton(p, dir, q)).unwrap_or((p, q));
|
||||
params.push(t);
|
||||
// The fit takes only points exactly on the intersection: a leader point Newton
|
||||
// cannot place (near-tangent, or the solution leaves a surface's domain where a
|
||||
// polyline leader overshoots a vertex) is off the curve and would bend the fit back
|
||||
// along it. The ends stay either way, so the fit spans the whole curve.
|
||||
let solved = d.normalized().and_then(|dir| it.newton(p, dir, q));
|
||||
prev = Some(solved.map_or(q, |(_, q)| q));
|
||||
let (p, q) = match solved {
|
||||
Some(x) => x,
|
||||
None if k == 0 || k == SEEDS => (p, q),
|
||||
None => continue,
|
||||
};
|
||||
// A seed that lands on the previous one adds no chord and stays out of the fit
|
||||
// (whose parameters must increase).
|
||||
let s = match it.seeds.last().zip(points.last()) {
|
||||
Some((&(_, s), &[x, y, z, ..])) => {
|
||||
let chord = p.dist(Vec3::new(x, y, z));
|
||||
if !(chord > it.eps) {
|
||||
continue;
|
||||
}
|
||||
s + chord
|
||||
}
|
||||
None => 0.0,
|
||||
};
|
||||
it.seeds.push((t, s));
|
||||
params.push(s);
|
||||
points.push([p.x, p.y, p.z, q[0], q[1], q[2], q[3]]);
|
||||
prev = Some(q);
|
||||
}
|
||||
it.leader = CurveN::interpolate(¶ms, &points, 3).ok()?;
|
||||
let degree = points.len().checked_sub(1)?.min(3);
|
||||
it.leader = CurveN::interpolate(¶ms, &points, degree).ok()?;
|
||||
Some(it)
|
||||
}
|
||||
|
||||
/// The point at curve parameter `t` and both surfaces' parameters there, or `None`
|
||||
/// (near-tangent, or Newton failed).
|
||||
pub(crate) fn eval(&self, t: f64) -> Option<(Vec3, [f64; 4])> {
|
||||
let (x, dx) = self.leader.eval(t)?;
|
||||
let (x, dx) = self.leader.eval(self.chord_at(t)?)?;
|
||||
let dir = Vec3::new(dx[0], dx[1], dx[2]).normalized()?;
|
||||
self.newton(Vec3::new(x[0], x[1], x[2]), dir, [x[3], x[4], x[5], x[6]])
|
||||
}
|
||||
|
||||
/// The chord length at curve parameter `t`: linear between the seeds, clamped to the ends.
|
||||
fn chord_at(&self, t: f64) -> Option<f64> {
|
||||
let (&(ta, sa), &(tb, sb)) = (self.seeds.first()?, self.seeds.last()?);
|
||||
if !(t > ta) {
|
||||
return Some(sa);
|
||||
}
|
||||
if !(t < tb) {
|
||||
return Some(sb);
|
||||
}
|
||||
let k = self.seeds.partition_point(|&(tk, _)| tk <= t);
|
||||
let (Some(&(t0, s0)), Some(&(t1, s1))) = (<[_]>::get(&self.seeds, k.checked_sub(1)?), <[_]>::get(&self.seeds, k)) else { return None };
|
||||
Some(s0 + (s1 - s0) * (t - t0) / (t1 - t0))
|
||||
}
|
||||
|
||||
/// The two surfaces, in the order of the parameters [`Intersection::eval`] gives.
|
||||
pub(crate) fn surfaces(&self) -> &[EvalSurface; 2] {
|
||||
&self.surfaces
|
||||
@@ -186,7 +234,7 @@ mod tests {
|
||||
|
||||
/// Points on the intersection edges of a boolean (a cross hole: two cylinders) lie on both
|
||||
/// surfaces, on truck's curve (the parameterisation follows our smooth leader, so the same
|
||||
/// parameter may sit at a slightly different place along it), and advance along the curve.
|
||||
/// parameter may sit at a slightly different place along it), and advance along it.
|
||||
#[test]
|
||||
fn intersection_points_lie_on_both_surfaces_and_on_truck_s_curve() {
|
||||
let Ok(pin) = crate::cylinder(Vec3::new(5.0, 5.0, -1.0), Vec3::Z, 2.0, 6.0) else { panic!("pin") };
|
||||
@@ -199,6 +247,21 @@ mod tests {
|
||||
let (t0, t1) = curve.range_tuple();
|
||||
let dense: Vec<Vec3> = (0..=1000).map(|k| from_p3(curve.subs(t0 + (t1 - t0) * f64::from(k) / 1000.0))).collect();
|
||||
let near = |p: Vec3| dense.windows(2).map(|w| p.dist_to_segment(w[0], w[1])).fold(f64::MAX, f64::min);
|
||||
// Arc length along truck's curve to the point nearest `p`.
|
||||
let along = |p: Vec3| {
|
||||
let (mut run, mut best) = (0.0, (f64::MAX, 0.0));
|
||||
for w in dense.windows(2) {
|
||||
let (a, b) = (w[0], w[1]);
|
||||
let len = a.dist(b);
|
||||
let f = if len > 0.0 { ((p - a).dot(b - a) / (len * len)).clamp(0.0, 1.0) } else { 0.0 };
|
||||
let d = p.dist(a + (b - a) * f);
|
||||
if d < best.0 {
|
||||
best = (d, run + f * len);
|
||||
}
|
||||
run += len;
|
||||
}
|
||||
best.1
|
||||
};
|
||||
let mut last: Option<Vec3> = None;
|
||||
for k in 0..=50 {
|
||||
let t = t0 + (t1 - t0) * f64::from(k) / 50.0;
|
||||
@@ -208,7 +271,7 @@ mod tests {
|
||||
}
|
||||
assert!(near(p) < 1e-4, "t {t}: {p:?} is {} from truck's curve", near(p));
|
||||
if let Some(q) = last {
|
||||
assert!(p.dist(q) > 0.0, "t {t}: the points advance");
|
||||
assert!(along(p) > along(q), "t {t}: the points advance along the curve");
|
||||
}
|
||||
last = Some(p);
|
||||
checked += 1;
|
||||
|
||||
@@ -0,0 +1,836 @@
|
||||
{
|
||||
"description": "Bike front triangle (lofted tubes joined to a bottom-bracket cylinder): the display mesh drew a flat triangle inside the down tube bend at Fine/Ultra.",
|
||||
"commands": [
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
12,
|
||||
0.0,
|
||||
18
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.947,
|
||||
0.0,
|
||||
-0.322
|
||||
]
|
||||
},
|
||||
"name": "S13"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.ellipse",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"major": [
|
||||
19.0,
|
||||
0
|
||||
],
|
||||
"minor_radius": 18.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
-13.0,
|
||||
0.0,
|
||||
91.5
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.947,
|
||||
0.0,
|
||||
-0.322
|
||||
]
|
||||
},
|
||||
"name": "S14"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.ellipse",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"major": [
|
||||
27.0,
|
||||
0
|
||||
],
|
||||
"minor_radius": 24.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
-38,
|
||||
0.0,
|
||||
165
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.959,
|
||||
0.0,
|
||||
-0.282
|
||||
]
|
||||
},
|
||||
"name": "S15"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.ellipse",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"major": [
|
||||
24.0,
|
||||
0
|
||||
],
|
||||
"minor_radius": 22.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
-76.646,
|
||||
0.0,
|
||||
320.0
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.97,
|
||||
0.0,
|
||||
-0.242
|
||||
]
|
||||
},
|
||||
"name": "S16"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.circle.center",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"radius": 20.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
-110.577,
|
||||
0.0,
|
||||
456.089
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.97,
|
||||
0.0,
|
||||
-0.242
|
||||
]
|
||||
},
|
||||
"name": "S17"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.circle.center",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"radius": 19.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "solid.loft",
|
||||
"params": {
|
||||
"sections": [
|
||||
{
|
||||
"sketch": "S13"
|
||||
},
|
||||
{
|
||||
"sketch": "S14"
|
||||
},
|
||||
{
|
||||
"sketch": "S15"
|
||||
},
|
||||
{
|
||||
"sketch": "S16"
|
||||
},
|
||||
{
|
||||
"sketch": "S17"
|
||||
}
|
||||
],
|
||||
"operation": "new",
|
||||
"name": "SeatTube",
|
||||
"body_name": "SeatTube"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.cylinder",
|
||||
"params": {
|
||||
"diameter": 46,
|
||||
"height": 73.0,
|
||||
"base": [
|
||||
0,
|
||||
-36.5,
|
||||
0
|
||||
],
|
||||
"axis": "Y",
|
||||
"operation": "new",
|
||||
"name": "BBShell",
|
||||
"body_name": "BBShell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
459.471,
|
||||
0.0,
|
||||
607.143
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
0.349,
|
||||
0.0,
|
||||
-0.937
|
||||
]
|
||||
},
|
||||
"name": "S10"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.ellipse",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"major": [
|
||||
22.0,
|
||||
0
|
||||
],
|
||||
"minor_radius": 20.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
181.44,
|
||||
0.0,
|
||||
503.572
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
0.349,
|
||||
0.0,
|
||||
-0.937
|
||||
]
|
||||
},
|
||||
"name": "S11"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.ellipse",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"major": [
|
||||
27.0,
|
||||
0
|
||||
],
|
||||
"minor_radius": 23.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
-96.592,
|
||||
0.0,
|
||||
400.0
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
0.349,
|
||||
0.0,
|
||||
-0.937
|
||||
]
|
||||
},
|
||||
"name": "S12"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.ellipse",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"major": [
|
||||
17.0,
|
||||
0
|
||||
],
|
||||
"minor_radius": 16.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "solid.loft",
|
||||
"params": {
|
||||
"sections": [
|
||||
{
|
||||
"sketch": "S10"
|
||||
},
|
||||
{
|
||||
"sketch": "S11"
|
||||
},
|
||||
{
|
||||
"sketch": "S12"
|
||||
}
|
||||
],
|
||||
"operation": "new",
|
||||
"name": "TopTube",
|
||||
"body_name": "TopTube"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
501.231,
|
||||
0.0,
|
||||
519.592
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.903,
|
||||
0.0,
|
||||
-0.431
|
||||
]
|
||||
},
|
||||
"name": "S1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.circle.center",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"radius": 32.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
473.678,
|
||||
0.0,
|
||||
577.358
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.903,
|
||||
0.0,
|
||||
-0.431
|
||||
]
|
||||
},
|
||||
"name": "S2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.circle.center",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"radius": 28.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
447.417,
|
||||
0.0,
|
||||
632.416
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.903,
|
||||
0.0,
|
||||
-0.431
|
||||
]
|
||||
},
|
||||
"name": "S3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.circle.center",
|
||||
"params": {
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"radius": 26.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "solid.loft",
|
||||
"params": {
|
||||
"sections": [
|
||||
{
|
||||
"sketch": "S1"
|
||||
},
|
||||
{
|
||||
"sketch": "S2"
|
||||
},
|
||||
{
|
||||
"sketch": "S3"
|
||||
}
|
||||
],
|
||||
"operation": "new",
|
||||
"name": "HeadTube",
|
||||
"body_name": "HeadTube"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
8,
|
||||
0.0,
|
||||
3
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.178,
|
||||
0.0,
|
||||
0.984
|
||||
]
|
||||
},
|
||||
"name": "S4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-19.0,
|
||||
-18.0
|
||||
],
|
||||
"p1": [
|
||||
19.0,
|
||||
18.0
|
||||
],
|
||||
"radius": 10.799999999999999
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
80,
|
||||
0.0,
|
||||
16
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.211,
|
||||
0.0,
|
||||
0.977
|
||||
]
|
||||
},
|
||||
"name": "S5"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-30.0,
|
||||
-26.0
|
||||
],
|
||||
"p1": [
|
||||
30.0,
|
||||
26.0
|
||||
],
|
||||
"radius": 15.6
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
175,
|
||||
0.0,
|
||||
40
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.596,
|
||||
0.0,
|
||||
0.803
|
||||
]
|
||||
},
|
||||
"name": "S6"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-38.0,
|
||||
-33.0
|
||||
],
|
||||
"p1": [
|
||||
38.0,
|
||||
33.0
|
||||
],
|
||||
"radius": 19.8
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
314.248,
|
||||
0.0,
|
||||
271.657
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.857,
|
||||
0.0,
|
||||
0.515
|
||||
]
|
||||
},
|
||||
"name": "S7"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-37.0,
|
||||
-33.0
|
||||
],
|
||||
"p1": [
|
||||
37.0,
|
||||
33.0
|
||||
],
|
||||
"radius": 19.8
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
422.553,
|
||||
0.0,
|
||||
451.835
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.857,
|
||||
0.0,
|
||||
0.515
|
||||
]
|
||||
},
|
||||
"name": "S8"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-33.0,
|
||||
-30.0
|
||||
],
|
||||
"p1": [
|
||||
33.0,
|
||||
30.0
|
||||
],
|
||||
"radius": 18.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
484.441,
|
||||
0.0,
|
||||
554.793
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.857,
|
||||
0.0,
|
||||
0.515
|
||||
]
|
||||
},
|
||||
"name": "S9"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-23.0,
|
||||
-22.0
|
||||
],
|
||||
"p1": [
|
||||
23.0,
|
||||
22.0
|
||||
],
|
||||
"radius": 13.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "solid.loft",
|
||||
"params": {
|
||||
"sections": [
|
||||
{
|
||||
"sketch": "S4"
|
||||
},
|
||||
{
|
||||
"sketch": "S5"
|
||||
},
|
||||
{
|
||||
"sketch": "S6"
|
||||
},
|
||||
{
|
||||
"sketch": "S7"
|
||||
},
|
||||
{
|
||||
"sketch": "S8"
|
||||
},
|
||||
{
|
||||
"sketch": "S9"
|
||||
}
|
||||
],
|
||||
"operation": "new",
|
||||
"name": "DownTube",
|
||||
"body_name": "DownTube"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.combine",
|
||||
"params": {
|
||||
"target": "SeatTube",
|
||||
"tools": [
|
||||
"BBShell"
|
||||
],
|
||||
"operation": "join"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.combine",
|
||||
"params": {
|
||||
"target": "SeatTube",
|
||||
"tools": [
|
||||
"TopTube"
|
||||
],
|
||||
"operation": "join"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.combine",
|
||||
"params": {
|
||||
"target": "SeatTube",
|
||||
"tools": [
|
||||
"HeadTube"
|
||||
],
|
||||
"operation": "join"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.combine",
|
||||
"params": {
|
||||
"target": "SeatTube",
|
||||
"tools": [
|
||||
"DownTube"
|
||||
],
|
||||
"operation": "join"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "body.rename",
|
||||
"params": {
|
||||
"body": "SeatTube",
|
||||
"name": "FrontTriangle"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
{
|
||||
"description": "A lofted down tube (rounded-rectangle sections, widest 82 mm) joined to a 46 mm \u00d7 73 mm bottom-bracket cylinder: the display mesh of the tube's side face gained a flat triangle across the inside of its bend.",
|
||||
"commands": [
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
-4,
|
||||
0.0,
|
||||
2
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.164,
|
||||
0.0,
|
||||
0.986
|
||||
]
|
||||
},
|
||||
"name": "S4b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-19.0,
|
||||
-18.0
|
||||
],
|
||||
"p1": [
|
||||
19.0,
|
||||
18.0
|
||||
],
|
||||
"radius": 10.799999999999999
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
80,
|
||||
0.0,
|
||||
16
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.205,
|
||||
0.0,
|
||||
0.979
|
||||
]
|
||||
},
|
||||
"name": "S5b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-41.0,
|
||||
-33.0
|
||||
],
|
||||
"p1": [
|
||||
41.0,
|
||||
33.0
|
||||
],
|
||||
"radius": 19.8
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
175,
|
||||
0.0,
|
||||
40
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.596,
|
||||
0.0,
|
||||
0.803
|
||||
]
|
||||
},
|
||||
"name": "S6b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-41.0,
|
||||
-35.0
|
||||
],
|
||||
"p1": [
|
||||
41.0,
|
||||
35.0
|
||||
],
|
||||
"radius": 21.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
314.248,
|
||||
0.0,
|
||||
271.657
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.857,
|
||||
0.0,
|
||||
0.515
|
||||
]
|
||||
},
|
||||
"name": "S7b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-39.0,
|
||||
-35.0
|
||||
],
|
||||
"p1": [
|
||||
39.0,
|
||||
35.0
|
||||
],
|
||||
"radius": 21.0
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
422.553,
|
||||
0.0,
|
||||
451.835
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.857,
|
||||
0.0,
|
||||
0.515
|
||||
]
|
||||
},
|
||||
"name": "S8b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-35.0,
|
||||
-32.0
|
||||
],
|
||||
"p1": [
|
||||
35.0,
|
||||
32.0
|
||||
],
|
||||
"radius": 19.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "sketch.create",
|
||||
"params": {
|
||||
"plane": {
|
||||
"origin": [
|
||||
484.441,
|
||||
0.0,
|
||||
554.793
|
||||
],
|
||||
"x_dir": [
|
||||
0.0,
|
||||
1.0,
|
||||
0.0
|
||||
],
|
||||
"y_dir": [
|
||||
-0.857,
|
||||
0.0,
|
||||
0.515
|
||||
]
|
||||
},
|
||||
"name": "S9b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.rectangle.rounded",
|
||||
"params": {
|
||||
"p0": [
|
||||
-23.0,
|
||||
-22.0
|
||||
],
|
||||
"p1": [
|
||||
23.0,
|
||||
22.0
|
||||
],
|
||||
"radius": 13.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "sketch.finish",
|
||||
"params": {}
|
||||
},
|
||||
{
|
||||
"command": "solid.loft",
|
||||
"params": {
|
||||
"sections": [
|
||||
{
|
||||
"sketch": "S4b"
|
||||
},
|
||||
{
|
||||
"sketch": "S5b"
|
||||
},
|
||||
{
|
||||
"sketch": "S6b"
|
||||
},
|
||||
{
|
||||
"sketch": "S7b"
|
||||
},
|
||||
{
|
||||
"sketch": "S8b"
|
||||
},
|
||||
{
|
||||
"sketch": "S9b"
|
||||
}
|
||||
],
|
||||
"operation": "new",
|
||||
"name": "DT2",
|
||||
"body_name": "DT2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.cylinder",
|
||||
"params": {
|
||||
"diameter": 46,
|
||||
"height": 73.0,
|
||||
"base": [
|
||||
0,
|
||||
-36.5,
|
||||
0
|
||||
],
|
||||
"axis": "Y",
|
||||
"operation": "new",
|
||||
"name": "BBShell",
|
||||
"body_name": "BBShell"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "solid.combine",
|
||||
"params": {
|
||||
"target": "DT2",
|
||||
"tools": [
|
||||
"BBShell"
|
||||
],
|
||||
"operation": "join"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user