forked from 1b-agent/opencadstudio-upstream
fix(automation): validate spline degree, knots and weights on entities_create
#1688 accepted any degree (falling back to 3 on a malformed value) and overwrote the degree the codec had clamped to the control-point count, so a two-point default spline stored degree 3 against a degree-1 knot vector. Reject a non-integer or out-of-range degree, too few control points for the degree, a knot vector that is not control_points + degree + 1 non-decreasing values, and weights that are not one positive value per control point. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cdf8e60fbf
commit
731286c8cc
1 file changed
+38
-6
@@ -212,14 +212,27 @@ fn build_entity(spec: &Value) -> Parsed<codec::EntityType> {
|
|||||||
}
|
}
|
||||||
let degree = match spec.get("degree") {
|
let degree = match spec.get("degree") {
|
||||||
None | Some(Value::Null) => 3,
|
None | Some(Value::Null) => 3,
|
||||||
Some(value) => value.as_i64().unwrap_or(3),
|
Some(value) => value
|
||||||
|
.as_i64()
|
||||||
|
.filter(|degree| (1..=10).contains(degree))
|
||||||
|
.ok_or_else(|| {
|
||||||
|
failure("invalid_spline_values", "degree must be an integer 1..10")
|
||||||
|
})? as i32,
|
||||||
};
|
};
|
||||||
|
let count = control_points.len();
|
||||||
|
if count > 0 && count <= degree as usize {
|
||||||
|
return Err(failure(
|
||||||
|
"invalid_spline_values",
|
||||||
|
format!("a degree {degree} spline needs more than {degree} control_points"),
|
||||||
|
));
|
||||||
|
}
|
||||||
let mut spline = if control_points.is_empty() {
|
let mut spline = if control_points.is_empty() {
|
||||||
Spline::from_fit_points(fit_points.clone())
|
let mut spline = Spline::from_fit_points(fit_points.clone());
|
||||||
|
spline.degree = degree;
|
||||||
|
spline
|
||||||
} else {
|
} else {
|
||||||
Spline::from_control_points(degree as i32, control_points)
|
Spline::from_control_points(degree, control_points)
|
||||||
};
|
};
|
||||||
spline.degree = degree as i32;
|
|
||||||
if !fit_points.is_empty() {
|
if !fit_points.is_empty() {
|
||||||
spline.fit_points = fit_points;
|
spline.fit_points = fit_points;
|
||||||
}
|
}
|
||||||
@@ -230,7 +243,19 @@ fn build_entity(spec: &Value) -> Parsed<codec::EntityType> {
|
|||||||
"spline knots require control_points",
|
"spline knots require control_points",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
spline.knots = finite_number_list_field(spec, "knots")?;
|
let knots = finite_number_list_field(spec, "knots")?;
|
||||||
|
if knots.len() != count + degree as usize + 1
|
||||||
|
|| knots.windows(2).any(|pair| pair[1] < pair[0])
|
||||||
|
{
|
||||||
|
return Err(failure(
|
||||||
|
"invalid_spline_values",
|
||||||
|
format!(
|
||||||
|
"knots must be {} non-decreasing values (control_points + degree + 1)",
|
||||||
|
count + degree as usize + 1
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
spline.knots = knots;
|
||||||
}
|
}
|
||||||
if spec.get("weights").is_some() {
|
if spec.get("weights").is_some() {
|
||||||
if spline.control_points.is_empty() {
|
if spline.control_points.is_empty() {
|
||||||
@@ -239,7 +264,14 @@ fn build_entity(spec: &Value) -> Parsed<codec::EntityType> {
|
|||||||
"spline weights require control_points",
|
"spline weights require control_points",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
spline.weights = finite_number_list_field(spec, "weights")?;
|
let weights = finite_number_list_field(spec, "weights")?;
|
||||||
|
if weights.len() != count || weights.iter().any(|weight| *weight <= 0.0) {
|
||||||
|
return Err(failure(
|
||||||
|
"invalid_spline_values",
|
||||||
|
format!("weights must be {count} positive values, one per control point"),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
spline.weights = weights;
|
||||||
spline.flags.rational = true;
|
spline.flags.rational = true;
|
||||||
}
|
}
|
||||||
if let Some(value) = spec["closed"].as_bool() {
|
if let Some(value) = spec["closed"].as_bool() {
|
||||||
|
|||||||
Reference in new issue
Block a user