Merge pull request #820 from storytold/fix/812-ellipse-pie
Part of #812: an ellipse's Pie Start and End Angle and Invert Pie
This commit is contained in:
commit
4cf912fa94
|
|
@ -129,7 +129,7 @@ grade by behaviour against `plan/illustrator/`, not by whether a menu item exist
|
|||
Clear deletes individual selected compound-path members without deleting their siblings, and explicit object ids work without a selection; Cut still removes the same objects it copies (#786).
|
||||
- **Saved selections:** Select ▸ Save Selection… asks for a name and keeps the selection in the document (saved with the file, undoable, up to 25); each one is listed at the bottom of the Select menu and a click selects its objects again; Edit Selection… lists them to rename or delete, applied together on OK. A file's saved selections are capped and checked against its objects when it opens. Not yet: Update Selection and the Layers panel's saved-selection menu.
|
||||
- **Drawing:** Pen, Curvature, anchor tools, Pencil, Paintbrush, Blob Brush, Smooth, Path Eraser, Join. Direction handles dragged with Direct Selection or the Anchor Point tool snap to smart guides, and Shift keeps them at 45° steps from their anchor. The Pen continues any open path from the end clicked, and while drawing, a click on an end of another open path joins the two into one (#776). Every point a drawing tool places snaps to Smart Guides through one snapper (#506): the Pen's and Curvature tool's anchors, the Pencil's ends, the shape tools' start and dragged corner or radius (Rectangle, Rounded Rectangle, Ellipse, Polygon, Star, Line Segment, Arc, Spiral, the grids), the Type tool's click and frame, a new artboard's corners.
|
||||
- **Shapes:** all shape tools (including Flare) and the line, arc, spiral and grid tools. Space held while dragging out a shape, line, arc, spiral or grid moves it at its current size. While a grid is dragged out, Up/Down change its rows (concentric dividers) and Left/Right its columns (radial dividers).
|
||||
- **Shapes:** all shape tools (including Flare) and the line, arc, spiral and grid tools. Space held while dragging out a shape, line, arc, spiral or grid moves it at its current size. While a grid is dragged out, Up/Down change its rows (concentric dividers) and Left/Right its columns (radial dividers). An ellipse's Pie Start and End Angle and Invert Pie (Transform and Properties panels' Ellipse Properties, `object.setLiveShape {pieStart, pieEnd, invertPie}`) cut it as a pie (#812; the on-canvas pie widget is still to come).
|
||||
- **Cutting:** Eraser, Scissors, Knife, Mirror & Cut, Line Cut and Rectangle Cut (real geometry, compound paths keep their holes).
|
||||
- **Transform:** Rotate, Reflect, Scale, Shear (click or Alt-click snaps the reference point to anchors and centres; Alt-click opens the tool's dialog, and a drag with Alt down at the release, from the press or pressed mid-drag, transforms a copy), Reshape, Free Transform (free and perspective distort and shear, switched by the keys held during a handle drag).
|
||||
- **Live Corners:** drag a live rectangle's corner widgets (Selection or Direct Selection) to round all corners, with a radius readout; with Direct Selection, select corner anchors to show and round just those corners (#381). Each corner keeps its own radius and kind (Round, Inverted Round, Chamfer): Alt-click a widget to cycle the kind, double-click it for the Corners dialog, and the Properties and Transform radius fields set the selected corners (`object.setLiveShape {corners, radius, kind}`). Corners stay circular when the rectangle is scaled unevenly or along its own sides when rotated (the radius keeps its size, or scales by the mean scale with Scale Corners on); only a shear distorts them. On a rectangle that isn't square every corner is the same circle: a radius past half the shorter side stops there for all of them, the widgets sit on the corners as drawn and turn the edited corners red at that limit, CSS export writes the drawn radii, and rectangles saved stretched by older versions get circular corners when their corners are next set (#442). Live Corners work on any path's corners too, as on a rectangle's (#511): a star's, a live polygon's (which also shows them to the Selection tool, has a Corner Radius field in the Properties and Transform panels and keeps its radius as its sides change) and a pen path's, wherever two straight sides meet at an anchor. Direct Selection shows the widgets of every corner, or of the corners holding selected anchors; each corner is a circle tangent to both sides, drawn no larger than takes its cut halfway along its shorter side; a path keeps its uncut outline so its corners stay editable, and the same command, dialog, angle preference and View › Hide Corner Widget apply.
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ impl LiveShape {
|
|||
LiveShape::Rectangle { w, h, radii, kinds, xf } => {
|
||||
shapes::rectangle_with_corners(Rect::new(0.0, 0.0, *w, *h), *radii, *kinds).transformed(*xf)
|
||||
}
|
||||
LiveShape::Ellipse { w, h, xf, .. } => shapes::ellipse(Rect::new(0.0, 0.0, *w, *h)).transformed(*xf),
|
||||
LiveShape::Ellipse { w, h, pie, xf } => shapes::ellipse_pie(Rect::new(0.0, 0.0, *w, *h), pie.0, pie.1).transformed(*xf),
|
||||
LiveShape::Polygon { radii, kinds, .. } => cut_corners(&self.polygon_outline(), radii, kinds).0,
|
||||
LiveShape::Line { a, b } => shapes::line(*a, *b),
|
||||
LiveShape::Path { base, radii, kinds } => cut_corners(base, radii, kinds).0,
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
|||
"Live Shape Properties",
|
||||
[],
|
||||
None,
|
||||
"{id?, ids?, radius?: pt, kind?: \"round\"|\"invertedRound\"|\"chamfer\", corners?: [i…], sides?: n} (Live Corners on any path: radius and kind set its corners (anchors without handles between two straight sides): `corners` (anchor indices of the path with its corners uncut, counting every subpath's anchors in order: a rectangle's 0 top-left, 1 top-right, 2 bottom-right, 3 bottom-left; a polygon's from the first vertex clockwise), else the corners with a Direct-Selected anchor, else every corner; each radius is drawn no larger than half the corner's shorter side allows; a path that isn't a live shape keeps its uncut outline so its corners stay editable, and is plain again once none is cut; sides: a polygon's, which keep the radius they shared)",
|
||||
"{id?, ids?, radius?: pt, kind?: \"round\"|\"invertedRound\"|\"chamfer\", corners?: [i…], sides?: n, pieStart?, pieEnd?: degrees (an ellipse's pie, counterclockwise from 3 o'clock; 0 to 360 is the whole ellipse), invertPie?: true (swaps them: the other part of the ellipse)} (Live Corners on any path: radius and kind set its corners (anchors without handles between two straight sides): `corners` (anchor indices of the path with its corners uncut, counting every subpath's anchors in order: a rectangle's 0 top-left, 1 top-right, 2 bottom-right, 3 bottom-left; a polygon's from the first vertex clockwise), else the corners with a Direct-Selected anchor, else every corner; each radius is drawn no larger than half the corner's shorter side allows; a path that isn't a live shape keeps its uncut outline so its corners stay editable, and is plain again once none is cut; sides: a polygon's, which keep the radius they shared)",
|
||||
has_selection,
|
||||
set_live_shape
|
||||
),
|
||||
|
|
@ -1095,6 +1095,19 @@ fn set_live_shape(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
.transpose()?;
|
||||
let radius = p.get("radius").and_then(Value::as_f64);
|
||||
let sides = p.get("sides").and_then(Value::as_u64);
|
||||
// An ellipse's pie (Ellipse Properties: Pie Start and End Angle, degrees; Invert Pie swaps them).
|
||||
let angle = |k: &str| -> Result<Option<f64>> {
|
||||
match p.get(k) {
|
||||
None | Some(Value::Null) => Ok(None),
|
||||
Some(v) => v
|
||||
.as_f64()
|
||||
.filter(|a| a.is_finite())
|
||||
.map(|a| Some(a.rem_euclid(360.0)))
|
||||
.ok_or_else(|| bad("object.setLiveShape", format!("`{k}` must be an angle in degrees, not {v}"))),
|
||||
}
|
||||
};
|
||||
let (pie_start, pie_end) = (angle("pieStart")?, angle("pieEnd")?);
|
||||
let invert = p.get("invertPie").and_then(Value::as_bool) == Some(true);
|
||||
s.edit("Live Shape", |d, sel| {
|
||||
for id in &ids {
|
||||
let Some(NodeKind::Path { path, live, .. }) = d.node_mut(*id).map(|n| &mut n.kind) else { continue };
|
||||
|
|
@ -1102,6 +1115,19 @@ fn set_live_shape(s: &mut Session, p: &Value) -> Result<Value> {
|
|||
l.set_sides(n);
|
||||
*path = l.to_path();
|
||||
}
|
||||
if let Some(l @ LiveShape::Ellipse { .. }) = live.as_mut()
|
||||
&& (pie_start.is_some() || pie_end.is_some() || invert)
|
||||
{
|
||||
if let LiveShape::Ellipse { pie, .. } = l {
|
||||
// An end of 0 is a full turn (Illustrator shows a whole ellipse as 0° to 360°).
|
||||
let end_of = |a: f64| if a == 0.0 { 360.0 } else { a };
|
||||
*pie = (pie_start.unwrap_or(pie.0), pie_end.map_or(pie.1, end_of));
|
||||
if invert {
|
||||
*pie = (pie.1.rem_euclid(360.0), end_of(pie.0));
|
||||
}
|
||||
}
|
||||
*path = l.to_path();
|
||||
}
|
||||
if radius.is_none() && kind.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -279,3 +279,31 @@ fn a_pen_path_rounds_its_corners_but_not_its_ends() {
|
|||
run(&mut s, json!({"id": e.0, "radius": 8}));
|
||||
assert_eq!(node_path(&s, e), before);
|
||||
}
|
||||
|
||||
/// #812: an ellipse's Pie Start and End Angle cut it as a pie; Invert Pie shows the other part;
|
||||
/// one undo step each; a bad angle is an error.
|
||||
#[test]
|
||||
fn an_ellipse_takes_pie_angles_and_inverts() {
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 300, "height": 300})).unwrap();
|
||||
let id = NodeId(s.execute("shape.ellipse", &json!({"x": 0, "y": 0, "width": 200, "height": 100})).unwrap()["id"].as_u64().unwrap());
|
||||
let pie = |s: &Session| match &s.doc().unwrap().doc.node(id).unwrap().kind {
|
||||
NodeKind::Path { live: Some(LiveShape::Ellipse { pie, .. }), path, .. } => (*pie, path.bounds().unwrap()),
|
||||
k => panic!("{k:?}"),
|
||||
};
|
||||
s.execute("object.setLiveShape", &json!({"pieStart": 0, "pieEnd": 90})).unwrap();
|
||||
let (p, b) = pie(&s);
|
||||
assert_eq!(p, (0.0, 90.0));
|
||||
assert!((b.x0 - 100.0).abs() < 1e-6 && (b.y1 - 50.0).abs() < 1e-6, "the quarter up and right of the centre: {b:?}");
|
||||
s.execute("object.setLiveShape", &json!({"invertPie": true})).unwrap();
|
||||
let (p, b) = pie(&s);
|
||||
assert_eq!(p, (90.0, 360.0));
|
||||
assert!(b.x0.abs() < 1e-6 && (b.y1 - 100.0).abs() < 1e-6, "the other three quarters: {b:?}");
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(pie(&s).0, (0.0, 90.0));
|
||||
// 0 to 360 (or an end of 0) is the whole ellipse again.
|
||||
s.execute("object.setLiveShape", &json!({"pieStart": 0, "pieEnd": 0})).unwrap();
|
||||
assert_eq!(pie(&s).0, (0.0, 360.0));
|
||||
assert_eq!(s.doc().unwrap().doc.node(id).unwrap().path_data().unwrap().anchor_count(), 4);
|
||||
assert!(s.execute("object.setLiveShape", &json!({"pieStart": "half"})).is_err());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,50 @@ pub fn ellipse(r: Rect) -> PathData {
|
|||
PathData::single(SubPath::new(anchors, true))
|
||||
}
|
||||
|
||||
/// The ellipse inscribed in `r` cut as a pie: from `start` to `end` degrees, measured
|
||||
/// counterclockwise on screen from 3 o'clock (as Illustrator's Pie Start and End Angle are), then
|
||||
/// back through the centre. A pie spanning the whole turn (or nothing) is the whole [`ellipse`].
|
||||
/// The arc is one cubic per quarter turn at most: the centre, then the arc's anchors.
|
||||
pub fn ellipse_pie(r: Rect, start: f64, end: f64) -> PathData {
|
||||
let span = (end - start).rem_euclid(360.0);
|
||||
if !(start.is_finite() && end.is_finite()) || span < 1e-9 || 360.0 - span < 1e-9 {
|
||||
return ellipse(r);
|
||||
}
|
||||
let r = r.abs();
|
||||
let c = r.center();
|
||||
let (rx, ry) = (r.width() / 2.0, r.height() / 2.0);
|
||||
// Screen y runs down: counterclockwise on screen is a falling y.
|
||||
let at = |deg: f64| {
|
||||
let t = deg.to_radians();
|
||||
Point::new(c.x + rx * t.cos(), c.y - ry * t.sin())
|
||||
};
|
||||
let tangent = |deg: f64| {
|
||||
let t = deg.to_radians();
|
||||
Vec2::new(-rx * t.sin(), -ry * t.cos())
|
||||
};
|
||||
let pieces = if span <= 90.0 {
|
||||
1
|
||||
} else if span <= 180.0 {
|
||||
2
|
||||
} else if span <= 270.0 {
|
||||
3
|
||||
} else {
|
||||
4
|
||||
};
|
||||
let step = span / f64::from(pieces);
|
||||
let k = 4.0 / 3.0 * (step.to_radians() / 4.0).tan();
|
||||
let mut anchors = vec![Anchor::corner(c)];
|
||||
for i in 0..=pieces {
|
||||
let a = start + step * f64::from(i);
|
||||
let (p, d) = (at(a), tangent(a) * k);
|
||||
let h_in = if i == 0 { p } else { p - d };
|
||||
let h_out = if i == pieces { p } else { p + d };
|
||||
let kind = if i == 0 || i == pieces { AnchorKind::Corner } else { AnchorKind::Smooth };
|
||||
anchors.push(Anchor { p, h_in, h_out, kind });
|
||||
}
|
||||
PathData::single(SubPath::new(anchors, true))
|
||||
}
|
||||
|
||||
/// Regular polygon with `sides` (≥ 3), first vertex straight up from the centre.
|
||||
pub fn polygon(center: Point, radius: f64, sides: u32, rotation_deg: f64) -> PathData {
|
||||
let n = sides.max(3);
|
||||
|
|
@ -207,6 +251,27 @@ mod tests {
|
|||
crate::corners::cut_corners(&rectangle(r), &radii, &[]).1.concat()
|
||||
}
|
||||
|
||||
/// A pie is the arc from its start to its end angle, counterclockwise on screen, closed through
|
||||
/// the centre; a whole turn (or none) is the ellipse.
|
||||
#[test]
|
||||
fn a_pie_is_its_arc_closed_through_the_centre() {
|
||||
let r = Rect::new(0.0, 0.0, 200.0, 100.0);
|
||||
assert_eq!(ellipse_pie(r, 0.0, 360.0), ellipse(r));
|
||||
assert_eq!(ellipse_pie(r, 90.0, 90.0), ellipse(r));
|
||||
// A quarter, from 3 o'clock up to 12: the centre, then the arc's two ends.
|
||||
let q = ellipse_pie(r, 0.0, 90.0);
|
||||
let a: Vec<Point> = q.subpaths[0].anchors.iter().map(|a| a.p).collect();
|
||||
assert_eq!(a.len(), 3);
|
||||
assert!(a[0].distance(Point::new(100.0, 50.0)) < 1e-9 && a[1].distance(Point::new(200.0, 50.0)) < 1e-9);
|
||||
assert!(a[2].distance(Point::new(100.0, 0.0)) < 1e-9, "counterclockwise on screen: up");
|
||||
assert_eq!(q.bounds().map(|b| (b.x0.round(), b.y0.round(), b.x1.round(), b.y1.round())), Some((100.0, 0.0, 200.0, 50.0)));
|
||||
// Three quarters, from 12 round to 3 o'clock: four pieces at most, the whole box covered.
|
||||
let big = ellipse_pie(r, 90.0, 0.0);
|
||||
assert!(big.subpaths[0].anchors.len() <= 6 && big.subpaths[0].closed);
|
||||
assert_eq!(big.bounds().map(|b| (b.x0.round(), b.y0.round(), b.x1.round(), b.y1.round())), Some((0.0, 0.0, 200.0, 100.0)));
|
||||
assert!(ellipse_pie(r, f64::NAN, 10.0).anchor_count() == 4, "not a number: the whole ellipse");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rect_is_4_corners_clockwise() {
|
||||
let p = rectangle(Rect::new(0.0, 0.0, 100.0, 50.0));
|
||||
|
|
|
|||
|
|
@ -221,6 +221,10 @@
|
|||
Auto Size New Area Type Ajustar automáticamente el tamaño del nuevo texto de área
|
||||
Auto Spell Check Revisión ortográfica automática
|
||||
Auto-Collapse Iconic Panels Contraer automáticamente los paneles de iconos
|
||||
Ellipse Properties: Propiedades de la elipse:
|
||||
Invert Pie Invertir sector
|
||||
Pie End Angle: Ángulo final del sector:
|
||||
Pie Start Angle: Ángulo inicial del sector:
|
||||
Show Tool Group Labels Mostrar etiquetas de grupos de herramientas
|
||||
Auto-Rotate (turn the paper to each artboard) Rotar automáticamente (girar el papel según cada mesa de trabajo)
|
||||
Automatic flatness Lisura automática
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
|
|
@ -222,6 +222,10 @@
|
|||
Auto Size New Area Type Taille automatique du nouveau texte captif
|
||||
Auto Spell Check Vérification orthographique automatique
|
||||
Auto-Collapse Iconic Panels Réduire automatiquement les panneaux en icônes
|
||||
Ellipse Properties: Propriétés de l’ellipse :
|
||||
Invert Pie Inverser le secteur
|
||||
Pie End Angle: Angle de fin du secteur :
|
||||
Pie Start Angle: Angle de début du secteur :
|
||||
Show Tool Group Labels Afficher les libellés des groupes d'outils
|
||||
Auto-Rotate (turn the paper to each artboard) Rotation automatique (orienter le papier selon chaque plan de travail)
|
||||
Automatic flatness Planéité automatique
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
|
|
@ -221,6 +221,10 @@
|
|||
Auto Size New Area Type Dimensionamento automatico del nuovo testo in area
|
||||
Auto Spell Check Controllo ortografico automatico
|
||||
Auto-Collapse Iconic Panels Comprimi automaticamente i pannelli a icona
|
||||
Ellipse Properties: Proprietà dell’ellisse:
|
||||
Invert Pie Inverti settore
|
||||
Pie End Angle: Angolo finale del settore:
|
||||
Pie Start Angle: Angolo iniziale del settore:
|
||||
Show Tool Group Labels Mostra etichette dei gruppi di strumenti
|
||||
Auto-Rotate (turn the paper to each artboard) Rotazione automatica (orienta la carta in base a ogni tavola da disegno)
|
||||
Automatic flatness Planarità automatica
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
|
|
@ -345,9 +345,13 @@
|
|||
Appearance Attribute アピアランス属性
|
||||
Apply 適用
|
||||
Blending Mode 描画モード
|
||||
Ellipse Properties: 楕円形のプロパティ:
|
||||
Fill & Stroke 塗りと線
|
||||
Fill Color カラー (塗り)
|
||||
Invert Pie 扇形を反転
|
||||
Opacity 不透明度
|
||||
Pie End Angle: 扇形の終了角度:
|
||||
Pie Start Angle: 扇形の開始角度:
|
||||
Stroke Color カラー (線)
|
||||
Stroke Weight 線幅
|
||||
Graphic Style グラフィックスタイル
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
|
|
@ -219,6 +219,10 @@
|
|||
Auto Size New Area Type Автоматически подгонять размер нового текста в области
|
||||
Auto Spell Check Автоматическая проверка орфографии
|
||||
Auto-Collapse Iconic Panels Автоматически сворачивать значки панелей
|
||||
Ellipse Properties: Свойства эллипса:
|
||||
Invert Pie Инвертировать сектор
|
||||
Pie End Angle: Конечный угол сектора:
|
||||
Pie Start Angle: Начальный угол сектора:
|
||||
Show Tool Group Labels Показывать названия групп инструментов
|
||||
Auto-Rotate (turn the paper to each artboard) Автоповорот (поворачивать лист по каждой монтажной области)
|
||||
Automatic flatness Автоматическая плоскостность
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
|
|
@ -215,6 +215,10 @@
|
|||
Auto Size New Area Type Автоматично підганяти розмір нового тексту в області
|
||||
Auto Spell Check Автоматична перевірка орфографії
|
||||
Auto-Collapse Iconic Panels Автоматично згортати значки панелей
|
||||
Ellipse Properties: Властивості еліпса:
|
||||
Invert Pie Інвертувати сектор
|
||||
Pie End Angle: Кінцевий кут сектора:
|
||||
Pie Start Angle: Початковий кут сектора:
|
||||
Show Tool Group Labels Показувати назви груп інструментів
|
||||
Auto-Rotate (turn the paper to each artboard) Автоматичний поворот (повертати аркуш відповідно до кожної монтажної області)
|
||||
Automatic flatness Автоматична площинність
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 14.
|
|
|
@ -204,6 +204,10 @@
|
|||
Auto Size New Area Type 自动调整新区域文字大小
|
||||
Auto Spell Check 自动拼写检查
|
||||
Auto-Collapse Iconic Panels 自动折叠图标面板
|
||||
Ellipse Properties: 椭圆属性:
|
||||
Invert Pie 反转扇形
|
||||
Pie End Angle: 扇形终止角度:
|
||||
Pie Start Angle: 扇形起始角度:
|
||||
Show Tool Group Labels 显示工具组标签
|
||||
Auto-Rotate (turn the paper to each artboard) 自动旋转 (为每个画板转动纸张)
|
||||
Automatic flatness 自动平滑度
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 2 and column 73.
|
|
|
@ -215,6 +215,10 @@
|
|||
Auto Size New Area Type 自動調整新區域文字大小
|
||||
Auto Spell Check 自動拼字檢查
|
||||
Auto-Collapse Iconic Panels 自動收合圖示面板
|
||||
Ellipse Properties: 橢圓屬性:
|
||||
Invert Pie 反轉扇形
|
||||
Pie End Angle: 扇形終止角度:
|
||||
Pie Start Angle: 扇形起始角度:
|
||||
Show Tool Group Labels 顯示工具組標籤
|
||||
Auto-Rotate (turn the paper to each artboard) 自動旋轉 (將紙張轉向各工作區域)
|
||||
Automatic flatness 自動平滑度
|
||||
|
|
|
|||
|
Can't render this file because it contains an unexpected character in line 6 and column 47.
|
|
|
@ -468,6 +468,7 @@ pub fn transform_section(app: &mut VectorcraftApp, ui: &mut Ui) {
|
|||
});
|
||||
corner_radius_row(app, ui, &n, "radius");
|
||||
}
|
||||
vectorcraft_doc::LiveShape::Ellipse { pie, .. } => super::transform::pie_rows(app, ui, *pie, "props-pie"),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,10 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
|||
});
|
||||
corner_radius_row(app, ui, &n, "xfp-radius");
|
||||
}
|
||||
vectorcraft_doc::LiveShape::Ellipse { pie, .. } => {
|
||||
widgets::subheader(ui, tl!("Ellipse Properties:"));
|
||||
pie_rows(app, ui, *pie, "xfp-pie");
|
||||
}
|
||||
_ => {
|
||||
widgets::subheader(ui, tl!("Shape Properties:"));
|
||||
widgets::dim_label(ui, tl!(n.kind_label()));
|
||||
|
|
@ -211,6 +215,22 @@ pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) {
|
|||
}
|
||||
}
|
||||
|
||||
/// An ellipse's Pie Start and End Angle (degrees, counterclockwise from 3 o'clock; 0 to 360 is the
|
||||
/// whole ellipse) and Invert Pie, which shows the other part of it (Transform and Properties panels).
|
||||
pub(crate) fn pie_rows(app: &mut VectorcraftApp, ui: &mut Ui, pie: (f64, f64), id: &str) {
|
||||
for (label, key, v) in [(tl!("Pie Start Angle:"), "pieStart", pie.0), (tl!("Pie End Angle:"), "pieEnd", pie.1)] {
|
||||
ui.horizontal(|ui| {
|
||||
widgets::dim_label(ui, label);
|
||||
if let Some(a) = widgets::plain_field(ui, (id, key), v, "°", 1, 60.0) {
|
||||
app.run("object.setLiveShape", json!({ key: a })).ok();
|
||||
}
|
||||
});
|
||||
}
|
||||
if widgets::flat_button(ui, tl!("Invert Pie"), 90.0).clicked() {
|
||||
app.run("object.setLiveShape", json!({"invertPie": true})).ok();
|
||||
}
|
||||
}
|
||||
|
||||
/// The Control bar's underlined "Transform" link: a click toggles the Transform panel in a
|
||||
/// popover under it, as the Stroke link does with the Stroke panel.
|
||||
pub fn link(app: &mut VectorcraftApp, ui: &mut Ui) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue