/* GTS - Library for the manipulation of triangulated surfaces * Copyright (C) 1999 Stéphane Popinet * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "gts.h" #include "gts-private.h" #include "config.h" const guint gts_major_version = GTS_MAJOR_VERSION; const guint gts_minor_version = GTS_MINOR_VERSION; const guint gts_micro_version = GTS_MICRO_VERSION; const guint gts_interface_age = GTS_INTERFACE_AGE; const guint gts_binary_age = GTS_BINARY_AGE; static gboolean char_in_string (char c, const char * s) { while (*s != '\0') if (*(s++) == c) return TRUE; return FALSE; } static GtsFile * file_new (void) { GtsFile * f; f = g_malloc (sizeof (GtsFile)); f->fp = NULL; f->s = f->s1 = NULL; f->curline = 1; f->curpos = 1; f->token = g_string_new (""); f->type = '\0'; f->error = NULL; f->next_token = '\0'; f->scope = f->scope_max = 0; f->delimiters = g_strdup (" \t"); f->comments = g_strdup (GTS_COMMENTS); f->tokens = g_strdup ("\n{}()="); return f; } /** * gts_file_new: * @fp: a file pointer. * * Returns: a new #GtsFile. */ GtsFile * gts_file_new (FILE * fp) { GtsFile * f; g_return_val_if_fail (fp != NULL, NULL); f = file_new (); f->fp = fp; gts_file_next_token (f); return f; } /** * gts_file_new_from_string: * @s: a string. * * Returns: a new #GtsFile. */ GtsFile * gts_file_new_from_string (const gchar * s) { GtsFile * f; g_return_val_if_fail (s != NULL, NULL); f = file_new (); f->s1 = f->s = g_strdup (s); gts_file_next_token (f); return f; } /** * gts_file_destroy: * @f: a #GtsFile. * * Frees all the memory allocated for @f. */ void gts_file_destroy (GtsFile * f) { g_return_if_fail (f != NULL); g_free (f->delimiters); g_free (f->comments); g_free (f->tokens); if (f->error) g_free (f->error); if (f->s1) g_free (f->s1); g_string_free (f->token, TRUE); g_free (f); } /** * gts_file_verror: * @f: a @GtsFile. * @format: the standard sprintf() format string. * @args: the list of parameters to insert into the format string. * * Sets the @error field of @f using g_strdup_vprintf(). * * This function can be called only once and disables any other * operation on @f (gts_file_close() excepted). */ void gts_file_verror (GtsFile * f, const gchar * format, va_list args) { g_return_if_fail (f != NULL); g_return_if_fail (format != NULL); g_assert (f->type != GTS_ERROR); f->error = g_strdup_vprintf (format, args); f->type = GTS_ERROR; } /** * gts_file_error: * @f: a @GtsFile. * @format: the standard sprintf() format string. * @...: the parameters to insert into the format string. * * Sets the @error field of @f using gts_file_verror(). * * This function can be called only once and disables any other * operation on @f (gts_file_close() excepted). */ void gts_file_error (GtsFile * f, const gchar * format, ...) { va_list args; g_return_if_fail (f != NULL); g_return_if_fail (format != NULL); va_start (args, format); gts_file_verror (f, format, args); va_end (args); } static gint next_char (GtsFile * f) { if (f->fp) return fgetc (f->fp); else if (*f->s == '\0') return EOF; return *(f->s++); } /** * gts_file_getc : * @f: a #GtsFile. * * Returns: the next character in @f or EOF if the end of the file is * reached or if an error occured. */ gint gts_file_getc (GtsFile * f) { gint c; g_return_val_if_fail (f != NULL, EOF); if (f->type == GTS_ERROR) return EOF; c = next_char (f); f->curpos++; while (char_in_string (c, f->comments)) { while (c != EOF && c != '\n') c = next_char (f); if (c == '\n') { f->curline++; f->curpos = 1; c = next_char (f); } } switch (c) { case '\n': f->curline++; f->curpos = 1; break; case '{': f->scope++; break; case '}': if (f->scope == 0) { f->line = f->curline; f->pos = f->curpos - 1; gts_file_error (f, "no matching opening brace"); c = EOF; } else f->scope--; } return c; } /** * gts_file_read: * @f: a #GtsFile. * @ptr: a pointer. * @size: size of an element. * @nmemb: number of elements. * * Reads @nmemb elements of data, each @size bytes long, from @f, * storing them at the location given by @ptr. * * Returns: the number of elements read. */ guint gts_file_read (GtsFile * f, gpointer ptr, guint size, guint nmemb) { guint i, n; gchar * p; g_return_val_if_fail (f != NULL, 0); g_return_val_if_fail (ptr != NULL, 0); g_return_val_if_fail (f->fp != NULL, 0); if (f->type == GTS_ERROR) return 0; n = fread (ptr, size, nmemb, f->fp); for (i = 0, p = ptr; i < n*size; i++, p++) { f->curpos++; if (*p == '\n') { f->curline++; f->curpos = 1; } } return n; } /** * gts_file_getc_scope : * @f: a #GtsFile. * * Returns: the next character in @f in the scope defined by * @f->scope_max or EOF if the end of the file is reached or if an * error occured. */ gint gts_file_getc_scope (GtsFile * f) { gint c; g_return_val_if_fail (f != NULL, EOF); if (f->type == GTS_ERROR) return EOF; if (f->scope <= f->scope_max) c = gts_file_getc (f); else { c = gts_file_getc (f); while (c != EOF && f->scope > f->scope_max) c = gts_file_getc (f); } return c; } /** * gts_file_next_token: * @f: a #GtsFile. * * Reads next token from @f and updates its @token and @delim fields. */ void gts_file_next_token (GtsFile * f) { gint c; gboolean in_string = FALSE; g_return_if_fail (f != NULL); if (f->type == GTS_ERROR) return; f->token->str[0] = '\0'; f->token->len = 0; if (f->next_token != '\0') { if (char_in_string (f->next_token, f->tokens)) { f->line = f->curline; f->pos = f->curpos - 1; g_string_append_c (f->token, f->next_token); f->type = f->next_token; f->next_token = '\0'; return; } else { c = f->next_token; f->next_token = '\0'; } } else c = gts_file_getc_scope (f); f->type = GTS_NONE; while (c != EOF && (!in_string || !char_in_string (c, f->delimiters))) { if (in_string) { if (char_in_string (c, f->tokens)) { f->next_token = c; break; } g_string_append_c (f->token, c); } else if (!char_in_string (c, f->delimiters)) { in_string = TRUE; f->line = f->curline; f->pos = f->curpos - 1; g_string_append_c (f->token, c); if (char_in_string (c, f->tokens)) { f->type = c; break; } } c = gts_file_getc_scope (f); } if (f->type == GTS_NONE && f->token->len > 0) { gchar * a; a = f->token->str; while (*a != '\0' && char_in_string (*a, "+-")) a++; if (*a == '\0') { f->type = GTS_STRING; return; } a = f->token->str; while (*a != '\0' && char_in_string (*a, "+-0123456789")) a++; if (*a == '\0') { f->type = GTS_INT; return; } a = f->token->str; while (*a != '\0' && char_in_string (*a, "+-eE.")) a++; if (*a == '\0') { f->type = GTS_STRING; return; } a = f->token->str; while (*a != '\0' && char_in_string (*a, "+-0123456789eE.")) a++; if (*a == '\0') { f->type = GTS_FLOAT; return; } a = f->token->str; if (!strncmp (a, "0x", 2) || !strncmp (a, "-0x", 3) || !strncmp (a, "+0x", 3)) { while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx")) a++; if (*a == '\0') { f->type = GTS_INT; return; } a = f->token->str; while (*a != '\0' && char_in_string (*a, "+-0123456789abcdefx.p")) a++; if (*a == '\0') { f->type = GTS_FLOAT; return; } } f->type = GTS_STRING; } } /** * gts_file_first_token_after: * @f: a #GtsFile. * @type: a #GtsTokenType. * * Finds and sets the first token of a type different from @type * occuring after a token of type @type. */ void gts_file_first_token_after (GtsFile * f, GtsTokenType type) { g_return_if_fail (f != NULL); while (f->type != GTS_ERROR && f->type != GTS_NONE && f->type != type) gts_file_next_token (f); while (f->type == type) gts_file_next_token (f); } /** * gts_file_assign_start: * @f: a #GtsFile. * @vars: a %GTS_NONE terminated array of #GtsFileVariable. * * Opens a block delimited by braces to read a list of optional * arguments specified by @vars. * * If an error is encountered the @error field of @f is set. */ void gts_file_assign_start (GtsFile * f, GtsFileVariable * vars) { GtsFileVariable * var; g_return_if_fail (f != NULL); g_return_if_fail (vars != NULL); var = vars; while (var->type != GTS_NONE) (var++)->set = FALSE; if (f->type != '{') { gts_file_error (f, "expecting an opening brace"); return; } f->scope_max++; gts_file_next_token (f); } /** * gts_file_assign_next: * @f: a #GtsFile. * @vars: a %GTS_NONE terminated array of #GtsFileVariable. * * Assigns the next optional argument of @vars read from @f. * * Returns: the variable of @vars which has been assigned or %NULL if * no variable has been assigned (if an error has been encountered the * @error field of @f is set). */ GtsFileVariable * gts_file_assign_next (GtsFile * f, GtsFileVariable * vars) { GtsFileVariable * var; gboolean found = FALSE; g_return_val_if_fail (f != NULL, NULL); g_return_val_if_fail (vars != NULL, NULL); while (f->type == '\n') gts_file_next_token (f); if (f->type == '}') { f->scope_max--; gts_file_next_token (f); return NULL; } if (f->type == GTS_ERROR) return NULL; var = vars; while (f->type != GTS_ERROR && var->type != GTS_NONE && !found) { if (!strcmp (var->name, f->token->str)) { found = TRUE; if (var->unique && var->set) gts_file_error (f, "variable `%s' was already set at line %d:%d", var->name, var->line, var->pos); else { var->line = f->line; var->pos = f->pos; gts_file_next_token (f); if (f->type != '=') gts_file_error (f, "expecting `='"); else { var->set = TRUE; switch (var->type) { case GTS_FILE: break; case GTS_INT: gts_file_next_token (f); if (f->type != GTS_INT) { gts_file_error (f, "expecting an integer"); var->set = FALSE; } else if (var->data) *((gint *) var->data) = atoi (f->token->str); break; case GTS_UINT: gts_file_next_token (f); if (f->type != GTS_INT) { gts_file_error (f, "expecting an integer"); var->set = FALSE; } else if (var->data) *((guint *) var->data) = atoi (f->token->str); break; case GTS_FLOAT: gts_file_next_token (f); if (f->type != GTS_INT && f->type != GTS_FLOAT) { gts_file_error (f, "expecting a number"); var->set = FALSE; } else if (var->data) *((gfloat *) var->data) = atof (f->token->str); break; case GTS_DOUBLE: gts_file_next_token (f); if (f->type != GTS_INT && f->type != GTS_FLOAT) { gts_file_error (f, "expecting a number"); var->set = FALSE; } else if (var->data) *((gdouble *) var->data) = atof (f->token->str); break; case GTS_STRING: gts_file_next_token (f); if (f->type != GTS_INT && f->type != GTS_FLOAT && f->type != GTS_STRING) { gts_file_error (f, "expecting a string"); var->set = FALSE; } else if (var->data) *((gchar **) var->data) = g_strdup (f->token->str); break; default: g_assert_not_reached (); } } } } else var++; } if (!found) gts_file_error (f, "unknown identifier `%s'", f->token->str); else if (f->type != GTS_ERROR) { g_assert (var->set); gts_file_next_token (f); return var; } return NULL; } /** * gts_file_assign_variables: * @f: a #GtsFile. * @vars: an array of #GtsFileVariable. * * Assigns all the variables belonging to @vars found in @f. * * If an error is encountered the @error field of @f is set. */ void gts_file_assign_variables (GtsFile * f, GtsFileVariable * vars) { g_return_if_fail (f != NULL); g_return_if_fail (vars != NULL); gts_file_assign_start (f, vars); while (gts_file_assign_next (f, vars)) ; } /** * gts_file_variable_error: * @f: a #GtsFile. * @vars: an array of #GtsFileVariable. * @name: the name of a variable in @vars. * @format: the standard sprintf() format string. * @...: the parameters to insert into the format string. * * Sets the @error field of @f using gts_file_verror(). * * String @name must match one of the variable names in @vars. * * If variable @name has been assigned (using gts_file_assign_variables()) * sets the @line and @pos fields of @f to the line and position where * it has been assigned. */ void gts_file_variable_error (GtsFile * f, GtsFileVariable * vars, const gchar * name, const gchar * format, ...) { va_list args; GtsFileVariable * var; g_return_if_fail (f != NULL); g_return_if_fail (vars != NULL); g_return_if_fail (name != NULL); g_return_if_fail (format != NULL); var = vars; while (var->type != GTS_NONE && strcmp (var->name, name)) var++; g_return_if_fail (var->type != GTS_NONE); /* @name not found in @vars */ if (var->set) { f->line = var->line; f->pos = var->pos; } va_start (args, format); gts_file_verror (f, format, args); va_end (args); } #ifdef DEBUG_FUNCTIONS static GHashTable * ids = NULL; static guint next_id = 1; guint id (gpointer p) { g_return_val_if_fail (p != NULL, 0); g_return_val_if_fail (ids != NULL, 0); g_assert (g_hash_table_lookup (ids, p)); return GPOINTER_TO_UINT (g_hash_table_lookup (ids, p)); } void id_insert (gpointer p) { g_return_if_fail (p != NULL); if (ids == NULL) ids = g_hash_table_new (NULL, NULL); g_assert (g_hash_table_lookup (ids, p) == NULL); g_hash_table_insert (ids, p, GUINT_TO_POINTER (next_id++)); } void id_remove (gpointer p) { g_assert (g_hash_table_lookup (ids, p)); g_hash_table_remove (ids, p); } void gts_write_triangle (GtsTriangle * t, GtsPoint * o, FILE * fptr) { gdouble xo = o ? o->x : 0.0; gdouble yo = o ? o->y : 0.0; gdouble zo = o ? o->z : 0.0; g_return_if_fail (t != NULL && fptr != NULL); fprintf (fptr, "(hdefine geometry \"t%d\" { =\n", id (t)); fprintf (fptr, "OFF 3 1 0\n" "%g %g %g\n%g %g %g\n%g %g %g\n3 0 1 2\n})\n" "(geometry \"t%d\" { : \"t%d\"})\n" "(normalization \"t%d\" none)\n", GTS_POINT (GTS_SEGMENT (t->e1)->v1)->x - xo, GTS_POINT (GTS_SEGMENT (t->e1)->v1)->y - yo, GTS_POINT (GTS_SEGMENT (t->e1)->v1)->z - zo, GTS_POINT (GTS_SEGMENT (t->e1)->v2)->x - xo, GTS_POINT (GTS_SEGMENT (t->e1)->v2)->y - yo, GTS_POINT (GTS_SEGMENT (t->e1)->v2)->z - zo, GTS_POINT (gts_triangle_vertex (t))->x - xo, GTS_POINT (gts_triangle_vertex (t))->y - yo, GTS_POINT (gts_triangle_vertex (t))->z - zo, id (t), id (t), id (t)); } void gts_write_segment (GtsSegment * s, GtsPoint * o, FILE * fptr) { gdouble xo = o ? o->x : 0.0; gdouble yo = o ? o->y : 0.0; gdouble zo = o ? o->z : 0.0; g_return_if_fail (s != NULL && fptr != NULL); fprintf (fptr, "(geometry \"s%d\" { =\n", id (s)); fprintf (fptr, "VECT 1 2 0 2 0 %g %g %g %g %g %g })\n" "(normalization \"s%d\" none)\n", GTS_POINT (s->v1)->x - xo, GTS_POINT (s->v1)->y - yo, GTS_POINT (s->v1)->z - zo, GTS_POINT (s->v2)->x - xo, GTS_POINT (s->v2)->y - yo, GTS_POINT (s->v2)->z - zo, id (s)); } #endif /* DEBUG_FUNCTIONS */