Commit b2a30f6e authored by Lucas Bourneuf's avatar Lucas Bourneuf

auto-generation of gifs based on models + working example

parent 5e15cd42
...@@ -10,6 +10,9 @@ run-config-test: ...@@ -10,6 +10,9 @@ run-config-test:
python -m biseau -c ./configs/concept-lattice.json -o out/concept-lattice.png $(VERBOSITY) python -m biseau -c ./configs/concept-lattice.json -o out/concept-lattice.png $(VERBOSITY)
xdg-open out/concept-lattice.png xdg-open out/concept-lattice.png
run-gif-test:
python -m biseau examples/path-to-gif.lp --dotfile "out/todel{}.dot" --outfile out/out.gif --gif-duration 1000 $(VERBOSITY)
t: test t: test
test: test:
python -m pytest biseau test --ignore=venv --doctest-module python -m pytest biseau test --ignore=venv --doctest-module
......
...@@ -23,11 +23,13 @@ def parse_cli(args:iter=None) -> dict: ...@@ -23,11 +23,13 @@ def parse_cli(args:iter=None) -> dict:
help="output file. Will be overwritten with dot data. Can be templated with '{model_number}'") help="output file. Will be overwritten with dot data. Can be templated with '{model_number}'")
parser.add_argument('--config', '-c', type=str, default=None, parser.add_argument('--config', '-c', type=str, default=None,
help="configuration file, specifying scripts and their options") help="configuration file, specifying scripts and their options")
parser.add_argument('--gif-duration', '-gd', type=int, default=500,
help="gif duration in second")
parser.add_argument('-v', '--verbosity', action='count', default=0) parser.add_argument('-v', '--verbosity', action='count', default=0)
# flags # flags
parser.add_argument('--flag-example', action='store_true', parser.add_argument('--gif', '-g', action='store_true',
help="Do nothing currently") help="Do not merge graphs ; build a gif with frame for each model")
return parser.parse_args(args) return parser.parse_args(args)
...@@ -40,10 +42,22 @@ if __name__ == '__main__': ...@@ -40,10 +42,22 @@ if __name__ == '__main__':
all_infiles, all_infiles,
core.build_pipeline.from_configfile(args.config, verbosity=args.verbosity) core.build_pipeline.from_configfile(args.config, verbosity=args.verbosity)
) )
core.single_image_from_filenames( if os.path.splitext(args.outfile)[1] == '.gif' and args.gif is None:
all_infiles, args.gif = True # turn on the gif behavior
dotfile=args.dotfile,
outfile=args.outfile, if args.gif:
return_image=False, core.gif_from_filenames(
verbosity=args.verbosity, all_infiles,
) giffile=args.outfile,
dotfile_template=args.dotfile,
duration=args.gif_duration,
verbosity=args.verbosity,
)
else:
core.single_image_from_filenames(
all_infiles,
dotfile=args.dotfile,
outfile=args.outfile,
return_image=False,
verbosity=args.verbosity,
)
...@@ -31,6 +31,15 @@ def single_image_from_filenames(fnames:[str], outfile:str=None, dotfile:str=None ...@@ -31,6 +31,15 @@ def single_image_from_filenames(fnames:[str], outfile:str=None, dotfile:str=None
return compile_to_single_image(final_context, outfile=outfile, dotfile=dotfile, return_image=return_image, verbosity=verbosity) return compile_to_single_image(final_context, outfile=outfile, dotfile=dotfile, return_image=return_image, verbosity=verbosity)
def gif_from_filenames(fnames:[str], giffile:str, dotfile_template:str=None, duration:int=1000, verbosity:int=0) -> str:
"""Make a gif, with each ASP model as an image. Save it in outfile and dotfile_template"""
pipeline = build_pipeline(fnames, verbosity)
final_context = run(pipeline, verbosity=verbosity)
first, *lasts = compile_to_images(final_context, dotfile_template=dotfile_template, return_image=True, verbosity=verbosity)
first.save(giffile, save_all=True, append_images=lasts, duration=duration, loop=0)
return giffile
def build_pipeline(fnames:[str], verbosity:int=0) -> [Script]: def build_pipeline(fnames:[str], verbosity:int=0) -> [Script]:
"Yield scripts found in given filenames" "Yield scripts found in given filenames"
for fname in fnames: for fname in fnames:
...@@ -117,3 +126,36 @@ def compile_to_single_image(context:str, outfile:str=None, dotfile:str=None, ...@@ -117,3 +126,36 @@ def compile_to_single_image(context:str, outfile:str=None, dotfile:str=None,
if del_outfile: if del_outfile:
os.unlink(outfile) os.unlink(outfile)
return img return img
def compile_to_images(context:str, outfile_template:str=None, dotfile_template:str=None,
return_image:bool=True, verbosity:int=0) -> [Image]:
"""Yield pillow.Image objects, and write it to outfile if given
outfile_template -- template name for png files, or None
dotfile_template -- template name for dot files, or None
"""
configs = asp_to_dot.visual_config_from_asp(
solve_context(context)
)
if outfile_template and '{}' not in outfile_template:
raise ValueError("Outfile argument is not a valid template")
if dotfile_template and '{}' not in dotfile_template:
raise ValueError("Dotfile argument is not a valid template")
dots = dot_writer.multiple_graphs_from_configs(configs)
for idx, dot in enumerate(dots, start=1):
del_outfile = False
if outfile_template is None:
with tempfile.NamedTemporaryFile(delete=False) as fd:
outfile = fd.name
del_outfile = True
else:
outfile = outfile_template.format(idx)
dotfile = dotfile_template.format(idx) if dotfile_template else None
dot = dot_writer.dot_to_png(dot, outfile, dotfile=dotfile)
if return_image:
img = Image.open(outfile)
if del_outfile:
os.unlink(outfile)
yield img
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
""" """
import itertools
import pydot import pydot
from .asp_to_dot import VisualConfig from .asp_to_dot import VisualConfig
...@@ -27,20 +28,22 @@ def _dot_from_properties(properties:dict or None, prefix:str=' ') -> str: ...@@ -27,20 +28,22 @@ def _dot_from_properties(properties:dict or None, prefix:str=' ') -> str:
return '' return ''
def multiple_graphs_from_configs(visual_configs:[VisualConfig]) -> iter: def multiple_graphs_from_configs(visual_configs:[VisualConfig]) -> [[str]]:
"""Yield lines of dot describing the given VisualConfig instances. """Yield generators of lines of dot describing the given VisualConfig instances.
Produce one graph per VisualConfig. Produce one graph per VisualConfig.
See function counterpart, one_graph_from_configs. See function counterpart, one_graph_from_configs.
""" """
for visual_config in visual_configs: for visual_config in visual_configs:
yield 'Digraph biseau_graph {\n' yield itertools.chain(
yield from _from_config(visual_config) ['Digraph biseau_graph {\n'],
yield '}\n\n\n' _from_config(visual_config),
['}\n\n\n'],
)
def one_graph_from_configs(visual_configs:[VisualConfig]) -> iter: def one_graph_from_configs(visual_configs:[VisualConfig]) -> [str]:
"""Yield lines of dot describing the given VisualConfig instances. """Yield lines of dot describing the given VisualConfig instances.
Produce only one graph, Produce only one graph,
...@@ -53,7 +56,7 @@ def one_graph_from_configs(visual_configs:[VisualConfig]) -> iter: ...@@ -53,7 +56,7 @@ def one_graph_from_configs(visual_configs:[VisualConfig]) -> iter:
yield '}' yield '}'
def _from_config(visual_config:VisualConfig) -> iter: def _from_config(visual_config:VisualConfig) -> [str]:
"""Yield lines of dot's graph describing the given VisualConfig instance. """Yield lines of dot's graph describing the given VisualConfig instance.
""" """
......
% To use with something like:
% python -m biseau examples/path-to-gif.lp -o out/out.gif
% Graph.
link(a,(b;c)).
link(b,d).
link(d,(e;f)).
link(c,(e;g)).
link(e,h).
start(a). target(h).
nb_link(N):- N={link(_,_)}.
% Initial state.
place(1,N) :- start(N).
% Choose a path.
1 { place(I+1,N): link(T,N) } 1 :- place(I,T) ; not target(T) ; I<Nlink ; nb_link(Nlink).
% Discards paths not terminating at target, and looping paths.
:- not place(_,T) ; target(T).
:- place(I,N) ; place(J,N) ; I!=J.
% Colorize the path.
color(N,green) :- place(_,N).
% Highlight the path: make edges red and directed.
color(A,B,red) :- link(A,B) ; place(_,A) ; place(_,B).
dot_property(A,B,arrowhead,normal) :- link(A,B) ; place(_,A) ; place(_,B).
dot_property(A,B,penwidth,5) :- link(A,B) ; place(_,A) ; place(_,B).
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment