View source with raw comments or as raw
    1/*  Part of SWISH
    2
    3    Author:        Jan Wielemaker
    4    E-mail:        J.Wielemaker@cs.vu.nl
    5    WWW:           http://www.swi-prolog.org
    6    Copyright (C): 2016, VU University Amsterdam
    7			 CWI Amsterdam
    8    All rights reserved.
    9
   10    Redistribution and use in source and binary forms, with or without
   11    modification, are permitted provided that the following conditions
   12    are met:
   13
   14    1. Redistributions of source code must retain the above copyright
   15       notice, this list of conditions and the following disclaimer.
   16
   17    2. Redistributions in binary form must reproduce the above copyright
   18       notice, this list of conditions and the following disclaimer in
   19       the documentation and/or other materials provided with the
   20       distribution.
   21
   22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   23    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   24    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   25    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   26    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   27    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   28    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
   29    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
   30    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   31    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
   32    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   33    POSSIBILITY OF SUCH DAMAGE.
   34*/
   35
   36:- module(noble_avatar,
   37	  [ noble_avatar/2,			% ?Gender, -File
   38	    noble_avatar/3,			% ?Gender, -File, ?New
   39	    create_avatar/2,			% +PNG, -File
   40
   41	    existing_noble_avatar/2		% -Gender, -File
   42	  ]).   43:- use_module(library(apply)).   44:- use_module(library(lists)).   45:- use_module(library(random)).   46:- use_module(library(filesex)).   47:- use_module(library(process)).

Noble Avatar generator

This library generates random avatar images from components. The file locations of the library are defined by file_search_path/2 rules for noble_avatar_components (the components) and noble_avatar for the generated avatars.

The probalities for the various components are specified in part/1.

Credits and license

The component images can be downloaded from the address below. The images are licensed under CC-BY-3.0. If you use this library with these images, please include the following acknowledgements:

  1. Credit Noble Master Games as follows (linking is optional): "Avatar graphics created by Noble Master Games" and link to http://www.noblemaster.com
  2. Credit the artist "Liea" as follows (optional): "Avatar graphics designed by Mei-Li Nieuwland" and link to http://liea.deviantart.com
See also
- http://opengameart.org/content/avatar-generator-with-15-trillion-combinations */
   76:- multifile
   77	user:file_search_path/2.   78
   79user:file_search_path(noble,		       icons(noble)).
   80user:file_search_path(noble_avatar_components, noble(components)).
   81user:file_search_path(noble_avatar,	       data(avatars)).
   82
   83:- dynamic
   84	noble_dir/1,				% Directory
   85	noble/4.				% Part, Gender, I, File
 noble_avatar(?Gender, -File) is det
 noble_avatar(?Gender, -File, ?New) is det
True when File is the image file name for a generated avatar of Gender. If Gender is unspecified, it is generated randomly.
Arguments:
New- is a boolean that indicates whether the avatar is newly generated (true) or we re-generated an existing one (false). It may be specified as true to force generating a new avatar. Previously generated avatars can be queried using existing_noble_avatar/2.
   99noble_avatar(Gender, Image) :-
  100	noble_avatar(Gender, Image, _).
  101
  102noble_avatar(Gender, Image, New) :-
  103	var(New), !,
  104	noble_index_components,
  105	avatar_components(Gender, IDs, Components),
  106	maplist(plus(0'a), IDs, Codes),
  107	atom_codes(Base, Codes),
  108	file_name_extension(Base, png, PNG),
  109	with_mutex(noble_avatar,
  110		   create_avatar_sync(Components,
  111				      noble_avatar, PNG, Image, New)).
  112noble_avatar(Gender, Image, true) :- !,
  113	repeat,
  114	    noble_avatar(Gender, Image, New),
  115	    New == true, !.
 create_avatar(+PNG, -Image) is det
(Re-)create avatar with basename PNG.
  121create_avatar(PNG, Image) :-
  122	file_name_extension(Base, png, PNG),
  123	atom_codes(Base, Codes),
  124	maplist(plus(0'a), IDs, Codes),
  125	noble_index_components,
  126	avatar_components(_Gender, IDs, Components),
  127	with_mutex(noble_avatar,
  128		   create_avatar_sync(Components,
  129				      noble_avatar, PNG, Image, _New)).
 existing_noble_avatar(-Gender, -Image) is nondet
True when Image is the image file of a previously generated avatar of Gender.
  136existing_noble_avatar(Gender, Image) :-
  137	absolute_file_name(noble_avatar(.), Dir,
  138			   [ file_type(directory),
  139			     solutions(all)
  140			   ]),
  141	directory_files(Dir, Files),
  142	member(Image, Files),
  143	file_name_extension(Base, png, Image),
  144	sub_atom(Base, 0, 1, _, First),
  145	char_code(First, Code),
  146	Index is Code-0'a,
  147	gender_id(Gender, Index).
  148
  149
  150create_avatar_sync(Components, DirAlias, File, Image, New) :-
  151	Location =.. [DirAlias,File],
  152	(   absolute_file_name(Location, Image,
  153			       [ access(read),
  154				 file_errors(fail)
  155			       ])
  156	->  New = false
  157	;   absolute_file_name(Location, Image,
  158			       [ access(write), file_errors(fail) ])
  159	->  composite(Components, Image),
  160	    New	= true
  161	;   Dir =.. [DirAlias,.],
  162	    absolute_file_name(Dir, DirPath, [solutions(all)]),
  163	    file_directory_name(DirPath, Parent),
  164	    exists_directory(Parent),
  165	    \+ exists_directory(DirPath)
  166	->  make_directory(DirPath),
  167	    absolute_file_name(Location, Image, [access(write)]),
  168	    composite(Components, Image)
  169	).
  170
  171composite(Components, Image) :-
  172	noble_dir(Dir),
  173	phrase(composite(Components, Dir), Argv, [file(Image)]),
  174	process_create(path(convert), Argv, []).
  175
  176composite([], _) -->
  177	[ '-background', 'none', '-flatten' ].
  178composite([File|T], Dir) -->
  179	{ directory_file_path(Dir, File, AbsFile)
  180	},
  181	[ '-page', '+0+0', file(AbsFile) ],
  182	composite(T, Dir).
  183
  184avatar_components(Gender, [GID|IDs], Files) :-
  185	gender_id(Gender, GID),
  186	parts(Parts),
  187	files(Parts, Gender, IDs, Files).
  188
  189files([], _, [], []).
  190files([P:H-Gender|T], Gender, [I|IDs], [File|Files]) :-
  191	(   var(I), I \== 0
  192	->  maybe(P)
  193	;   true
  194	),
  195	file(H, Gender, I, File), !,
  196	files(T, Gender, IDs, Files).
  197files([_|T], Gender, [0|IDs], Files) :-
  198	files(T, Gender, IDs, Files).
  199
  200file(Part, Gender, I, File) :-
  201	findall(I, noble(Part, Gender, I, _), IL),
  202	random_member(I, IL),
  203	noble(Part, Gender, I, File).
  204
  205gender_id(Var, ID) :-
  206	var(Var), var(ID),
  207	ID is 1+random(2),
  208	gender_id(Var, ID), !.
  209gender_id(male, 1).
  210gender_id(female, 2).
 parts(-Parts:list) is det
True when Parts is the list of part specifications for creating a new avatar. Each specification is a term
Probability:Part-Gender

Part is included with Probability and only of Gender matches the target Gender.

  223parts([ 0.5:pattern - _,
  224	1.0:head - _,
  225	1.0:mouth - _,
  226	1.0:eye - _,
  227	0.5:eyepatch - _,
  228	0.3:glasses - _,
  229	0.3:mustache - male,
  230	0.5:beard - male,
  231	0.8:hair - _,
  232	0.2:accessory - _,
  233	0.5:necklace - _,
  234	0.3:boa - _,
  235	0.2:scar - _,
  236	0.1:sideburn - _
  237      ]).
 noble_index_components
Create an index for the Noble Avatar components. The components are searched for in the directory noble_avatar_components(.).
  244noble_index_components :-
  245	noble_dir(_), !.
  246noble_index_components :-
  247	with_mutex(noble_avatar, noble_index_components_sync).
  248
  249noble_index_components_sync :-
  250	noble_dir(_), !.
  251noble_index_components_sync :-
  252	retractall(noble_dir(_)),
  253	retractall(noble(_,_,_,_)),
  254	absolute_file_name(noble_avatar_components(.), Dir,
  255			   [ file_type(directory)
  256			   ]),
  257	directory_files(Dir, Files),
  258	maplist(noble_file, Files),
  259	assertz(noble_dir(Dir)).
  260
  261noble_file(File) :-
  262	file_name_extension(Base, png, File),
  263	atomic_list_concat([avatar,Part,V], '_', Base),
  264	(   atom_concat(f, NA, V),
  265	    atom_number(NA, N)
  266	->  Gender = female
  267	;   atom_concat(m, NA, V),
  268	    atom_number(NA, N)
  269	->  Gender = male
  270	;   atom_number(V, N)
  271	), !,
  272	assert(noble(Part, Gender, N, File)).
  273noble_file(_)