LineNode
This commit is contained in:
parent
288a926fea
commit
4d617bcd67
@ -40,6 +40,16 @@ impl Angle {
|
||||
(self.0).0
|
||||
}
|
||||
|
||||
/// Get the sine of this angle.
|
||||
pub fn sin(self) -> f64 {
|
||||
self.to_rad().sin()
|
||||
}
|
||||
|
||||
/// Get the cosine of this angle.
|
||||
pub fn cos(self) -> f64 {
|
||||
self.to_rad().cos()
|
||||
}
|
||||
|
||||
/// Create an angle from a value in a unit.
|
||||
pub fn with_unit(val: f64, unit: AngularUnit) -> Self {
|
||||
Self(Scalar(val * unit.raw_scale()))
|
||||
|
@ -49,6 +49,12 @@ impl Point {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Spec<Length>> for Point {
|
||||
fn from(spec: Spec<Length>) -> Self {
|
||||
Self::new(spec.x, spec.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl Get<SpecAxis> for Point {
|
||||
type Component = Length;
|
||||
|
||||
|
61
src/library/graphics/line.rs
Normal file
61
src/library/graphics/line.rs
Normal file
@ -0,0 +1,61 @@
|
||||
use crate::library::prelude::*;
|
||||
|
||||
/// Display a line without affecting the layout.
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct LineNode(Spec<Linear>, Spec<Linear>);
|
||||
|
||||
#[node]
|
||||
impl LineNode {
|
||||
/// How the stroke the line.
|
||||
pub const STROKE: Smart<Paint> = Smart::Auto;
|
||||
/// The line's thickness.
|
||||
pub const THICKNESS: Length = Length::pt(1.0);
|
||||
|
||||
fn construct(_: &mut Context, args: &mut Args) -> TypResult<Content> {
|
||||
let origin = args.named::<Spec<Linear>>("origin")?.unwrap_or_default();
|
||||
let to = match args.named::<Spec<Linear>>("to")? {
|
||||
Some(to) => to.zip(origin).map(|(to, from)| to - from),
|
||||
None => {
|
||||
let length =
|
||||
args.named::<Linear>("length")?.unwrap_or(Length::cm(1.0).into());
|
||||
let angle = args.named::<Angle>("angle")?.unwrap_or_default();
|
||||
|
||||
let x = angle.cos() * length;
|
||||
let y = angle.sin() * length;
|
||||
|
||||
Spec::new(x, y)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Content::inline(Self(origin, to)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Layout for LineNode {
|
||||
fn layout(
|
||||
&self,
|
||||
_: &mut Context,
|
||||
regions: &Regions,
|
||||
styles: StyleChain,
|
||||
) -> TypResult<Vec<Arc<Frame>>> {
|
||||
let target = regions.expand.select(regions.first, Size::zero());
|
||||
let mut frame = Frame::new(target);
|
||||
|
||||
let thickness = styles.get(Self::THICKNESS);
|
||||
let stroke = Some(Stroke {
|
||||
paint: styles.get(Self::STROKE).unwrap_or(Color::BLACK.into()),
|
||||
thickness,
|
||||
});
|
||||
|
||||
let resolved_origin =
|
||||
self.0.zip(regions.base).map(|(l, b)| Linear::resolve(l, b));
|
||||
let resolved_to = self.1.zip(regions.base).map(|(l, b)| Linear::resolve(l, b));
|
||||
|
||||
let geometry = Geometry::Line(resolved_to.into());
|
||||
|
||||
let shape = Shape { geometry, fill: None, stroke };
|
||||
frame.prepend(resolved_origin.into(), Element::Shape(shape));
|
||||
|
||||
Ok(vec![Arc::new(frame)])
|
||||
}
|
||||
}
|
@ -2,10 +2,12 @@
|
||||
|
||||
mod hide;
|
||||
mod image;
|
||||
mod line;
|
||||
mod shape;
|
||||
mod transform;
|
||||
|
||||
pub use self::image::*;
|
||||
pub use hide::*;
|
||||
pub use line::*;
|
||||
pub use shape::*;
|
||||
pub use transform::*;
|
||||
|
@ -53,6 +53,7 @@ pub fn new() -> Scope {
|
||||
|
||||
// Graphics.
|
||||
std.def_node::<graphics::ImageNode>("image");
|
||||
std.def_node::<graphics::LineNode>("line");
|
||||
std.def_node::<graphics::RectNode>("rect");
|
||||
std.def_node::<graphics::SquareNode>("square");
|
||||
std.def_node::<graphics::EllipseNode>("ellipse");
|
||||
@ -170,3 +171,14 @@ castable! {
|
||||
Expected: "content",
|
||||
Value::Content(content) => content.pack(),
|
||||
}
|
||||
|
||||
castable! {
|
||||
Spec<Linear>,
|
||||
Expected: "two-dimensional length array",
|
||||
Value::Array(array) => {
|
||||
let e = "point array must contain exactly two entries";
|
||||
let a = array.get(0).map_err(|_| e)?.clone().cast::<Linear>()?;
|
||||
let b = array.get(1).map_err(|_| e)?.clone().cast::<Linear>()?;
|
||||
Spec::new(a, b)
|
||||
},
|
||||
}
|
||||
|
BIN
tests/ref/graphics/line.png
Normal file
BIN
tests/ref/graphics/line.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
47
tests/typ/graphics/line.typ
Normal file
47
tests/typ/graphics/line.typ
Normal file
@ -0,0 +1,47 @@
|
||||
// Test lines
|
||||
|
||||
---
|
||||
// Default line.
|
||||
#line()
|
||||
|
||||
---
|
||||
// Test the to argument.
|
||||
{
|
||||
line(to: (10pt, 0pt))
|
||||
line(origin: (0pt, 10pt), to: (0pt, 0pt))
|
||||
line(to: (15pt, 15pt))
|
||||
}
|
||||
#v(.5cm)
|
||||
|
||||
---
|
||||
|
||||
#set page(fill: rgb("0B1026"))
|
||||
#set line(stroke: white)
|
||||
|
||||
#let star(width, ..args) = box(width: width, height: width)[
|
||||
#set text(spacing: 0%)
|
||||
#set line(..args)
|
||||
|
||||
#line(length: +30%, origin: (09.0%, 02%))
|
||||
#line(length: +30%, origin: (38.7%, 02%), angle: -72deg)
|
||||
#line(length: +30%, origin: (57.5%, 02%), angle: 252deg)
|
||||
#line(length: +30%, origin: (57.3%, 02%))
|
||||
#line(length: -30%, origin: (88.0%, 02%), angle: -36deg)
|
||||
#line(length: +30%, origin: (73.3%, 48%), angle: 252deg)
|
||||
#line(length: -30%, origin: (73.5%, 48%), angle: 36deg)
|
||||
#line(length: +30%, origin: (25.4%, 48%), angle: -36deg)
|
||||
#line(length: +30%, origin: (25.6%, 48%), angle: -72deg)
|
||||
#line(length: +32%, origin: (8.50%, 02%), angle: 34deg)
|
||||
]
|
||||
|
||||
#grid(columns: (1fr, ) * 3, ..((star(20pt, thickness: .5pt), ) * 9))
|
||||
|
||||
---
|
||||
// Test errors.
|
||||
|
||||
// Error: 11-18 point array must contain exactly two entries
|
||||
#line(to: (50pt,))
|
||||
|
||||
---
|
||||
// Error: 15-27 expected relative length, found angle
|
||||
#line(origin: (3deg, 10pt), length: 5cm)
|
Loading…
x
Reference in New Issue
Block a user