diff --git a/src/renderer/src/scene/study/ShelfArchive.tsx b/src/renderer/src/scene/study/ShelfArchive.tsx index dc8296651..a6cc3b6c4 100644 --- a/src/renderer/src/scene/study/ShelfArchive.tsx +++ b/src/renderer/src/scene/study/ShelfArchive.tsx @@ -117,11 +117,10 @@ export function shelfLabel( /** * How thick one painted spine is, as a share of the shelf it stands on. * - * A mark is a whole bay of books, but the number is pasted on ONE of them, the - * way a library labels a volume rather than a shelf. Sized from the bay's width - * the label would be a plate across a run of spines with a glyph taller than - * any book on the wall; the volumes painted there are roughly a quarter as - * thick as the shelf is tall, so that is how wide the label is. + * A mark is one painted spine, and the label is as wide as that spine — up to + * this share of its height. The volumes painted on the wall are roughly a + * quarter as thick as they are tall, and the cap keeps the label on the thick + * ones from growing into a plate with a glyph taller than any book beside it. */ export const SPINE_THICKNESS = 0.25; diff --git a/src/renderer/src/scene/study/shelfBooks.ts b/src/renderer/src/scene/study/shelfBooks.ts index 064b383fd..7806a26a3 100644 --- a/src/renderer/src/scene/study/shelfBooks.ts +++ b/src/renderer/src/scene/study/shelfBooks.ts @@ -50,87 +50,383 @@ * drawn: archiving darkens the painting inside one of these rectangles, and the * thing that stands out is paint the painter put there. * - * Each rectangle is one BAY of the wall — the run of books standing between two - * posts on one shelf, from the ledge up to the shelf above it. It used to be one - * spine, four to a shelf and spread along it, and however much work had been - * finished the wall read as a book lit here and there: twenty-two spines of the - * several hundred painted, and the rest of the wall could never change. Aaron: - * *"the whole background of books should be able to be darkened."* A bay is how - * much of the wall one finished commission is worth if the whole wall is to - * fill, and a filled bay is a thing the eye counts where a single darkened spine - * among dark spines is not. + * Each rectangle is one painted SPINE, and one finished commission darkens + * exactly one of them. The wall had been cut into bays — the run of books + * between two posts on one shelf — so that fifty-odd marks could fill it, but a + * bay is a chunk of several books standing for one piece of work, and the wall + * could then only ever say "about this much". A spine per commission keeps both + * readings: a full wall is still a wall that has filled, and every darkened + * book on it is one thing that was finished. * - * The bays stop short of the three things on the wall that are not books. A + * The spines stop short of the three things on the wall that are not books. A * lamp is what lights the wall, and a shade taken down with the books beside it * reads as the light going out; the ladder and the sleeping cat stand in front - * of the shelves rather than on them. Where one of those stands in a bay, the - * bay is cut at its edge, and a sliver too narrow to be read as books is left - * out rather than marked. + * of the shelves rather than on them. A volume behind one of those is not + * offered at all. * * The order is the order they are handed out in — along each shelf, top shelf * first — which is how a wall of books fills, so the archive reads as a level. * - * Read off the painting rather than authored freehand: the posts and ledges are - * the columns and rows of that panel where the paint is shelving all the way - * across, and the lamps are where it is lamplight. + * Read off the painting rather than authored freehand. Within each run of books + * between two posts, a spine's sides are where the colour of the paint changes + * from one column to the next — the painted titles are left out of that, since + * a glyph is an edge inside a book rather than between two — and a run too wide + * to be one volume is split at its strongest remaining edge. A spine's top is + * the first row, going down, below which the paint is that book's own colour. + * Every spine stands on its ledge, so the bottoms of one shelf's spines agree + * where their tops do not. */ export const SHELF_BOOKS: readonly { x: number; y: number; w: number; h: number }[] = [ - { x: 0.0108, y: 0.0074, w: 0.0867, h: 0.1369 }, - { x: 0.1084, y: 0.0074, w: 0.0969, h: 0.1369 }, - { x: 0.2353, y: 0.0074, w: 0.1582, h: 0.1369 }, - { x: 0.4043, y: 0.0074, w: 0.0874, h: 0.1369 }, - { x: 0.5096, y: 0.0074, w: 0.0867, h: 0.1369 }, - { x: 0.6078, y: 0.0074, w: 0.0963, h: 0.1369 }, - { x: 0.7156, y: 0.0074, w: 0.0893, h: 0.1369 }, - { x: 0.8170, y: 0.0074, w: 0.0861, h: 0.1369 }, - { x: 0.9133, y: 0.0074, w: 0.0861, h: 0.1369 }, - { x: 0.0344, y: 0.1905, w: 0.0631, h: 0.1131 }, - { x: 0.1084, y: 0.1905, w: 0.0867, h: 0.1131 }, - { x: 0.2481, y: 0.1905, w: 0.1454, h: 0.1131 }, - { x: 0.4043, y: 0.1905, w: 0.0746, h: 0.1131 }, - { x: 0.5287, y: 0.1905, w: 0.0676, h: 0.1131 }, - { x: 0.6078, y: 0.1905, w: 0.0708, h: 0.1131 }, - { x: 0.7302, y: 0.1905, w: 0.0746, h: 0.1131 }, - { x: 0.8170, y: 0.1905, w: 0.0625, h: 0.1131 }, - { x: 0.9426, y: 0.1905, w: 0.0568, h: 0.1131 }, - { x: 0.0344, y: 0.3557, w: 0.0631, h: 0.1086 }, - { x: 0.1084, y: 0.3557, w: 0.0867, h: 0.1086 }, - { x: 0.2481, y: 0.3557, w: 0.1454, h: 0.1086 }, - { x: 0.4043, y: 0.3557, w: 0.0746, h: 0.1086 }, - { x: 0.5287, y: 0.3557, w: 0.0676, h: 0.1086 }, - { x: 0.6078, y: 0.3557, w: 0.0708, h: 0.1086 }, - { x: 0.7302, y: 0.3557, w: 0.0746, h: 0.1086 }, - { x: 0.8170, y: 0.3557, w: 0.0364, h: 0.1086 }, - { x: 0.9426, y: 0.3557, w: 0.0568, h: 0.1086 }, - { x: 0.0351, y: 0.5074, w: 0.0625, h: 0.1146 }, - { x: 0.1084, y: 0.5074, w: 0.0867, h: 0.1146 }, - { x: 0.2481, y: 0.5074, w: 0.1454, h: 0.1146 }, - { x: 0.4043, y: 0.5074, w: 0.0727, h: 0.1146 }, - { x: 0.5319, y: 0.5074, w: 0.0644, h: 0.1146 }, - { x: 0.6078, y: 0.5074, w: 0.0689, h: 0.1146 }, - { x: 0.7296, y: 0.5074, w: 0.0753, h: 0.1146 }, - { x: 0.8170, y: 0.5074, w: 0.0364, h: 0.1146 }, - { x: 0.9330, y: 0.5074, w: 0.0663, h: 0.1146 }, - { x: 0.0351, y: 0.6622, w: 0.0625, h: 0.1205 }, - { x: 0.1084, y: 0.6622, w: 0.0867, h: 0.1205 }, - { x: 0.2481, y: 0.6622, w: 0.1454, h: 0.1205 }, - { x: 0.4043, y: 0.6622, w: 0.0727, h: 0.1205 }, - { x: 0.5319, y: 0.6622, w: 0.0644, h: 0.1205 }, - { x: 0.6078, y: 0.6622, w: 0.0689, h: 0.1205 }, - { x: 0.7296, y: 0.6622, w: 0.0753, h: 0.1205 }, - { x: 0.8170, y: 0.6622, w: 0.0364, h: 0.1205 }, - { x: 0.9330, y: 0.6622, w: 0.0663, h: 0.1205 }, - { x: 0.0357, y: 0.8304, w: 0.0619, h: 0.1146 }, - { x: 0.1084, y: 0.8304, w: 0.0855, h: 0.1146 }, - { x: 0.2494, y: 0.8304, w: 0.1441, h: 0.1146 }, - { x: 0.4043, y: 0.8304, w: 0.0714, h: 0.1146 }, - { x: 0.5319, y: 0.8304, w: 0.0644, h: 0.1146 }, - { x: 0.6078, y: 0.8304, w: 0.0963, h: 0.1146 }, - { x: 0.8170, y: 0.8304, w: 0.0364, h: 0.1146 }, - { x: 0.9343, y: 0.8304, w: 0.0651, h: 0.1146 } + { x: 0.0108, y: 0.0268, w: 0.0141, h: 0.1175 }, + { x: 0.0249, y: 0.0268, w: 0.0076, h: 0.1175 }, + { x: 0.0325, y: 0.0283, w: 0.0141, h: 0.1160 }, + { x: 0.0466, y: 0.0432, w: 0.0185, h: 0.1011 }, + { x: 0.0651, y: 0.0179, w: 0.0197, h: 0.1264 }, + { x: 0.0848, y: 0.0074, w: 0.0128, h: 0.1369 }, + { x: 0.1084, y: 0.0446, w: 0.0134, h: 0.0997 }, + { x: 0.1218, y: 0.0432, w: 0.0115, h: 0.1011 }, + { x: 0.1333, y: 0.0417, w: 0.0121, h: 0.1026 }, + { x: 0.1454, y: 0.0461, w: 0.0083, h: 0.0982 }, + { x: 0.1537, y: 0.0313, w: 0.0185, h: 0.1130 }, + { x: 0.1722, y: 0.0446, w: 0.0064, h: 0.0997 }, + { x: 0.1786, y: 0.0357, w: 0.0185, h: 0.1086 }, + { x: 0.1971, y: 0.0253, w: 0.0083, h: 0.1190 }, + { x: 0.2353, y: 0.0476, w: 0.0058, h: 0.0967 }, + { x: 0.2411, y: 0.0298, w: 0.0115, h: 0.1145 }, + { x: 0.2526, y: 0.0313, w: 0.0082, h: 0.1130 }, + { x: 0.2608, y: 0.0313, w: 0.0058, h: 0.1130 }, + { x: 0.2666, y: 0.0476, w: 0.0210, h: 0.0967 }, + { x: 0.2876, y: 0.0193, w: 0.0058, h: 0.1250 }, + { x: 0.2934, y: 0.0179, w: 0.0172, h: 0.1264 }, + { x: 0.3106, y: 0.0461, w: 0.0108, h: 0.0982 }, + { x: 0.3214, y: 0.0357, w: 0.0115, h: 0.1086 }, + { x: 0.3329, y: 0.0357, w: 0.0128, h: 0.1086 }, + { x: 0.3457, y: 0.0446, w: 0.0134, h: 0.0997 }, + { x: 0.3591, y: 0.0476, w: 0.0178, h: 0.0967 }, + { x: 0.3769, y: 0.0253, w: 0.0166, h: 0.1190 }, + { x: 0.4043, y: 0.0476, w: 0.0102, h: 0.0967 }, + { x: 0.4145, y: 0.0432, w: 0.0147, h: 0.1011 }, + { x: 0.4292, y: 0.0461, w: 0.0166, h: 0.0982 }, + { x: 0.4458, y: 0.0476, w: 0.0236, h: 0.0967 }, + { x: 0.4694, y: 0.0283, w: 0.0159, h: 0.1160 }, + { x: 0.4853, y: 0.0283, w: 0.0064, h: 0.1160 }, + { x: 0.5096, y: 0.0446, w: 0.0089, h: 0.0997 }, + { x: 0.5185, y: 0.0446, w: 0.0108, h: 0.0997 }, + { x: 0.5293, y: 0.0298, w: 0.0058, h: 0.1145 }, + { x: 0.5351, y: 0.0461, w: 0.0178, h: 0.0982 }, + { x: 0.5529, y: 0.0313, w: 0.0192, h: 0.1130 }, + { x: 0.5721, y: 0.0446, w: 0.0108, h: 0.0997 }, + { x: 0.5829, y: 0.0417, w: 0.0134, h: 0.1026 }, + { x: 0.6078, y: 0.0268, w: 0.0191, h: 0.1175 }, + { x: 0.6269, y: 0.0372, w: 0.0058, h: 0.1071 }, + { x: 0.6327, y: 0.0372, w: 0.0165, h: 0.1071 }, + { x: 0.6492, y: 0.0461, w: 0.0090, h: 0.0982 }, + { x: 0.6582, y: 0.0074, w: 0.0076, h: 0.1369 }, + { x: 0.6658, y: 0.0313, w: 0.0121, h: 0.1130 }, + { x: 0.6779, y: 0.0357, w: 0.0122, h: 0.1086 }, + { x: 0.6901, y: 0.0298, w: 0.0140, h: 0.1145 }, + { x: 0.7156, y: 0.0357, w: 0.0095, h: 0.1086 }, + { x: 0.7251, y: 0.0476, w: 0.0109, h: 0.0967 }, + { x: 0.7360, y: 0.0283, w: 0.0057, h: 0.1160 }, + { x: 0.7417, y: 0.0298, w: 0.0102, h: 0.1145 }, + { x: 0.7519, y: 0.0461, w: 0.0147, h: 0.0982 }, + { x: 0.7666, y: 0.0283, w: 0.0153, h: 0.1160 }, + { x: 0.7819, y: 0.0461, w: 0.0229, h: 0.0982 }, + { x: 0.8170, y: 0.0238, w: 0.0095, h: 0.1205 }, + { x: 0.8265, y: 0.0446, w: 0.0102, h: 0.0997 }, + { x: 0.8367, y: 0.0417, w: 0.0160, h: 0.1026 }, + { x: 0.8527, y: 0.0417, w: 0.0108, h: 0.1026 }, + { x: 0.8635, y: 0.0417, w: 0.0128, h: 0.1026 }, + { x: 0.8763, y: 0.0565, w: 0.0127, h: 0.0878 }, + { x: 0.8890, y: 0.0446, w: 0.0141, h: 0.0997 }, + { x: 0.9133, y: 0.0372, w: 0.0114, h: 0.1071 }, + { x: 0.9247, y: 0.0298, w: 0.0122, h: 0.1145 }, + { x: 0.9369, y: 0.0536, w: 0.0095, h: 0.0907 }, + { x: 0.9464, y: 0.0074, w: 0.0141, h: 0.1369 }, + { x: 0.9605, y: 0.0461, w: 0.0114, h: 0.0982 }, + { x: 0.9719, y: 0.0461, w: 0.0102, h: 0.0982 }, + { x: 0.9821, y: 0.0446, w: 0.0173, h: 0.0997 }, + { x: 0.0344, y: 0.1905, w: 0.0173, h: 0.1131 }, + { x: 0.0517, y: 0.1905, w: 0.0089, h: 0.1131 }, + { x: 0.0606, y: 0.1979, w: 0.0166, h: 0.1057 }, + { x: 0.0772, y: 0.2024, w: 0.0063, h: 0.1012 }, + { x: 0.0835, y: 0.1905, w: 0.0141, h: 0.1131 }, + { x: 0.1084, y: 0.1949, w: 0.0134, h: 0.1087 }, + { x: 0.1218, y: 0.2024, w: 0.0109, h: 0.1012 }, + { x: 0.1327, y: 0.1920, w: 0.0146, h: 0.1116 }, + { x: 0.1473, y: 0.1935, w: 0.0134, h: 0.1101 }, + { x: 0.1607, y: 0.2068, w: 0.0128, h: 0.0968 }, + { x: 0.1735, y: 0.2188, w: 0.0095, h: 0.0848 }, + { x: 0.1830, y: 0.1905, w: 0.0122, h: 0.1131 }, + { x: 0.2481, y: 0.2277, w: 0.0096, h: 0.0759 }, + { x: 0.2577, y: 0.1905, w: 0.0076, h: 0.1131 }, + { x: 0.2653, y: 0.2083, w: 0.0077, h: 0.0953 }, + { x: 0.2730, y: 0.2083, w: 0.0063, h: 0.0953 }, + { x: 0.2793, y: 0.2202, w: 0.0102, h: 0.0834 }, + { x: 0.2895, y: 0.1905, w: 0.0128, h: 0.1131 }, + { x: 0.3023, y: 0.1905, w: 0.0096, h: 0.1131 }, + { x: 0.3119, y: 0.1979, w: 0.0172, h: 0.1057 }, + { x: 0.3291, y: 0.2068, w: 0.0147, h: 0.0968 }, + { x: 0.3438, y: 0.2068, w: 0.0095, h: 0.0968 }, + { x: 0.3533, y: 0.1964, w: 0.0140, h: 0.1072 }, + { x: 0.3673, y: 0.2009, w: 0.0103, h: 0.1027 }, + { x: 0.3776, y: 0.1905, w: 0.0076, h: 0.1131 }, + { x: 0.3852, y: 0.1905, w: 0.0083, h: 0.1131 }, + { x: 0.4043, y: 0.1905, w: 0.0166, h: 0.1131 }, + { x: 0.4209, y: 0.1920, w: 0.0109, h: 0.1116 }, + { x: 0.4318, y: 0.1905, w: 0.0127, h: 0.1131 }, + { x: 0.4445, y: 0.2039, w: 0.0096, h: 0.0997 }, + { x: 0.4541, y: 0.1905, w: 0.0108, h: 0.1131 }, + { x: 0.4649, y: 0.2262, w: 0.0141, h: 0.0774 }, + { x: 0.5287, y: 0.1905, w: 0.0179, h: 0.1131 }, + { x: 0.5466, y: 0.1920, w: 0.0121, h: 0.1116 }, + { x: 0.5587, y: 0.1905, w: 0.0127, h: 0.1131 }, + { x: 0.5714, y: 0.1994, w: 0.0141, h: 0.1042 }, + { x: 0.5855, y: 0.1905, w: 0.0108, h: 0.1131 }, + { x: 0.6078, y: 0.1979, w: 0.0102, h: 0.1057 }, + { x: 0.6180, y: 0.2068, w: 0.0089, h: 0.0968 }, + { x: 0.6269, y: 0.1905, w: 0.0223, h: 0.1131 }, + { x: 0.6492, y: 0.1920, w: 0.0115, h: 0.1116 }, + { x: 0.6607, y: 0.1905, w: 0.0121, h: 0.1131 }, + { x: 0.6728, y: 0.1905, w: 0.0058, h: 0.1131 }, + { x: 0.7302, y: 0.1905, w: 0.0102, h: 0.1131 }, + { x: 0.7404, y: 0.2202, w: 0.0141, h: 0.0834 }, + { x: 0.7545, y: 0.1905, w: 0.0108, h: 0.1131 }, + { x: 0.7653, y: 0.2009, w: 0.0121, h: 0.1027 }, + { x: 0.7774, y: 0.2068, w: 0.0115, h: 0.0968 }, + { x: 0.7889, y: 0.1905, w: 0.0159, h: 0.1131 }, + { x: 0.8170, y: 0.2009, w: 0.0063, h: 0.1027 }, + { x: 0.8233, y: 0.1905, w: 0.0147, h: 0.1131 }, + { x: 0.8380, y: 0.2083, w: 0.0058, h: 0.0953 }, + { x: 0.8438, y: 0.1935, w: 0.0057, h: 0.1101 }, + { x: 0.8495, y: 0.1905, w: 0.0096, h: 0.1131 }, + { x: 0.8591, y: 0.2143, w: 0.0070, h: 0.0893 }, + { x: 0.8661, y: 0.1905, w: 0.0057, h: 0.1131 }, + { x: 0.8718, y: 0.1905, w: 0.0077, h: 0.1131 }, + { x: 0.9426, y: 0.1905, w: 0.0140, h: 0.1131 }, + { x: 0.9566, y: 0.2158, w: 0.0102, h: 0.0878 }, + { x: 0.9668, y: 0.2009, w: 0.0058, h: 0.1027 }, + { x: 0.9726, y: 0.1949, w: 0.0095, h: 0.1087 }, + { x: 0.9821, y: 0.1905, w: 0.0173, h: 0.1131 }, + { x: 0.0344, y: 0.3557, w: 0.0090, h: 0.1086 }, + { x: 0.0434, y: 0.3824, w: 0.0102, h: 0.0819 }, + { x: 0.0536, y: 0.3676, w: 0.0102, h: 0.0967 }, + { x: 0.0638, y: 0.3557, w: 0.0102, h: 0.1086 }, + { x: 0.0740, y: 0.3557, w: 0.0121, h: 0.1086 }, + { x: 0.0861, y: 0.3631, w: 0.0115, h: 0.1012 }, + { x: 0.1084, y: 0.3661, w: 0.0077, h: 0.0982 }, + { x: 0.1161, y: 0.3661, w: 0.0115, h: 0.0982 }, + { x: 0.1276, y: 0.3557, w: 0.0204, h: 0.1086 }, + { x: 0.1480, y: 0.3631, w: 0.0095, h: 0.1012 }, + { x: 0.1575, y: 0.3780, w: 0.0198, h: 0.0863 }, + { x: 0.1773, y: 0.3557, w: 0.0179, h: 0.1086 }, + { x: 0.2481, y: 0.3557, w: 0.0147, h: 0.1086 }, + { x: 0.2628, y: 0.3780, w: 0.0153, h: 0.0863 }, + { x: 0.2781, y: 0.3824, w: 0.0102, h: 0.0819 }, + { x: 0.2883, y: 0.3557, w: 0.0210, h: 0.1086 }, + { x: 0.3093, y: 0.3571, w: 0.0115, h: 0.1072 }, + { x: 0.3208, y: 0.3958, w: 0.0159, h: 0.0685 }, + { x: 0.3367, y: 0.3557, w: 0.0160, h: 0.1086 }, + { x: 0.3527, y: 0.3557, w: 0.0076, h: 0.1086 }, + { x: 0.3603, y: 0.3705, w: 0.0166, h: 0.0938 }, + { x: 0.3769, y: 0.3557, w: 0.0166, h: 0.1086 }, + { x: 0.4043, y: 0.3646, w: 0.0211, h: 0.0997 }, + { x: 0.4254, y: 0.3557, w: 0.0121, h: 0.1086 }, + { x: 0.4375, y: 0.3557, w: 0.0102, h: 0.1086 }, + { x: 0.4477, y: 0.3810, w: 0.0134, h: 0.0833 }, + { x: 0.4611, y: 0.3557, w: 0.0179, h: 0.1086 }, + { x: 0.5287, y: 0.3557, w: 0.0102, h: 0.1086 }, + { x: 0.5389, y: 0.3557, w: 0.0108, h: 0.1086 }, + { x: 0.5497, y: 0.3795, w: 0.0096, h: 0.0848 }, + { x: 0.5593, y: 0.3631, w: 0.0121, h: 0.1012 }, + { x: 0.5714, y: 0.3824, w: 0.0115, h: 0.0819 }, + { x: 0.5829, y: 0.3958, w: 0.0134, h: 0.0685 }, + { x: 0.6078, y: 0.3586, w: 0.0121, h: 0.1057 }, + { x: 0.6199, y: 0.3631, w: 0.0217, h: 0.1012 }, + { x: 0.6416, y: 0.3854, w: 0.0095, h: 0.0789 }, + { x: 0.6511, y: 0.3557, w: 0.0115, h: 0.1086 }, + { x: 0.6626, y: 0.3824, w: 0.0083, h: 0.0819 }, + { x: 0.6709, y: 0.3557, w: 0.0077, h: 0.1086 }, + { x: 0.7302, y: 0.3557, w: 0.0064, h: 0.1086 }, + { x: 0.7366, y: 0.3795, w: 0.0236, h: 0.0848 }, + { x: 0.7602, y: 0.3557, w: 0.0083, h: 0.1086 }, + { x: 0.7685, y: 0.3557, w: 0.0159, h: 0.1086 }, + { x: 0.7844, y: 0.3661, w: 0.0109, h: 0.0982 }, + { x: 0.7953, y: 0.3795, w: 0.0095, h: 0.0848 }, + { x: 0.8170, y: 0.3571, w: 0.0108, h: 0.1072 }, + { x: 0.8278, y: 0.3795, w: 0.0147, h: 0.0848 }, + { x: 0.8425, y: 0.3557, w: 0.0108, h: 0.1086 }, + { x: 0.9426, y: 0.3824, w: 0.0115, h: 0.0819 }, + { x: 0.9541, y: 0.3661, w: 0.0229, h: 0.0982 }, + { x: 0.9770, y: 0.3824, w: 0.0134, h: 0.0819 }, + { x: 0.9904, y: 0.3780, w: 0.0090, h: 0.0863 }, + { x: 0.0351, y: 0.5253, w: 0.0121, h: 0.0967 }, + { x: 0.0472, y: 0.5327, w: 0.0140, h: 0.0893 }, + { x: 0.0612, y: 0.5074, w: 0.0147, h: 0.1146 }, + { x: 0.0759, y: 0.5372, w: 0.0064, h: 0.0848 }, + { x: 0.0823, y: 0.5074, w: 0.0095, h: 0.1146 }, + { x: 0.0918, y: 0.5074, w: 0.0058, h: 0.1146 }, + { x: 0.1084, y: 0.5074, w: 0.0153, h: 0.1146 }, + { x: 0.1237, y: 0.5179, w: 0.0102, h: 0.1041 }, + { x: 0.1339, y: 0.5164, w: 0.0090, h: 0.1056 }, + { x: 0.1429, y: 0.5253, w: 0.0261, h: 0.0967 }, + { x: 0.1690, y: 0.5074, w: 0.0115, h: 0.1146 }, + { x: 0.1805, y: 0.5074, w: 0.0083, h: 0.1146 }, + { x: 0.1888, y: 0.5074, w: 0.0064, h: 0.1146 }, + { x: 0.2481, y: 0.5313, w: 0.0121, h: 0.0907 }, + { x: 0.2602, y: 0.5074, w: 0.0191, h: 0.1146 }, + { x: 0.2793, y: 0.5104, w: 0.0102, h: 0.1116 }, + { x: 0.2895, y: 0.5208, w: 0.0217, h: 0.1012 }, + { x: 0.3112, y: 0.5387, w: 0.0179, h: 0.0833 }, + { x: 0.3291, y: 0.5253, w: 0.0185, h: 0.0967 }, + { x: 0.3476, y: 0.5074, w: 0.0121, h: 0.1146 }, + { x: 0.3597, y: 0.5074, w: 0.0102, h: 0.1146 }, + { x: 0.3699, y: 0.5387, w: 0.0070, h: 0.0833 }, + { x: 0.3769, y: 0.5074, w: 0.0166, h: 0.1146 }, + { x: 0.4043, y: 0.5253, w: 0.0102, h: 0.0967 }, + { x: 0.4145, y: 0.5402, w: 0.0115, h: 0.0818 }, + { x: 0.4260, y: 0.5074, w: 0.0109, h: 0.1146 }, + { x: 0.4369, y: 0.5089, w: 0.0185, h: 0.1131 }, + { x: 0.4554, y: 0.5074, w: 0.0140, h: 0.1146 }, + { x: 0.4694, y: 0.5342, w: 0.0076, h: 0.0878 }, + { x: 0.5319, y: 0.5372, w: 0.0096, h: 0.0848 }, + { x: 0.5415, y: 0.5372, w: 0.0133, h: 0.0848 }, + { x: 0.5548, y: 0.5193, w: 0.0122, h: 0.1027 }, + { x: 0.5670, y: 0.5342, w: 0.0140, h: 0.0878 }, + { x: 0.5810, y: 0.5074, w: 0.0153, h: 0.1146 }, + { x: 0.6078, y: 0.5342, w: 0.0217, h: 0.0878 }, + { x: 0.6295, y: 0.5179, w: 0.0089, h: 0.1041 }, + { x: 0.6384, y: 0.5179, w: 0.0121, h: 0.1041 }, + { x: 0.6505, y: 0.5074, w: 0.0153, h: 0.1146 }, + { x: 0.6658, y: 0.5074, w: 0.0109, h: 0.1146 }, + { x: 0.7296, y: 0.5372, w: 0.0083, h: 0.0848 }, + { x: 0.7379, y: 0.5327, w: 0.0140, h: 0.0893 }, + { x: 0.7519, y: 0.5089, w: 0.0089, h: 0.1131 }, + { x: 0.7608, y: 0.5089, w: 0.0109, h: 0.1131 }, + { x: 0.7717, y: 0.5193, w: 0.0115, h: 0.1027 }, + { x: 0.7832, y: 0.5238, w: 0.0216, h: 0.0982 }, + { x: 0.8170, y: 0.5149, w: 0.0102, h: 0.1071 }, + { x: 0.8272, y: 0.5119, w: 0.0121, h: 0.1101 }, + { x: 0.8393, y: 0.5119, w: 0.0083, h: 0.1101 }, + { x: 0.8476, y: 0.5357, w: 0.0057, h: 0.0863 }, + { x: 0.9330, y: 0.5104, w: 0.0109, h: 0.1116 }, + { x: 0.9439, y: 0.5104, w: 0.0095, h: 0.1116 }, + { x: 0.9534, y: 0.5268, w: 0.0147, h: 0.0952 }, + { x: 0.9681, y: 0.5074, w: 0.0128, h: 0.1146 }, + { x: 0.9809, y: 0.5208, w: 0.0127, h: 0.1012 }, + { x: 0.9936, y: 0.5327, w: 0.0058, h: 0.0893 }, + { x: 0.0351, y: 0.6622, w: 0.0095, h: 0.1205 }, + { x: 0.0446, y: 0.6905, w: 0.0141, h: 0.0922 }, + { x: 0.0587, y: 0.6711, w: 0.0172, h: 0.1116 }, + { x: 0.0759, y: 0.6622, w: 0.0089, h: 0.1205 }, + { x: 0.0848, y: 0.6622, w: 0.0128, h: 0.1205 }, + { x: 0.1084, y: 0.6801, w: 0.0109, h: 0.1026 }, + { x: 0.1193, y: 0.6622, w: 0.0146, h: 0.1205 }, + { x: 0.1339, y: 0.6771, w: 0.0090, h: 0.1056 }, + { x: 0.1429, y: 0.6801, w: 0.0178, h: 0.1026 }, + { x: 0.1607, y: 0.6622, w: 0.0083, h: 0.1205 }, + { x: 0.1690, y: 0.6622, w: 0.0128, h: 0.1205 }, + { x: 0.1818, y: 0.6726, w: 0.0134, h: 0.1101 }, + { x: 0.2481, y: 0.6622, w: 0.0172, h: 0.1205 }, + { x: 0.2653, y: 0.6622, w: 0.0108, h: 0.1205 }, + { x: 0.2761, y: 0.6622, w: 0.0109, h: 0.1205 }, + { x: 0.2870, y: 0.6711, w: 0.0178, h: 0.1116 }, + { x: 0.3048, y: 0.6622, w: 0.0090, h: 0.1205 }, + { x: 0.3138, y: 0.6696, w: 0.0083, h: 0.1131 }, + { x: 0.3221, y: 0.6801, w: 0.0121, h: 0.1026 }, + { x: 0.3342, y: 0.6815, w: 0.0134, h: 0.1012 }, + { x: 0.3476, y: 0.6637, w: 0.0064, h: 0.1190 }, + { x: 0.3540, y: 0.6637, w: 0.0070, h: 0.1190 }, + { x: 0.3610, y: 0.6637, w: 0.0083, h: 0.1190 }, + { x: 0.3693, y: 0.6622, w: 0.0134, h: 0.1205 }, + { x: 0.3827, y: 0.6622, w: 0.0108, h: 0.1205 }, + { x: 0.4043, y: 0.7009, w: 0.0147, h: 0.0818 }, + { x: 0.4190, y: 0.6979, w: 0.0134, h: 0.0848 }, + { x: 0.4324, y: 0.6637, w: 0.0089, h: 0.1190 }, + { x: 0.4413, y: 0.6637, w: 0.0153, h: 0.1190 }, + { x: 0.4566, y: 0.6801, w: 0.0204, h: 0.1026 }, + { x: 0.5319, y: 0.6622, w: 0.0096, h: 0.1205 }, + { x: 0.5415, y: 0.6979, w: 0.0133, h: 0.0848 }, + { x: 0.5548, y: 0.6741, w: 0.0134, h: 0.1086 }, + { x: 0.5682, y: 0.6771, w: 0.0128, h: 0.1056 }, + { x: 0.5810, y: 0.6622, w: 0.0153, h: 0.1205 }, + { x: 0.6078, y: 0.6801, w: 0.0191, h: 0.1026 }, + { x: 0.6269, y: 0.6830, w: 0.0083, h: 0.0997 }, + { x: 0.6352, y: 0.6622, w: 0.0057, h: 0.1205 }, + { x: 0.6409, y: 0.6845, w: 0.0268, h: 0.0982 }, + { x: 0.6677, y: 0.6726, w: 0.0090, h: 0.1101 }, + { x: 0.7296, y: 0.6949, w: 0.0089, h: 0.0878 }, + { x: 0.7385, y: 0.6622, w: 0.0096, h: 0.1205 }, + { x: 0.7481, y: 0.6622, w: 0.0121, h: 0.1205 }, + { x: 0.7602, y: 0.6801, w: 0.0185, h: 0.1026 }, + { x: 0.7787, y: 0.6801, w: 0.0070, h: 0.1026 }, + { x: 0.7857, y: 0.6622, w: 0.0089, h: 0.1205 }, + { x: 0.7946, y: 0.6622, w: 0.0102, h: 0.1205 }, + { x: 0.8170, y: 0.6726, w: 0.0102, h: 0.1101 }, + { x: 0.8272, y: 0.6741, w: 0.0261, h: 0.1086 }, + { x: 0.9330, y: 0.6622, w: 0.0160, h: 0.1205 }, + { x: 0.9490, y: 0.6786, w: 0.0064, h: 0.1041 }, + { x: 0.9554, y: 0.6994, w: 0.0178, h: 0.0833 }, + { x: 0.9732, y: 0.6637, w: 0.0262, h: 0.1190 }, + { x: 0.0357, y: 0.8304, w: 0.0089, h: 0.1145 }, + { x: 0.0446, y: 0.8304, w: 0.0122, h: 0.1145 }, + { x: 0.0568, y: 0.8363, w: 0.0185, h: 0.1086 }, + { x: 0.0753, y: 0.8304, w: 0.0223, h: 0.1145 }, + { x: 0.1084, y: 0.8378, w: 0.0115, h: 0.1071 }, + { x: 0.1199, y: 0.8423, w: 0.0172, h: 0.1026 }, + { x: 0.1371, y: 0.8423, w: 0.0064, h: 0.1026 }, + { x: 0.1435, y: 0.8304, w: 0.0185, h: 0.1145 }, + { x: 0.1620, y: 0.8304, w: 0.0115, h: 0.1145 }, + { x: 0.1735, y: 0.8304, w: 0.0121, h: 0.1145 }, + { x: 0.1856, y: 0.8304, w: 0.0083, h: 0.1145 }, + { x: 0.2494, y: 0.8304, w: 0.0178, h: 0.1145 }, + { x: 0.2672, y: 0.8304, w: 0.0089, h: 0.1145 }, + { x: 0.2761, y: 0.8304, w: 0.0064, h: 0.1145 }, + { x: 0.2825, y: 0.8571, w: 0.0281, h: 0.0878 }, + { x: 0.3106, y: 0.8348, w: 0.0159, h: 0.1101 }, + { x: 0.3265, y: 0.8304, w: 0.0070, h: 0.1145 }, + { x: 0.3335, y: 0.8304, w: 0.0071, h: 0.1145 }, + { x: 0.3406, y: 0.8438, w: 0.0083, h: 0.1011 }, + { x: 0.3489, y: 0.8571, w: 0.0089, h: 0.0878 }, + { x: 0.3578, y: 0.8571, w: 0.0146, h: 0.0878 }, + { x: 0.3724, y: 0.8304, w: 0.0211, h: 0.1145 }, + { x: 0.4043, y: 0.8586, w: 0.0179, h: 0.0863 }, + { x: 0.4222, y: 0.8601, w: 0.0127, h: 0.0848 }, + { x: 0.4349, y: 0.8318, w: 0.0134, h: 0.1131 }, + { x: 0.4483, y: 0.8318, w: 0.0071, h: 0.1131 }, + { x: 0.4554, y: 0.8304, w: 0.0140, h: 0.1145 }, + { x: 0.4694, y: 0.8304, w: 0.0064, h: 0.1145 }, + { x: 0.5319, y: 0.8304, w: 0.0102, h: 0.1145 }, + { x: 0.5421, y: 0.8304, w: 0.0089, h: 0.1145 }, + { x: 0.5510, y: 0.8601, w: 0.0141, h: 0.0848 }, + { x: 0.5651, y: 0.8601, w: 0.0153, h: 0.0848 }, + { x: 0.5804, y: 0.8304, w: 0.0082, h: 0.1145 }, + { x: 0.5886, y: 0.8304, w: 0.0077, h: 0.1145 }, + { x: 0.6078, y: 0.8571, w: 0.0127, h: 0.0878 }, + { x: 0.6205, y: 0.8304, w: 0.0153, h: 0.1145 }, + { x: 0.6358, y: 0.8631, w: 0.0077, h: 0.0818 }, + { x: 0.6435, y: 0.8393, w: 0.0274, h: 0.1056 }, + { x: 0.6709, y: 0.8304, w: 0.0243, h: 0.1145 }, + { x: 0.6952, y: 0.8378, w: 0.0089, h: 0.1071 }, + { x: 0.8170, y: 0.8304, w: 0.0057, h: 0.1145 }, + { x: 0.8227, y: 0.8304, w: 0.0140, h: 0.1145 }, + { x: 0.8367, y: 0.8304, w: 0.0102, h: 0.1145 }, + { x: 0.8469, y: 0.8616, w: 0.0064, h: 0.0833 }, + { x: 0.9343, y: 0.8304, w: 0.0058, h: 0.1145 }, + { x: 0.9401, y: 0.8304, w: 0.0184, h: 0.1145 }, + { x: 0.9585, y: 0.8601, w: 0.0103, h: 0.0848 }, + { x: 0.9688, y: 0.8318, w: 0.0127, h: 0.1131 }, + { x: 0.9815, y: 0.8601, w: 0.0070, h: 0.0848 }, + { x: 0.9885, y: 0.8304, w: 0.0109, h: 0.1145 } ]; -/** How many books the wall can carry: one per painted volume it can mark. */ +/** + * How many books the wall can carry: one per painted spine it can mark. + * + * This is a ceiling, not a cycle. Past it the OLDEST commission comes off the + * wall and the newest takes a spine, so the wall never marks one book twice or + * starts a second, fainter pass over the first. + */ export const ARCHIVE_MAX = SHELF_BOOKS.length; /** How far back the wall remembers, for the things that carry a date. */ diff --git a/test/study-pulled-books.test.cjs b/test/study-pulled-books.test.cjs index 42dd2cf74..935275cee 100644 --- a/test/study-pulled-books.test.cjs +++ b/test/study-pulled-books.test.cjs @@ -40,10 +40,10 @@ test('the ring never vanishes on a book drawn very small', () => { }); test('a mark on the shelf wall is ringed for the shelf, not for the run of books', () => { - // The wall's marks are whole bays — a run of books far wider than it is tall - // — so a ring taken from the width would be a band a third of a shelf thick - // around the one piece of the house the pointer is resting on. Measured from - // the smaller side it is the ring a single volume that tall would get. + // A ring taken from the larger side of a mark would be a band a third of a + // shelf thick around the one piece of the house the pointer is resting on. + // Measured from the smaller side it is the ring a single volume that tall + // would get. const view = { x: 0, y: 0, w: 1568, h: 672 }; let checked = 0; for (let i = 0; i < 200; i++) { diff --git a/test/study-shelf-archive.test.cjs b/test/study-shelf-archive.test.cjs index 84c73eb6b..56268d825 100644 --- a/test/study-shelf-archive.test.cjs +++ b/test/study-shelf-archive.test.cjs @@ -36,7 +36,7 @@ test('the wall holds what it can hold, and the OLDEST is what falls off', () => const ids = new Set(kept.map((k) => k.id)); for (let i = 0; i < 5; i++) assert.ok(!ids.has(`c${i}`), `c${i} — the oldest — was kept`); assert.ok(ids.has(`c${S.ARCHIVE_MAX + 4}`), 'the newest fell off'); - assert.ok(S.ARCHIVE_MAX > 0 && S.ARCHIVE_MAX <= 64, 'ARCHIVE_MAX is not a bound'); + assert.ok(S.ARCHIVE_MAX > 0 && S.ARCHIVE_MAX <= S.SHELF_BOOKS.length, 'ARCHIVE_MAX is not a bound'); assert.ok(S.ARCHIVE_WINDOW_DAYS > 0, 'ARCHIVE_WINDOW_DAYS is not a window'); }); @@ -167,8 +167,11 @@ test('no two archived things darken the same volume', () => { for (const a of boxes) { for (const b of boxes) { if (a === b) continue; - const apart = a.left + a.width <= b.left || b.left + b.width <= a.left - || a.top + a.height <= b.top || b.top + b.height <= a.top; + // Neighbouring spines share an edge, and an edge is not a volume: the + // tolerance is for the sum of two rounded fractions, far under a pixel. + const e = 1e-6; + const apart = a.left + a.width <= b.left + e || b.left + b.width <= a.left + e + || a.top + a.height <= b.top + e || b.top + b.height <= a.top + e; assert.ok(apart, 'two archived things were given the same painted volume'); } } @@ -196,25 +199,65 @@ test('a slot past the last volume is still a volume, not NaN geometry', () => { const isLamp = ([r, g, b]) => r > 200 && g > 110 && b < 110; test('between them the marks can darken the whole wall of books', () => { + // Measured along the foot of each shelf, where every painted book stands. A + // mark is one spine and stops at that book's own top, so the dark wall above + // a short volume is rightly left alone — and a probe of the whole panel would + // count that wall as a book the marks had missed. const panel = readPng(at(SHELVES)); + // A ledge reads as more than one run of shelving — its front edge, its + // shadowed underside, and the plinth below the last shelf — so a shelf floor + // is a ledge with a shelf's height of books standing above it. + const floors = carpentry(panel).ledges + .filter(([top], i, all) => top - (i === 0 ? 0 : all[i - 1][1]) > 40) + .map(([top]) => top); const inSlot = (x, y) => S.SHELF_BOOKS.some((b) => x >= b.x * panel.width && x < (b.x + b.w) * panel.width && y >= b.y * panel.height && y < (b.y + b.h) * panel.height); let books = 0; let covered = 0; - for (let y = 0; y < panel.height; y += 3) { - for (let x = 0; x < panel.width; x += 3) { - const px = panel.at(x, y); - if (isShelving(px) || isLamp(px)) continue; - books++; - if (inSlot(x, y)) covered++; + for (const top of floors) { + for (let y = top - 12; y < top - 4; y += 2) { + if (y < 0) continue; + for (let x = 0; x < panel.width; x += 2) { + const px = panel.at(x, y); + if (isShelving(px) || isLamp(px)) continue; + books++; + if (inSlot(x, y)) covered++; + } } } - assert.ok(covered / books >= 0.7, + assert.ok(books > 1000, `only ${books} book pixels found at the foot of the shelves`); + assert.ok(covered / books >= 0.8, `the marks can reach only ${Math.round((covered / books) * 100)}% of the painted books, ` + 'so however much is archived the wall stays mostly as it was painted'); }); +test('a mark is one standing book, not a run of them', () => { + // A bay of several books for one finished commission says "about this much"; + // a single spine per commission is what lets the wall be counted. A standing + // volume is narrower than it is tall, and a run of them on one shelf is not. + const panel = readPng(at(SHELVES)); + for (const [i, book] of S.SHELF_BOOKS.entries()) { + const w = book.w * panel.width; + const h = book.h * panel.height; + assert.ok(w < h * 0.75, `volume ${i} is ${w.toFixed(0)}×${h.toFixed(0)} — a run, not a book`); + } +}); + +test('one archived commission darkens exactly one spine', () => { + const A = loadTs('src/renderer/src/scene/study/ShelfArchive.tsx'); + const view = { x: 0, y: 0, w: 1568, h: 672 }; + const one = [{ id: 'T-7', label: 'the seventh folio', kind: 'commission', at: null }]; + const marks = all(A.ShelfArchive({ books: one, panelSrc: 'shelves.png', view }), + (n) => n.props?.['data-shelf-book'] !== undefined); + assert.equal(marks.length, 1, `one commission made ${marks.length} marks`); + const { left, top, width, height } = marks[0].props.style; + const spine = S.SHELF_BOOKS[0]; + assert.deepEqual([left, top, width, height], + [spine.x * view.w, spine.y * view.h, spine.w * view.w, spine.h * view.h], + 'the mark is not the first painted spine'); +}); + test('no mark darkens a lamp', () => { // The lamps are what light the wall. A lamp shade taken down with the books // beside it reads as the light going out, not as the shelf filling. @@ -236,7 +279,8 @@ test('the wall fills the way a wall of books does: top shelf first, left to righ for (let i = 1; i < S.SHELF_BOOKS.length; i++) { const a = S.SHELF_BOOKS[i - 1]; const b = S.SHELF_BOOKS[i]; - const sameShelf = Math.abs(a.y - b.y) < 0.02; + // One shelf's spines stand on one ledge, so they share a foot, not a top. + const sameShelf = Math.abs((a.y + a.h) - (b.y + b.h)) < 0.02; assert.ok(sameShelf ? b.x > a.x : b.y > a.y, `volume ${i} is handed out before the one to its left or on the shelf above`); } @@ -562,9 +606,9 @@ test('the number fits on every volume the wall can mark', () => { } }); -test('the number is pasted on one spine of a bay, not across the whole run', async () => { - // A mark is a run of books, and a label as wide as the run is a plate across - // several spines with a glyph taller than any book on the wall. +test('the number is pasted on its spine, never as a plate wider than a book', async () => { + // The wall's spines are not one thickness, and a label as wide as a thick + // one is a plate with a glyph taller than any book on the wall. const A = loadTs('src/renderer/src/scene/study/ShelfArchive.tsx'); const view = { x: 0, y: 0, w: 1568, h: 672 }; const rendered = A.ShelfArchive({ @@ -581,7 +625,7 @@ test('the number is pasted on one spine of a bay, not across the whole run', asy assert.ok(width <= box.height / 3, `the label is ${width.toFixed(0)}px wide on a shelf ${box.height.toFixed(0)}px tall`); assert.ok(plate.props.style.left >= 0 && plate.props.style.left + width <= box.width + 0.01, - 'the label hangs off the bay'); + 'the label hangs off the spine'); }); test('the number is set from the spine, not from a type token', () => {