mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-01 05:37:23 -06:00
Merge pull request #5019 from dotCooCoo/feat/grid-drag-divider-skews
Grid tools: F, V, X and C change the divider skews by 10 while a grid is dragged out
This commit is contained in:
@@ -1463,6 +1463,36 @@ fn rectangular_grid_options_frame_fill_and_skew() {
|
||||
assert!(most.iter().all(|v| *v > 0.0 && *v < 100.0), "{most:?}");
|
||||
}
|
||||
|
||||
/// F, V, X and C pressed while a grid is dragged out change its divider skews by 10 each: the
|
||||
/// grid drawn on release is the one `shape.rectangularGrid` / `shape.polarGrid` draws with those
|
||||
/// skews, and they stay as the tool's options.
|
||||
#[test]
|
||||
fn grid_drag_keys_skew_the_grid_drawn() {
|
||||
let v = view();
|
||||
for (tool, keys, skews) in [
|
||||
("rectangularGrid", "vvvfcc", json!({"rows": 5, "columns": 5, "rowSkew": 20.0, "columnSkew": 20.0})),
|
||||
("polarGrid", "fffxvc", json!({"concentric": 5, "radial": 5, "radialSkew": -20.0, "concentricSkew": 0.0})),
|
||||
] {
|
||||
let mut s = session();
|
||||
s.select_tool(tool, v).unwrap();
|
||||
s.pointer(&PointerEvent::new(PointerKind::Down, 100.0, 100.0), v).unwrap();
|
||||
s.pointer(&PointerEvent::new(PointerKind::Drag, 200.0, 200.0), v).unwrap();
|
||||
for c in keys.chars() {
|
||||
assert!(s.tool_claims_key(ToolKey::Letter(c), v), "{tool}: {c}");
|
||||
s.tool_key(ToolKey::Letter(c), Mods::default(), v).unwrap();
|
||||
}
|
||||
s.pointer(&PointerEvent::new(PointerKind::Up, 200.0, 200.0), v).unwrap();
|
||||
let drawn = s.doc().unwrap().doc.layers[0].children().unwrap().last().unwrap().children().unwrap().to_vec();
|
||||
let want = grid(&mut s, &format!("shape.{tool}"), with(&json!({"x": 100, "y": 100, "width": 100, "height": 100}), skews.clone()));
|
||||
let geometry = |g: &[std::sync::Arc<vectorcraft_doc::Node>]| g.iter().map(|n| format!("{:?}", n.geometric_bounds())).collect::<Vec<_>>();
|
||||
assert_eq!(geometry(&drawn), geometry(&want), "{tool}");
|
||||
let o = s.tool_options();
|
||||
for (k, value) in skews.as_object().unwrap() {
|
||||
assert_eq!(&o[k], value, "{tool}: {k}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// #1377: the Polar Grid options. The ellipses lie under the radial dividers, largest first;
|
||||
/// `compound` pairs them into compound-path rings, `fill` paints the current fill and the skews
|
||||
/// crowd the ellipses outward and the radial dividers clockwise of the top one (positive).
|
||||
|
||||
@@ -1492,6 +1492,56 @@ fn c_and_f_during_an_arc_gesture_go_to_the_arc_tool() {
|
||||
assert_ne!(serde_json::from_str::<Value>(&text_of(&r)).unwrap()["handledBy"], "tool", "{r}");
|
||||
}
|
||||
|
||||
/// `press_key` with `f`, `v`, `x` and `c` while a rectangular or polar grid is being dragged goes
|
||||
/// to the grid tool, and the release draws one grid. With nothing dragged, V is a shortcut again.
|
||||
#[test]
|
||||
fn f_v_x_and_c_during_a_grid_gesture_go_to_the_grid_tool() {
|
||||
let count = |s: &mut Server, id: u64| {
|
||||
let r = call(s, id, "inspect_document", json!({"depth": 0}));
|
||||
serde_json::from_str::<Value>(&text_of(&r)).unwrap()["layers"][0]["childCount"].as_u64().unwrap()
|
||||
};
|
||||
for tool in ["rectangularGrid", "polarGrid"] {
|
||||
let mut s = server();
|
||||
let before = count(&mut s, 1);
|
||||
let begin = json!([{"kind": "down", "x": 100, "y": 100}, {"kind": "drag", "x": 200, "y": 180}]);
|
||||
let r = call(&mut s, 2, "pointer_gesture", json!({"tool": tool, "events": begin}));
|
||||
assert_eq!(r["isError"], false, "{r}");
|
||||
for (id, key) in [(3, "f"), (4, "v"), (5, "x"), (6, "c")] {
|
||||
let r = call(&mut s, id, "press_key", json!({"key": key}));
|
||||
assert_eq!(serde_json::from_str::<Value>(&text_of(&r)).unwrap()["handledBy"], "tool", "{tool} {key}: {r}");
|
||||
}
|
||||
let r = call(&mut s, 7, "pointer_gesture", json!({"tool": tool, "events": [{"kind": "up", "x": 200, "y": 180}]}));
|
||||
assert_eq!(r["isError"], false, "{r}");
|
||||
assert_eq!(count(&mut s, 8), before + 1, "{tool}");
|
||||
let r = call(&mut s, 9, "press_key", json!({"key": "v"}));
|
||||
assert_ne!(serde_json::from_str::<Value>(&text_of(&r)).unwrap()["handledBy"], "tool", "{tool}: {r}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Grid letters with Shift reach the grid tool during a drag; with Ctrl, Cmd or Alt they do not.
|
||||
#[test]
|
||||
fn shifted_grid_letters_during_a_drag_go_to_the_grid_tool() {
|
||||
let mut s = server();
|
||||
let r = call(
|
||||
&mut s,
|
||||
1,
|
||||
"pointer_gesture",
|
||||
json!({"tool": "polarGrid", "events": [{"kind": "down", "x": 100, "y": 100}, {"kind": "drag", "x": 200, "y": 180}]}),
|
||||
);
|
||||
assert_eq!(r["isError"], false, "{r}");
|
||||
for (id, key, mods, tool) in [
|
||||
(2, "v", json!({"shift": true}), true),
|
||||
(3, "f", json!({"ctrl": true}), false),
|
||||
(4, "x", json!({"cmd": true}), false),
|
||||
(5, "c", json!({"cmd": true}), false),
|
||||
(6, "v", json!({"alt": true}), false),
|
||||
] {
|
||||
let r = call(&mut s, id, "press_key", json!({"key": key, "mods": mods}));
|
||||
let handled = serde_json::from_str::<Value>(&text_of(&r)).ok().is_some_and(|v| v["handledBy"] == "tool");
|
||||
assert_eq!(handled, tool, "{key} {mods}: {r}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A click on one of two selected objects makes it the key object: `pointer_gesture` replies
|
||||
/// with it as `key` and `inspect_document` lists it, and a click on the key again lets it go.
|
||||
#[test]
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
//! and Ctrl/Cmd keep its inner end at the distance from the center on the first modified drag as
|
||||
//! the outer end follows the pointer: Alt adds or removes whole winds of four segments, and
|
||||
//! Ctrl/Cmd changes the decay.
|
||||
//! On a grid, F and V lower and raise
|
||||
//! the skew of the horizontal (rectangular) or radial (polar) dividers, and X and C lower and raise
|
||||
//! that of the vertical or concentric ones.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_geom::Point;
|
||||
@@ -20,6 +23,9 @@ use crate::{Action, Cursor, Mods, Overlay, PointerEvent, PointerKind, Tool, Tool
|
||||
/// How much ↑ or ↓ changes the arc's slope while it is dragged out.
|
||||
const ARC_SLOPE_STEP: f64 = 10.0;
|
||||
|
||||
/// How much F, V, X or C changes a grid's divider skew (percent) while it is dragged out.
|
||||
const GRID_SKEW_STEP: f64 = 10.0;
|
||||
|
||||
pub struct FamilyTool {
|
||||
id: &'static str,
|
||||
start: Option<Point>,
|
||||
@@ -157,6 +163,25 @@ impl FamilyTool {
|
||||
}
|
||||
}
|
||||
|
||||
/// The skew a letter changes while a grid is dragged out, as an index into `skew`, and the
|
||||
/// direction: F lowers and V raises the skew of the horizontal dividers (a rectangular grid's
|
||||
/// rows) or the radial dividers (a polar grid's), X lowers and C raises that of the vertical
|
||||
/// dividers (columns) or the concentric dividers.
|
||||
fn grid_skew_key(&self, key: ToolKey) -> Option<(usize, f64)> {
|
||||
let (horizontal_or_radial, vertical_or_concentric) = match self.id {
|
||||
"rectangularGrid" => (0, 1),
|
||||
"polarGrid" => (1, 0),
|
||||
_ => return None,
|
||||
};
|
||||
match key {
|
||||
ToolKey::Letter('f') => Some((horizontal_or_radial, -1.0)),
|
||||
ToolKey::Letter('v') => Some((horizontal_or_radial, 1.0)),
|
||||
ToolKey::Letter('x') => Some((vertical_or_concentric, -1.0)),
|
||||
ToolKey::Letter('c') => Some((vertical_or_concentric, 1.0)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// A grid's options a drag draws with: its dividers, skews and switches (the Default Size and
|
||||
/// Reference Point are for a click's dialog).
|
||||
fn grid_options(&self) -> Value {
|
||||
@@ -316,6 +341,14 @@ impl Tool for FamilyTool {
|
||||
self.columns = self.columns.saturating_sub(1);
|
||||
true
|
||||
}
|
||||
("rectangularGrid" | "polarGrid", ToolKey::Letter(_)) => match self.grid_skew_key(key) {
|
||||
Some((i, sign)) => {
|
||||
let max = vectorcraft_geom::shapes::GRID_SKEW_MAX;
|
||||
self.skew[i] = (self.skew[i] + sign * GRID_SKEW_STEP).clamp(-max, max);
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
if changed {
|
||||
@@ -338,8 +371,14 @@ impl Tool for FamilyTool {
|
||||
}
|
||||
}
|
||||
/// The Arc tool takes C and F while an arc is dragged out: C switches it between open and closed, and F switches its base axis.
|
||||
/// The grid tools take F, V, X and C while a grid is dragged out, for its divider skews.
|
||||
fn claims_key(&self, _cx: &ToolContext, key: ToolKey) -> bool {
|
||||
self.id == "arc" && self.began && self.start.is_some() && matches!(key, ToolKey::Letter('c' | 'f'))
|
||||
self.began
|
||||
&& self.start.is_some()
|
||||
&& match self.id {
|
||||
"arc" => matches!(key, ToolKey::Letter('c' | 'f')),
|
||||
_ => self.grid_skew_key(key).is_some(),
|
||||
}
|
||||
}
|
||||
fn overlays(&self, cx: &ToolContext) -> Vec<Overlay> {
|
||||
let mut o = self.snap.guides().to_vec();
|
||||
@@ -458,6 +497,48 @@ mod tests {
|
||||
assert_eq!(t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 90.0, 60.0)), vec![Action::Commit]);
|
||||
}
|
||||
|
||||
/// While a grid is dragged out, F/V lower/raise the skew of the horizontal (rectangular) or
|
||||
/// radial (polar) dividers by 10 and X/C change that of the vertical or concentric ones, within
|
||||
/// ±500; the grid drawn next keeps them.
|
||||
#[test]
|
||||
fn grid_drag_keys_change_the_divider_skews() {
|
||||
let (d, _) = doc_with_rect();
|
||||
let s = Selection::default();
|
||||
let p = paint();
|
||||
let mut cx = cx(&d, &s, &p);
|
||||
cx.smart_guides = false;
|
||||
let key = ToolKey::Letter;
|
||||
for (id, horizontal_or_radial, vertical_or_concentric) in
|
||||
[("rectangularGrid", "rowSkew", "columnSkew"), ("polarGrid", "radialSkew", "concentricSkew")]
|
||||
{
|
||||
let mut t = FamilyTool::new(id);
|
||||
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 10.0, 10.0));
|
||||
assert!(!t.claims_key(&cx, key('f')), "{id}: no drag yet");
|
||||
t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 60.0, 40.0));
|
||||
assert!("fvxc".chars().all(|c| t.claims_key(&cx, key(c))), "{id}");
|
||||
assert!(!t.claims_key(&cx, key('e')), "{id}");
|
||||
let skews = |a: Vec<Action>| match &a[..] {
|
||||
[Action::Preview(c, v)] if c == &format!("shape.{id}") => (v[horizontal_or_radial].clone(), v[vertical_or_concentric].clone()),
|
||||
_ => panic!("{id}: {a:?}"),
|
||||
};
|
||||
assert_eq!(skews(t.key(&cx, key('v'), Mods::default())), (json!(10.0), json!(0.0)), "{id}: V");
|
||||
assert_eq!(skews(t.key(&cx, key('f'), Mods::default())), (json!(0.0), json!(0.0)), "{id}: F");
|
||||
assert_eq!(skews(t.key(&cx, key('f'), Mods::default())), (json!(-10.0), json!(0.0)), "{id}: F");
|
||||
assert_eq!(skews(t.key(&cx, key('c'), Mods::default())), (json!(-10.0), json!(10.0)), "{id}: C");
|
||||
assert_eq!(skews(t.key(&cx, key('x'), Mods::default())), (json!(-10.0), json!(0.0)), "{id}: X");
|
||||
for _ in 0..60 {
|
||||
t.key(&cx, key('x'), Mods::default());
|
||||
t.key(&cx, key('v'), Mods::default());
|
||||
}
|
||||
assert_eq!(skews(t.key(&cx, key('x'), Mods::default())), (json!(500.0), json!(-500.0)), "{id}: within ±500");
|
||||
assert_eq!(skews(t.key(&cx, key('v'), Mods::default())), (json!(500.0), json!(-500.0)), "{id}: within ±500");
|
||||
assert_eq!(t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 60.0, 40.0)), vec![Action::Commit]);
|
||||
assert!(!t.claims_key(&cx, key('v')), "{id}: the drag is over");
|
||||
let o = t.options();
|
||||
assert_eq!((o[horizontal_or_radial].clone(), o[vertical_or_concentric].clone()), (json!(500.0), json!(-500.0)), "{id}");
|
||||
}
|
||||
}
|
||||
|
||||
/// #1377: a drag draws with the tool's stored options, which keep to their types and range.
|
||||
#[test]
|
||||
fn a_grid_drag_uses_the_stored_options() {
|
||||
@@ -739,8 +820,8 @@ mod tests {
|
||||
assert!(!t.claims_key(&cx, c), "the drag is over");
|
||||
let o = t.options();
|
||||
assert_eq!((o["closed"].clone(), o["baseAlong"].clone(), o["slope"].clone()), (json!(false), json!("y"), json!(100.0)));
|
||||
// The other tools of the family leave C and F to their shortcuts.
|
||||
let mut g = FamilyTool::new("rectangularGrid");
|
||||
// The Spiral tool leaves C and F to their shortcuts.
|
||||
let mut g = FamilyTool::new("spiral");
|
||||
g.pointer(&cx, &PointerEvent::new(PointerKind::Down, 10.0, 10.0));
|
||||
g.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 60.0, 40.0));
|
||||
assert!(!g.claims_key(&cx, c) && !g.claims_key(&cx, f));
|
||||
|
||||
@@ -845,6 +845,38 @@ fn arrows_while_dragging_a_grid_change_its_rows_and_columns() {
|
||||
assert_eq!(grid(&mut app, &[Key::ArrowLeft, Key::ArrowDown]), (rows, columns + 1));
|
||||
}
|
||||
|
||||
/// Grid letters change the stored skews once per press while a polar grid is dragged, plain or
|
||||
/// with Shift; with Alt, Ctrl or Cmd they do not reach the grid.
|
||||
#[test]
|
||||
fn grid_drag_letters_take_plain_and_shift_presses() {
|
||||
use vectorcraft_tools::{PointerEvent, PointerKind};
|
||||
let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default());
|
||||
app.session.execute("file.new", &json!({"width": 400, "height": 300})).unwrap();
|
||||
app.select_tool("polarGrid");
|
||||
let view = app.view_info();
|
||||
app.session.pointer(&PointerEvent::new(PointerKind::Down, 100.0, 80.0), view).unwrap();
|
||||
app.session.pointer(&PointerEvent::new(PointerKind::Drag, 300.0, 220.0), view).unwrap();
|
||||
let skews = |app: &VectorcraftApp| (app.session.tool_options()["radialSkew"].clone(), app.session.tool_options()["concentricSkew"].clone());
|
||||
frame(&mut app, press(Key::V, Modifiers::NONE));
|
||||
assert_eq!(skews(&app), (json!(10.0), json!(0.0)));
|
||||
frame(&mut app, press(Key::F, Modifiers::SHIFT));
|
||||
assert_eq!(skews(&app), (json!(0.0), json!(0.0)));
|
||||
frame(&mut app, press(Key::V, Modifiers::SHIFT));
|
||||
assert_eq!(skews(&app), (json!(10.0), json!(0.0)));
|
||||
frame(&mut app, press(Key::X, Modifiers::SHIFT));
|
||||
assert_eq!(skews(&app), (json!(10.0), json!(-10.0)));
|
||||
frame(&mut app, press(Key::C, Modifiers::SHIFT));
|
||||
assert_eq!(skews(&app), (json!(10.0), json!(0.0)));
|
||||
frame(&mut app, press(Key::X, Modifiers::ALT));
|
||||
assert_eq!(skews(&app), (json!(10.0), json!(0.0)));
|
||||
for key in [Key::F, Key::X] {
|
||||
frame(&mut app, press(key, command_key()));
|
||||
assert_eq!(skews(&app), (json!(10.0), json!(0.0)), "{key:?} with Ctrl/Cmd");
|
||||
}
|
||||
app.session.pointer(&PointerEvent::new(PointerKind::Up, 300.0, 220.0), view).unwrap();
|
||||
assert_eq!(skews(&app), (json!(10.0), json!(0.0)), "the options stay after release");
|
||||
}
|
||||
|
||||
/// A press of `key` with `m` held.
|
||||
fn press(key: Key, m: Modifiers) -> Vec<egui::Event> {
|
||||
vec![egui::Event::ModifiersChanged(m), egui::Event::Key { key, physical_key: Some(key), pressed: true, repeat: false, modifiers: m }]
|
||||
|
||||
+8
-1
@@ -421,7 +421,7 @@ objects' fills or strokes differ (`fillMixed` / `strokeMixed`, drawn as a "?" pr
|
||||
| `draw_path` | `{points \| d, closed?, fill?, stroke?, strokeWidth?, dash?, cap?, join?, opacity?, name?, effects?}` | `points` is `[[x,y],…]` or `[{x,y,in?,out?,smooth?},…]`. `d` is SVG path data. The options are below. |
|
||||
| `draw_shape` | `{shape, …geometry, fill?, stroke?, strokeWidth?, dash?, cap?, join?, opacity?, name?, effects?}` | `rectangle`/`ellipse`: `x,y,width,height` (plus `radius` for corners). `polygon`: `cx,cy,radius,sides`. `star`: `cx,cy,radius1,radius2,points`. `line`: `x1,y1,x2,y2`. |
|
||||
| `set_paint` | `{fill?, stroke?, strokeWidth?, ids?}` | Applies to the selection (or `ids`) and becomes the default for new art. Returns `{applied, objects}`: `objects` is the number of objects painted, as the `objects` of the `paint.setFill`, `paint.setStroke` and `stroke.set` replies (the largest of them): a selected group counts the objects inside it. When it is 0, a `note` says that only the paint for new art changed. |
|
||||
| `press_key` | `{key, mods?}` | Remote: a real key event. Headless: runs the command or tool bound to that shortcut, or sends the key to the busy tool (digits too: `5` while dragging with the Perspective Selection tool; `e` while dragging a Selection or Group Selection marquee; `c` and `f` while dragging out an arc). |
|
||||
| `press_key` | `{key, mods?}` | Remote: a real key event. Headless: runs the command or tool bound to that shortcut, or sends the key to the busy tool (digits too: `5` while dragging with the Perspective Selection tool; `e` while dragging a Selection or Group Selection marquee; `c` and `f` while dragging out an arc; `f`, `v`, `x` and `c` while dragging out a rectangular or polar grid). |
|
||||
| `type_text` | `{text}` | Remote only. |
|
||||
| `answer_dialog` | `{set?: [{field, value?}], action?}` | Remote only. Sets fields of the app's open dialog in the order given with one `ui.dialog.set {fields}` (a missing `value` is `null`). The app stores all of them or none. When the app refuses the `ui.dialog.set`, for example because no dialog is open, the call is an error and `action` is not sent. Then `action` `confirm` (OK, `ui.dialog.confirm`) or `cancel` (`ui.dialog.cancel`). Give `set`, `action` or both; `set` holds at most 256 fields. `inspect_ui` shows the open dialog and its fields as `ui.dialog`. Returns `{dialog, result}`: the dialog after the fields were set (or `null`) and the reply to `confirm` or `cancel` (or `null`). After `invoke_menu` `app.quit`, `file.close` or `file.closeAll` replies `{"pending": "saveChanges"}`, `{action: "confirm"}` saves, `{set: [{field: "discard", value: true}], action: "confirm"}` closes without saving and `{action: "cancel"}` cancels the close or quit. |
|
||||
| `invoke_menu` | `{command, params?}` | Invokes a menu item by command id. Includes UI commands such as `view.*` and `window.*` in remote mode. |
|
||||
@@ -500,6 +500,13 @@ the new values, and they stay as the Spiral tool's `segments` and `decay` option
|
||||
{"name":"pointer_gesture","arguments":{"tool":"spiral","events":[{"kind":"down","x":100,"y":100},{"kind":"drag","x":200,"y":100,"mods":{"alt":true}},{"kind":"drag","x":300,"y":100,"mods":{"alt":true}},{"kind":"up","x":300,"y":100}]}}
|
||||
```
|
||||
|
||||
While a grid is being dragged out with the Rectangular Grid or Polar Grid tool, `press_key {key: "f"}` and
|
||||
`{key: "v"}` lower and raise the skew of the horizontal dividers (`rowSkew`) or the radial dividers (`radialSkew`) by
|
||||
10, and `{key: "x"}` and `{key: "c"}` lower and raise that of the vertical dividers (`columnSkew`) or the concentric
|
||||
dividers (`concentricSkew`), within ±500. Plain and Shift presses change the skew; with Alt, Ctrl or Cmd held they do not.
|
||||
The grid drawn on release uses these skews, and they stay as the tool's options for the next grid. With nothing
|
||||
being dragged, the four letters run their shortcuts as before.
|
||||
|
||||
Appearance stacks: an object can carry several fills and strokes (`appearance.addFill`, `appearance.addStroke`), indexed
|
||||
in paint order (0 is painted first, the bottom row of the Appearance panel). Cmd+/ runs `appearance.addFill` and
|
||||
Cmd+Alt+/ runs `appearance.addStroke` (`press_key {key: "/", mods: {cmd: true, alt: true}}`). `paint.setFill`, `paint.setStroke`,
|
||||
|
||||
@@ -699,6 +699,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
|
||||
- **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. The Flare tool's center drag turns the rays by 0.1 radian at each drag update in which the pointer's document y grows, and back by as much at each one in which it falls, Shift held keeps their turn, and Space held during either of its drags moves the flare. While a grid is dragged out, Up/Down change its rows (concentric dividers) and Left/Right its columns (radial dividers). A click with a grid tool asks for the size, the divider counts and the grid options (#1377, in part): a skew for each set of dividers, Use Outside Rectangle As Frame or Create Compound Path From Ellipses, and Fill Grid; they are parameters of `shape.rectangularGrid` and `shape.polarGrid`, stay as the tool's options and apply to drags too. The curve a skew percentage follows and the direction of the radial skew are our own (the public documentation gives neither), and the dialogs have no reference point locator. 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). With the bounding box showing, the Selection tool shows a selected ellipse's two pie widgets just outside it; dragging one sets its angle in one undo step and double-clicking one makes the ellipse whole again. A live polygon's Polygon Properties there: side count, angle, corner type and radius, radius, side length and Make Sides Equal once an uneven scale has made its sides differ (`object.setLiveShape {sides, polygonAngle, polygonRadius, sideLength, makeSidesEqual}`, part of #812); a star is a plain path, as in Illustrator. The Flare Tool Options last as the tool's options: the next flare dragged out uses them, a click's dialog shows them and double-clicking the tool opens them (part of #812).
|
||||
- **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. The Flare tool's center drag turns the rays with the pointer, Shift keeps them in their original orientation (unturned, or turned by a nonzero General › Constrain Angle), and Space held during either of its drags moves the flare. While a grid is dragged out, Up/Down change its rows (concentric dividers) and Left/Right its columns (radial dividers). A click with a grid tool asks for the size, the divider counts and the grid options (#1377, in part): a skew for each set of dividers, Use Outside Rectangle As Frame or Create Compound Path From Ellipses, and Fill Grid; they are parameters of `shape.rectangularGrid` and `shape.polarGrid`, stay as the tool's options and apply to drags too. The curve a skew percentage follows and the direction of the radial skew are our own (the public documentation gives neither), and the dialogs have no reference point locator. 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). With the bounding box showing, the Selection tool shows a selected ellipse's two pie widgets just outside it; dragging one sets its angle in one undo step and double-clicking one makes the ellipse whole again. A live polygon's Polygon Properties there: side count, angle, corner type and radius, radius, side length and Make Sides Equal once an uneven scale has made its sides differ (`object.setLiveShape {sides, polygonAngle, polygonRadius, sideLength, makeSidesEqual}`, part of #812); a star is a plain path, as in Illustrator. The Flare Tool Options last as the tool's options: the next flare dragged out uses them, a click's dialog shows them and double-clicking the tool opens them (part of #812).
|
||||
- **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. The Flare tool's center drag turns the rays with the pointer, Shift keeps them in their original orientation, and Space held during either of its drags moves the flare. While a grid is dragged out, Up/Down change its rows (concentric dividers) and Left/Right its columns (radial dividers). A click with a grid tool asks for the size, the divider counts and the grid options (#1377, in part): a skew for each set of dividers, Use Outside Rectangle As Frame or Create Compound Path From Ellipses, and Fill Grid; they are parameters of `shape.rectangularGrid` and `shape.polarGrid`, stay as the tool's options and apply to drags too. The curve a skew percentage follows and the direction of the radial skew are our own (the public documentation gives neither), and the dialogs have no reference point locator. 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). With the bounding box showing, the Selection tool shows a selected ellipse's two pie widgets just outside it; dragging one sets its angle in one undo step and double-clicking one makes the ellipse whole again. A live polygon's Polygon Properties there: side count, angle, corner type and radius, radius, side length and Make Sides Equal once an uneven scale has made its sides differ (`object.setLiveShape {sides, polygonAngle, polygonRadius, sideLength, makeSidesEqual}`, part of #812); a star is a plain path, as in Illustrator. The Flare Tool Options last as the tool's options: the next flare dragged out uses them, a click's dialog shows them and double-clicking the tool opens them (part of #812).
|
||||
While a grid is dragged out, F lowers `rowSkew` (rectangular) or `radialSkew` (polar) by 10 percentage points and V raises it by 10; X lowers `columnSkew` (rectangular) or `concentricSkew` (polar) by 10 and C raises it by 10. Plain and Shift presses work; with Alt, Ctrl or Cmd held they do not, as for the other letters a tool takes while dragging. Each press changes the skew once and updates the preview; values stay within ±500% and remain in the tool options after release. The [default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) (archived copy) list the four drag keys and a 10% change. The signs (positive values follow VectorCraft's existing skew convention, so F and X lower the stored value) and one change per press with key repeat ignored are our own rules.
|
||||
While an arc is dragged out, C switches it between open and closed, F switches its base between the X and Y axes in the same box, and Up/Down raise and lower its slope by 10 within −100…100; each press switches once, and the values stay as the Arc tool's options. The [default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) (archived copy) list C, F and Up/Down for an arc being dragged out. F as the base switch, the arrows changing the slope, the step of 10 and one switch per press are our own rules.
|
||||
While a spiral is dragged out, Alt adds or removes whole winds of four segments and Ctrl/Cmd changes the decay. The spiral's inner end keeps the distance from the center it had on the first modified drag event, while the outer end follows the pointer. The [default keyboard shortcuts](https://web.archive.org/web/20260726060015/https://helpx.adobe.com/illustrator/using/default-keyboard-shortcuts.html) (archived copy) list Alt-drag to add or subtract winds and Ctrl-drag to change the decay rate; [Draw spirals](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-spirals.html) defines four segments per full wind. Rounding the fitted count to whole winds from the count on the first Alt-only drag event, the held inner radius, the ranges (2…1000 segments, 5…99.99 % decay), Ctrl/Cmd taking precedence when both are held, and the new values staying as Spiral tool options are our own rules. Switching back to Alt uses the current count as its new base while keeping the held inner end.
|
||||
- **Cutting:** Eraser, Scissors, Knife, Mirror & Cut, Line Cut and Rectangle Cut (real geometry, compound paths keep their holes).
|
||||
|
||||
Reference in New Issue
Block a user