From 1dc61af4d1eb0f3ebfc70bf422e162e3ad4b58bc Mon Sep 17 00:00:00 2001 From: FredApps <153173854+FredApps@users.noreply.github.com> Date: Mon, 12 Oct 2026 14:22:02 +0700 Subject: [PATCH 1/2] Add a Polar Grid canvas-click dialog A click with the Polar Grid tool opens the shared shape dialog (width, height, concentric and radial dividers), as the Rectangular Grid tool does, and keeps the divider counts as the tool's options. --- crates/ui-egui/src/dialogs/form.rs | 5 +- crates/ui-egui/src/dialogs/mod.rs | 2 +- crates/ui-egui/src/dialogs/shapes.rs | 10 +++- crates/ui-egui/src/dialogs/tests.rs | 71 ++++++++++++++++++++++++++++ crates/ui-egui/src/dialogs/tools.rs | 7 +++ crates/ui-egui/src/i18n/cs.tsv | 2 + crates/ui-egui/src/i18n/de.tsv | 2 + crates/ui-egui/src/i18n/es.tsv | 2 + crates/ui-egui/src/i18n/fr.tsv | 2 + crates/ui-egui/src/i18n/it.tsv | 2 + crates/ui-egui/src/i18n/ja.tsv | 2 + crates/ui-egui/src/i18n/pt-br.tsv | 2 + crates/ui-egui/src/i18n/ru.tsv | 2 + crates/ui-egui/src/i18n/uk.tsv | 2 + crates/ui-egui/src/i18n/zh-hans.tsv | 2 + crates/ui-egui/src/i18n/zh-hant.tsv | 2 + 16 files changed, 113 insertions(+), 4 deletions(-) diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index 40329c8c..fd14ff40 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -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); diff --git a/crates/ui-egui/src/dialogs/mod.rs b/crates/ui-egui/src/dialogs/mod.rs index 7ff56acb..85dff13b 100644 --- a/crates/ui-egui/src/dialogs/mod.rs +++ b/crates/ui-egui/src/dialogs/mod.rs @@ -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, diff --git a/crates/ui-egui/src/dialogs/shapes.rs b/crates/ui-egui/src/dialogs/shapes.rs index 56680c12..ac479732 100644 --- a/crates/ui-egui/src/dialogs/shapes.rs +++ b/crates/ui-egui/src/dialogs/shapes.rs @@ -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 { 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 { ), }; // 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, ¶ms, 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) } diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 5095b7c0..cfba1dcb 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -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); +} diff --git a/crates/ui-egui/src/dialogs/tools.rs b/crates/ui-egui/src/dialogs/tools.rs index 88013a34..2697a48d 100644 --- a/crates/ui-egui/src/dialogs/tools.rs +++ b/crates/ui-egui/src/dialogs/tools.rs @@ -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", diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 440777bf..defd6191 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -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: diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index e09d40f2..9f347288 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -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: diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 833308da..4954d10a 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -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: diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 5ad2bd5f..49139253 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -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 : diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 0cde4d46..2e924593 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -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: diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 6b05d121..4125621d 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -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: 放射状の分割線: diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index c9d86310..04e66fd0 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -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: diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index 4bab5e25..f95584f4 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -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: Радиальные разделители: diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index ea77f63b..489a369f 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -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: Радіальні розділювачі: diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index 3a7a15b0..836b4259 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -3786,3 +3786,5 @@ lighting Light 光源 Artistic 艺术效果 Film Grain… 胶片颗粒… Highlight Area 高光区域 + Concentric Dividers: 同心分隔线: + Radial Dividers: 径向分隔线: diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 3bb7a640..2c962956 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -3567,3 +3567,5 @@ lighting Light 光源 Artistic 藝術效果 Film Grain… 底片顆粒… Highlight Area 亮部範圍 + Concentric Dividers: 同心分隔線: + Radial Dividers: 徑向分隔線: From 62bf8c862c301c5bb8173cd7165e8a156c8956f0 Mon Sep 17 00:00:00 2001 From: bflatastic Date: Mon, 12 Oct 2026 14:22:21 +0700 Subject: [PATCH 2/2] Polar Grid dialog: Hungarian and Korean rows for its two labels --- crates/ui-egui/src/i18n/hu.tsv | 2 ++ crates/ui-egui/src/i18n/ko.tsv | 2 ++ 2 files changed, 4 insertions(+) diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 499982d3..878605bc 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -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: diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index 00978c95..cfd724e4 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -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: 방사형 구분선: