fx9860g and fxcg50 2D math rendering library with support for TeX syntax.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

360 lines
8.4KB

  1. #include <TeX/TeX.h>
  2. #include <TeX/structure.h>
  3. #include <TeX/render.h>
  4. #include <string.h>
  5. //---
  6. // Chunks to place subscripts and superscripts
  7. //---
  8. #define TEX_CHUNK_INLINE 0
  9. #define TEX_CHUNK_DISPLAY 1
  10. #define TEX_CHUNK_SLANTED 2
  11. /* struct chunk: Chunk of nodes positioned relative to each other
  12. The notion of "chunk" comprises a base with a subscript and a superscript.
  13. Together they can be arranged in three different ways:
  14. * TEX_CHUNK_INLINE:
  15. Normal inline setting, subscript and superscript are placed to the right
  16. of the base and moved below and above the baseline.
  17. * TEX_CHUNK_DISPLAY:
  18. Similar to LaTeX's display style, subscript and superscript are placed
  19. under and above the base, and are centered horizontally.
  20. * TEX_CHUNK_SLANTED:
  21. Similar to inline mode, but subscript is slightly moved to the left and
  22. superscript is slightly moved to the right. Looks good on integrals.
  23. The user of a chunk sets the [x] and [l] members of the [base] node, then
  24. selects the display mode and optionally adds subscript and superscript. When
  25. the chunk_finalize() function is called, the position of the elements is
  26. computed and the [x] and [l] members of the [sub] and [sup] nodes, as well
  27. as the geometry of the chunk, are set. */
  28. struct chunk
  29. {
  30. /* Base node, baseline aligned on the flow */
  31. struct TeX_Node *base;
  32. /* Subscript and superscript (might both be NULL) */
  33. struct TeX_Node *sub, *sup;
  34. /* Display mode */
  35. int mode;
  36. /* Chunk geometry */
  37. int width;
  38. int height;
  39. int line;
  40. };
  41. /* chunk_make(): Make a new chunk for a base node */
  42. void chunk_make(struct chunk *chunk, struct TeX_Node *base, int mode)
  43. {
  44. chunk->base = base;
  45. chunk->mode = mode;
  46. chunk->sub = NULL;
  47. chunk->sup = NULL;
  48. chunk->width = 0;
  49. chunk->height = 0;
  50. chunk->line = 0;
  51. }
  52. /* chunk_reset(): Empty a chunk */
  53. void chunk_reset(struct chunk *chunk)
  54. {
  55. memset(chunk, 0, sizeof *chunk);
  56. }
  57. /* chunk_set(): Check whether a chunk is empty */
  58. int chunk_set(struct chunk *chunk)
  59. {
  60. return chunk->base != NULL;
  61. }
  62. /* chunk_set_subscript(): Add a subscript on a chunk */
  63. void chunk_set_subscript(struct chunk *chunk, struct TeX_Node *sub)
  64. {
  65. /* Silently refuse double index */
  66. if(chunk->sub) return;
  67. chunk->sub = sub;
  68. }
  69. /* chunk_set_superscript(): Add a superscript on a chunk */
  70. void chunk_set_superscript(struct chunk *chunk, struct TeX_Node *sup)
  71. {
  72. /* Silently refuse double exponent */
  73. if(chunk->sup) return;
  74. chunk->sup = sup;
  75. }
  76. /* chunk_compute_inline(): Compute chunk layout for inline mode */
  77. void chunk_compute_inline(struct chunk *chunk)
  78. {
  79. struct TeX_Node *base = chunk->base;
  80. struct TeX_Node *sup = chunk->sup;
  81. struct TeX_Node *sub = chunk->sub;
  82. /* Height over and below baseline */
  83. int over = base->line;
  84. int under = base->height - base->line;
  85. /* Additional width */
  86. int right = 0;
  87. if(sub)
  88. {
  89. int elevation = TEX_SUBSCRIPT_ELEVATION;
  90. sub->x = base->x + base->width + TEX_LAYOUT_SPACING;
  91. sub->l = base->l + (base->height - base->line + sub->line -
  92. elevation);
  93. under = max(under, under + sub->height - elevation);
  94. right = max(right, sub->width);
  95. }
  96. if(sup)
  97. {
  98. int depth = TEX_SUPERSCRIPT_DEPTH;
  99. sup->x = base->x + base->width + TEX_LAYOUT_SPACING;
  100. sup->l = base->l - (base->line + (sup->height - sup->line) -
  101. depth);
  102. over = max(over, over + sup->height - depth);
  103. right = max(right, sup->width);
  104. }
  105. if(right > 0) right += TEX_LAYOUT_SPACING;
  106. chunk->width = base->width + right;
  107. chunk->height = over + under;
  108. chunk->line = over;
  109. }
  110. /* chunk_compute_display(): Compute chunk layout for display mode */
  111. void chunk_compute_display(struct chunk *chunk)
  112. {
  113. struct TeX_Node *base = chunk->base;
  114. struct TeX_Node *sup = chunk->sup;
  115. struct TeX_Node *sub = chunk->sub;
  116. int spacing = TEX_LAYOUT_SPACING;
  117. /* Chunk width and height */
  118. chunk->width = base->width;
  119. chunk->height = base->height;
  120. chunk->line = base->line;
  121. if(sub)
  122. {
  123. chunk->width = max(chunk->width, sub->width);
  124. chunk->height += sub->height + spacing;
  125. }
  126. if(sup)
  127. {
  128. chunk->width = max(chunk->width, sup->width);
  129. chunk->height += sup->height + spacing;
  130. chunk->line += sup->height + spacing;
  131. }
  132. /* Now that the dimensions are clear, compute the position */
  133. if(sub)
  134. {
  135. sub->l += sub->line + (base->height - base->line) + spacing;
  136. sub->x = base->x + ((chunk->width - sub->width) >> 1);
  137. }
  138. if(sup)
  139. {
  140. sup->l -= (sup->height - sup->line) + base->line + spacing;
  141. sup->x = base->x + ((chunk->width - sup->width) >> 1);
  142. }
  143. base->x += (chunk->width - base->width) >> 1;
  144. }
  145. /* chunk_compute_slanted(): Compute chunk layout for slanted mode */
  146. void chunk_compute_slanted(struct chunk *chunk)
  147. {
  148. /* TODO: Slanted mode for exponents and subscripts */
  149. chunk_compute_inline(chunk);
  150. }
  151. /* chunk_compute(): Compute the layout of a chunk after it's filled */
  152. void chunk_compute(struct chunk *chunk)
  153. {
  154. if(chunk->mode == TEX_CHUNK_INLINE)
  155. {
  156. chunk_compute_inline(chunk);
  157. }
  158. if(chunk->mode == TEX_CHUNK_DISPLAY)
  159. {
  160. chunk_compute_display(chunk);
  161. }
  162. if(chunk->mode == TEX_CHUNK_SLANTED)
  163. {
  164. chunk_compute_slanted(chunk);
  165. }
  166. }
  167. //---
  168. // Groups to compute the size of parenthesis-like elements
  169. //---
  170. struct group
  171. {
  172. /* Opening parenthesis/bracket/whatever */
  173. struct TeX_Node *left;
  174. /* Maximum height above and below baseline */
  175. int above;
  176. int below;
  177. };
  178. /* group_open(): Create new group initialized with a left node */
  179. void group_open(struct group *group, struct TeX_Node *left)
  180. {
  181. group->left = left;
  182. group->above = 0;
  183. group->below = 0;
  184. }
  185. /* group_update(): Update a group with a new node */
  186. void group_update(struct group *group, struct TeX_Node *node)
  187. {
  188. /* Account for node displacement around baseline */
  189. group->above = max(node->line - node->l, group->above);
  190. group->below = max(node->height - node->line + node->l, group->below);
  191. }
  192. /* group_close(): Close a group with its right node */
  193. void group_close(struct group *group, struct TeX_Node *right)
  194. {
  195. struct TeX_Node *left = group->left;
  196. int above = group->above;
  197. int below = group->below;
  198. int line = above;
  199. #if TEX_LEFTRIGHT_SYMMETRICAL
  200. above = below = max(above, below);
  201. #endif
  202. #if ! TEX_LEFTRIGHT_ALIGNED
  203. line = (above + below) >> 1;
  204. #endif
  205. /* Set the height of the left and right node */
  206. left->height = above + below;
  207. left->line = line;
  208. left->l += (line - above);
  209. right->height = above + below;
  210. right->line = line;
  211. right->l += (line - above);
  212. }
  213. //---
  214. // Laying out flows
  215. //---
  216. static void update(struct chunk *c, int *x, int *above, int *below)
  217. {
  218. if(!chunk_set(c)) return;
  219. chunk_compute(c);
  220. *x += c->width + TEX_LAYOUT_SPACING;
  221. *above = max(*above, c->line - c->base->l);
  222. *below = max(*below, c->height - c->line + c->base->l);
  223. chunk_reset(c);
  224. }
  225. /* size_flow(): Calculate the layout of a flow */
  226. void size_flow(struct TeX_Flow *flow)
  227. {
  228. /* Current node and current chunk */
  229. struct TeX_Node *node;
  230. struct chunk c = { NULL };
  231. /* Position in flow */
  232. int x = 0;
  233. /* Height above baseline (excluded), and below baseline (included) */
  234. int above = 0;
  235. int below = 0;
  236. /* Nested groups and current position */
  237. struct group groups[TEX_LEFTRIGHT_NESTING];
  238. struct group *g = groups;
  239. for(node = flow->first; node; node = node->next)
  240. {
  241. char const *class = "";
  242. size_node(node);
  243. group_update(g, node);
  244. if(node->type != TEX_NODECLASS_TEXT)
  245. class = TeX_table[node->type - 1].name;
  246. if(!strcmp(class, "\\sup"))
  247. {
  248. chunk_set_superscript(&c, node);
  249. continue;
  250. }
  251. if(!strcmp(class, "\\sub"))
  252. {
  253. chunk_set_subscript(&c, node);
  254. continue;
  255. }
  256. if(!strcmp(class, "left") && g-groups < TEX_LEFTRIGHT_NESTING)
  257. {
  258. g++;
  259. group_open(g, node);
  260. group_update(g, node);
  261. }
  262. if(!strcmp(class, "right") && g - groups > 0)
  263. {
  264. group_close(g, node);
  265. g--;
  266. }
  267. /* Leave the last chunk and make a new one */
  268. update(&c, &x, &above, &below);
  269. int mode = TEX_CHUNK_INLINE;
  270. if(!strcmp(class, "sum") || !strcmp(class, "prod")
  271. #if TEX_INT_DISPLAY
  272. || !strcmp(class, "int")
  273. #endif
  274. )
  275. {
  276. mode = TEX_CHUNK_DISPLAY;
  277. }
  278. node->x = x;
  279. chunk_make(&c, node, mode);
  280. }
  281. /* Finish the last chunk */
  282. update(&c, &x, &above, &below);
  283. flow->height = above + below;
  284. flow->line = above;
  285. flow->width = max(x - TEX_LAYOUT_SPACING, 0);
  286. }
  287. //---
  288. // Rendering flows
  289. //---
  290. /* render_flow() - Render a flow and all its components */
  291. void render_flow(struct TeX_Flow const * flow, int x, int y, int color)
  292. {
  293. struct TeX_Node const * node;
  294. for(node = flow->first; node; node = node->next)
  295. {
  296. render_node(node, x + node->x, y + flow->line - node->line +
  297. node->l, color);
  298. }
  299. }