Merge pull request #1192 from FredApps/fix/polar-grid-click-dialog-20261011

Add a Polar Grid canvas-click dialog
This commit is contained in:
BFlat 2026-10-12 15:39:37 +07:00 committed by GitHub
commit a48ed5e253
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 117 additions and 4 deletions

View File

@ -49,7 +49,7 @@ pub(super) fn length(ui: &mut egui::Ui, d: &mut Dialog, key: &str, unit: Unit, w
fn lengths(kind: &str) -> &'static [&'static str] {
match kind {
"move" => &["dx", "dy"],
"rectangle" | "ellipse" | "rectangularGrid" | "artboardOptions" => &["width", "height"],
"rectangle" | "ellipse" | "rectangularGrid" | "polarGrid" | "artboardOptions" => &["width", "height"],
"roundedRectangle" => &["width", "height", "radius"],
"polygon" => &["radius"],
"star" => &["radius1", "radius2"],
@ -68,6 +68,7 @@ fn order(kind: &str) -> &'static [&'static str] {
"offsetPath" => &["offset", "joins", "miterLimit"],
"splitIntoGrid" => &["rows", "columns", "gutter"],
"rectangularGrid" => &["width", "height", "rows", "columns"],
"polarGrid" => &["width", "height", "concentric", "radial"],
"artboardOptions" => &["name", "width", "height"],
_ => &[],
}
@ -182,6 +183,8 @@ pub(super) fn grid(ui: &mut egui::Ui, d: &mut Dialog, unit: Unit) {
let label = match (d.kind.as_str(), k.as_str()) {
("rectangularGrid", "rows") => tl!("Horizontal Dividers:").into(),
("rectangularGrid", "columns") => tl!("Vertical Dividers:").into(),
("polarGrid", "concentric") => tl!("Concentric Dividers:").into(),
("polarGrid", "radial") => tl!("Radial Dividers:").into(),
_ => humanized(&k),
};
field(ui, d, &k, &label);

View File

@ -209,7 +209,7 @@ macro_rules! registry {
registry! {
NewDocument: [new_document::KIND] => new_document::SPEC,
Shape: ["rectangle", "roundedRectangle", "ellipse", "polygon", "star", "lineSegment", "rectangularGrid"] => shapes::SPEC,
Shape: ["rectangle", "roundedRectangle", "ellipse", "polygon", "star", "lineSegment", "rectangularGrid", "polarGrid"] => shapes::SPEC,
Transform: ["move", "rotate", "scale", "reflect", "shear"] => transform::SPEC,
PathOp: ["average", "offsetPath", "simplify", "splitIntoGrid"] => path_ops::SPEC,
DocumentSetup: ["documentSetup"] => document_setup::SPEC,

View File

@ -17,6 +17,7 @@ fn title(kind: &str) -> &'static str {
"polygon" => tl!("Polygon"),
"star" => tl!("Star"),
"rectangularGrid" => tl!("Rectangular Grid"),
"polarGrid" => tl!("Polar Grid"),
_ => tl!("Line Segment Tool Options"),
}
}
@ -35,6 +36,11 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
json!({"x": x, "y": y, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0),
"rows": (d.f64("rows", 5.0) as u64).min(999), "columns": (d.f64("columns", 5.0) as u64).min(999)}),
),
"polarGrid" => (
"shape.polarGrid",
json!({"x": x, "y": y, "width": d.f64("width", 100.0), "height": d.f64("height", 100.0),
"concentric": (d.f64("concentric", 5.0) as u64).min(999), "radial": (d.f64("radial", 5.0) as u64).min(999)}),
),
"lineSegment" => {
let (l, a) = (d.f64("length", 100.0), d.f64("angle", 0.0).to_radians());
("shape.line", json!({"x1": x, "y1": y, "x2": x + l * a.cos(), "y2": y - l * a.sin()}))
@ -45,13 +51,13 @@ fn confirm(app: &mut VectorcraftApp, d: &Dialog) -> Result<Value, String> {
),
};
// On the active plane while the perspective grid shows, sized in plane units from the click.
let options = (d.kind == "rectangularGrid").then(|| json!({"rows": params["rows"], "columns": params["columns"]}));
let options = matches!(d.kind.as_str(), "rectangularGrid" | "polarGrid").then(|| params.clone());
let result = match vectorcraft_engine::perspective_click(&app.session, id, &params, vectorcraft_geom::Point::new(x, y)) {
Some((id, params)) => run_and_close(app, &id, params),
None => run_and_close(app, id, params),
}?;
if let Some(options) = options {
app.run("tool.setOption", json!({"tool": "rectangularGrid", "values": options}))?;
app.run("tool.setOption", json!({"tool": d.kind, "values": options}))?;
}
Ok(result)
}

View File

@ -41,6 +41,7 @@ fn kinds_map_to_their_dialogs() {
("roundedRectangle", DialogKind::Shape),
("lineSegment", DialogKind::Shape),
("rectangularGrid", DialogKind::Shape),
("polarGrid", DialogKind::Shape),
("shear", DialogKind::Transform),
("splitIntoGrid", DialogKind::PathOp),
("artboardOptions", DialogKind::ArtboardOptions),
@ -87,6 +88,7 @@ fn every_dialog_draws() {
"star",
"lineSegment",
"rectangularGrid",
"polarGrid",
"rotate",
"reflect",
"scale",
@ -650,3 +652,72 @@ fn zig_zag_has_sliders_and_radio_buttons() {
}
assert!(!lines.contains(&"Ridges:"), "{lines:?}");
}
#[test]
fn polar_grid_shared_canvas_click_defaults_units_counts_and_history() {
use vectorcraft_doc::NodeId;
use vectorcraft_tools::{PointerEvent, PointerKind};
let mut app = app();
app.select_tool("polarGrid");
app.run("tool.setOption", json!({"tool": "polarGrid", "values": {"concentric": 2, "radial": 3}})).unwrap();
let before = app.session.doc().unwrap().doc.clone();
let undo = app.session.doc().unwrap().history.undo.len();
for kind in [PointerKind::Down, PointerKind::Up] {
crate::canvas::dispatch(&mut app, &PointerEvent::new(kind, 30.0, 40.0), Default::default());
}
assert_eq!(DialogKind::of("polarGrid"), Some(DialogKind::Shape));
let d = app.ui.dialog.as_mut().expect("canvas click opens the shared shape form");
assert_eq!(d.fields["width"], json!("100 pt"));
assert_eq!(d.fields["height"], json!("100 pt"));
assert_eq!((d.fields["concentric"].clone(), d.fields["radial"].clone()), (json!(2), json!(3)));
d.fields.insert("width".into(), json!("25.4 mm"));
d.fields.insert("height".into(), json!("0.5 in"));
frame(&mut app, Default::default());
let result = confirm(&mut app).unwrap();
assert!(app.ui.dialog.is_none());
let s = app.session.doc().unwrap();
let group = s.doc.node(NodeId(result["id"].as_u64().unwrap())).unwrap();
let children = group.children().unwrap();
assert_eq!(children.len(), 6);
assert_eq!(children.iter().filter(|n| n.path_data().unwrap().is_closed()).count(), 3);
assert_eq!(s.history.undo.len(), undo + 1);
let made = s.doc.clone();
app.run("edit.undo", json!({})).unwrap();
assert_eq!(app.session.doc().unwrap().doc, before);
app.run("edit.redo", json!({})).unwrap();
assert_eq!(app.session.doc().unwrap().doc, made);
}
#[test]
fn polar_grid_shared_cancel_reopen_zero_counts_and_failed_creation() {
let mut app = app();
app.run("tool.setOption", json!({"tool": "polarGrid", "values": {"concentric": 2, "radial": 3}})).unwrap();
let before = app.session.doc().unwrap().doc.clone();
open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20}));
let d = app.ui.dialog.as_mut().expect("shared Polar Grid form opens");
d.fields.insert("concentric".into(), json!(8));
cancel(&mut app);
assert_eq!(app.session.doc().unwrap().doc, before);
open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20}));
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["concentric"], json!(2));
for key in ["concentric", "radial"] {
app.ui.dialog.as_mut().unwrap().fields.insert(key.into(), json!(0));
}
let result = confirm(&mut app).unwrap();
let group = app.session.doc().unwrap().doc.node(vectorcraft_doc::NodeId(result["id"].as_u64().unwrap())).unwrap();
assert_eq!(group.children().unwrap().len(), 1);
assert!(group.children().unwrap()[0].path_data().unwrap().is_closed());
open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20}));
assert_eq!(app.ui.dialog.as_ref().unwrap().fields["radial"], json!(0));
cancel(&mut app);
let layer = app.session.doc().unwrap().doc.layers[0].id;
app.run("layer.setProps", json!({"ids": [layer.0], "locked": true})).unwrap();
let options = app.session.tool_options_of("polarGrid");
let before = app.session.doc().unwrap().doc.clone();
open_tool_dialog(&mut app, "polarGrid", json!({"x": 10, "y": 20}));
app.ui.dialog.as_mut().unwrap().fields.insert("radial".into(), json!(9));
assert!(confirm(&mut app).is_err());
assert!(app.ui.dialog.is_none());
assert_eq!(app.session.tool_options_of("polarGrid"), options);
assert_eq!(app.session.doc().unwrap().doc, before);
}

View File

@ -20,6 +20,13 @@ pub fn open_tool_dialog(app: &mut VectorcraftApp, kind: &str, p: Value) {
let options = app.session.tool_options_of(kind);
Dialog::new(kind, json!({"x": x, "y": y, "width": "100 pt", "height": "100 pt", "rows": options["rows"], "columns": options["columns"]}))
}
"polarGrid" => {
let options = app.session.tool_options_of(kind);
Dialog::new(
kind,
json!({"x": x, "y": y, "width": "100 pt", "height": "100 pt", "concentric": options["concentric"], "radial": options["radial"]}),
)
}
// Graph tool click: the graph's size.
"graph" => Dialog::new(
"command",

View File

@ -4498,3 +4498,5 @@ mark-style Japanese Japonské
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) komprimovaný soubor se rozbalí na více, než VectorCraft přečte ({_1} MB dokumentu, {_2} MB na obrázek)
@msg the document is too large to save compressed ({0}): save it with Use Compression off dokument je příliš velký pro komprimované uložení ({0}): uložte ho s vypnutou volbou Použít kompresi
@msg the file stopped being read (please report this bug) čtení souboru se přerušilo (nahlaste prosím tuto chybu)
Concentric Dividers: Soustředné dělicí čáry:
Radial Dividers: Radiální dělicí čáry:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4149,3 +4149,5 @@ theme Appearance Erscheinungsbild
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) die komprimierte Datei entpackt sich auf mehr, als VectorCraft liest ({_1} MB Dokument, {_2} MB pro Bild)
@msg the document is too large to save compressed ({0}): save it with Use Compression off das Dokument ist zu groß, um komprimiert gespeichert zu werden ({0}): speichern Sie es mit deaktivierter Option „Komprimierung verwenden“
@msg the file stopped being read (please report this bug) das Lesen der Datei wurde abgebrochen (bitte melden Sie diesen Fehler)
Concentric Dividers: Konzentrische Trennlinien:
Radial Dividers: Radiale Trennlinien:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4192,3 +4192,5 @@ lighting Light Luz
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) el archivo comprimido ocupa al descomprimirse más de lo que VectorCraft lee ({_1} MB de documento, {_2} MB por imagen)
@msg the document is too large to save compressed ({0}): save it with Use Compression off el documento es demasiado grande para guardarlo comprimido ({0}): guárdelo con Usar compresión desactivado
@msg the file stopped being read (please report this bug) se dejó de leer el archivo (informe de este error)
Concentric Dividers: Divisiones concéntricas:
Radial Dividers: Divisiones radiales:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4202,3 +4202,5 @@ lighting Light Lumière
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) le fichier compressé dépasse une fois décompressé ce que VectorCraft lit ({_1} Mo de document, {_2} Mo par image)
@msg the document is too large to save compressed ({0}): save it with Use Compression off le document est trop volumineux pour être enregistré compressé ({0}) : enregistrez-le avec Utiliser la compression désactivé
@msg the file stopped being read (please report this bug) la lecture du fichier s’est interrompue (veuillez signaler ce bogue)
Concentric Dividers: Divisions concentriques :
Radial Dividers: Divisions radiales :

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4592,3 +4592,5 @@ theme Appearance Kinézet
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) a tömörített fájl kicsomagolva nagyobb lenne, mint amennyit a VectorCraft beolvas ({_1} MB dokumentum, képenként {_2} MB)
@msg the document is too large to save compressed ({0}): save it with Use Compression off a dokumentum túl nagy a tömörített mentéshez ({0}): mentsd a Tömörítés használata beállítás kikapcsolásával
@msg the file stopped being read (please report this bug) a fájl beolvasása megszakadt (kérjük, jelentsd ezt a hibát)
Concentric Dividers: Koncentrikus osztók:
Radial Dividers: Sugárirányú osztók:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4194,3 +4194,5 @@ lighting Light Luce
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) il file compresso, una volta decompresso, supera ciò che VectorCraft legge ({_1} MB di documento, {_2} MB per immagine)
@msg the document is too large to save compressed ({0}): save it with Use Compression off il documento è troppo grande per essere salvato compresso ({0}): salvalo con Usa compressione disattivato
@msg the file stopped being read (please report this bug) la lettura del file si è interrotta (segnala questo errore)
Concentric Dividers: Divisori concentrici:
Radial Dividers: Divisori radiali:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4188,3 +4188,5 @@ lighting Light ライト
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) 圧縮ファイルを展開すると VectorCraft が読み込める量を超えます (ドキュメント {_1} MB、画像 1 枚あたり {_2} MB)
@msg the document is too large to save compressed ({0}): save it with Use Compression off ドキュメントが大きすぎて圧縮保存できません ({0}): 「圧縮を使用」をオフにして保存してください
@msg the file stopped being read (please report this bug) ファイルの読み込みが中断されました (このバグを報告してください)
Concentric Dividers: 同心円の分割線:
Radial Dividers: 放射状の分割線:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4158,3 +4158,5 @@ lighting Light 광원
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) 압축된 파일의 압축을 풀면 VectorCraft가 읽을 수 있는 한도를 넘습니다(문서 {_1}MB, 이미지당 {_2}MB)
@msg the document is too large to save compressed ({0}): save it with Use Compression off 문서가 너무 커서 압축 저장할 수 없습니다({0}): 압축 사용을 끄고 저장하세요
@msg the file stopped being read (please report this bug) 파일 읽기가 중단되었습니다(이 버그를 신고해 주세요)
Concentric Dividers: 동심원 구분선:
Radial Dividers: 방사형 구분선:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -3677,3 +3677,5 @@ axis Both Ambos
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) o arquivo compactado se expande para mais do que o VectorCraft lê ({_1} MB de documento, {_2} MB por imagem)
@msg the document is too large to save compressed ({0}): save it with Use Compression off o documento é grande demais para ser salvo compactado ({0}): salve-o com Usar Compressão desativado
@msg the file stopped being read (please report this bug) a leitura do arquivo foi interrompida (informe este erro)
Concentric Dividers: Divisórias concêntricas:
Radial Dividers: Divisórias radiais:

Can't render this file because it contains an unexpected character in line 6 and column 47.

View File

@ -4190,3 +4190,5 @@ lighting Light Свет
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) сжатый файл распаковывается в объём больше, чем читает VectorCraft ({_1} МБ документа, {_2} МБ на изображение)
@msg the document is too large to save compressed ({0}): save it with Use Compression off документ слишком велик для сохранения со сжатием ({0}): сохраните его с выключенным параметром «Использовать сжатие»
@msg the file stopped being read (please report this bug) чтение файла прервалось (пожалуйста, сообщите об этой ошибке)
Concentric Dividers: Концентрические разделители:
Radial Dividers: Радиальные разделители:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -4174,3 +4174,5 @@ lighting Light Світло
@msg the compressed file unpacks to more than VectorCraft reads ({_1} MB of document, {_2} MB per image) стислий файл розпаковується в обсяг більший, ніж читає VectorCraft ({_1} МБ документа, {_2} МБ на зображення)
@msg the document is too large to save compressed ({0}): save it with Use Compression off документ завеликий для збереження зі стиском ({0}): збережіть його з вимкненим параметром «Використовувати стиск»
@msg the file stopped being read (please report this bug) читання файлу перервалося (будь ласка, повідомте про цю помилку)
Concentric Dividers: Концентричні розділювачі:
Radial Dividers: Радіальні розділювачі:

Can't render this file because it has a wrong number of fields in line 13.

View File

@ -3786,3 +3786,5 @@ lighting Light 光源
Artistic 艺术效果
Film Grain… 胶片颗粒…
Highlight Area 高光区域
Concentric Dividers: 同心分隔线:
Radial Dividers: 径向分隔线:

Can't render this file because it contains an unexpected character in line 2 and column 73.

View File

@ -3567,3 +3567,5 @@ lighting Light 光源
Artistic 藝術效果
Film Grain… 底片顆粒…
Highlight Area 亮部範圍
Concentric Dividers: 同心分隔線:
Radial Dividers: 徑向分隔線:

Can't render this file because it contains an unexpected character in line 6 and column 47.