:- lib(os_lib). :- lib(real). :- lib(options). :- lib(debug_call). :- lib(mtx). :- lib( stoics_lib:kv_decompose/3 ). :- lib( stoics_lib:list_proportions/3 ). :- lib( stoics_lib:holds/2 ). :- lib( stoics_lib:io_lines/2 ). :- lib( stoics_lib:en_list/2 ). :- lib( stoics_lib:portray_clauses/2 ). :- lib( b_real:mtx_pheatmap/2 ). svg_legend_defaults( Defs ) :- Defs = [ cache(false), clr_theme(org), heatmaps(false), postfix(leg), postfix_mtx(mtx), placement(bottom_right), place_with_space(200), place_with_decrement(20), place_with_space_min(100), upward(false) ]. :- debug( svg_legend ). /** svg_legend( +FilesAndOpts ) Adds legend to svg files. Atoms in Files and Opts are taken to be the files, and everything else is options. See option atoms/1 in options_append/4. If no Files are given, then svgs from current directory are used. If one or more svg files with postfix fclr and a single svg with postfix (os_postfix/2) exist, then those are taken as the inputs, else all svgs are considered. Opts * cache(Cache=false) whether to cache the intermediary (distance) matrices to files * clr_theme(Theme=org) alternatively use =bic= for bi-colour, =gates= for gated and =jyoti= for modern * heatmaps(Hmaps=false) whether to show matrix heatmaps * postfix(Psfx=leg) postfix for new svg * postfix_mtx(Msfx=mtx) postfix for the cache file of the intermediary matrices * placement(Plc=bottom_right) legend placement strategy. also implemented: * at see place_at_* * on_rhs extends the svg canvas by ??? and adds the legened at bottom right of extension * place_at_x(X) known to placement: at. mandatory for this placement. pos values work from top, neg from bottom * place_at_y(y) known to placement: at. mandatory for this placement. pos values work from left, neg from right * place_with_space(Spc=200) space parameter for strategies (known to bottom_right) * place_with_decrement(Decr=20) decrement parameter with the amount with which Spc is reduced if placement is not possible * place_with_space_min(SpcMin=100) mimimum space required * upward(Upw=false) set to _true_ for the y to interpreted as the bottom of the legend (currently only supported in jyoti) == ?- cd(pack(sanger/examples/svg) ). ?- svg_legend( testo.svg ). % pupsh svg_legend *gated.svg clr_theme=gates placement=at place_at_x=1000 place_at_y=-160 % pupsh svg_legend *fclr.svg placement=at place_at_x=1000 place_at_y=-90 % pupsh svg_legend tng/rea_smp-e10_fclr_gated.svg clr_theme=upward placement=at place_at_x=100 place_at_y=-1 % pupsh svg_legend tnx/rea_xsm-e1_fclr_gated.svg clr_theme=upward placement=at place_at_x=160 place_at_y=-1 ERROR: Unknown message: could_not_mtx_csv_file('tnx/rea_xsm-e1_fclr_gated.csv') == @author nicos angelopoulos @version 0.1 2016/12/10 @version 0.2 2017/8/21, added placement: at (by passing distances, more or less properly) @tbd optionise more */ svg_legend( Args ) :- % spy( options_def_append ), options_append( svg_legend, Args, Opts, [process(debug),atoms(Files)] ), ( Files = [] -> svg_legend_dir( Opts ) ; maplist( svg_legend_file(Opts), Files ) ). svg_legend_dir( Opts ) :- os_sel( os_files, ext(svg), Svgs ), ( ( include( os_postfix(fclr), Svgs, Fclrs ), Fclrs \== [], include( os_postfix(fisher), Svgs, [FisherSvg] ) ) -> ToLeg = [FisherSvg|Fclrs] ; ToLeg = Svgs ), include( svg_legend_file(Opts), ToLeg, _Successes ). /* svg_legend_file( Opts, File ) :- memberchk( place_at_x(X), Opts ), memberchk( place_at_y(Y), Opts ), !, svg_legend_place( Opts, File, X, Y ). */ svg_legend_file( Opts, File ) :- debug( svg_legend, 'Svg input file: ~p', File ), Mtx = m, W = w, H = h, options( cache(Cache), Opts ), options( placement(Plc), Opts ), svg_legend_placement_requires_distance( Plc, Dist ), svg_cache( Cache, File, Mtx, Dist, W, H, Opts ), options( heatmaps(HmapsB), Opts ), heatmaps( HmapsB, Mtx, H, W ), Dims <- dim(Mtx), Dims = [Y, X], svg_legend_placement( Plc, Mtx, W, H, Y, X, Xi, Yi, Opts ), svg_legend_abs_x_coord( Xi, X, Xa ), svg_legend_abs_y_coord( Yi, Y, Ya ), % NegYi is Yi - Y, % fixme: add/subt 1 ? svg_legend_place( Opts, File, Xa, Ya ). svg_legend_abs_x_coord( Rel, Edge, Abs ) :- Rel < 0, !, Abs is Edge + Rel. svg_legend_abs_x_coord( Rel, _Edge, Abs ) :- Abs is Rel. svg_legend_abs_y_coord( Rel, _Edge, Abs ) :- Rel < 0, !, Abs is Rel. svg_legend_abs_y_coord( Rel, Edge, Abs ) :- Abs is Rel - Edge. svg_legend_place( Opts, File, Xi, NegYi ) :- debug( svg_legend, 'Placing at: ~w,~w', [Xi,NegYi] ), options( postfix(Psfx), Opts ), os_postfix( Psfx, File, Lile ), io_lines( File, Lines ), once( append(Unchanged,[Penu,Last],Lines) ), options( clr_theme(Theme), Opts ), options( upward(UpW), Opts ), svg_add_lines( Theme, Xi, NegYi, UpW, File, AddLines ), append( AddLines, [Penu,Last], NewTail ), append( Unchanged, NewTail, New ), io_lines( Lile, New ), debug_call( svg_legend, wrote, Lile ). /* fixme: svg_add_lines( bic_gates_or_not, Xi, Yi, UpW, File, Add ) see: rearrange_muts-17.01.17_man */ gates_term_functor( Atom, Funcs ) :- atomic( Atom ), !, Funcs = []. gates_term_functor( Term, [Token|FuncNest] ) :- Term =.. [Func|Args], gates_term_functor_atom( Func, Token ), maplist( gates_term_functor, Args, FuncNest ). gates_term_functor_atom( o, or ) :- !. gates_term_functor_atom( a, and ) :- !. gates_term_functor_atom( n, not ) :- !. gates_term_functor_atom( x, xor ) :- !, throw( xor(ing) ). gates_term_functor_atom( Oth, _ ) :- !, throw( unknown_gate_functor(Oth) ). svg_add_lines_gates( [], _Xi, Y, Y, [] ). svg_add_lines_gates( [G|Gs], Xi, Yi, Y5, [Ln1,Ln2,Ln3,Ln4|GatesAtms] ) :- svg_add_lines_gate( G, Xi, Yi, Ln1, Ln2, Ln3, Ln4 ), Yj is Yi - 20, svg_add_lines_gates( Gs, Xi, Yj, Y5, GatesAtms ). svg_add_lines_gate( xor, _Xi, _Yi, _Yj, _L1, _L2, _L3, _L4 ) :- throw( xor(ing) ). svg_add_lines_gate( not, Xi, Yi, NotL1, NotL2, NotL3, NotL4 ) :- % NOT gate NotTxX is Xi + 30, NotTxY is Yi, NotX is Xi + 17, NotY is Yi - 5, NotL1a = 'NOT gate', NotL2 = 'LegNot', NotL3a = '', NotL4 = '', atomic_list_concat( [NotL1a,NotTxX,NotL1b,NotTxY,NotL1c], NotL1 ), atomic_list_concat( [NotL3a,NotX,NotL3b,NotY,NotL3c], NotL3 ). svg_add_lines_gate( and, Xi, Yi, AndLat, AndMat, AndNat, Aat ) :- % AND GATE T1 = '', T4 = '', AndX is Xi + 30, AndY is Yi, AndTxt = 'AND gate', AndMat = ' LegAND', AndN1 = ' ', Aat = '', atomic_list_concat( [T1,AndX,T2,AndY,T3,AndTxt,T4], AndLat ), atomic_list_concat( [AndN1,AndPoly,AndN2], AndNat ). svg_add_lines_gate( or, Xi, Yi, Lat, Mat, Nat, Oat ) :- % OR GATE T1 = '', T4 = '', OrX is Xi + 30, OrY is Yi, OrTxt = 'OR gate', Mat = ' LegOR', N1 = ' ', Oat = '', atomic_list_concat( [T1,OrX,T2,OrY,T3,OrTxt,T4], Lat ), atomic_list_concat( [N1,Poly,N2], Nat ). gates_sort( GatesBag, Gates ) :- sort( GatesBag, GatesOrd ), reverse( GatesOrd, GatesInRev ), ( select(not,GatesInRev,GatesNoNot) -> Gates = [not|GatesNoNot] ; Gates = GatesInRev ). svg_line_segs_as( A1, A2, A3, A4 ) :- A1 = '', A4 = ''. svg_line_segs_bs( B1, B2, B3 ) :- B1 = ''. svg_add_lines_compos( [], _File, _Xi, _Xt, _Yi, _Dir, [] ). svg_add_lines_compos( [C|Cs], File, Xi, Xt, Yi, Dir, [Lns|TLns] ) :- svg_add_lines_compo( C, File, Xi, Xt, Yi, IncY, Lns ), Expr =.. [Dir,Yi,IncY], Yj is Expr, % Yj is Yi + IncY, svg_add_lines_compos( Cs, File, Xi, Xt, Yj, Dir, TLns ). svg_add_lines_compo( slide, _File, Xi, Xt, Yi, 20, [I,J] ) :- % compo: slide svg_line_segs_as( A1, A2, A3, A4 ), L5 = 'Fisher test odds', atomic_list_concat( [A1,Xt,A2,Yi,A3,L5,A4], '', I ), J1 = '', atomic_list_concat( [J1,Tx1,',',Ty1,' ',Tx2,',',Ty2,' ',Tx3,',',Ty3,J1b], J ). svg_add_lines_compo( cooc, _File, Xi, Xt, Yi, 20, [A,E] ) :- svg_line_segs_as( A1, A2, A3, A4 ), svg_line_segs_bs( B1, B2, B3 ), L1 = 'Co-occur (shown odds=4)', atomic_list_concat( [A1,Xt,A2,Yi,A3,L1,A4], '', A ), Ya is Yi - 5, atomic_list_concat( [B1,'35978F',B2,Xi,',',Ya,B3], E ). svg_add_lines_compo( muex, _File, Xi, Xt, Yi, 20, [A,E] ) :- svg_line_segs_as( A1, A2, A3, A4 ), svg_line_segs_bs( B1, B2, B3 ), L2 = 'Mut.excl (shown odds=0.25)', Ya is Yi - 5, atomic_list_concat( [A1,Xt,A2,Yi,A3,L2,A4], '', A ), atomic_list_concat( [B1,'BF812D',B2,Xi,',',Ya,B3], E ). svg_add_lines_compo( pval, File, Xi, Xt, Yi, Pad, Lns ) :- svg_line_segs_as( A1, A2, A3, A4 ), svg_line_segs_bs( _B1, B2, _B3 ), os_ext( _, csv, File, CsvFPrv ), ( exists_file(CsvFPrv) -> CsvF = CsvFPrv; os_postfix(gated,CsvF,CsvFPrv) ), ( mtx(CsvF,Mets) -> true; throw( could_not_mtx_csv_file(CsvF) ) ), maplist( arg(3), Mets, Pvals ), max_list( Pvals, MaxPv ), ( MaxPv < 0.05 -> % fixme: = means no significance... Pad is 0, Lns = [] ; Pad is 20, L3 = '0.05 < q.val', D1 = '', atomic_list_concat( [D1,'35978F',B2,Xi,',',Yim,C3], DA ), Xib is Xi + 14, atomic_list_concat( [D1,'BF812D',B2,Xib,',',Yim,C3], DB ), Lns = [C,DA,DB] ). svg_add_lines_compo( node, File, Xi, Xt, Yi, 20, [G,H] ) :- svg_line_segs_as( A1, A2, A3, A4 ), svg_line_segs_bs( _B1, B2, B3 ), L4 = '# of events (median=', L3b = ')', atomic_list_concat( [FStem|_], '-', File ), os_ext( dat, FStem, DatFile ), write( 'dat.file'(DatFile) ), nl, mtx( DatFile, DatMtx, sep(0' ) ), mtx_value_column_frequencies( DatMtx, 1, Freqs ), kv_decompose( Freqs, _Lbls, Times ), TmMedian <- as.integer( median( Times ) ), list_proportions( Times, Propos, to_range(r(1,4)) ), NwMedian <- median( Propos ), % Yl is Yk + 20, atomic_list_concat( [A1,Xt,A2,Yi,A3,L4,TmMedian,L3b,A4], '', G ), G1 = '', atomic_list_concat( [D1,'35978F',B2,Xi,',',Ykm,C3], DA ), Xib is Xi + 14, atomic_list_concat( [D1,'BF812D',B2,Xib,',',Ykm,C3], DB ), Atoms = [A,B,E,F, C,DA,DB, G,H, I,J] ), L4 = '# of events (median=', L3b = ')', atomic_list_concat( [FStem|_], '-', File ), os_ext( dat, FStem, DatFile ), write( 'dat.file'(DatFile) ), nl, mtx( DatFile, DatMtx, sep(0' ) ), mtx_value_column_frequencies( DatMtx, 1, Freqs ), kv_decompose( Freqs, _Lbls, Times ), TmMedian <- as.integer( median( Times ) ), list_proportions( Times, Propos, to_range(r(1,4)) ), NwMedian <- median( Propos ), Yl is Yk + 20, atomic_list_concat( [A1,Xt,A2,Yl,A3,L4,TmMedian,L3b,A4], '', G ), G1 = ' svg_add_lines_compo( [slide], File, Xt, Xi, Yi, +, [I,J] ), maplist( atom_codes, Atoms, Add ). % maplist( atom_codes, [A,B,E,F, C,G], [ACs,BCs,ECs,FCs, CCs, GCs] ), % Add = [ACs,ECs,BCs,FCs, CCs,GCs]. svg_add_lines( bic, Xi, Yi, _UpW, _File, Add ) :- L1 = 'Co-occur', L2 = 'Mut.excl', A1 = '', A4 = '', Xt is Xi + 30, atomic_list_concat( [A1,Xt,A2,Yi,A3,L1,A4], '', A ), Yj is Yi + 20, atomic_list_concat( [A1,Xt,A2,Yj,A3,L2,A4], '', B ), B1 = '', Ya is Yi - 5, atomic_list_concat( [B1,'35978F',B2,Xi,',',Ya,B3], E ), Yb is Yj - 5, atomic_list_concat( [B1,'BF812D',B2,Xi,',',Yb,B3], F ), maplist( atom_codes, [A,B,E,F], [ACs,BCs,ECs,FCs] ), Add = [ACs,ECs,BCs,FCs]. svg_add_lines( gates, Xi, Yi, UpW, File, Add ) :- T1 = '', T4 = '', % inter gate edge InTxX is Xi + 30, InTxY is Yi + 140, InTxt = 'Inter gates edge', B1 = '', Ya is Yi + 135, atomic_list_concat( [B1,'000000',B2,Xi,',',Ya,B3], I1atm ), % black atomic_list_concat( [T1,InTxX,T2,InTxY,T3,InTxt,T4], I2atm ), maplist( atom_codes, [I1atm,I2atm], [I1,I2] ), % NOT gate NotTxX is Xi + 30, NotTxY is Yi + 120, NotX is Xi + 17, NotY is Yi + 115 , NotL1a = 'NOT gate', NotL2 = 'LegNot', NotL3a = '', NotL4 = '', atomic_list_concat( [NotL1a,NotTxX,NotL1b,NotTxY,NotL1c], NotL1 ), atomic_list_concat( [NotL3a,NotX,NotL3b,NotY,NotL3c], NotL3 ), maplist( atom_codes, [NotL1,NotL2,NotL3,NotL4], [Not1,Not2,Not3,Not4] ), % OR GATE OrX is Xi + 30, OrY is Yi + 80, OrTxt = 'OR gate', Mat = ' LegOR', N1 = ' ', Oat = '', atomic_list_concat( [T1,OrX,T2,OrY,T3,OrTxt,T4], Lat ), atomic_list_concat( [N1,Poly,N2], Nat ), maplist( atom_codes, [Lat, Mat, Nat, Oat], [LCs,MCs,NCs,OCs] ), % AND GATE AndX is Xi + 30, AndY is Yi + 100, AndTxt = 'AND gate', AndMat = ' LegAND', AndN1 = ' ', atomic_list_concat( [T1,AndX,T2,AndY,T3,AndTxt,T4], AndLat ), atomic_list_concat( [AndN1,AndPoly,AndN2], AndNat ), maplist( atom_codes, [AndLat,AndMat,AndNat], [AndLCs,AndMCs,AndNCs] ), svg_add_lines( org, Xi, Yi, UpW, File, OrgAdd ), Add = [Not1,Not2,Not3,Not4,I1,I2,LCs,MCs,NCs,OCs,AndLCs,AndMCs,AndNCs,OCs|OrgAdd]. svg_add_lines( org, Xi, Yi, _UpW, _File, Add ) :- L1 = 'Co-occur (signif)', L2 = 'Mut.excl (signif)', L3 = 'Co-occur', L4 = 'Mut.excl', A1 = '', A4 = '', Xt is Xi + 30, atomic_list_concat( [A1,Xt,A2,Yi,A3,L1,A4], '', A ), Yj is Yi + 20, atomic_list_concat( [A1,Xt,A2,Yj,A3,L2,A4], '', B ), Yk is Yj + 20, atomic_list_concat( [A1,Xt,A2,Yk,A3,L3,A4], '', C ), Yl is Yk + 20, atomic_list_concat( [A1,Xt,A2,Yl,A3,L4,A4], '', D ), B1 = '', Ya is Yi - 5, atomic_list_concat( [B1,'35978F',B2,Xi,',',Ya,B3], E ), Yb is Yj - 5, atomic_list_concat( [B1,'BF812D',B2,Xi,',',Yb,B3], F ), Yc is Yk - 5, % 4B5320, army green % #008000, % Ao atomic_list_concat( [B1,'008000',B2,Xi,',',Yc,B3], G ), % this and the one below were switched prior to 17.01.06 Yd is Yl - 5, atomic_list_concat( [B1,'CC0000',B2,Xi,',',Yd,B3], H ), % maplist( atom_codes, [A,B,C,D], [ACs,BCs,CCs,DCs] ), maplist( atom_codes, [E,F,G,H], [ECs,FCs,GCs,HCs] ), Add = [ACs,ECs,BCs,FCs,CCs,GCs,DCs,HCs]. svg_legend_placement( at, _Mtx, _W, _H, _Y, _X, Xi, Yi, Opts ) :- options( place_at_x(Xi), Opts ), options( place_at_y(Yi), Opts ). svg_legend_placement( bottom_right, Mtx, W, H, Y, X, Xi, Yi, Opts ) :- options( place_with_space(Spc), Opts ), options( place_with_decrement(Dcr), Opts ), options( place_with_space_min(Smn), Opts ), svg_bottom_right( Mtx, W, H, Y, X, Spc, Dcr, Smn, Xi, Yi ). svg_bottom_right( Mtx, W, H, Y, X, Spc, _Dcr, _Smn, Xi, Yi ) :- debug( svg_legend, 'Trying bottom_right with space: ~w', Spc ), svg_bottom_right( Mtx, W, H, Y, X, Spc, Xi, Yi ), debug( svg_legend, 'Committing to space: ~w', Spc ), !. svg_bottom_right( Mtx, W, H, Y, X, Spc, Dcr, Smn, Xi, Yi ) :- New is Spc - Dcr, Spc =< Smn, % fixme, error if this fails svg_bottom_right( Mtx, W, H, Y, X, New, Dcr, Smn, Xi, Yi ). svg_bottom_right( Mtx, W, H, Y, X, Xspc, Xi, Yi ) :- Yspc is 36, StartY is Y - Yspc, StartX is X - Xspc, svg_bottom_right( StartY, StartX, Yspc, Xspc, Mtx, W, H, Y, X, Xi, Yi ). svg_bottom_right( Y, X, Yspc, Xspc, _Mtx, W, H, _Yc, _Xc, Xo, Yo ) :- Wv <- W[Y,X], Xspc =< Wv, Hv <- H[Y,X], Yspc =< Hv, Ydwn is Yspc - 1, svg_slide_box( Ydwn, Y, X, Yspc, Xspc, W, H ), !, Xo = X, Yo = Y. svg_bottom_right( Y, X, Yspc, Xspc, Mtx, W, H, Yc, Xc, Xo, Yo ) :- Xp is X - 1, ( 0 < Xp -> X1 is Xp, Y1 is Y ; X1 is Xc - Xspc, Y1 is Y - 1, 0 < Y1 ), svg_bottom_right( Y1, X1, Yspc, Xspc, Mtx, W, H, Yc, Xc, Xo, Yo ). svg_slide_box( 0, _Y, _X, _Yspc, _Xspc, _W, _H ) :- !. svg_slide_box( I, Y, X, Yspc, Xspc, W, H ) :- Yi is Y + I, Wv <- W[Yi,X], Xspc =< Wv, Hv <- H[Yi,X], Yspc =< Hv, Im is I - 1, svg_slide_box( Im, Y, X, Yspc, Xspc, W, H ). svg_cache( false, File, Mtx, Dist, W, H, _Opts ) :- read_svg_lines( File, Mtx, Dist, W, H ). svg_cache( true, File, Mtx, Dist, W, H, Opts ) :- options( postfix_mtx(Msfx), Opts ), os_postfix( Msfx, File, Mile ), os_ext( _, pl, Mile, Cile ), holds( exists_file(Cile), Cexists ), svg_cache_on( Cexists, Cile, File, Mtx, Dist, W, H ). svg_cache_on( true, Cile, _File, Mtx, _Dist, W, H ) :- % fixme: mtx_from_file( Cile ), debug_call( svg_legend, read, Cile ), % fixme: make sure those were created... Mtx = m, W = w, H = h, % ensures those where as per loaded % heatmaps( Mtx, H, W ), true. svg_cache_on( false, Cile, File, Mtx, Dist, W, H ) :- read_svg_lines( File, Mtx, Dist, W, H ), mtx_to_file( [Mtx,W,H], Cile ), debug_call( svg_legend, wrote, Cile ). mtx_from_file( File ) :- tmp:ensure_loaded( File ), mtx_from_module( tmp ), abolish( matrix/2 ). mtx_from_module( Mod ) :- Mod:matrix(Rtx,X), Row =.. [Rtx,Arg], findall( Arg, (call(Mod:Row),length(Arg,X)), Args ), length( Args, NRows ), Rtx <- matrix(0, nrow=NRows, ncol=X), Rtx <- Args, abolish( Rtx/1 ), fail. mtx_from_module( _ ). mtx_to_file( MtcPrv, File ) :- en_list( MtcPrv, Mtc ), maplist( mtx_to_clauses, Mtc, Infos, ClausesNest ), flatten( ClausesNest, Clauses ), append( Infos, Clauses, Alauses ), portray_clauses( Alauses, file(File) ). % @tbd headers, then move to b_real ? mtx_to_clauses( Rtx, Info, Clauses ) :- Mtx <- Rtx, Mtx = [First|_], length( First, Len ), Info = matrix(Rtx,Len), findall( Row, (member(RRow,Mtx),Row=..[Rtx,RRow]), Clauses ). read_svg_lines( SvgF, Mtx, Dist, W, H ) :- open( SvgF, read, In ), read_line_to_codes( In, Line ), read_svg_stream( Line, In, Mtx, Dist, W, H ), close( In ). read_svg_mtx( end_of_file, _Pfx, _X, _Y, _In, _Mtx ) :- !. read_svg_mtx( Line, Pfx, Xc, Yc, In, Mtx ) :- append( Pfx, Rest, Line ), !, read_svg_int( XCs, Rest, [0',|Rem] ), number_codes( X, XCs ), read_svg_int( YCs, Rem, RemXY ), number_codes( Y, YCs ), read_svg_to_space( RemXY, Shift ), read_svg_int( PCs, Shift, _Leftovers ), number_codes( P, PCs ), % mtx_pheatmap( Rws, [cluster_rows='FALSE',cluster_cols='FALSE'] ), read_svg_mtx_score( Mtx, Xc, Yc, X, P, Y ), read_line_to_codes( In, NewLine ), read_svg_mtx( NewLine, Pfx, Xc, Yc, In, Mtx ). read_svg_mtx( _Line, Pfx, Xc, Yc, In, Mtx ) :- read_line_to_codes( In, NewLine ), read_svg_mtx( NewLine, Pfx, Xc, Yc, In, Mtx ). read_svg_mtx_score( Mtx, _Xc, Yc, X, P, Y ) :- Max is integer( X + 12 ), Min is integer( P - 12 ), % fixme: check Y is negative MxY is Yc + integer( Y + 36 ), MnY is Yc + integer(Y), findall( _, ( between(Min,Max,AnX), between(MnY,MxY,AnY), Mtx[AnY,AnX] <- 1 ), _ ). read_svg_to_space --> [C], { C \== 0' }, !, read_svg_to_space. read_svg_to_space --> [_C]. read_svg_stream( Line, In, Mtx, Dist, W, H ) :- append( `