TeX/src/env.c

325 lines
6.7 KiB
C

#include <TeX/node.h>
#include <TeX/flow.h>
#include <TeX/env.h>
#include <TeX/vector.h>
#include <stdlib.h>
#include <string.h>
#define fill_prototype(env, prefix) { \
env->free = prefix ## _free; \
env->add_node = prefix ## _add_node; \
env->add_separator = prefix ## _add_separator; \
env->add_break = prefix ## _add_break; \
env->layout = prefix ## _layout; \
env->render = prefix ## _render; \
}
//---
// Primary environment: a simple flow
//
// This environment ignores separators and breaks and concatenates all
// nodes in a single flow. It is the type of the outermost environment of
// any formula.
//---
struct TeX_Env_Primary
{
struct TeX_Env env;
struct TeX_Flow *flow;
};
static void primary_free(struct TeX_Env *env)
{
struct TeX_Env_Primary *p = (void *)env;
TeX_flow_free(p->flow);
free(env);
}
static void primary_add_node(struct TeX_Env *env, struct TeX_Node *node)
{
struct TeX_Env_Primary *p = (void *)env;
p->flow = TeX_flow_add_node(p->flow, node);
}
static void primary_add_separator(__attribute__((unused)) struct TeX_Env *env)
{
}
static void primary_add_break(__attribute__((unused)) struct TeX_Env *env)
{
}
static void primary_layout(struct TeX_Env *env)
{
struct TeX_Env_Primary *p = (void *)env;
TeX_flow_layout(p->flow);
env->width = p->flow->width;
env->height = p->flow->height;
env->line = p->flow->line;
}
static void primary_render(struct TeX_Env *env, int x, int y, int color)
{
struct TeX_Env_Primary *p = (void *)env;
TeX_flow_render(p->flow, x, y, color);
}
struct TeX_Env *TeX_env_primary(void)
{
struct TeX_Env_Primary *p = malloc(sizeof *p);
struct TeX_Env *env = (void *)p;
env->name = "primary";
fill_prototype(env, primary);
p->flow = NULL;
return env;
}
//---
// Matrix environment.
//---
struct TeX_Env_Matrix
{
struct TeX_Env env;
vector_type(struct TeX_Flow *, elements);
vector_type(int, rowstart);
int *rowdepth;
int *rowline;
int *colwidth;
int rows;
int cols;
};
static void matrix_free(struct TeX_Env *env)
{
struct TeX_Env_Matrix *m = (void *)env;
free(m->elements);
free(m->rowstart);
free(m->rowdepth);
free(m->rowline);
free(m->colwidth);
free(env);
}
static void matrix_add_node(struct TeX_Env *env, struct TeX_Node *node)
{
struct TeX_Env_Matrix *m = (void *)env;
int last = m->elements_len - 1;
m->elements[last] = TeX_flow_add_node(m->elements[last], node);
}
static void matrix_add_separator(struct TeX_Env *env)
{
struct TeX_Env_Matrix *m = (void *)env;
/* Create a new flow at the end of the element array */
vector_append(m->elements, NULL);
/* Current row length is automatically increased. Set column count */
int row_length = m->elements_len - m->rowstart[m->rows - 1];
m->cols = max(m->cols, row_length);
}
static void matrix_add_break(struct TeX_Env *env)
{
struct TeX_Env_Matrix *m = (void *)env;
/* First add a new flow */
vector_append(m->elements, NULL);
/* Then add a row and its start index */
vector_append(m->rowstart, m->elements_len - 1);
m->rows++;
}
static void matrix_layout(struct TeX_Env *env)
{
struct TeX_Env_Matrix *m = (void *)env;
int row = 0;
int col = 0;
env->width = 0;
env->height = 0;
env->line = 0;
m->rowdepth = calloc(m->rows, sizeof *m->rowdepth);
m->rowline = calloc(m->rows, sizeof *m->rowline);
m->colwidth = calloc(m->cols, sizeof *m->colwidth);
if(!m->rowdepth || !m->rowline || !m->colwidth) return;
/* Compute the layout of the grid */
for(int i = 0; i < m->elements_len; i++)
{
if(row + 1 < m->rows && i == m->rowstart[row + 1])
{
col = 0;
row++;
}
struct TeX_Flow *f = m->elements[i];
TeX_flow_layout(f);
/* Update the current row and column */
m->rowline[row] = max(m->rowline[row], f->line);
m->rowdepth[row] = max(m->rowdepth[row], f->height - f->line);
m->colwidth[col] = max(m->colwidth[col], f->width);
col++;
}
/* TODO: Add width spacing and height spacing */
for(int i = 0; i < m->rows; i++)
{
env->height += m->rowline[i] + m->rowdepth[i];
}
for(int j = 0; j < m->cols; j++)
{
env->width += m->colwidth[j];
}
env->width += TEX_MATRIX_COL_SPACING * (m->cols - 1);
env->height += TEX_MATRIX_ROW_SPACING * (m->rows - 1);
env->line = env->height >> 1;
}
static void matrix_render(struct TeX_Env *env, int x, int y, int color)
{
struct TeX_Env_Matrix *m = (void *)env;
int n = 0, dy = 0;
for(int row = 0; row < m->rows; row++)
{
int col = 0, dx = 0;
while((row + 1 < m->rows && n < m->rowstart[row + 1]) ||
(row + 1 >= m->rows && n < m->elements_len))
{
struct TeX_Flow *flow = m->elements[n++];
int cw = m->colwidth[col];
int rh = m->rowline[row] + m->rowdepth[row];
TeX_flow_render(flow,
x + dx + ((cw - flow->width) >> 1),
y + dy + ((rh - flow->height) >> 1),
color);
dx += cw + TEX_MATRIX_COL_SPACING;
col++;
}
dy += m->rowline[row]+m->rowdepth[row]+TEX_MATRIX_ROW_SPACING;
}
}
struct TeX_Env *TeX_env_matrix(void)
{
struct TeX_Env_Matrix *m = malloc(sizeof *m);
struct TeX_Env *env = (void *)m;
env->name = "matrix";
fill_prototype(env, matrix);
vector_init(m->elements);
vector_init(m->rowstart);
m->rowdepth = NULL;
m->rowline = NULL;
m->colwidth = NULL;
vector_extend(m->elements);
m->elements[0] = NULL;
m->elements_len = 1;
vector_extend(m->rowstart);
m->rowstart[0] = 0;
m->rowstart_len = 1;
m->rows = 1;
m->cols = 0;
return env;
}
//---
// Environment printers
//---
#ifdef TEX_PRINT
#include <stdio.h>
#include <TeX/print.h>
/* TeX_print_env_primary(): Print a primary environment to stdout */
void TeX_print_env_primary(struct TeX_Env *env, int indent)
{
struct TeX_Env_Primary *p = (void *)env;
printf("%*senv:primary", indent, "");
printf(GRAY " %dx%d,%d" END "\n", env->width, env->height, env->line);
TeX_print_flow(p->flow, indent + 4);
}
/* TeX_print_env_matrix(): Print a matrix environment to stdout */
void TeX_print_env_matrix(struct TeX_Env *env, int indent)
{
struct TeX_Env_Matrix *m = (void *)env;
printf("%*senv:matrix %dx%d", indent, "", m->rows, m->cols);
printf(GRAY " %dx%d,%d", env->width, env->height, env->line);
for(int i = 0; i < m->cols; i++)
{
printf("%c%d", (i ? ';' : ' '), m->colwidth[i]);
}
printf(END "\n");
int row = 0;
for(int i = 0; i < m->elements_len; i++)
{
if(row < m->rows && i == m->rowstart[row])
{
printf("%*s%d: " GRAY "%d,%d" END "\n", indent + 4, "",
row, m->rowline[row] + m->rowdepth[row],
m->rowline[row]);
row++;
}
TeX_print_flow(m->elements[i], indent + 8);
}
}
/* TeX_print_env(): Recursively print an environment */
void TeX_print_env(struct TeX_Env *env, int indent)
{
if(!strcmp(env->name, "primary"))
{
TeX_print_env_primary(env, indent);
}
else if(!strcmp(env->name, "matrix"))
{
TeX_print_env_matrix(env, indent);
}
else
{
printf("%*senv:%s (?)\n", indent, "", env->name);
}
}
#endif /* TEX_PRINT */