diff options
Diffstat (limited to 'graphics/sketch/opts.c')
-rw-r--r-- | graphics/sketch/opts.c | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/graphics/sketch/opts.c b/graphics/sketch/opts.c new file mode 100644 index 0000000000..72983c25aa --- /dev/null +++ b/graphics/sketch/opts.c @@ -0,0 +1,624 @@ +/* opts.c + Copyright (C) 2005,2006,2007 Eugene K. Ressler, Jr. + +This file is part of Sketch, a small, simple system for making +3d drawings with LaTeX and the PSTricks or TikZ package. + +Sketch is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3, or (at your option) +any later version. + +Sketch is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Sketch; see the file COPYING.txt. If not, see +http://www.gnu.org/copyleft */ + +#include "opts.h" +#include "geometry.h" + +DECLARE_DYNAMIC_ARRAY_FUNCS (OPT_LIST, OPT, opt_list, elt, n_elts, NO_OTHER_INIT) + +// ---- useful string stuff ---------------------------------------------------- +// slice a string using Perl/Python position indexing conventions +// (position == dst_size is always at the end of the string) + +char *str_slice (char *dst, int dst_size, char *src, int beg, int end) +{ + int len; + + if (dst_size > 0) + { + len = strlen (src); + if (beg < 0) + beg = len + beg; + else if (beg > len) + beg = len; + if (end < 0) + end = len + end; + else if (end > len) + end = len; + len = end - beg; + if (len <= 0) + { + dst[0] = '\0'; + } + else + { + if (len >= dst_size) + len = dst_size - 1; + memcpy (dst, &src[beg], len); + dst[len] = '\0'; + } + } + return dst; +} + +// a modified version of C library strtok +// uses state variable p, which should be +// initially set to zero. +char * +istrtok (int *p, char *s, char sep) +{ + int i, r; + + // advance r to next non-space character + for (r = *p; s[r] == ' ' || s[r] == '\t'; r++) + /* skip */ ; + + // if we're at terminating null, return null + if (s[r] == '\0') + { + *p = r; + return NULL; + } + // look for a separator character + for (i = r; s[i] != '\0'; i++) + { + if (s[i] == sep) + { + // found one; set to null char, + // advance state variable, and + // return pointer to first char + s[i] = '\0'; + *p = i + 1; + return &s[r]; + } + } + // did not find a terminator, so this + // is the last token; return it + *p = i; + return &s[r]; +} + +int +str_last_occurance (char *src, char *set) +{ + int i; + + for (i = 0; src[i]; i++) + /* skip */ ; + for (--i; i >= 0 && !strchr (set, src[i]); --i) + /* skip */ ; + return i; +} + +// ---- options ---------------------------------------------------------------- + +void +init_opts (OPTS * opts) +{ + init_opt_list (opts->list); +} + +OPTS * +raw_opts (void) +{ + OPTS *r = safe_malloc (sizeof *r); + init_opts (r); + return r; +} + +void +setup_opts (OPTS * opts, char *opts_str, SRC_LINE line) +{ + int p_pair, p_side; + char *pair, *key, *val, *buf; + OPT *opt; + + clear_opts (opts); + buf = safe_strdup (opts_str); + p_pair = 0; + while ((pair = istrtok (&p_pair, buf, ',')) != NULL) + { + p_side = 0; + key = istrtok (&p_side, pair, '='); + if (key == NULL) + { + err (line, "null keyword in option"); + key = ""; + } + val = istrtok (&p_side, pair, ','); + if (val == NULL) + { + err (line, "null value in option"); + val = ""; + } + opt = pushed_opt_list_elt (opts->list); + opt->key = safe_strdup (key); + opt->val = safe_strdup (val); + } + safe_free (buf); +} + +OPTS * +new_opts (char *opts_str, SRC_LINE line) +{ + OPTS *r = raw_opts (); + setup_opts (r, opts_str, line); + return r; +} + +void +clear_opts (OPTS * opts) +{ + int i; + + for (i = 0; i < opts->list->n_elts; ++i) + { + safe_free (opts->list->elt[i].key); + safe_free (opts->list->elt[i].val); + } + clear_opt_list (opts->list); +} + +char * +opt_val (OPTS * opts, char *opt) +{ + int i; + + if (!opts) + return 0; + + for (i = 0; i < opts->list->n_elts; i++) + if (strcmp (opts->list->elt[i].key, opt) == 0) + return opts->list->elt[i].val; + return NULL; +} + +int +bool_opt_p (OPTS * opts, char *opt, int default_p) +{ + char *r = opt_val (opts, opt); + if (!r) + return default_p; + return strcmp (r, "false") != 0; // all not false is true +} + +typedef struct opt_desc_t +{ + char *opt; + int type; +} +OPT_DESC; + +typedef struct opt_desc_tbl_t +{ + OPT_DESC *key_desc; + int n_key_desc; + OPT_DESC *val_desc; + int n_val_desc; +} +OPT_DESC_TBL; + +static OPT_DESC key_tbl_pst[] = { + {"arrows", OPT_LINE}, + {"cull", OPT_INTERNAL}, + {"dash", OPT_LINE}, + {"dotsep", OPT_LINE}, + {"fillcolor", OPT_POLYGON | OPT_FILL_COLOR}, + {"fillstyle", OPT_POLYGON | OPT_FILL_STYLE}, + {"lay", OPT_INTERNAL}, + {"linecolor", OPT_LINE}, + {"linestyle", OPT_LINE | OPT_LINE_STYLE}, + {"linewidth", OPT_LINE}, + {"opacity", OPT_POLYGON}, + {"showpoints", OPT_LINE | OPT_POLYGON}, + {"split", OPT_INTERNAL}, + {"strokeopacity", OPT_LINE }, + {"transpalpha", OPT_POLYGON} +}; + +OPT_DESC_TBL opt_desc_tbl_pst[1] = { { + key_tbl_pst, ARRAY_SIZE (key_tbl_pst), + NULL, 0} +}; + +static OPT_DESC opt_key_tbl_tikz[] = { + {"arrows", OPT_LINE}, + {"cap", OPT_LINE}, + {"color", OPT_LINE | OPT_POLYGON | OPT_FILL_COLOR}, + {"cull", OPT_INTERNAL}, + {"dash pattern", OPT_LINE}, + {"dash phase", OPT_LINE}, + {"double distance", OPT_LINE}, + {"draw", OPT_LINE | OPT_LINE_STYLE}, + {"draw opacity", OPT_LINE}, + {"fill", OPT_POLYGON | OPT_FILL_COLOR}, + {"fill opacity", OPT_POLYGON}, + {"fill style", OPT_POLYGON | OPT_FILL_COLOR | OPT_EMIT_VAL}, + {"join", OPT_LINE}, + {"lay", OPT_INTERNAL}, + {"line style", OPT_LINE | OPT_EMIT_VAL}, + {"line width", OPT_LINE}, + {"miter limit", OPT_LINE}, + {"pattern", OPT_POLYGON | OPT_FILL_COLOR}, + {"pattern color", OPT_POLYGON}, + {"split", OPT_INTERNAL}, + {"style", OPT_TYPE_IN_VAL | OPT_EMIT_VAL}, +}; + +static OPT_DESC opt_val_tbl_tikz[] = { + {"dashed", OPT_LINE}, + {"densely dashed", OPT_LINE}, + {"densely dotted", OPT_LINE}, + {"dotted", OPT_LINE}, + {"double", OPT_LINE}, + {"loosely dashed", OPT_LINE}, + {"loosely dotted", OPT_LINE}, + {"nearly opaque", OPT_POLYGON}, + {"nearly transparent", OPT_POLYGON}, + {"semithick", OPT_LINE}, + {"semitransparent", OPT_POLYGON}, + {"solid", OPT_LINE}, + {"thick", OPT_LINE}, + {"thin", OPT_LINE}, + {"transparent", OPT_POLYGON}, + {"ultra nearly transparent", OPT_POLYGON}, + {"ultra thick", OPT_LINE}, + {"ultra thin", OPT_LINE}, + {"very nearly transparent", OPT_POLYGON}, + {"very thick", OPT_LINE}, + {"very thin", OPT_LINE}, +}; + +OPT_DESC_TBL opt_desc_tbl_tikz[1] = { { + opt_key_tbl_tikz, + ARRAY_SIZE (opt_key_tbl_tikz), + opt_val_tbl_tikz, + ARRAY_SIZE (opt_val_tbl_tikz), + } +}; + +int +opt_index (char *opt, OPT_DESC * desc, int n_desc) +{ + int hi, lo, mid, cmp_val; + + hi = n_desc - 1; + lo = 0; + while (hi >= lo) + { + mid = (hi + lo) / 2; + cmp_val = strcmp (opt, desc[mid].opt); + if (cmp_val < 0) + hi = mid - 1; + else if (cmp_val > 0) + lo = mid + 1; + else + return mid; + } + return -1; +} + +static OPT_DESC_TBL *lang_to_opt_desc_tbl[] = { + opt_desc_tbl_pst, + opt_desc_tbl_tikz, + opt_desc_tbl_pst, + opt_desc_tbl_tikz, +}; + +int +simple_opt_type (OPT * opt, int default_type, int lang) +{ + OPT_DESC_TBL *desc; + int i; + + if (lang < 0) + return default_type; + desc = lang_to_opt_desc_tbl[lang]; + i = opt_index (opt->key, desc->key_desc, desc->n_key_desc); + return (i < 0) ? default_type : desc->key_desc[i].type; +} + +int +opt_type (OPT * opt, int default_type, int lang) +{ + OPT_DESC_TBL *desc; + int i, type; + + type = simple_opt_type (opt, default_type, lang); + if (type & OPT_TYPE_IN_VAL) + { + desc = lang_to_opt_desc_tbl[lang]; + i = opt_index (opt->val, desc->val_desc, desc->n_val_desc); + if (i < 0) + return default_type; + type = desc->val_desc[i].type; + } + return type; +} + +typedef struct opts_desc_t +{ + OPT *opts; + int n_opts; +} +OPTS_DESC; + +OPT no_edges_opts_pst[] = { + {"linestyle", "none"} +}; + +OPT no_edges_opts_tikz[] = { + {"draw", "none"} +}; + +OPTS_DESC no_edges_opts_desc_tbl[] = { + {no_edges_opts_pst, ARRAY_SIZE (no_edges_opts_pst)}, + {no_edges_opts_tikz, ARRAY_SIZE (no_edges_opts_tikz)}, + {no_edges_opts_pst, ARRAY_SIZE (no_edges_opts_pst)}, + {no_edges_opts_tikz, ARRAY_SIZE (no_edges_opts_tikz)}, +}; + +static int +any_opt_p (OPTS * opts, int type, int lang) +{ + int i; + + if (!opts) + return 0; + for (i = 0; i < opts->list->n_elts; i++) + if (type & opt_type (&opts->list->elt[i], OPT_NONE, lang)) + return 1; + return 0; +} + +static void +add_default_opt (OPTS ** opts_ptr, OPT * default_opt, int lang) +{ + OPT *opt; + OPTS *opts; + int default_type; + + opts = *opts_ptr; + default_type = opt_type (default_opt, OPT_NONE, lang) & OPT_DEFAULTS; + if (any_opt_p (opts, default_type, lang)) + return; + if (!opts) + opts = raw_opts (); + opt = pushed_opt_list_elt (opts->list); + opt->key = safe_strdup (default_opt->key); + opt->val = safe_strdup (default_opt->val); + *opts_ptr = opts; +} + +static void +add_default_opts (OPTS ** opts_ptr, OPTS_DESC * opts_desc, int lang) +{ + int i; + for (i = 0; i < opts_desc->n_opts; i++) + add_default_opt (opts_ptr, &opts_desc->opts[i], lang); +} + +void +add_no_edges_default_opt (OPTS ** opts_ptr, int lang) +{ + add_default_opts (opts_ptr, &no_edges_opts_desc_tbl[lang], lang); +} + +OPT solid_white_opts_pst[] = { + {"fillstyle", "solid"}, + {"fillcolor", "white"} +}; + +OPT solid_white_opts_tikz[] = { + {"fill", "white"} +}; + +OPTS_DESC solid_white_opts_desc_tbl[] = { + {solid_white_opts_pst, ARRAY_SIZE (solid_white_opts_pst)}, + {solid_white_opts_tikz, ARRAY_SIZE (solid_white_opts_tikz)}, + {solid_white_opts_pst, ARRAY_SIZE (solid_white_opts_pst)}, + {solid_white_opts_tikz, ARRAY_SIZE (solid_white_opts_tikz)}, +}; + +void +add_solid_white_default_opt (OPTS ** opts_ptr, int lang) +{ + add_default_opts (opts_ptr, &solid_white_opts_desc_tbl[lang], lang); +} + +void +check_opts (OPTS * opts, + int allowed, char *allowed_msg, int lang, SRC_LINE line) +{ + int i, type; + + if (!opts) + return; + + for (i = 0; i < opts->list->n_elts; i++) + { + type = opt_type (&opts->list->elt[i], OPT_NONE, lang); + if ((type & allowed) == 0) + warn (line, allowed_msg, opts->list->elt[i].key, + opts->list->elt[i].val); + } +} + +// selective copy for splitting option lists by type +OPTS * +copy_opts (OPTS * opts, int type_mask, int lang) +{ + int i; + OPTS *r; + OPT *opt; + + if (!opts) + return NULL; + + r = raw_opts (); + for (i = 0; i < opts->list->n_elts; i++) + if (type_mask & opt_type (&opts->list->elt[i], OPT_NONE, lang)) + { + opt = pushed_opt_list_elt (r->list); + opt->key = safe_strdup (opts->list->elt[i].key); + opt->val = safe_strdup (opts->list->elt[i].val); + } + return r; +} + +OPTS * +cat_opts (OPTS * dst, OPTS * src) +{ + int i; + OPT *opt; + + for (i = 0; i < src->list->n_elts; i++) + { + opt = pushed_opt_list_elt (dst->list); + opt->key = safe_strdup(src->list->elt[i].key); + opt->val = safe_strdup(src->list->elt[i].val); + } + return dst; +} + +// selective copy for splitting out line options and modifying arrows +OPTS * +copy_line_opts (OPTS * opts, int first_p, int last_p, int lang) +{ + int i; + OPTS *r; + char buf[100]; + + if (!opts) + return NULL; + + // no modifications necessary if line contains first and last points + if (first_p && last_p) + return opts; + + // make a clean copy and modify the arrows + r = copy_opts (opts, OPT_LINE, lang); + + for (i = 0; i < r->list->n_elts; i++) + { + if (strcmp ("arrows", r->list->elt[i].key) == 0) + { + char *val = r->list->elt[i].val; + char *dash = strchr (val, '-'); + if (!dash) + { + warn (no_line, + "could not find '-' while splitting arrows option"); + continue; + } + if (first_p) + { + str_slice (buf, sizeof buf, val, 0, dash - val + 1); + } + else if (last_p) + { + str_slice (buf, sizeof buf, val, dash - val, SLICE_TO_END); + } + else + { + // could just delete option entirely, but this is good for debugging + str_slice (buf, sizeof buf, val, dash - val, dash - val + 1); + } + r->list->elt[i].val = safe_strdup (buf); + } + } + return r; +} + +static int member_p(char *str, char **str_list) +{ + if (str_list == NULL) + return 0; + + while (*str_list) + { + if (strcmp(str, *str_list) == 0) + return 1; + ++str_list; + } + return 0; +} + +static void +emit_opts_internal (FILE * f, OPTS * opts, char ** exceptions, int brackets_p, int lang) +{ + int i, n, type; + + // do nothing if no options + if (!opts || !opts->list || opts->list->n_elts == 0) + return; + + // do nothing if no non-excepted options + for (n = i = 0; i < opts->list->n_elts; i++) + { + if ( !member_p(opts->list->elt[i].key, exceptions) ) + ++n; + } + if (n == 0) + return; + + if (brackets_p) + fputc ('[', f); + + for (n = i = 0; i < opts->list->n_elts; i++) + { + + if ( member_p(opts->list->elt[i].key, exceptions) ) + continue; + + type = simple_opt_type (&opts->list->elt[i], OPT_NONE, lang); + if ((type & OPT_INTERNAL) == 0) + { + if (n > 0) + fprintf (f, ","); + if (type & OPT_EMIT_VAL) + fprintf (f, "%s", opts->list->elt[i].val); + else + fprintf (f, "%s=%s", opts->list->elt[i].key, + opts->list->elt[i].val); + ++n; + } + } + if (brackets_p) + fputc (']', f); +} + +void +emit_opts_raw (FILE * f, OPTS * opts, int lang) +{ + emit_opts_internal (f, opts, NULL, 0, lang); +} + +void +emit_opts (FILE * f, OPTS * opts, int lang) +{ + emit_opts_internal (f, opts, NULL, 1, lang); +} + +void +emit_opts_with_exceptions (FILE * f, OPTS * opts, char ** exceptions, int lang) +{ + emit_opts_internal (f, opts, exceptions, 1, lang); +} |