chiark / gitweb /
Fix completion checking in Killer Solo.
[sgt-puzzles.git] / printing.c
1 /*
2  * printing.c: Cross-platform printing manager. Handles document
3  * setup and layout.
4  */
5
6 #include "puzzles.h"
7
8 struct puzzle {
9     const game *game;
10     game_params *par;
11     game_state *st;
12     game_state *st2;
13 };
14
15 struct document {
16     int pw, ph;
17     int npuzzles;
18     struct puzzle *puzzles;
19     int puzzlesize;
20     int got_solns;
21     float *colwid, *rowht;
22     float userscale;
23 };
24
25 /*
26  * Create a new print document. pw and ph are the layout
27  * parameters: they state how many puzzles will be printed across
28  * the page, and down the page.
29  */
30 document *document_new(int pw, int ph, float userscale)
31 {
32     document *doc = snew(document);
33
34     doc->pw = pw;
35     doc->ph = ph;
36     doc->puzzles = NULL;
37     doc->puzzlesize = doc->npuzzles = 0;
38     doc->got_solns = FALSE;
39
40     doc->colwid = snewn(pw, float);
41     doc->rowht = snewn(ph, float);
42
43     doc->userscale = userscale;
44
45     return doc;
46 }
47
48 /*
49  * Free a document structure, whether it's been printed or not.
50  */
51 void document_free(document *doc)
52 {
53     int i;
54
55     for (i = 0; i < doc->npuzzles; i++) {
56         doc->puzzles[i].game->free_params(doc->puzzles[i].par);
57         doc->puzzles[i].game->free_game(doc->puzzles[i].st);
58         if (doc->puzzles[i].st2)
59             doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
60     }
61
62     sfree(doc->colwid);
63     sfree(doc->rowht);
64
65     sfree(doc->puzzles);
66     sfree(doc);
67 }
68
69 /*
70  * Called from midend.c to add a puzzle to be printed. Provides a
71  * game_params (for initial layout computation), a game_state, and
72  * optionally a second game_state to be printed in parallel on
73  * another sheet (typically the solution to the first game_state).
74  */
75 void document_add_puzzle(document *doc, const game *game, game_params *par,
76                          game_state *st, game_state *st2)
77 {
78     if (doc->npuzzles >= doc->puzzlesize) {
79         doc->puzzlesize += 32;
80         doc->puzzles = sresize(doc->puzzles, doc->puzzlesize, struct puzzle);
81     }
82     doc->puzzles[doc->npuzzles].game = game;
83     doc->puzzles[doc->npuzzles].par = par;
84     doc->puzzles[doc->npuzzles].st = st;
85     doc->puzzles[doc->npuzzles].st2 = st2;
86     doc->npuzzles++;
87     if (st2)
88         doc->got_solns = TRUE;
89 }
90
91 static void get_puzzle_size(document *doc, struct puzzle *pz,
92                             float *w, float *h, float *scale)
93 {
94     float ww, hh, ourscale;
95
96     /* Get the preferred size of the game, in mm. */
97     pz->game->print_size(pz->par, &ww, &hh);
98
99     /* Adjust for user-supplied scale factor. */
100     ourscale = doc->userscale;
101
102     /*
103      * FIXME: scale it down here if it's too big for the page size.
104      * Rather than do complicated things involving scaling all
105      * columns down in proportion, the simplest approach seems to
106      * me to be to scale down until the game fits within one evenly
107      * divided cell of the page (i.e. width/pw by height/ph).
108      * 
109      * In order to do this step we need the page size available.
110      */
111
112     *scale = ourscale;
113     *w = ww * ourscale;
114     *h = hh * ourscale;
115 }
116
117 /*
118  * Having accumulated a load of puzzles, actually do the printing.
119  */
120 void document_print(document *doc, drawing *dr)
121 {
122     int ppp;                           /* puzzles per page */
123     int pages, passes;
124     int page, pass;
125     int pageno;
126
127     ppp = doc->pw * doc->ph;
128     pages = (doc->npuzzles + ppp - 1) / ppp;
129     passes = (doc->got_solns ? 2 : 1);
130
131     print_begin_doc(dr, pages * passes);
132
133     pageno = 1;
134     for (pass = 0; pass < passes; pass++) {
135         for (page = 0; page < pages; page++) {
136             int i, n, offset;
137             float colsum, rowsum;
138
139             print_begin_page(dr, pageno);
140
141             offset = page * ppp;
142             n = min(ppp, doc->npuzzles - offset);
143
144             for (i = 0; i < doc->pw; i++)
145                 doc->colwid[i] = 0;
146             for (i = 0; i < doc->ph; i++)
147                 doc->rowht[i] = 0;
148
149             /*
150              * Lay the page out by computing all the puzzle sizes.
151              */
152             for (i = 0; i < n; i++) {
153                 struct puzzle *pz = doc->puzzles + offset + i;
154                 int x = i % doc->pw, y = i / doc->pw;
155                 float w, h, scale;
156
157                 get_puzzle_size(doc, pz, &w, &h, &scale);
158
159                 /* Update the maximum width/height of this column. */
160                 doc->colwid[x] = max(doc->colwid[x], w);
161                 doc->rowht[y] = max(doc->rowht[y], h);
162             }
163
164             /*
165              * Add up the maximum column/row widths to get the
166              * total amount of space used up by puzzles on the
167              * page. We will use this to compute gutter widths.
168              */
169             colsum = 0.0;
170             for (i = 0; i < doc->pw; i++)
171                 colsum += doc->colwid[i];
172             rowsum = 0.0;
173             for (i = 0; i < doc->ph; i++)
174                 rowsum += doc->rowht[i];
175
176             /*
177              * Now do the printing.
178              */
179             for (i = 0; i < n; i++) {
180                 struct puzzle *pz = doc->puzzles + offset + i;
181                 int x = i % doc->pw, y = i / doc->pw, j;
182                 float w, h, scale, xm, xc, ym, yc;
183                 int pixw, pixh, tilesize;
184
185                 if (pass == 1 && !pz->st2)
186                     continue;          /* nothing to do */
187
188                 /*
189                  * The total amount of gutter space is the page
190                  * width minus colsum. This is divided into pw+1
191                  * gutters, so the amount of horizontal gutter
192                  * space appearing to the left of this puzzle
193                  * column is
194                  * 
195                  *   (width-colsum) * (x+1)/(pw+1)
196                  * = width * (x+1)/(pw+1) - (colsum * (x+1)/(pw+1))
197                  */
198                 xm = (float)(x+1) / (doc->pw + 1);
199                 xc = -xm * colsum;
200                 /* And similarly for y. */
201                 ym = (float)(y+1) / (doc->ph + 1);
202                 yc = -ym * rowsum;
203
204                 /*
205                  * However, the amount of space to the left of this
206                  * puzzle isn't just gutter space: we must also
207                  * count the widths of all the previous columns.
208                  */
209                 for (j = 0; j < x; j++)
210                     xc += doc->colwid[j];
211                 /* And similarly for rows. */
212                 for (j = 0; j < y; j++)
213                     yc += doc->rowht[j];
214
215                 /*
216                  * Now we adjust for this _specific_ puzzle, which
217                  * means centring it within the cell we've just
218                  * computed.
219                  */
220                 get_puzzle_size(doc, pz, &w, &h, &scale);
221                 xc += (doc->colwid[x] - w) / 2;
222                 yc += (doc->rowht[y] - h) / 2;
223
224                 /*
225                  * And now we know where and how big we want to
226                  * print the puzzle, just go ahead and do so. For
227                  * the moment I'll pick a standard pixel tile size
228                  * of 512.
229                  * 
230                  * (FIXME: would it be better to pick this value
231                  * with reference to the printer resolution? Or
232                  * permit each game to choose its own?)
233                  */
234                 tilesize = 512;
235                 pz->game->compute_size(pz->par, tilesize, &pixw, &pixh);
236                 print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
237                 pz->game->print(dr, pass == 0 ? pz->st : pz->st2, tilesize);
238                 print_end_puzzle(dr);
239             }
240
241             print_end_page(dr, pageno);
242             pageno++;
243         }
244     }
245
246     print_end_doc(dr);
247 }