M examples/iangle.et +1 -1
@@ 1,4 1,4 @@
(setq A (point 5 5))
(setq C (circle A 5))
C
-(triangle C)
+(iangle C 90)
A => examples/incenter.et +8 -0
@@ 0,0 1,8 @@
+(setq A (point 3 3))
+(setq B (point 3 6))
+(setq C (point 7 3))
+(setq triangle_a (triangle A B C))
+(setq I (incenter triangle_a))
+
+triangle_a
+(circle I (inradius triangle_a))
M src/lang/functions.rs +74 -28
@@ 217,6 217,80 @@ impl Operation for FnAngle {
}
}
+#[derive(Clone)]
+pub struct FnIncenter;
+impl Operation for FnIncenter {
+ clone_impl!(FnIncenter);
+ fn call(&self, args: &[Value]) -> Result<Value, String> {
+ // check for 1 argument
+ if args.len() < 1 {
+ return Err("Incenter requires exactly 1 argument".to_string());
+ }
+
+ // check for 1 triangle
+ let triangle = match &args[0] {
+ Value::Triangle(t) => t.clone(),
+ _ => return Err("Invalid types for triangle".to_string()),
+ };
+
+ // try getting the incenter
+ return Ok(Value::Point(triangle.incenter()));
+ }
+}
+
+#[derive(Clone)]
+pub struct FnPoint;
+impl Operation for FnPoint {
+ clone_impl!(FnPoint);
+ fn call(&self, args: &[Value]) -> Result<Value, String> {
+ // check for 2 arguments
+ if args.len() < 2 {
+ return Err("Point requires exactly 2 arguments".to_string());
+ }
+
+ // try forcing the arguments into floats
+ let mut floats = Vec::new();
+ for arg in args {
+ match arg {
+ Value::Int(i) => floats.push(*i as f64),
+ Value::Float(f) => floats.push(*f),
+ _ => return Err("Invalid types for point".to_string()),
+ }
+ }
+
+ // return the point
+ Ok(Value::Point(Point {
+ x: floats[0],
+ y: floats[1],
+ }))
+ }
+}
+
+/*
+Functions that return properties
+*/
+
+#[derive(Clone)]
+pub struct FnInradius;
+impl Operation for FnInradius {
+ clone_impl!(FnInradius);
+ fn call(&self, args: &[Value]) -> Result<Value, String> {
+ // check for 1 argument
+ if args.len() < 1 {
+ return Err("Inradius requires exactly 1 argument".to_string());
+ }
+
+ // check for 1 triangle
+ let triangle = match &args[0] {
+ Value::Triangle(t) => t.clone(),
+ _ => return Err("Invalid types for triangle".to_string()),
+ };
+
+ // try getting the inradius
+ return Ok(Value::Float(triangle.inradius()));
+ }
+}
+
/*
Basic geometric shapes
*/
@@ 385,31 459,3 @@ impl Operation for FnTriangle {
}
}
}
-
-#[derive(Clone)]
-pub struct FnPoint;
-impl Operation for FnPoint {
- clone_impl!(FnPoint);
- fn call(&self, args: &[Value]) -> Result<Value, String> {
- // check for 2 arguments
- if args.len() < 2 {
- return Err("Point requires exactly 2 arguments".to_string());
- }
-
- // try forcing the arguments into floats
- let mut floats = Vec::new();
- for arg in args {
- match arg {
- Value::Int(i) => floats.push(*i as f64),
- Value::Float(f) => floats.push(*f),
- _ => return Err("Invalid types for point".to_string()),
- }
- }
-
- // return the point
- Ok(Value::Point(Point {
- x: floats[0],
- y: floats[1],
- }))
- }
-}
M src/lang/types.rs +54 -1
@@ 76,7 76,7 @@ impl Element for Point {
fn to_svg(&self) -> Vec<Box<dyn Render>> {
vec![Box::new(SvgCircle {
center: *self,
- radius: 2.0,
+ radius: 0.01,
})]
}
}
@@ 221,4 221,57 @@ impl Triangle {
// otherwise, return the triangle
Ok(Self { a, b, c })
}
+
+ /// Return the inradius of the triangle
+ pub fn inradius(&self) -> f64 {
+ // calculate the side lengths
+ let a = (self.b.x - self.c.x).hypot(self.b.y - self.c.y);
+ let b = (self.a.x - self.c.x).hypot(self.a.y - self.c.y);
+ let c = (self.a.x - self.b.x).hypot(self.a.y - self.b.y);
+
+ // calculate the semiperimeter
+ let s = (a + b + c) / 2.0;
+
+ // calculate the inradius
+ (s * (s - a) * (s - b) * (s - c)).sqrt() / s
+ }
+
+ /// Return the incenter of the triangle
+ pub fn incenter(&self) -> Point {
+ // calculate the side lengths
+ let a = (self.b.x - self.c.x).hypot(self.b.y - self.c.y);
+ let b = (self.a.x - self.c.x).hypot(self.a.y - self.c.y);
+ let c = (self.a.x - self.b.x).hypot(self.a.y - self.b.y);
+
+ // calculate the incenter
+ let x = (a * self.a.x + b * self.b.x + c * self.c.x) / (a + b + c);
+ let y = (a * self.a.y + b * self.b.y + c * self.c.y) / (a + b + c);
+
+ Point { x, y }
+ }
+
+ /// Return the orthocenter of the triangle
+ pub fn orthocenter(&self) -> Point {
+ // calculate the slopes of the sides
+ let m1 = (self.b.y - self.a.y) / (self.b.x - self.a.x);
+ let m2 = (self.c.y - self.b.y) / (self.c.x - self.b.x);
+ let m3 = (self.a.y - self.c.y) / (self.a.x - self.c.x);
+
+ // calculate the orthocenter
+ let x = (m1 * m2 * (self.a.y - self.c.y)
+ + m2 * m3 * (self.a.y - self.b.y)
+ + m3 * m1 * (self.b.y - self.c.y))
+ / (m1 * m2 + m2 * m3 + m3 * m1);
+ let y = self.a.y - m1 * (x - self.a.x);
+
+ Point { x, y }
+ }
+
+ /// Return the centroid of the triangle
+ pub fn centroid(&self) -> Point {
+ Point {
+ x: (self.a.x + self.b.x + self.c.x) / 3.0,
+ y: (self.a.y + self.b.y + self.c.y) / 3.0,
+ }
+ }
}
M src/lexer.rs +17 -5
@@ 93,6 93,23 @@ fn match_fn(name: String) -> Function {
args: Vec::new(),
function: Box::new(functions::FnInscribedAngle),
},
+ "point" => Function {
+ name,
+ args: Vec::new(),
+ function: Box::new(functions::FnPoint),
+ },
+ "incenter" => Function {
+ name,
+ args: Vec::new(),
+ function: Box::new(functions::FnIncenter),
+ },
+
+ // functions that return properties
+ "inradius" => Function {
+ name,
+ args: Vec::new(),
+ function: Box::new(functions::FnInradius),
+ },
// basic geometric functions
"circle" => Function {
@@ 100,11 117,6 @@ fn match_fn(name: String) -> Function {
args: Vec::new(),
function: Box::new(functions::FnCircle),
},
- "point" => Function {
- name,
- args: Vec::new(),
- function: Box::new(functions::FnPoint),
- },
"triangle" => Function {
name,
args: Vec::new(),