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.

flow.c 9.7KB

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