mirror of
git://git.tartarus.org/simon/puzzles.git
synced 2025-04-21 08:01:30 -07:00
Files

as seen by the back ends from the one implemented by the front end, and shoved a piece of middleware (drawing.c) in between to permit interchange of multiple kinds of the latter. I've also added a number of functions to the drawing API to permit printing as well as on-screen drawing, and retired print.py in favour of integrated printing done by means of that API. The immediate visible change is that print.py is dead, and each puzzle now does its own printing: where you would previously have typed `print.py solo 2x3', you now type `solo --print 2x3' and it should work in much the same way. Advantages of the new mechanism available right now: - Map is now printable, because the new print function can make use of the output from the existing game ID decoder rather than me having to replicate all those fiddly algorithms in Python. - the new print functions can cope with non-initial game states, which means each puzzle supporting --print also supports --with-solutions. - there's also a --scale option permitting users to adjust the size of the printed puzzles. Advantages which will be available at some point: - the new API should permit me to implement native printing mechanisms on Windows and OS X. [originally from svn r6190]
248 lines
6.3 KiB
C
248 lines
6.3 KiB
C
/*
|
|
* printing.c: Cross-platform printing manager. Handles document
|
|
* setup and layout.
|
|
*/
|
|
|
|
#include "puzzles.h"
|
|
|
|
struct puzzle {
|
|
const game *game;
|
|
game_params *par;
|
|
game_state *st;
|
|
game_state *st2;
|
|
};
|
|
|
|
struct document {
|
|
int pw, ph;
|
|
int npuzzles;
|
|
struct puzzle *puzzles;
|
|
int puzzlesize;
|
|
int got_solns;
|
|
float *colwid, *rowht;
|
|
float userscale;
|
|
};
|
|
|
|
/*
|
|
* Create a new print document. pw and ph are the layout
|
|
* parameters: they state how many puzzles will be printed across
|
|
* the page, and down the page.
|
|
*/
|
|
document *document_new(int pw, int ph, float userscale)
|
|
{
|
|
document *doc = snew(document);
|
|
|
|
doc->pw = pw;
|
|
doc->ph = ph;
|
|
doc->puzzles = NULL;
|
|
doc->puzzlesize = doc->npuzzles = 0;
|
|
doc->got_solns = FALSE;
|
|
|
|
doc->colwid = snewn(pw, float);
|
|
doc->rowht = snewn(ph, float);
|
|
|
|
doc->userscale = userscale;
|
|
|
|
return doc;
|
|
}
|
|
|
|
/*
|
|
* Free a document structure, whether it's been printed or not.
|
|
*/
|
|
void document_free(document *doc)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < doc->npuzzles; i++) {
|
|
doc->puzzles[i].game->free_params(doc->puzzles[i].par);
|
|
doc->puzzles[i].game->free_game(doc->puzzles[i].st);
|
|
if (doc->puzzles[i].st2)
|
|
doc->puzzles[i].game->free_game(doc->puzzles[i].st2);
|
|
}
|
|
|
|
sfree(doc->colwid);
|
|
sfree(doc->rowht);
|
|
|
|
sfree(doc->puzzles);
|
|
sfree(doc);
|
|
}
|
|
|
|
/*
|
|
* Called from midend.c to add a puzzle to be printed. Provides a
|
|
* game_params (for initial layout computation), a game_state, and
|
|
* optionally a second game_state to be printed in parallel on
|
|
* another sheet (typically the solution to the first game_state).
|
|
*/
|
|
void document_add_puzzle(document *doc, const game *game, game_params *par,
|
|
game_state *st, game_state *st2)
|
|
{
|
|
if (doc->npuzzles >= doc->puzzlesize) {
|
|
doc->puzzlesize += 32;
|
|
doc->puzzles = sresize(doc->puzzles, doc->puzzlesize, struct puzzle);
|
|
}
|
|
doc->puzzles[doc->npuzzles].game = game;
|
|
doc->puzzles[doc->npuzzles].par = par;
|
|
doc->puzzles[doc->npuzzles].st = st;
|
|
doc->puzzles[doc->npuzzles].st2 = st2;
|
|
doc->npuzzles++;
|
|
if (st2)
|
|
doc->got_solns = TRUE;
|
|
}
|
|
|
|
static void get_puzzle_size(document *doc, struct puzzle *pz,
|
|
float *w, float *h, float *scale)
|
|
{
|
|
float ww, hh, ourscale;
|
|
|
|
/* Get the preferred size of the game, in mm. */
|
|
pz->game->print_size(pz->par, &ww, &hh);
|
|
|
|
/* Adjust for user-supplied scale factor. */
|
|
ourscale = doc->userscale;
|
|
|
|
/*
|
|
* FIXME: scale it down here if it's too big for the page size.
|
|
* Rather than do complicated things involving scaling all
|
|
* columns down in proportion, the simplest approach seems to
|
|
* me to be to scale down until the game fits within one evenly
|
|
* divided cell of the page (i.e. width/pw by height/ph).
|
|
*
|
|
* In order to do this step we need the page size available.
|
|
*/
|
|
|
|
*scale = ourscale;
|
|
*w = ww * ourscale;
|
|
*h = hh * ourscale;
|
|
}
|
|
|
|
/*
|
|
* Having accumulated a load of puzzles, actually do the printing.
|
|
*/
|
|
void document_print(document *doc, drawing *dr)
|
|
{
|
|
int ppp; /* puzzles per page */
|
|
int pages, passes;
|
|
int page, pass;
|
|
int pageno;
|
|
|
|
ppp = doc->pw * doc->ph;
|
|
pages = (doc->npuzzles + ppp - 1) / ppp;
|
|
passes = (doc->got_solns ? 2 : 1);
|
|
|
|
print_begin_doc(dr, pages * passes);
|
|
|
|
pageno = 1;
|
|
for (pass = 0; pass < passes; pass++) {
|
|
for (page = 0; page < pages; page++) {
|
|
int i, n, offset;
|
|
float colsum, rowsum;
|
|
|
|
print_begin_page(dr, pageno);
|
|
|
|
offset = page * ppp;
|
|
n = min(ppp, doc->npuzzles - offset);
|
|
|
|
for (i = 0; i < doc->pw; i++)
|
|
doc->colwid[i] = 0;
|
|
for (i = 0; i < doc->ph; i++)
|
|
doc->rowht[i] = 0;
|
|
|
|
/*
|
|
* Lay the page out by computing all the puzzle sizes.
|
|
*/
|
|
for (i = 0; i < n; i++) {
|
|
struct puzzle *pz = doc->puzzles + offset + i;
|
|
int x = i % doc->pw, y = i / doc->pw;
|
|
float w, h, scale;
|
|
|
|
get_puzzle_size(doc, pz, &w, &h, &scale);
|
|
|
|
/* Update the maximum width/height of this column. */
|
|
doc->colwid[x] = max(doc->colwid[x], w);
|
|
doc->rowht[y] = max(doc->rowht[y], h);
|
|
}
|
|
|
|
/*
|
|
* Add up the maximum column/row widths to get the
|
|
* total amount of space used up by puzzles on the
|
|
* page. We will use this to compute gutter widths.
|
|
*/
|
|
colsum = 0.0;
|
|
for (i = 0; i < doc->pw; i++)
|
|
colsum += doc->colwid[i];
|
|
rowsum = 0.0;
|
|
for (i = 0; i < doc->ph; i++)
|
|
rowsum += doc->rowht[i];
|
|
|
|
/*
|
|
* Now do the printing.
|
|
*/
|
|
for (i = 0; i < n; i++) {
|
|
struct puzzle *pz = doc->puzzles + offset + i;
|
|
int x = i % doc->pw, y = i / doc->pw, j;
|
|
float w, h, scale, xm, xc, ym, yc;
|
|
int pixw, pixh, tilesize;
|
|
|
|
if (pass == 1 && !pz->st2)
|
|
continue; /* nothing to do */
|
|
|
|
/*
|
|
* The total amount of gutter space is the page
|
|
* width minus colsum. This is divided into pw+1
|
|
* gutters, so the amount of horizontal gutter
|
|
* space appearing to the left of this puzzle
|
|
* column is
|
|
*
|
|
* (width-colsum) * (x+1)/(pw+1)
|
|
* = width * (x+1)/(pw+1) - (colsum * (x+1)/(pw+1))
|
|
*/
|
|
xm = (float)(x+1) / (doc->pw + 1);
|
|
xc = -xm * colsum;
|
|
/* And similarly for y. */
|
|
ym = (float)(y+1) / (doc->ph + 1);
|
|
yc = -ym * rowsum;
|
|
|
|
/*
|
|
* However, the amount of space to the left of this
|
|
* puzzle isn't just gutter space: we must also
|
|
* count the widths of all the previous columns.
|
|
*/
|
|
for (j = 0; j < x; j++)
|
|
xc += doc->colwid[j];
|
|
/* And similarly for rows. */
|
|
for (j = 0; j < y; j++)
|
|
yc += doc->rowht[j];
|
|
|
|
/*
|
|
* Now we adjust for this _specific_ puzzle, which
|
|
* means centring it within the cell we've just
|
|
* computed.
|
|
*/
|
|
get_puzzle_size(doc, pz, &w, &h, &scale);
|
|
xc += (doc->colwid[x] - w) / 2;
|
|
yc += (doc->rowht[y] - h) / 2;
|
|
|
|
/*
|
|
* And now we know where and how big we want to
|
|
* print the puzzle, just go ahead and do so. For
|
|
* the moment I'll pick a standard pixel tile size
|
|
* of 512.
|
|
*
|
|
* (FIXME: would it be better to pick this value
|
|
* with reference to the printer resolution? Or
|
|
* permit each game to choose its own?)
|
|
*/
|
|
tilesize = 512;
|
|
pz->game->compute_size(pz->par, tilesize, &pixw, &pixh);
|
|
print_begin_puzzle(dr, xm, xc, ym, yc, pixw, pixh, w, scale);
|
|
pz->game->print(dr, pass == 0 ? pz->st : pz->st2, tilesize);
|
|
print_end_puzzle(dr);
|
|
}
|
|
|
|
print_end_page(dr, pageno);
|
|
pageno++;
|
|
}
|
|
}
|
|
|
|
print_end_doc(dr);
|
|
}
|