Skip to content

Commit 73b6bc0

Browse files
authored
CLI updates (#269)
- Add `--start` and `--target` shorthand for `--start-shape` and `--target-shape` - Rename `--ramp-in` and `--ramp-out` `ease*` everywhere - Add `--ease` flag as a shorthand for `--ease-in --ease-out` - Add `-o` as a shorthand for `--output-directory`
1 parent 5f51726 commit 73b6bc0

File tree

6 files changed

+74
-54
lines changed

6 files changed

+74
-54
lines changed

docs/cli.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ Examples
4949
5050
$ data-morph --start-shape music --target-shape bullseye --output-dir path/to/dir
5151
52-
7. Morph the sheep shape into vertical lines, slowly ramping in and out for the animation:
52+
7. Morph the sheep shape into vertical lines, slowly easing in and out for the animation:
5353

5454
.. code-block:: console
5555
56-
$ data-morph --start-shape sheep --target-shape v_lines --ramp-in --ramp-out
56+
$ data-morph --start-shape sheep --target-shape v_lines --ease

docs/quickstart.rst

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,13 @@ within your current working directory:
4949

5050
Morphing the panda :class:`.Dataset` into the star :class:`.Shape`.
5151

52-
You can smooth the transition with the ``--ramp-in`` and ``--ramp-out`` flags. The ``--freeze``
53-
flag allows you to start the animation with the specified number of frames of the initial shape:
52+
You can smooth the transition with the ``--ease`` or ``--ease-in`` and ``--ease-out`` flags.
53+
The ``--freeze`` flag allows you to start the animation with the specified number of frames
54+
of the initial shape:
5455

5556
.. code:: console
5657
57-
$ data-morph --start-shape panda --target-shape star --freeze 50 --ramp-in --ramp-out
58+
$ data-morph --start-shape panda --target-shape star --freeze 50 --ease
5859
5960
Here is the resulting animation:
6061

@@ -115,8 +116,8 @@ With the :class:`.Dataset` and :class:`.Shape` created, here is a minimal exampl
115116
start_shape=dataset,
116117
target_shape=target_shape,
117118
freeze_for=50,
118-
ramp_in=True,
119-
ramp_out=True,
119+
ease_in=True,
120+
ease_out=True,
120121
)
121122
122123
.. note::

src/data_morph/cli.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ def generate_parser() -> argparse.ArgumentParser:
5656
description='Specify the start and target shapes.',
5757
)
5858
shape_config_group.add_argument(
59+
'--start',
5960
'--start-shape',
61+
dest='start_shape',
6062
required=True,
6163
nargs='+',
6264
help=(
@@ -67,7 +69,9 @@ def generate_parser() -> argparse.ArgumentParser:
6769
),
6870
)
6971
shape_config_group.add_argument(
72+
'--target',
7073
'--target-shape',
74+
dest='target_shape',
7175
required=True,
7276
nargs='+',
7377
help=(
@@ -149,6 +153,7 @@ def generate_parser() -> argparse.ArgumentParser:
149153
),
150154
)
151155
file_group.add_argument(
156+
'-o',
152157
'--output-dir',
153158
default=ARG_DEFAULTS['output_dir'],
154159
metavar='DIRECTORY',
@@ -168,29 +173,17 @@ def generate_parser() -> argparse.ArgumentParser:
168173
'Animation Configuration', description='Customize aspects of the animation.'
169174
)
170175
frame_group.add_argument(
171-
'--forward-only',
176+
'--ease',
172177
default=False,
173178
action='store_true',
174179
help=(
175-
'By default, this module will create an animation that plays '
176-
'first forward (applying the transformation) and then unwinds, '
177-
'playing backward to undo the transformation. Pass this argument '
178-
'to only play the animation in the forward direction before looping.'
179-
),
180-
)
181-
frame_group.add_argument(
182-
'--freeze',
183-
default=ARG_DEFAULTS['freeze'],
184-
type=int,
185-
metavar='NUM_FRAMES',
186-
help=(
187-
'Number of frames to freeze at the first and final frame of the transition '
188-
'in the animation. This only affects the frames selected, not the algorithm. '
189-
f'Defaults to {ARG_DEFAULTS["freeze"]}.'
180+
'Whether to slow down the transition near the start and end of the '
181+
'transformation. This is a shortcut for --ease-in --ease-out. This only '
182+
'affects the frames selected, not the algorithm.'
190183
),
191184
)
192185
frame_group.add_argument(
193-
'--ramp-in',
186+
'--ease-in',
194187
default=False,
195188
action='store_true',
196189
help=(
@@ -199,14 +192,36 @@ def generate_parser() -> argparse.ArgumentParser:
199192
),
200193
)
201194
frame_group.add_argument(
202-
'--ramp-out',
195+
'--ease-out',
203196
default=False,
204197
action='store_true',
205198
help=(
206199
'Whether to slow down the transition from input to target towards the end '
207200
'of the animation. This only affects the frames selected, not the algorithm.'
208201
),
209202
)
203+
frame_group.add_argument(
204+
'--forward-only',
205+
default=False,
206+
action='store_true',
207+
help=(
208+
'By default, this module will create an animation that plays '
209+
'first forward (applying the transformation) and then rewinds, '
210+
'playing backward to undo the transformation. Pass this argument '
211+
'to only play the animation in the forward direction before looping.'
212+
),
213+
)
214+
frame_group.add_argument(
215+
'--freeze',
216+
default=ARG_DEFAULTS['freeze'],
217+
type=int,
218+
metavar='NUM_FRAMES',
219+
help=(
220+
'Number of frames to freeze at the first and final frame of the transition '
221+
'in the animation. This only affects the frames selected, not the algorithm. '
222+
f'Defaults to {ARG_DEFAULTS["freeze"]}.'
223+
),
224+
)
210225

211226
return parser
212227

@@ -260,7 +275,7 @@ def main(argv: Sequence[str] | None = None) -> None:
260275
target_shape=shape_factory.generate_shape(target_shape),
261276
iterations=args.iterations,
262277
min_shake=args.shake,
263-
ramp_in=args.ramp_in,
264-
ramp_out=args.ramp_out,
278+
ease_in=args.ease_in or args.ease,
279+
ease_out=args.ease_out or args.ease,
265280
freeze_for=args.freeze,
266281
)

src/data_morph/morpher.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def __init__(
124124
self._looper = tqdm.tnrange if in_notebook else tqdm.trange
125125

126126
def _select_frames(
127-
self, iterations: int, ramp_in: bool, ramp_out: bool, freeze_for: int
127+
self, iterations: int, ease_in: bool, ease_out: bool, freeze_for: int
128128
) -> list:
129129
"""
130130
Identify the frames to capture for the animation.
@@ -133,9 +133,9 @@ def _select_frames(
133133
----------
134134
iterations : int
135135
The number of iterations.
136-
ramp_in : bool
136+
ease_in : bool
137137
Whether to more slowly transition in the beginning.
138-
ramp_out : bool
138+
ease_out : bool
139139
Whether to slow down the transition at the end.
140140
freeze_for : int
141141
The number of frames to freeze at the beginning and end. Must be in the
@@ -166,11 +166,11 @@ def _select_frames(
166166
# freeze initial frame
167167
frames = [0] * freeze_for
168168

169-
if ramp_in and not ramp_out:
169+
if ease_in and not ease_out:
170170
easing_function = ease_in_sine
171-
elif ramp_out and not ramp_in:
171+
elif ease_out and not ease_in:
172172
easing_function = ease_out_sine
173-
elif ramp_out and ramp_in:
173+
elif ease_out and ease_in:
174174
easing_function = ease_in_out_sine
175175
else:
176176
easing_function = linear
@@ -346,8 +346,8 @@ def morph(
346346
min_shake: Number = 0.3,
347347
max_shake: Number = 1,
348348
allowed_dist: Number = 2,
349-
ramp_in: bool = False,
350-
ramp_out: bool = False,
349+
ease_in: bool = False,
350+
ease_out: bool = False,
351351
freeze_for: int = 0,
352352
) -> pd.DataFrame:
353353
"""
@@ -376,10 +376,10 @@ def morph(
376376
at ``max_shake`` and move toward ``min_shake``.
377377
allowed_dist : numbers.Number
378378
The farthest apart the perturbed points can be from the target shape.
379-
ramp_in : bool, default ``False``
379+
ease_in : bool, default ``False``
380380
Whether to more slowly transition in the beginning.
381381
This only affects the frames, not the algorithm.
382-
ramp_out : bool, default ``False``
382+
ease_out : bool, default ``False``
383383
Whether to slow down the transition at the end.
384384
This only affects the frames, not the algorithm.
385385
freeze_for : int, default ``0``
@@ -440,8 +440,8 @@ def morph(
440440
# iteration numbers that we will end up writing to file as frames
441441
frame_numbers = self._select_frames(
442442
iterations=iterations,
443-
ramp_in=ramp_in,
444-
ramp_out=ramp_out,
443+
ease_in=ease_in,
444+
ease_out=ease_out,
445445
freeze_for=freeze_for,
446446
)
447447

tests/test_cli.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def test_cli_bad_input_integers(field, value, capsys):
8282
@pytest.mark.input_validation
8383
@pytest.mark.parametrize('value', [1, 0, 's', -1, 0.5, True, False])
8484
@pytest.mark.parametrize(
85-
'field', ['ramp-in', 'ramp-out', 'forward-only', 'keep-frames']
85+
'field', ['ease-in', 'ease-out', 'forward-only', 'keep-frames']
8686
)
8787
def test_cli_bad_input_boolean(field, value, capsys):
8888
"""Test that invalid input for Boolean switches are handled correctly."""
@@ -133,8 +133,9 @@ def test_cli_one_shape(start_shape, flag, mocker, tmp_path):
133133
'min_shake': 0.5 if flag else None,
134134
'iterations': 1000,
135135
'freeze': 3 if flag else None,
136-
'ramp_in': flag,
137-
'ramp_out': flag,
136+
'ease_in': flag,
137+
'ease_out': flag,
138+
'ease': not flag,
138139
}
139140

140141
morpher_init = mocker.patch.object(cli.DataMorpher, '__init__', autospec=True)
@@ -153,8 +154,9 @@ def test_cli_one_shape(start_shape, flag, mocker, tmp_path):
153154
'--forward-only' if init_args['forward_only_animation'] else '',
154155
f'--shake={morph_args["min_shake"]}' if morph_args['min_shake'] else '',
155156
f'--freeze={morph_args["freeze"]}' if morph_args['freeze'] else '',
156-
'--ramp-in' if morph_args['ramp_in'] else '',
157-
'--ramp-out' if morph_args['ramp_out'] else '',
157+
'--ease-in' if morph_args['ease_in'] else '',
158+
'--ease-out' if morph_args['ease_out'] else '',
159+
'--ease' if morph_args['ease'] else '',
158160
]
159161
cli.main([arg for arg in argv if arg])
160162

@@ -171,6 +173,8 @@ def test_cli_one_shape(start_shape, flag, mocker, tmp_path):
171173
elif arg == 'start_shape':
172174
assert isinstance(value, Dataset)
173175
assert value.name == Path(morph_args['start_shape_name']).stem
176+
elif morph_args['ease'] and arg.startswith('ease_'):
177+
assert value
174178
elif arg in ['freeze_for', 'min_shake']:
175179
arg = 'freeze' if arg == 'freeze_for' else arg
176180
assert value == (morph_args[arg] or cli.ARG_DEFAULTS[arg])

tests/test_morpher.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def test_input_validation_freeze_for(self, freeze_for):
7070
ValueError, match='freeze_for must be a non-negative integer'
7171
):
7272
_ = morpher._select_frames(
73-
iterations=100, ramp_in=True, ramp_out=True, freeze_for=freeze_for
73+
iterations=100, ease_in=True, ease_out=True, freeze_for=freeze_for
7474
)
7575

7676
@pytest.mark.input_validation
@@ -81,19 +81,19 @@ def test_input_validation_iterations(self, iterations):
8181

8282
with pytest.raises(ValueError, match='iterations must be a positive integer'):
8383
_ = morpher._select_frames(
84-
iterations=iterations, ramp_in=True, ramp_out=True, freeze_for=0
84+
iterations=iterations, ease_in=True, ease_out=True, freeze_for=0
8585
)
8686

8787
@pytest.mark.parametrize(
88-
('ramp_in', 'ramp_out', 'expected_frames'),
88+
('ease_in', 'ease_out', 'expected_frames'),
8989
[
9090
(True, True, [0, 1, 2, 5, 8, 12, 15, 18, 19]),
9191
(True, False, [0, 0, 1, 3, 5, 7, 10, 13, 17]),
9292
(False, True, [0, 3, 7, 10, 13, 15, 17, 19, 20]),
9393
(False, False, [0, 2, 4, 7, 9, 11, 13, 16, 18]),
9494
],
9595
)
96-
def test_frames(self, ramp_in, ramp_out, expected_frames):
96+
def test_frames(self, ease_in, ease_out, expected_frames):
9797
"""Confirm that frames produced by the _select_frames() method are correct."""
9898
freeze_for = 2
9999
iterations = 20
@@ -103,8 +103,8 @@ def test_frames(self, ramp_in, ramp_out, expected_frames):
103103
)
104104
frames = morpher._select_frames(
105105
iterations=iterations,
106-
ramp_in=ramp_in,
107-
ramp_out=ramp_out,
106+
ease_in=ease_in,
107+
ease_out=ease_out,
108108
freeze_for=freeze_for,
109109
)
110110

@@ -166,8 +166,8 @@ def test_no_writing(self, capsys):
166166
start_shape=dataset,
167167
target_shape=shape_factory.generate_shape(target_shape),
168168
iterations=iterations,
169-
ramp_in=False,
170-
ramp_out=False,
169+
ease_in=False,
170+
ease_out=False,
171171
freeze_for=0,
172172
)
173173

@@ -205,8 +205,8 @@ def test_saving_data(self, tmp_path):
205205
start_shape=dataset,
206206
target_shape=shape_factory.generate_shape(target_shape),
207207
iterations=iterations,
208-
ramp_in=False,
209-
ramp_out=False,
208+
ease_in=False,
209+
ease_out=False,
210210
freeze_for=0,
211211
)
212212

0 commit comments

Comments
 (0)