From 881746789a489fad85aae8317555f73dbe261556 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 29 Apr 2017 18:21:56 -0400 Subject: puzzles: refactor and resync with upstream This brings puzzles up-to-date with upstream revision 2d333750272c3967cfd5cd3677572cddeaad5932, though certain changes made by me, including cursor-only Untangle and some compilation fixes remain. Upstream code has been moved to its separate subdirectory and future syncs can be done by simply copying over the new sources. Change-Id: Ia6506ca5f78c3627165ea6791d38db414ace0804 --- apps/plugins/puzzles/src/printing.c | 247 ++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 apps/plugins/puzzles/src/printing.c (limited to 'apps/plugins/puzzles/src/printing.c') diff --git a/apps/plugins/puzzles/src/printing.c b/apps/plugins/puzzles/src/printing.c new file mode 100644 index 0000000000..e921a4d545 --- /dev/null +++ b/apps/plugins/puzzles/src/printing.c @@ -0,0 +1,247 @@ +/* + * 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); +} -- cgit v1.2.3