Nicer geometry types (#522)
* Angle type * Use Point2d * use angle in more places * Fix doctests * Use angle in more places * Import pi
This commit is contained in:
@ -348,7 +348,7 @@ impl SourceRange {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Point2d {
|
pub struct Point2d {
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
@ -379,6 +379,16 @@ impl From<Point2d> for kittycad::types::Point2D {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Point2d {
|
||||||
|
pub const ZERO: Self = Self { x: 0.0, y: 0.0 };
|
||||||
|
pub fn scale(self, scalar: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
x: self.x * scalar,
|
||||||
|
y: self.y * scalar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct Point3d {
|
pub struct Point3d {
|
||||||
|
@ -7,7 +7,7 @@ use schemars::JsonSchema;
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{MemoryItem, SketchGroup},
|
executor::{MemoryItem, SketchGroup},
|
||||||
std::{utils::get_angle, Args},
|
std::{utils::Angle, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns the segment end of x.
|
/// Returns the segment end of x.
|
||||||
@ -174,9 +174,9 @@ fn inner_segment_angle(segment_name: &str, sketch_group: SketchGroup, args: &mut
|
|||||||
})?;
|
})?;
|
||||||
let line = path.get_base();
|
let line = path.get_base();
|
||||||
|
|
||||||
let result = get_angle(&line.from, &line.to);
|
let result = Angle::between(line.from.into(), line.to.into());
|
||||||
|
|
||||||
Ok(result)
|
Ok(result.degrees())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the angle to match the given length for x.
|
/// Returns the angle to match the given length for x.
|
||||||
|
@ -15,6 +15,8 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::utils::Angle;
|
||||||
|
|
||||||
/// Data to draw a line to a point.
|
/// Data to draw a line to a point.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -392,13 +394,13 @@ fn inner_angled_line_of_x_length(
|
|||||||
AngledLineData::AngleAndLength(angle_and_length) => (angle_and_length[0], angle_and_length[1]),
|
AngledLineData::AngleAndLength(angle_and_length) => (angle_and_length[0], angle_and_length[1]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let to = get_y_component(angle, length);
|
let to = get_y_component(Angle::from_degrees(angle), length);
|
||||||
|
|
||||||
let new_sketch_group = inner_line(
|
let new_sketch_group = inner_line(
|
||||||
if let AngledLineData::AngleWithTag { tag, .. } = data {
|
if let AngledLineData::AngleWithTag { tag, .. } = data {
|
||||||
LineData::PointWithTag { to, tag }
|
LineData::PointWithTag { to: to.into(), tag }
|
||||||
} else {
|
} else {
|
||||||
LineData::Point(to)
|
LineData::Point(to.into())
|
||||||
},
|
},
|
||||||
sketch_group,
|
sketch_group,
|
||||||
args,
|
args,
|
||||||
@ -487,13 +489,13 @@ fn inner_angled_line_of_y_length(
|
|||||||
AngledLineData::AngleAndLength(angle_and_length) => (angle_and_length[0], angle_and_length[1]),
|
AngledLineData::AngleAndLength(angle_and_length) => (angle_and_length[0], angle_and_length[1]),
|
||||||
};
|
};
|
||||||
|
|
||||||
let to = get_x_component(angle, length);
|
let to = get_x_component(Angle::from_degrees(angle), length);
|
||||||
|
|
||||||
let new_sketch_group = inner_line(
|
let new_sketch_group = inner_line(
|
||||||
if let AngledLineData::AngleWithTag { tag, .. } = data {
|
if let AngledLineData::AngleWithTag { tag, .. } = data {
|
||||||
LineData::PointWithTag { to, tag }
|
LineData::PointWithTag { to: to.into(), tag }
|
||||||
} else {
|
} else {
|
||||||
LineData::Point(to)
|
LineData::Point(to.into())
|
||||||
},
|
},
|
||||||
sketch_group,
|
sketch_group,
|
||||||
args,
|
args,
|
||||||
@ -588,16 +590,16 @@ fn inner_angled_line_that_intersects(
|
|||||||
|
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from = sketch_group.get_coords_from_paths()?;
|
||||||
let to = intersection_with_parallel_line(
|
let to = intersection_with_parallel_line(
|
||||||
&[intersect_path.from, intersect_path.to],
|
&[intersect_path.from.into(), intersect_path.to.into()],
|
||||||
data.offset.unwrap_or_default(),
|
data.offset.unwrap_or_default(),
|
||||||
data.angle,
|
data.angle,
|
||||||
from.into(),
|
from,
|
||||||
);
|
);
|
||||||
|
|
||||||
let line_to_data = if let Some(tag) = data.tag {
|
let line_to_data = if let Some(tag) = data.tag {
|
||||||
LineToData::PointWithTag { to, tag }
|
LineToData::PointWithTag { to: to.into(), tag }
|
||||||
} else {
|
} else {
|
||||||
LineToData::Point(to)
|
LineToData::Point(to.into())
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
let new_sketch_group = inner_line_to(line_to_data, sketch_group, args)?;
|
||||||
@ -766,7 +768,7 @@ pub fn arc(args: &mut Args) -> Result<MemoryItem, KclError> {
|
|||||||
name = "arc",
|
name = "arc",
|
||||||
}]
|
}]
|
||||||
fn inner_arc(data: ArcData, sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
fn inner_arc(data: ArcData, sketch_group: SketchGroup, args: &mut Args) -> Result<SketchGroup, KclError> {
|
||||||
let from = sketch_group.get_coords_from_paths()?;
|
let from: Point2d = sketch_group.get_coords_from_paths()?;
|
||||||
|
|
||||||
let (center, angle_start, angle_end, radius, end) = match &data {
|
let (center, angle_start, angle_end, radius, end) = match &data {
|
||||||
ArcData::AnglesAndRadiusWithTag {
|
ArcData::AnglesAndRadiusWithTag {
|
||||||
@ -775,23 +777,27 @@ fn inner_arc(data: ArcData, sketch_group: SketchGroup, args: &mut Args) -> Resul
|
|||||||
radius,
|
radius,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let (center, end) = arc_center_and_end(&from, *angle_start, *angle_end, *radius);
|
let a_start = Angle::from_degrees(*angle_start);
|
||||||
(center, *angle_start, *angle_end, *radius, end)
|
let a_end = Angle::from_degrees(*angle_end);
|
||||||
|
let (center, end) = arc_center_and_end(from, a_start, a_end, *radius);
|
||||||
|
(center, a_start, a_end, *radius, end)
|
||||||
}
|
}
|
||||||
ArcData::AnglesAndRadius {
|
ArcData::AnglesAndRadius {
|
||||||
angle_start,
|
angle_start,
|
||||||
angle_end,
|
angle_end,
|
||||||
radius,
|
radius,
|
||||||
} => {
|
} => {
|
||||||
let (center, end) = arc_center_and_end(&from, *angle_start, *angle_end, *radius);
|
let a_start = Angle::from_degrees(*angle_start);
|
||||||
(center, *angle_start, *angle_end, *radius, end)
|
let a_end = Angle::from_degrees(*angle_end);
|
||||||
|
let (center, end) = arc_center_and_end(from, a_start, a_end, *radius);
|
||||||
|
(center, a_start, a_end, *radius, end)
|
||||||
}
|
}
|
||||||
ArcData::CenterToRadiusWithTag { center, to, radius, .. } => {
|
ArcData::CenterToRadiusWithTag { center, to, radius, .. } => {
|
||||||
let (angle_start, angle_end) = arc_angles(&from, ¢er.into(), &to.into(), *radius, args.source_range)?;
|
let (angle_start, angle_end) = arc_angles(from, center.into(), to.into(), *radius, args.source_range)?;
|
||||||
(center.into(), angle_start, angle_end, *radius, to.into())
|
(center.into(), angle_start, angle_end, *radius, to.into())
|
||||||
}
|
}
|
||||||
ArcData::CenterToRadius { center, to, radius } => {
|
ArcData::CenterToRadius { center, to, radius } => {
|
||||||
let (angle_start, angle_end) = arc_angles(&from, ¢er.into(), &to.into(), *radius, args.source_range)?;
|
let (angle_start, angle_end) = arc_angles(from, center.into(), to.into(), *radius, args.source_range)?;
|
||||||
(center.into(), angle_start, angle_end, *radius, to.into())
|
(center.into(), angle_start, angle_end, *radius, to.into())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -803,8 +809,8 @@ fn inner_arc(data: ArcData, sketch_group: SketchGroup, args: &mut Args) -> Resul
|
|||||||
ModelingCmd::ExtendPath {
|
ModelingCmd::ExtendPath {
|
||||||
path: sketch_group.id,
|
path: sketch_group.id,
|
||||||
segment: kittycad::types::PathSegment::Arc {
|
segment: kittycad::types::PathSegment::Arc {
|
||||||
angle_start,
|
angle_start: angle_start.degrees(),
|
||||||
angle_end,
|
angle_end: angle_end.degrees(),
|
||||||
center: center.into(),
|
center: center.into(),
|
||||||
radius,
|
radius,
|
||||||
},
|
},
|
||||||
|
@ -1,30 +1,85 @@
|
|||||||
|
use std::f64::consts::PI;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{Point2d, SourceRange},
|
executor::{Point2d, SourceRange},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn get_angle(a: &[f64; 2], b: &[f64; 2]) -> f64 {
|
#[derive(Clone, Copy, Default, PartialOrd, PartialEq, Debug)]
|
||||||
let x = b[0] - a[0];
|
pub struct Angle {
|
||||||
let y = b[1] - a[1];
|
degrees: f64,
|
||||||
normalise_angle(y.atan2(x).to_degrees())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normalise_angle(angle: f64) -> f64 {
|
impl Angle {
|
||||||
let result = ((angle % 360.0) + 360.0) % 360.0;
|
const ZERO: Self = Self { degrees: 0.0 };
|
||||||
if result > 180.0 {
|
/// Make an angle of the given degrees.
|
||||||
result - 360.0
|
pub fn from_degrees(degrees: f64) -> Self {
|
||||||
} else {
|
Self { degrees }
|
||||||
result
|
}
|
||||||
|
/// Make an angle of the given radians.
|
||||||
|
pub fn from_radians(radians: f64) -> Self {
|
||||||
|
Self::from_degrees(radians.to_degrees())
|
||||||
|
}
|
||||||
|
/// Get the angle in degrees
|
||||||
|
pub fn degrees(&self) -> f64 {
|
||||||
|
self.degrees
|
||||||
|
}
|
||||||
|
/// Get the angle in radians
|
||||||
|
pub fn radians(&self) -> f64 {
|
||||||
|
self.degrees.to_radians()
|
||||||
|
}
|
||||||
|
/// Get the angle between these points
|
||||||
|
pub fn between(a: Point2d, b: Point2d) -> Self {
|
||||||
|
let x = b.x - a.x;
|
||||||
|
let y = b.y - a.y;
|
||||||
|
Self::from_radians(y.atan2(x)).normalize()
|
||||||
|
}
|
||||||
|
/// Normalize the angle
|
||||||
|
pub fn normalize(self) -> Self {
|
||||||
|
let angle = self.degrees();
|
||||||
|
let result = ((angle % 360.0) + 360.0) % 360.0;
|
||||||
|
Self::from_degrees(if result > 180.0 { result - 360.0 } else { result })
|
||||||
|
}
|
||||||
|
/// Gives the ▲-angle between from and to angles (shortest path), use radians.
|
||||||
|
///
|
||||||
|
/// Sign of the returned angle denotes direction, positive means counterClockwise 🔄
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use std::f64::consts::PI;
|
||||||
|
/// use kcl_lib::std::utils::Angle;
|
||||||
|
///
|
||||||
|
/// assert_eq!(
|
||||||
|
/// Angle::delta(Angle::from_radians(PI / 8.0), Angle::from_radians(PI / 4.0)),
|
||||||
|
/// Angle::from_radians(PI / 8.0)
|
||||||
|
/// );
|
||||||
|
/// ```
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn delta(from_angle: Self, to_angle: Self) -> Self {
|
||||||
|
let norm_from_angle = normalize_rad(from_angle.radians());
|
||||||
|
let norm_to_angle = normalize_rad(to_angle.radians());
|
||||||
|
let provisional = norm_to_angle - norm_from_angle;
|
||||||
|
|
||||||
|
if provisional > -PI && provisional <= PI {
|
||||||
|
return Angle::from_radians(provisional);
|
||||||
|
}
|
||||||
|
if provisional > PI {
|
||||||
|
return Angle::from_radians(provisional - 2.0 * PI);
|
||||||
|
}
|
||||||
|
if provisional < -PI {
|
||||||
|
return Angle::from_radians(provisional + 2.0 * PI);
|
||||||
|
}
|
||||||
|
Angle::ZERO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn clockwise_sign(points: &[[f64; 2]]) -> i32 {
|
pub fn clockwise_sign(points: &[Point2d]) -> i32 {
|
||||||
let mut sum = 0.0;
|
let mut sum = 0.0;
|
||||||
for i in 0..points.len() {
|
for i in 0..points.len() {
|
||||||
let current_point = points[i];
|
let current_point = points[i];
|
||||||
let next_point = points[(i + 1) % points.len()];
|
let next_point = points[(i + 1) % points.len()];
|
||||||
sum += (next_point[0] - current_point[0]) * (next_point[1] + current_point[1]);
|
sum += (next_point.x - current_point.x) * (next_point.y + current_point.y);
|
||||||
}
|
}
|
||||||
if sum >= 0.0 {
|
if sum >= 0.0 {
|
||||||
1
|
1
|
||||||
@ -35,139 +90,145 @@ pub fn clockwise_sign(points: &[[f64; 2]]) -> i32 {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn normalize_rad(angle: f64) -> f64 {
|
pub fn normalize_rad(angle: f64) -> f64 {
|
||||||
let draft = angle % (2.0 * std::f64::consts::PI);
|
let draft = angle % (2.0 * PI);
|
||||||
if draft < 0.0 {
|
if draft < 0.0 {
|
||||||
draft + 2.0 * std::f64::consts::PI
|
draft + 2.0 * PI
|
||||||
} else {
|
} else {
|
||||||
draft
|
draft
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gives the ▲-angle between from and to angles (shortest path), use radians.
|
|
||||||
///
|
|
||||||
/// Sign of the returned angle denotes direction, positive means counterClockwise 🔄
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// assert_eq!(
|
|
||||||
/// kcl_lib::std::utils::delta_angle(std::f64::consts::PI / 8.0, std::f64::consts::PI / 4.0),
|
|
||||||
/// std::f64::consts::PI / 8.0
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn delta_angle(from_angle: f64, to_angle: f64) -> f64 {
|
|
||||||
let norm_from_angle = normalize_rad(from_angle);
|
|
||||||
let norm_to_angle = normalize_rad(to_angle);
|
|
||||||
let provisional = norm_to_angle - norm_from_angle;
|
|
||||||
|
|
||||||
if provisional > -std::f64::consts::PI && provisional <= std::f64::consts::PI {
|
|
||||||
return provisional;
|
|
||||||
}
|
|
||||||
if provisional > std::f64::consts::PI {
|
|
||||||
return provisional - 2.0 * std::f64::consts::PI;
|
|
||||||
}
|
|
||||||
if provisional < -std::f64::consts::PI {
|
|
||||||
return provisional + 2.0 * std::f64::consts::PI;
|
|
||||||
}
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates the distance between two points.
|
/// Calculates the distance between two points.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
|
/// use kcl_lib::executor::Point2d;
|
||||||
|
///
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// kcl_lib::std::utils::distance_between_points(&[0.0, 0.0], &[0.0, 5.0]),
|
/// kcl_lib::std::utils::distance_between_points(Point2d::ZERO, Point2d{x: 0.0, y: 5.0}),
|
||||||
/// 5.0
|
/// 5.0
|
||||||
/// );
|
/// );
|
||||||
/// assert_eq!(
|
/// assert_eq!(
|
||||||
/// kcl_lib::std::utils::distance_between_points(&[0.0, 0.0], &[3.0, 4.0]),
|
/// kcl_lib::std::utils::distance_between_points(Point2d::ZERO, Point2d{x: 3.0, y: 4.0}),
|
||||||
/// 5.0
|
/// 5.0
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn distance_between_points(point_a: &[f64; 2], point_b: &[f64; 2]) -> f64 {
|
pub fn distance_between_points(point_a: Point2d, point_b: Point2d) -> f64 {
|
||||||
let x1 = point_a[0];
|
let x1 = point_a.x;
|
||||||
let y1 = point_a[1];
|
let y1 = point_a.y;
|
||||||
let x2 = point_b[0];
|
let x2 = point_b.x;
|
||||||
let y2 = point_b[1];
|
let y2 = point_b.y;
|
||||||
|
|
||||||
((y2 - y1).powi(2) + (x2 - x1).powi(2)).sqrt()
|
((y2 - y1).powi(2) + (x2 - x1).powi(2)).sqrt()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_intersection_of_two_lines(line1: &[[f64; 2]; 2], line2_angle: f64, line2_point: [f64; 2]) -> [f64; 2] {
|
pub fn calculate_intersection_of_two_lines(line1: &[Point2d; 2], line2_angle: f64, line2_point: Point2d) -> Point2d {
|
||||||
let line2_point_b = [
|
let line2_point_b = Point2d {
|
||||||
line2_point[0] + f64::cos(line2_angle.to_radians()) * 10.0,
|
x: line2_point.x + f64::cos(line2_angle.to_radians()) * 10.0,
|
||||||
line2_point[1] + f64::sin(line2_angle.to_radians()) * 10.0,
|
y: line2_point.y + f64::sin(line2_angle.to_radians()) * 10.0,
|
||||||
];
|
};
|
||||||
intersect(line1[0], line1[1], line2_point, line2_point_b)
|
intersect(line1[0], line1[1], line2_point, line2_point_b)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn intersect(p1: [f64; 2], p2: [f64; 2], p3: [f64; 2], p4: [f64; 2]) -> [f64; 2] {
|
pub fn intersect(p1: Point2d, p2: Point2d, p3: Point2d, p4: Point2d) -> Point2d {
|
||||||
let slope = |p1: [f64; 2], p2: [f64; 2]| (p1[1] - p2[1]) / (p1[0] - p2[0]);
|
let slope = |p1: Point2d, p2: Point2d| (p1.y - p2.y) / (p1.x - p2.x);
|
||||||
let constant = |p1: [f64; 2], p2: [f64; 2]| p1[1] - slope(p1, p2) * p1[0];
|
let constant = |p1: Point2d, p2: Point2d| p1.y - slope(p1, p2) * p1.x;
|
||||||
let get_y = |for_x: f64, p1: [f64; 2], p2: [f64; 2]| slope(p1, p2) * for_x + constant(p1, p2);
|
let get_y = |for_x: f64, p1: Point2d, p2: Point2d| slope(p1, p2) * for_x + constant(p1, p2);
|
||||||
|
|
||||||
if p1[0] == p2[0] {
|
if p1.x == p2.x {
|
||||||
return [p1[0], get_y(p1[0], p3, p4)];
|
return Point2d {
|
||||||
|
x: p1.x,
|
||||||
|
y: get_y(p1.x, p3, p4),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
if p3[0] == p4[0] {
|
if p3.x == p4.x {
|
||||||
return [p3[0], get_y(p3[0], p1, p2)];
|
return Point2d {
|
||||||
|
x: p3.x,
|
||||||
|
y: get_y(p3.x, p1, p2),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let x = (constant(p3, p4) - constant(p1, p2)) / (slope(p1, p2) - slope(p3, p4));
|
let x = (constant(p3, p4) - constant(p1, p2)) / (slope(p1, p2) - slope(p3, p4));
|
||||||
let y = get_y(x, p1, p2);
|
let y = get_y(x, p1, p2);
|
||||||
[x, y]
|
Point2d { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn intersection_with_parallel_line(
|
pub fn intersection_with_parallel_line(
|
||||||
line1: &[[f64; 2]; 2],
|
line1: &[Point2d; 2],
|
||||||
line1_offset: f64,
|
line1_offset: f64,
|
||||||
line2_angle: f64,
|
line2_angle: f64,
|
||||||
line2_point: [f64; 2],
|
line2_point: Point2d,
|
||||||
) -> [f64; 2] {
|
) -> Point2d {
|
||||||
calculate_intersection_of_two_lines(&offset_line(line1_offset, line1[0], line1[1]), line2_angle, line2_point)
|
calculate_intersection_of_two_lines(&offset_line(line1_offset, line1[0], line1[1]), line2_angle, line2_point)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset_line(offset: f64, p1: [f64; 2], p2: [f64; 2]) -> [[f64; 2]; 2] {
|
fn offset_line(offset: f64, p1: Point2d, p2: Point2d) -> [Point2d; 2] {
|
||||||
if p1[0] == p2[0] {
|
if p1.x == p2.x {
|
||||||
let direction = (p1[1] - p2[1]).signum();
|
let direction = (p1.y - p2.y).signum();
|
||||||
return [[p1[0] + offset * direction, p1[1]], [p2[0] + offset * direction, p2[1]]];
|
return [
|
||||||
|
Point2d {
|
||||||
|
x: p1.x + offset * direction,
|
||||||
|
y: p1.y,
|
||||||
|
},
|
||||||
|
Point2d {
|
||||||
|
x: p2.x + offset * direction,
|
||||||
|
y: p2.y,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
if p1[1] == p2[1] {
|
if p1.y == p2.y {
|
||||||
let direction = (p2[0] - p1[0]).signum();
|
let direction = (p2.x - p1.x).signum();
|
||||||
return [[p1[0], p1[1] + offset * direction], [p2[0], p2[1] + offset * direction]];
|
return [
|
||||||
|
Point2d {
|
||||||
|
x: p1.x,
|
||||||
|
y: p1.y + offset * direction,
|
||||||
|
},
|
||||||
|
Point2d {
|
||||||
|
x: p2.x,
|
||||||
|
y: p2.y + offset * direction,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
let x_offset = offset / f64::sin(f64::atan2(p1[1] - p2[1], p1[0] - p2[0]));
|
let x_offset = offset / f64::sin(f64::atan2(p1.y - p2.y, p1.x - p2.x));
|
||||||
[[p1[0] + x_offset, p1[1]], [p2[0] + x_offset, p2[1]]]
|
[
|
||||||
|
Point2d {
|
||||||
|
x: p1.x + x_offset,
|
||||||
|
y: p1.y,
|
||||||
|
},
|
||||||
|
Point2d {
|
||||||
|
x: p2.x + x_offset,
|
||||||
|
y: p2.y,
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_y_component(angle_degree: f64, x_component: f64) -> [f64; 2] {
|
pub fn get_y_component(angle: Angle, x: f64) -> Point2d {
|
||||||
let normalised_angle = ((angle_degree % 360.0) + 360.0) % 360.0; // between 0 and 360
|
let normalised_angle = ((angle.degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
||||||
let y_component = x_component * f64::tan(normalised_angle.to_radians());
|
let y = x * f64::tan(normalised_angle.to_radians());
|
||||||
let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 {
|
let sign = if normalised_angle > 90.0 && normalised_angle <= 270.0 {
|
||||||
-1.0
|
-1.0
|
||||||
} else {
|
} else {
|
||||||
1.0
|
1.0
|
||||||
};
|
};
|
||||||
[sign * x_component, sign * y_component]
|
Point2d { x, y }.scale(sign)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_x_component(angle_degree: f64, y_component: f64) -> [f64; 2] {
|
pub fn get_x_component(angle: Angle, y: f64) -> Point2d {
|
||||||
let normalised_angle = ((angle_degree % 360.0) + 360.0) % 360.0; // between 0 and 360
|
let normalised_angle = ((angle.degrees() % 360.0) + 360.0) % 360.0; // between 0 and 360
|
||||||
let x_component = y_component / f64::tan(normalised_angle.to_radians());
|
let x = y / f64::tan(normalised_angle.to_radians());
|
||||||
let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 {
|
let sign = if normalised_angle > 180.0 && normalised_angle <= 360.0 {
|
||||||
-1.0
|
-1.0
|
||||||
} else {
|
} else {
|
||||||
1.0
|
1.0
|
||||||
};
|
};
|
||||||
[sign * x_component, sign * y_component]
|
Point2d { x, y }.scale(sign)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn arc_center_and_end(from: &Point2d, start_angle_deg: f64, end_angle_deg: f64, radius: f64) -> (Point2d, Point2d) {
|
pub fn arc_center_and_end(from: Point2d, start_angle: Angle, end_angle: Angle, radius: f64) -> (Point2d, Point2d) {
|
||||||
let start_angle = start_angle_deg.to_radians();
|
let start_angle = start_angle.radians();
|
||||||
let end_angle = end_angle_deg.to_radians();
|
let end_angle = end_angle.radians();
|
||||||
|
|
||||||
let center = Point2d {
|
let center = Point2d {
|
||||||
x: -1.0 * (radius * start_angle.cos() - from.x),
|
x: -1.0 * (radius * start_angle.cos() - from.x),
|
||||||
@ -183,12 +244,12 @@ pub fn arc_center_and_end(from: &Point2d, start_angle_deg: f64, end_angle_deg: f
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn arc_angles(
|
pub fn arc_angles(
|
||||||
from: &Point2d,
|
from: Point2d,
|
||||||
to: &Point2d,
|
to: Point2d,
|
||||||
center: &Point2d,
|
center: Point2d,
|
||||||
radius: f64,
|
radius: f64,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
) -> Result<(f64, f64), KclError> {
|
) -> Result<(Angle, Angle), KclError> {
|
||||||
// First make sure that the points are on the circumference of the circle.
|
// First make sure that the points are on the circumference of the circle.
|
||||||
// If not, we'll return an error.
|
// If not, we'll return an error.
|
||||||
if !is_on_circumference(center, from, radius) {
|
if !is_on_circumference(center, from, radius) {
|
||||||
@ -214,13 +275,10 @@ pub fn arc_angles(
|
|||||||
let start_angle = (from.y - center.y).atan2(from.x - center.x);
|
let start_angle = (from.y - center.y).atan2(from.x - center.x);
|
||||||
let end_angle = (to.y - center.y).atan2(to.x - center.x);
|
let end_angle = (to.y - center.y).atan2(to.x - center.x);
|
||||||
|
|
||||||
let start_angle_deg = start_angle.to_degrees();
|
Ok((Angle::from_radians(start_angle), Angle::from_radians(end_angle)))
|
||||||
let end_angle_deg = end_angle.to_degrees();
|
|
||||||
|
|
||||||
Ok((start_angle_deg, end_angle_deg))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_on_circumference(center: &Point2d, point: &Point2d, radius: f64) -> bool {
|
pub fn is_on_circumference(center: Point2d, point: Point2d, radius: f64) -> bool {
|
||||||
let dx = point.x - center.x;
|
let dx = point.x - center.x;
|
||||||
let dy = point.y - center.y;
|
let dy = point.y - center.y;
|
||||||
|
|
||||||
@ -237,7 +295,7 @@ mod tests {
|
|||||||
// Here you can bring your functions into scope
|
// Here you can bring your functions into scope
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::{get_x_component, get_y_component};
|
use super::{get_x_component, get_y_component, Angle};
|
||||||
use crate::executor::SourceRange;
|
use crate::executor::SourceRange;
|
||||||
|
|
||||||
static EACH_QUAD: [(i32, [i32; 2]); 12] = [
|
static EACH_QUAD: [(i32, [i32; 2]); 12] = [
|
||||||
@ -261,28 +319,28 @@ mod tests {
|
|||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
for &(angle, expected_result) in EACH_QUAD.iter() {
|
for &(angle, expected_result) in EACH_QUAD.iter() {
|
||||||
let res = get_y_component(angle as f64, 1.0);
|
let res = get_y_component(Angle::from_degrees(angle as f64), 1.0);
|
||||||
results.push([res[0].round() as i32, res[1].round() as i32]);
|
results.push([res.x.round() as i32, res.y.round() as i32]);
|
||||||
expected.push(expected_result);
|
expected.push(expected_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(results, expected);
|
assert_eq!(results, expected);
|
||||||
|
|
||||||
let result = get_y_component(0.0, 1.0);
|
let result = get_y_component(Angle::ZERO, 1.0);
|
||||||
assert_eq!(result[0] as i32, 1);
|
assert_eq!(result.x as i32, 1);
|
||||||
assert_eq!(result[1] as i32, 0);
|
assert_eq!(result.y as i32, 0);
|
||||||
|
|
||||||
let result = get_y_component(90.0, 1.0);
|
let result = get_y_component(Angle::from_degrees(90.0), 1.0);
|
||||||
assert_eq!(result[0] as i32, 1);
|
assert_eq!(result.x as i32, 1);
|
||||||
assert!(result[1] > 100000.0);
|
assert!(result.y > 100000.0);
|
||||||
|
|
||||||
let result = get_y_component(180.0, 1.0);
|
let result = get_y_component(Angle::from_degrees(180.0), 1.0);
|
||||||
assert_eq!(result[0] as i32, -1);
|
assert_eq!(result.x as i32, -1);
|
||||||
assert!((result[1] - 0.0).abs() < f64::EPSILON);
|
assert!((result.y - 0.0).abs() < f64::EPSILON);
|
||||||
|
|
||||||
let result = get_y_component(270.0, 1.0);
|
let result = get_y_component(Angle::from_degrees(270.0), 1.0);
|
||||||
assert_eq!(result[0] as i32, -1);
|
assert_eq!(result.x as i32, -1);
|
||||||
assert!(result[1] < -100000.0);
|
assert!(result.y < -100000.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -291,45 +349,60 @@ mod tests {
|
|||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
|
|
||||||
for &(angle, expected_result) in EACH_QUAD.iter() {
|
for &(angle, expected_result) in EACH_QUAD.iter() {
|
||||||
let res = get_x_component(angle as f64, 1.0);
|
let res = get_x_component(Angle::from_degrees(angle as f64), 1.0);
|
||||||
results.push([res[0].round() as i32, res[1].round() as i32]);
|
results.push([res.x.round() as i32, res.y.round() as i32]);
|
||||||
expected.push(expected_result);
|
expected.push(expected_result);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(results, expected);
|
assert_eq!(results, expected);
|
||||||
|
|
||||||
let result = get_x_component(0.0, 1.0);
|
let result = get_x_component(Angle::ZERO, 1.0);
|
||||||
assert!(result[0] > 100000.0);
|
assert!(result.x > 100000.0);
|
||||||
assert_eq!(result[1] as i32, 1);
|
assert_eq!(result.y as i32, 1);
|
||||||
|
|
||||||
let result = get_x_component(90.0, 1.0);
|
let result = get_x_component(Angle::from_degrees(90.0), 1.0);
|
||||||
assert!((result[0] - 0.0).abs() < f64::EPSILON);
|
assert!((result.x - 0.0).abs() < f64::EPSILON);
|
||||||
assert_eq!(result[1] as i32, 1);
|
assert_eq!(result.y as i32, 1);
|
||||||
|
|
||||||
let result = get_x_component(180.0, 1.0);
|
let result = get_x_component(Angle::from_degrees(180.0), 1.0);
|
||||||
assert!(result[0] < -100000.0);
|
assert!(result.x < -100000.0);
|
||||||
assert_eq!(result[1] as i32, 1);
|
assert_eq!(result.y as i32, 1);
|
||||||
|
|
||||||
let result = get_x_component(270.0, 1.0);
|
let result = get_x_component(Angle::from_degrees(270.0), 1.0);
|
||||||
assert!((result[0] - 0.0).abs() < f64::EPSILON);
|
assert!((result.x - 0.0).abs() < f64::EPSILON);
|
||||||
assert_eq!(result[1] as i32, -1);
|
assert_eq!(result.y as i32, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_arc_center_and_end() {
|
fn test_arc_center_and_end() {
|
||||||
let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 90.0, 1.0);
|
let (center, end) = super::arc_center_and_end(
|
||||||
|
super::Point2d { x: 0.0, y: 0.0 },
|
||||||
|
Angle::ZERO,
|
||||||
|
Angle::from_degrees(90.0),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
assert_eq!(center.x.round(), -1.0);
|
assert_eq!(center.x.round(), -1.0);
|
||||||
assert_eq!(center.y, 0.0);
|
assert_eq!(center.y, 0.0);
|
||||||
assert_eq!(end.x.round(), -1.0);
|
assert_eq!(end.x.round(), -1.0);
|
||||||
assert_eq!(end.y, 1.0);
|
assert_eq!(end.y, 1.0);
|
||||||
|
|
||||||
let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 180.0, 1.0);
|
let (center, end) = super::arc_center_and_end(
|
||||||
|
super::Point2d { x: 0.0, y: 0.0 },
|
||||||
|
Angle::ZERO,
|
||||||
|
Angle::from_degrees(180.0),
|
||||||
|
1.0,
|
||||||
|
);
|
||||||
assert_eq!(center.x.round(), -1.0);
|
assert_eq!(center.x.round(), -1.0);
|
||||||
assert_eq!(center.y, 0.0);
|
assert_eq!(center.y, 0.0);
|
||||||
assert_eq!(end.x.round(), -2.0);
|
assert_eq!(end.x.round(), -2.0);
|
||||||
assert_eq!(end.y.round(), 0.0);
|
assert_eq!(end.y.round(), 0.0);
|
||||||
|
|
||||||
let (center, end) = super::arc_center_and_end(&super::Point2d { x: 0.0, y: 0.0 }, 0.0, 180.0, 10.0);
|
let (center, end) = super::arc_center_and_end(
|
||||||
|
super::Point2d { x: 0.0, y: 0.0 },
|
||||||
|
Angle::ZERO,
|
||||||
|
Angle::from_degrees(180.0),
|
||||||
|
10.0,
|
||||||
|
);
|
||||||
assert_eq!(center.x.round(), -10.0);
|
assert_eq!(center.x.round(), -10.0);
|
||||||
assert_eq!(center.y, 0.0);
|
assert_eq!(center.y, 0.0);
|
||||||
assert_eq!(end.x.round(), -20.0);
|
assert_eq!(end.x.round(), -20.0);
|
||||||
@ -339,42 +412,42 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_arc_angles() {
|
fn test_arc_angles() {
|
||||||
let (angle_start, angle_end) = super::arc_angles(
|
let (angle_start, angle_end) = super::arc_angles(
|
||||||
&super::Point2d { x: 0.0, y: 0.0 },
|
super::Point2d { x: 0.0, y: 0.0 },
|
||||||
&super::Point2d { x: -1.0, y: 1.0 },
|
super::Point2d { x: -1.0, y: 1.0 },
|
||||||
&super::Point2d { x: -1.0, y: 0.0 },
|
super::Point2d { x: -1.0, y: 0.0 },
|
||||||
1.0,
|
1.0,
|
||||||
SourceRange(Default::default()),
|
SourceRange(Default::default()),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(angle_start.round(), 0.0);
|
assert_eq!(angle_start.degrees().round(), 0.0);
|
||||||
assert_eq!(angle_end.round(), 90.0);
|
assert_eq!(angle_end.degrees().round(), 90.0);
|
||||||
|
|
||||||
let (angle_start, angle_end) = super::arc_angles(
|
let (angle_start, angle_end) = super::arc_angles(
|
||||||
&super::Point2d { x: 0.0, y: 0.0 },
|
super::Point2d { x: 0.0, y: 0.0 },
|
||||||
&super::Point2d { x: -2.0, y: 0.0 },
|
super::Point2d { x: -2.0, y: 0.0 },
|
||||||
&super::Point2d { x: -1.0, y: 0.0 },
|
super::Point2d { x: -1.0, y: 0.0 },
|
||||||
1.0,
|
1.0,
|
||||||
SourceRange(Default::default()),
|
SourceRange(Default::default()),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(angle_start.round(), 0.0);
|
assert_eq!(angle_start.degrees().round(), 0.0);
|
||||||
assert_eq!(angle_end.round(), 180.0);
|
assert_eq!(angle_end.degrees().round(), 180.0);
|
||||||
|
|
||||||
let (angle_start, angle_end) = super::arc_angles(
|
let (angle_start, angle_end) = super::arc_angles(
|
||||||
&super::Point2d { x: 0.0, y: 0.0 },
|
super::Point2d { x: 0.0, y: 0.0 },
|
||||||
&super::Point2d { x: -20.0, y: 0.0 },
|
super::Point2d { x: -20.0, y: 0.0 },
|
||||||
&super::Point2d { x: -10.0, y: 0.0 },
|
super::Point2d { x: -10.0, y: 0.0 },
|
||||||
10.0,
|
10.0,
|
||||||
SourceRange(Default::default()),
|
SourceRange(Default::default()),
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(angle_start.round(), 0.0);
|
assert_eq!(angle_start.degrees().round(), 0.0);
|
||||||
assert_eq!(angle_end.round(), 180.0);
|
assert_eq!(angle_end.degrees().round(), 180.0);
|
||||||
|
|
||||||
let result = super::arc_angles(
|
let result = super::arc_angles(
|
||||||
&super::Point2d { x: 0.0, y: 5.0 },
|
super::Point2d { x: 0.0, y: 5.0 },
|
||||||
&super::Point2d { x: 5.0, y: 5.0 },
|
super::Point2d { x: 5.0, y: 5.0 },
|
||||||
&super::Point2d { x: 10.0, y: -10.0 },
|
super::Point2d { x: 10.0, y: -10.0 },
|
||||||
10.0,
|
10.0,
|
||||||
SourceRange(Default::default()),
|
SourceRange(Default::default()),
|
||||||
);
|
);
|
||||||
@ -384,7 +457,7 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
panic!("Expected error");
|
panic!("Expected error");
|
||||||
}
|
}
|
||||||
assert_eq!(angle_start.round(), 0.0);
|
assert_eq!(angle_start.degrees().round(), 0.0);
|
||||||
assert_eq!(angle_end.round(), 180.0);
|
assert_eq!(angle_end.degrees().round(), 180.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user