gmio_stl: major revamp of API

This commit is contained in:
Hugues Delorme 2015-12-09 18:51:03 +01:00
parent 99e1d9aefa
commit 5aa0b38f5a
35 changed files with 1432 additions and 617 deletions

View File

@ -284,14 +284,14 @@ static void get_triangle(
static void stl_read(const char* filepath) static void stl_read(const char* filepath)
{ {
gmio_stl_mesh_creator mesh_creator = {}; gmio_stl_read_args read = {};
mesh_creator.cookie = &globalSceneHelper; read.mesh_creator.cookie = &globalSceneHelper;
mesh_creator.func_ascii_begin_solid = func_ascii_begin_solid; read.mesh_creator.func_ascii_begin_solid = func_ascii_begin_solid;
mesh_creator.func_binary_begin_solid = binary_begin_solid; read.mesh_creator.func_binary_begin_solid = binary_begin_solid;
mesh_creator.func_add_triangle = add_triangle; read.mesh_creator.func_add_triangle = add_triangle;
mesh_creator.func_end_solid = end_solid; read.mesh_creator.func_end_solid = end_solid;
const int error = gmio_stl_read_file(filepath, NULL, &mesh_creator); const int error = gmio_stl_read_file(&read, filepath);
if (error != GMIO_ERROR_OK) if (error != GMIO_ERROR_OK)
printf("gmio error: 0x%X\n", error); printf("gmio error: 0x%X\n", error);
@ -304,15 +304,14 @@ static void stl_write(const char* filepath, gmio_stl_format format)
{ {
const aiMesh* sceneMesh = globalSceneHelper.scene->mMeshes[0]; const aiMesh* sceneMesh = globalSceneHelper.scene->mMeshes[0];
gmio_stl_mesh mesh = {}; gmio_stl_write_args write = {};
mesh.cookie = sceneMesh; write.format = format;
mesh.triangle_count = sceneMesh->mNumFaces; write.mesh.cookie = sceneMesh;
mesh.func_get_triangle = get_triangle; write.mesh.triangle_count = sceneMesh->mNumFaces;
write.mesh.func_get_triangle = get_triangle;
gmio_stl_write_options opts = {}; write.options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE;
opts.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE; write.options.stla_float32_prec = 7;
opts.stla_float32_prec = 7; const int error = gmio_stl_write_file(&write, filepath);
const int error = gmio_stl_write_file(filepath, NULL, &mesh, format, &opts);
if (error != GMIO_ERROR_OK) if (error != GMIO_ERROR_OK)
printf("gmio error: 0x%X\n", error); printf("gmio error: 0x%X\n", error);
} }

View File

@ -19,6 +19,7 @@
#include <gmio_stl/stl_io_options.h> #include <gmio_stl/stl_io_options.h>
#include <gmio_stl/stl_mesh.h> #include <gmio_stl/stl_mesh.h>
#include <gmio_stl/stl_mesh_creator.h> #include <gmio_stl/stl_mesh_creator.h>
#include <gmio_stl/stl_infos.h>
#include "../commons/benchmark_tools.h" #include "../commons/benchmark_tools.h"
@ -46,12 +47,12 @@ static void dummy_process_triangle(
static void bmk_gmio_stl_read(const char* filepath) static void bmk_gmio_stl_read(const char* filepath)
{ {
struct my_igeom cookie = {0}; struct my_igeom cookie = {0};
struct gmio_stl_mesh_creator mesh_creator = {0}; struct gmio_stl_read_args read = {0};
int error = GMIO_ERROR_OK; int error = GMIO_ERROR_OK;
mesh_creator.cookie = &cookie; read.mesh_creator.cookie = &cookie;
mesh_creator.func_add_triangle = dummy_process_triangle; read.mesh_creator.func_add_triangle = dummy_process_triangle;
error = gmio_stl_read_file(filepath, NULL, &mesh_creator); error = gmio_stl_read_file(&read, filepath);
if (error != GMIO_ERROR_OK) if (error != GMIO_ERROR_OK)
printf("gmio error: 0x%X\n", error); printf("gmio error: 0x%X\n", error);
} }
@ -127,15 +128,16 @@ static void readwrite_get_triangle(
static void stl_readwrite_flush_triangles(struct stl_readwrite_conv* rw_conv) static void stl_readwrite_flush_triangles(struct stl_readwrite_conv* rw_conv)
{ {
struct gmio_stl_mesh mesh = {0}; struct gmio_stl_write_args write = {0};
struct gmio_stl_write_options options = {0}; write.core = rw_conv->rwargs;
mesh.cookie = &rw_conv->triangle_array[0]; write.format = rw_conv->out_format;
mesh.triangle_count = rw_conv->triangle_pos; write.mesh.cookie = &rw_conv->triangle_array[0];
mesh.func_get_triangle = &readwrite_get_triangle; write.mesh.triangle_count = rw_conv->triangle_pos;
options.stl_write_triangles_only = GMIO_TRUE; write.mesh.func_get_triangle = &readwrite_get_triangle;
options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE; write.options.stl_write_triangles_only = GMIO_TRUE;
options.stla_float32_prec = 6; write.options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SCIENTIFIC_LOWERCASE;
gmio_stl_write(&rw_conv->rwargs, &mesh, rw_conv->out_format, &options); write.options.stla_float32_prec = 6;
gmio_stl_write(&write);
rw_conv->triangle_pos = 0; rw_conv->triangle_pos = 0;
} }
@ -173,18 +175,17 @@ static void bmk_gmio_stl_readwrite_conv(const char* filepath)
{ {
FILE* infile = fopen(filepath, "rb"); FILE* infile = fopen(filepath, "rb");
FILE* outfile = fopen("_readwrite_conv.stl", "wb"); FILE* outfile = fopen("_readwrite_conv.stl", "wb");
struct gmio_rwargs in_rwargs = {0}; struct gmio_stl_read_args read = {0};
struct stl_readwrite_conv rw_conv = {0}; struct stl_readwrite_conv rw_conv = {0};
struct gmio_stl_mesh_creator mesh_creator = {0};
int error = GMIO_ERROR_OK; int error = GMIO_ERROR_OK;
/* rw_conv.out_format = GMIO_STL_FORMAT_BINARY_LE; */ /* rw_conv.out_format = GMIO_STL_FORMAT_BINARY_LE; */
rw_conv.out_format = GMIO_STL_FORMAT_ASCII; rw_conv.out_format = GMIO_STL_FORMAT_ASCII;
if (infile != NULL) { if (infile != NULL) {
in_rwargs.memblock = gmio_memblock_malloc(512 * 1024); read.core.memblock = gmio_memblock_malloc(512 * 1024);
in_rwargs.stream = gmio_stream_stdio(infile); read.core.stream = gmio_stream_stdio(infile);
rw_conv.in_format = gmio_stl_get_format(&in_rwargs.stream); rw_conv.in_format = gmio_stl_get_format(&read.core.stream);
} }
if (outfile != NULL) { if (outfile != NULL) {
rw_conv.rwargs.memblock = gmio_memblock_malloc(512 * 1024); rw_conv.rwargs.memblock = gmio_memblock_malloc(512 * 1024);
@ -194,19 +195,45 @@ static void bmk_gmio_stl_readwrite_conv(const char* filepath)
&rw_conv.out_stream_pos_begin); &rw_conv.out_stream_pos_begin);
} }
mesh_creator.cookie = &rw_conv; read.mesh_creator.cookie = &rw_conv;
mesh_creator.func_ascii_begin_solid = &readwrite_ascii_begin_solid; read.mesh_creator.func_ascii_begin_solid = &readwrite_ascii_begin_solid;
mesh_creator.func_binary_begin_solid = &readwrite_binary_begin_solid; read.mesh_creator.func_binary_begin_solid = &readwrite_binary_begin_solid;
mesh_creator.func_add_triangle = &readwrite_add_triangle; read.mesh_creator.func_add_triangle = &readwrite_add_triangle;
mesh_creator.func_end_solid = &readwrite_end_solid; read.mesh_creator.func_end_solid = &readwrite_end_solid;
error = gmio_stl_read(&in_rwargs, &mesh_creator); error = gmio_stl_read(&read);
gmio_memblock_deallocate(&in_rwargs.memblock); gmio_memblock_deallocate(&read.core.memblock);
gmio_memblock_deallocate(&rw_conv.rwargs.memblock); gmio_memblock_deallocate(&rw_conv.rwargs.memblock);
if (error != GMIO_ERROR_OK) if (error != GMIO_ERROR_OK)
printf("gmio error: 0x%X\n", error); printf("gmio error: 0x%X\n", error);
fclose(infile);
fclose(outfile);
}
void bmk_gmio_stl_infos_get(const char* filepath)
{
static gmio_bool_t already_exec = GMIO_FALSE;
FILE* file = fopen(filepath, "rb");
if (file != NULL) {
struct gmio_stl_infos infos = {0};
struct gmio_stl_infos_get_args args = {0};
int error = GMIO_ERROR_OK;
args.stream = gmio_stream_stdio(file);
args.memblock = gmio_memblock_malloc(64 * 1024); /* 64Ko */
args.format = GMIO_STL_FORMAT_ASCII;
error = gmio_stl_infos_get(&args, &infos, GMIO_STL_INFO_FLAG_ALL);
if (!already_exec) {
printf("stl_infos_get()\n File: %s\n Size: %uKo\n Facets: %u\n",
filepath, infos.size / 1024, infos.facet_count);
}
already_exec = GMIO_TRUE;
}
fclose(file);
} }
int main(int argc, char** argv) int main(int argc, char** argv)
@ -222,16 +249,22 @@ int main(int argc, char** argv)
{ "readwrite_conv()", { "readwrite_conv()",
bmk_gmio_stl_readwrite_conv, NULL, bmk_gmio_stl_readwrite_conv, NULL,
NULL, NULL }, NULL, NULL },
{ "stl_infos_get(ALL)",
bmk_gmio_stl_infos_get, NULL,
NULL, NULL },
{0} {0}
}; };
const size_t cmp_count = const size_t cmp_count =
sizeof(cmp_args) / sizeof(struct benchmark_cmp_arg) - 1; sizeof(cmp_args) / sizeof(struct benchmark_cmp_arg) - 1;
struct benchmark_cmp_result cmp_res[2] = {0}; struct benchmark_cmp_result cmp_res[3] = {0};
struct benchmark_cmp_result_array res_array = {0}; struct benchmark_cmp_result_array res_array = {0};
const struct benchmark_cmp_result_header header = { "gmio", NULL }; const struct benchmark_cmp_result_header header = { "gmio", NULL };
cmp_args[0].func1_filepath = filepath; {
cmp_args[1].func1_filepath = filepath; size_t i = 0;
for (i = 0; i < cmp_count; ++i)
cmp_args[i].func1_filepath = filepath;
}
res_array.ptr = &cmp_res[0]; res_array.ptr = &cmp_res[0];
res_array.count = cmp_count; res_array.count = cmp_count;

View File

@ -59,8 +59,9 @@ Handle_StlMesh_Mesh stlMesh;
static void stl_read(const char* filepath) static void stl_read(const char* filepath)
{ {
stlMesh = new StlMesh_Mesh; stlMesh = new StlMesh_Mesh;
gmio_stl_mesh_creator mesh_creator = gmio_stl_hnd_occmesh_creator(stlMesh); gmio_stl_read_args read = {};
int error = gmio_stl_read_file(filepath, NULL, &mesh_creator); args.mesh_creator = gmio_stl_hnd_occmesh_creator(stlMesh);
int error = gmio_stl_read_file(&read, filepath);
if (error != GMIO_ERROR_OK) if (error != GMIO_ERROR_OK)
printf("gmio error: 0x%X\n", error); printf("gmio error: 0x%X\n", error);
} }
@ -68,12 +69,12 @@ static void stl_read(const char* filepath)
static void stl_write(const char* filepath, gmio_stl_format format) static void stl_write(const char* filepath, gmio_stl_format format)
{ {
const gmio_occ_stl_mesh_domain occ_mesh_domain(stlMesh); const gmio_occ_stl_mesh_domain occ_mesh_domain(stlMesh);
const gmio_stl_mesh mesh = gmio_stl_occmesh(&occ_mesh_domain); gmio_stl_write_args write = {};
write.format = format;
gmio_stl_write_options opts = {}; write.mesh = gmio_stl_occmesh(&occ_mesh_domain);
opts.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE; write.options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_UPPERCASE;
opts.stla_float32_prec = 7; write.options.stla_float32_prec = 7;
const int error = gmio_stl_write_file(filepath, NULL, &mesh, format, &opts); const int error = gmio_stl_write_file(&write, filepath);
if (error != GMIO_ERROR_OK) if (error != GMIO_ERROR_OK)
printf("gmio error: 0x%X\n", error); printf("gmio error: 0x%X\n", error);
} }

View File

@ -0,0 +1,69 @@
/****************************************************************************
** gmio
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software is a reusable library whose purpose is to provide complete
** I/O support for various CAD file formats (eg. STL)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
#include "string_utils.h"
static int gmio_ascii_ustrincmp(
const unsigned char* str1, const unsigned char* str2, size_t n)
{
return gmio_ascii_strincmp((const char*)str1, (const char*)str2, n);
}
#define RETURN_TYPE const char*
#define AVAILABLE(h, h_l, j, n_l) \
(!memchr ((h) + (h_l), '\0', (j) + (n_l) - (h_l)) \
&& ((h_l) = (j) + (n_l)))
#define CANON_ELEMENT(c) ((int)gmio_ascii_tolower(c))
#define CMP_FUNC gmio_ascii_ustrincmp
#include "eblake_str_two_way.h"
const char *gmio_ascii_istrstr(const char *str1, const char *str2)
{
/* Guaranteed linear performance */
const char* haystack = str1;
const char* needle = str2;
size_t needle_len; /* Length of NEEDLE */
size_t haystack_len; /* Known minimum length of HAYSTACK */
int ok = 1; /* True if NEEDLE is prefix of HAYSTACK */
/* Determine length of NEEDLE, and in the process, make sure
HAYSTACK is at least as long (no point processing all of a long
NEEDLE if HAYSTACK is too short) */
while (*haystack && *needle) {
ok &= (gmio_ascii_tolower((unsigned char) *haystack++)
== gmio_ascii_tolower((unsigned char) *needle++));
}
if (*needle)
return NULL;
if (ok)
return str1;
needle_len = needle - str2;
haystack = str1 + 1;
haystack_len = needle_len - 1;
/* Perform the search */
if (needle_len < LONG_NEEDLE_THRESHOLD) {
return two_way_short_needle(
(const unsigned char*) haystack,
haystack_len,
(const unsigned char*) str2, needle_len);
}
return two_way_long_needle(
(const unsigned char*) haystack,
haystack_len,
(const unsigned char*) str2,
needle_len);
}

View File

@ -0,0 +1,530 @@
/* Byte-wise substring search, using the Two-Way algorithm.
Copyright (C) 2008-2015 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Written by Eric Blake <ebb9@byu.net>, 2008.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* Before including this file, you need to include <string.h> (and
<config.h> before that, if not part of libc), and define:
RETURN_TYPE A macro that expands to the return type.
AVAILABLE(h, h_l, j, n_l)
A macro that returns nonzero if there are
at least N_L bytes left starting at H[J].
H is 'unsigned char *', H_L, J, and N_L
are 'size_t'; H_L is an lvalue. For
NUL-terminated searches, H_L can be
modified each iteration to avoid having
to compute the end of H up front.
For case-insensitivity, you may optionally define:
CMP_FUNC(p1, p2, l) A macro that returns 0 iff the first L
characters of P1 and P2 are equal.
CANON_ELEMENT(c) A macro that canonicalizes an element right after
it has been fetched from one of the two strings.
The argument is an 'unsigned char'; the result
must be an 'unsigned char' as well.
Other macros you may optionally define:
RET0_IF_0(a) Documented below at default definition.
CHECK_EOL Same.
This file undefines the macros listed above, and defines
LONG_NEEDLE_THRESHOLD.
*/
#include "../global.h"
#include "min_max.h"
#include <limits.h>
#include <string.h>
/* We use the Two-Way string matching algorithm, which guarantees
linear complexity with constant space. Additionally, for long
needles, we also use a bad character shift table similar to the
Boyer-Moore algorithm to achieve improved (potentially sub-linear)
performance.
See http://www-igm.univ-mlv.fr/~lecroq/string/node26.html#SECTION00260
and http://en.wikipedia.org/wiki/Boyer-Moore_string_search_algorithm
*/
#define GMIO_SIZE_MAX ((size_t)-1)
/* Point at which computing a bad-byte shift table is likely to be
worthwhile. Small needles should not compute a table, since it
adds (1 << CHAR_BIT) + NEEDLE_LEN computations of preparation for a
speedup no greater than a factor of NEEDLE_LEN. The larger the
needle, the better the potential performance gain. On the other
hand, on non-POSIX systems with CHAR_BIT larger than eight, the
memory required for the table is prohibitive. */
#if CHAR_BIT < 10
# define LONG_NEEDLE_THRESHOLD 32U
#else
# define LONG_NEEDLE_THRESHOLD GMIO_SIZE_MAX
#endif
#ifndef CANON_ELEMENT
# define CANON_ELEMENT(c) c
#endif
#ifndef CMP_FUNC
# define CMP_FUNC memcmp
#endif
/* Check for end-of-line in strstr and strcasestr routines.
We piggy-back matching procedure for detecting EOL where possible,
and use AVAILABLE macro otherwise. */
#ifndef CHECK_EOL
# define CHECK_EOL (0)
#endif
/* Return NULL if argument is '\0'. */
#ifndef RET0_IF_0
# define RET0_IF_0(a) /* nothing */
#endif
/* Perform a critical factorization of NEEDLE, of length NEEDLE_LEN.
Return the index of the first byte in the right half, and set
*PERIOD to the global period of the right half.
The global period of a string is the smallest index (possibly its
length) at which all remaining bytes in the string are repetitions
of the prefix (the last repetition may be a subset of the prefix).
When NEEDLE is factored into two halves, a local period is the
length of the smallest word that shares a suffix with the left half
and shares a prefix with the right half. All factorizations of a
non-empty NEEDLE have a local period of at least 1 and no greater
than NEEDLE_LEN.
A critical factorization has the property that the local period
equals the global period. All strings have at least one critical
factorization with the left half smaller than the global period.
Given an ordered alphabet, a critical factorization can be computed
in linear time, with 2 * NEEDLE_LEN comparisons, by computing the
larger of two ordered maximal suffixes. The ordered maximal
suffixes are determined by lexicographic comparison of
periodicity. */
static size_t
critical_factorization (const unsigned char *needle, size_t needle_len,
size_t *period)
{
/* Index of last byte of left half, or SIZE_MAX. */
size_t max_suffix, max_suffix_rev;
size_t j; /* Index into NEEDLE for current candidate suffix. */
size_t k; /* Offset into current period. */
size_t p; /* Intermediate period. */
unsigned char a, b; /* Current comparison bytes. */
/* Invariants:
0 <= j < NEEDLE_LEN - 1
-1 <= max_suffix{,_rev} < j (treating SIZE_MAX as if it were signed)
min(max_suffix, max_suffix_rev) < global period of NEEDLE
1 <= p <= global period of NEEDLE
p == global period of the substring NEEDLE[max_suffix{,_rev}+1...j]
1 <= k <= p
*/
/* Perform lexicographic search. */
max_suffix = GMIO_SIZE_MAX;
j = 0;
k = p = 1;
while (j + k < needle_len)
{
a = CANON_ELEMENT (needle[j + k]);
b = CANON_ELEMENT (needle[max_suffix + k]);
if (a < b)
{
/* Suffix is smaller, period is entire prefix so far. */
j += k;
k = 1;
p = j - max_suffix;
}
else if (a == b)
{
/* Advance through repetition of the current period. */
if (k != p)
++k;
else
{
j += p;
k = 1;
}
}
else /* b < a */
{
/* Suffix is larger, start over from current location. */
max_suffix = j++;
k = p = 1;
}
}
*period = p;
/* Perform reverse lexicographic search. */
max_suffix_rev = GMIO_SIZE_MAX;
j = 0;
k = p = 1;
while (j + k < needle_len)
{
a = CANON_ELEMENT (needle[j + k]);
b = CANON_ELEMENT (needle[max_suffix_rev + k]);
if (b < a)
{
/* Suffix is smaller, period is entire prefix so far. */
j += k;
k = 1;
p = j - max_suffix_rev;
}
else if (a == b)
{
/* Advance through repetition of the current period. */
if (k != p)
++k;
else
{
j += p;
k = 1;
}
}
else /* a < b */
{
/* Suffix is larger, start over from current location. */
max_suffix_rev = j++;
k = p = 1;
}
}
/* Choose the longer suffix. Return the first byte of the right
half, rather than the last byte of the left half. */
if (max_suffix_rev + 1 < max_suffix + 1)
return max_suffix + 1;
*period = p;
return max_suffix_rev + 1;
}
/* Return the first location of non-empty NEEDLE within HAYSTACK, or
NULL. HAYSTACK_LEN is the minimum known length of HAYSTACK. This
method is optimized for NEEDLE_LEN < LONG_NEEDLE_THRESHOLD.
Performance is guaranteed to be linear, with an initialization cost
of 2 * NEEDLE_LEN comparisons.
If AVAILABLE does not modify HAYSTACK_LEN (as in memmem), then at
most 2 * HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching.
If AVAILABLE modifies HAYSTACK_LEN (as in strstr), then at most 3 *
HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching. */
static RETURN_TYPE
two_way_short_needle (const unsigned char *haystack, size_t haystack_len,
const unsigned char *needle, size_t needle_len)
{
size_t i; /* Index into current byte of NEEDLE. */
size_t j; /* Index into current window of HAYSTACK. */
size_t period; /* The period of the right half of needle. */
size_t suffix; /* The index of the right half of needle. */
/* Factor the needle into two halves, such that the left half is
smaller than the global period, and the right half is
periodic (with a period as large as NEEDLE_LEN - suffix). */
suffix = critical_factorization (needle, needle_len, &period);
/* Perform the search. Each iteration compares the right half
first. */
if (CMP_FUNC (needle, needle + period, suffix) == 0)
{
/* Entire needle is periodic; a mismatch can only advance by the
period, so use memory to avoid rescanning known occurrences
of the period. */
size_t memory = 0;
j = 0;
while (AVAILABLE (haystack, haystack_len, j, needle_len))
{
const unsigned char *pneedle;
const unsigned char *phaystack;
/* Scan for matches in right half. */
i = GMIO_MAX (suffix, memory);
pneedle = &needle[i];
phaystack = &haystack[i + j];
while (i < needle_len && (CANON_ELEMENT (*pneedle++)
== CANON_ELEMENT (*phaystack++)))
++i;
if (needle_len <= i)
{
/* Scan for matches in left half. */
i = suffix - 1;
pneedle = &needle[i];
phaystack = &haystack[i + j];
while (memory < i + 1 && (CANON_ELEMENT (*pneedle--)
== CANON_ELEMENT (*phaystack--)))
--i;
if (i + 1 < memory + 1)
return (RETURN_TYPE) (haystack + j);
/* No match, so remember how many repetitions of period
on the right half were scanned. */
j += period;
memory = needle_len - period;
}
else
{
j += i - suffix + 1;
memory = 0;
}
}
}
else
{
const unsigned char *phaystack = &haystack[suffix];
/* The comparison always starts from needle[suffix], so cache it
and use an optimized first-character loop. */
unsigned char needle_suffix = CANON_ELEMENT (needle[suffix]);
#if CHECK_EOL
/* We start matching from the SUFFIX'th element, so make sure we
don't hit '\0' before that. */
if (haystack_len < suffix + 1
&& !AVAILABLE (haystack, haystack_len, 0, suffix + 1))
return NULL;
#endif
/* The two halves of needle are distinct; no extra memory is
required, and any mismatch results in a maximal shift. */
period = GMIO_MAX (suffix, needle_len - suffix) + 1;
j = 0;
while (1
#if !CHECK_EOL
&& AVAILABLE (haystack, haystack_len, j, needle_len)
#endif
)
{
unsigned char haystack_char;
const unsigned char *pneedle;
/* TODO: The first-character loop can be sped up by adapting
longword-at-a-time implementation of memchr/strchr. */
if (needle_suffix
!= (haystack_char = CANON_ELEMENT (*phaystack++)))
{
RET0_IF_0 (haystack_char);
#if !CHECK_EOL
++j;
#endif
continue;
}
#if CHECK_EOL
/* Calculate J if it wasn't kept up-to-date in the first-character
loop. */
j = phaystack - &haystack[suffix] - 1;
#endif
/* Scan for matches in right half. */
i = suffix + 1;
pneedle = &needle[i];
while (i < needle_len)
{
if (CANON_ELEMENT (*pneedle++)
!= (haystack_char = CANON_ELEMENT (*phaystack++)))
{
RET0_IF_0 (haystack_char);
break;
}
++i;
}
if (needle_len <= i)
{
/* Scan for matches in left half. */
i = suffix - 1;
pneedle = &needle[i];
phaystack = &haystack[i + j];
while (i != GMIO_SIZE_MAX)
{
if (CANON_ELEMENT (*pneedle--)
!= (haystack_char = CANON_ELEMENT (*phaystack--)))
{
RET0_IF_0 (haystack_char);
break;
}
--i;
}
if (i == GMIO_SIZE_MAX)
return (RETURN_TYPE) (haystack + j);
j += period;
}
else
j += i - suffix + 1;
#if CHECK_EOL
if (!AVAILABLE (haystack, haystack_len, j, needle_len))
break;
#endif
phaystack = &haystack[suffix + j];
}
}
return NULL;
}
/* Return the first location of non-empty NEEDLE within HAYSTACK, or
NULL. HAYSTACK_LEN is the minimum known length of HAYSTACK. This
method is optimized for LONG_NEEDLE_THRESHOLD <= NEEDLE_LEN.
Performance is guaranteed to be linear, with an initialization cost
of 3 * NEEDLE_LEN + (1 << CHAR_BIT) operations.
If AVAILABLE does not modify HAYSTACK_LEN (as in memmem), then at
most 2 * HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching,
and sublinear performance O(HAYSTACK_LEN / NEEDLE_LEN) is possible.
If AVAILABLE modifies HAYSTACK_LEN (as in strstr), then at most 3 *
HAYSTACK_LEN - NEEDLE_LEN comparisons occur in searching, and
sublinear performance is not possible. */
static RETURN_TYPE
two_way_long_needle (const unsigned char *haystack, size_t haystack_len,
const unsigned char *needle, size_t needle_len)
{
size_t i; /* Index into current byte of NEEDLE. */
size_t j; /* Index into current window of HAYSTACK. */
size_t period; /* The period of the right half of needle. */
size_t suffix; /* The index of the right half of needle. */
size_t shift_table[1U << CHAR_BIT]; /* See below. */
/* Factor the needle into two halves, such that the left half is
smaller than the global period, and the right half is
periodic (with a period as large as NEEDLE_LEN - suffix). */
suffix = critical_factorization (needle, needle_len, &period);
/* Populate shift_table. For each possible byte value c,
shift_table[c] is the distance from the last occurrence of c to
the end of NEEDLE, or NEEDLE_LEN if c is absent from the NEEDLE.
shift_table[NEEDLE[NEEDLE_LEN - 1]] contains the only 0. */
for (i = 0; i < 1U << CHAR_BIT; i++)
shift_table[i] = needle_len;
for (i = 0; i < needle_len; i++)
shift_table[CANON_ELEMENT (needle[i])] = needle_len - i - 1;
/* Perform the search. Each iteration compares the right half
first. */
if (CMP_FUNC (needle, needle + period, suffix) == 0)
{
/* Entire needle is periodic; a mismatch can only advance by the
period, so use memory to avoid rescanning known occurrences
of the period. */
size_t memory = 0;
size_t shift;
j = 0;
while (AVAILABLE (haystack, haystack_len, j, needle_len))
{
const unsigned char *pneedle;
const unsigned char *phaystack;
/* Check the last byte first; if it does not match, then
shift to the next possible match location. */
shift = shift_table[CANON_ELEMENT (haystack[j + needle_len - 1])];
if (0 < shift)
{
if (memory && shift < period)
{
/* Since needle is periodic, but the last period has
a byte out of place, there can be no match until
after the mismatch. */
shift = needle_len - period;
}
memory = 0;
j += shift;
continue;
}
/* Scan for matches in right half. The last byte has
already been matched, by virtue of the shift table. */
i = GMIO_MAX (suffix, memory);
pneedle = &needle[i];
phaystack = &haystack[i + j];
while (i < needle_len - 1 && (CANON_ELEMENT (*pneedle++)
== CANON_ELEMENT (*phaystack++)))
++i;
if (needle_len - 1 <= i)
{
/* Scan for matches in left half. */
i = suffix - 1;
pneedle = &needle[i];
phaystack = &haystack[i + j];
while (memory < i + 1 && (CANON_ELEMENT (*pneedle--)
== CANON_ELEMENT (*phaystack--)))
--i;
if (i + 1 < memory + 1)
return (RETURN_TYPE) (haystack + j);
/* No match, so remember how many repetitions of period
on the right half were scanned. */
j += period;
memory = needle_len - period;
}
else
{
j += i - suffix + 1;
memory = 0;
}
}
}
else
{
/* The two halves of needle are distinct; no extra memory is
required, and any mismatch results in a maximal shift. */
size_t shift;
period = GMIO_MAX (suffix, needle_len - suffix) + 1;
j = 0;
while (AVAILABLE (haystack, haystack_len, j, needle_len))
{
const unsigned char *pneedle;
const unsigned char *phaystack;
/* Check the last byte first; if it does not match, then
shift to the next possible match location. */
shift = shift_table[CANON_ELEMENT (haystack[j + needle_len - 1])];
if (0 < shift)
{
j += shift;
continue;
}
/* Scan for matches in right half. The last byte has
already been matched, by virtue of the shift table. */
i = suffix;
pneedle = &needle[i];
phaystack = &haystack[i + j];
while (i < needle_len - 1 && (CANON_ELEMENT (*pneedle++)
== CANON_ELEMENT (*phaystack++)))
++i;
if (needle_len - 1 <= i)
{
/* Scan for matches in left half. */
i = suffix - 1;
pneedle = &needle[i];
phaystack = &haystack[i + j];
while (i != GMIO_SIZE_MAX && (CANON_ELEMENT (*pneedle--)
== CANON_ELEMENT (*phaystack--)))
--i;
if (i == GMIO_SIZE_MAX)
return (RETURN_TYPE) (haystack + j);
j += period;
}
else
j += i - suffix + 1;
}
}
return NULL;
}
#undef AVAILABLE
#undef CANON_ELEMENT
#undef CMP_FUNC
#undef RET0_IF_0
#undef RETURN_TYPE
#undef CHECK_EOL

View File

@ -0,0 +1,35 @@
/****************************************************************************
** gmio
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software is a reusable library whose purpose is to provide complete
** I/O support for various CAD file formats (eg. STL)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
#include "numeric_utils.h"
gmio_bool_t gmio_float32_ulp_equals(
gmio_float32_t a, gmio_float32_t b, uint32_t max_ulp_diff)
{
const int32_t ia = gmio_convert_int32(a);
const int32_t ib = gmio_convert_int32(b);
const int32_t slp_diff = ia - ib;
const uint32_t ulp_diff = slp_diff > 0 ? slp_diff : -slp_diff;
/* Different signs, we could maybe get difference to 0, but so close to 0
* using epsilons is better */
if (gmio_int32_sign(ia) != gmio_int32_sign(ib)) {
/* Check for equality to make sure +0 == -0 */
return ia == ib;
}
/* Find the difference in ULPs */
return ulp_diff <= max_ulp_diff;
}

View File

@ -0,0 +1,66 @@
/****************************************************************************
** gmio
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software is a reusable library whose purpose is to provide complete
** I/O support for various CAD file formats (eg. STL)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
#ifndef GMIO_INTERNAL_NUMERIC_UTILS_H
#define GMIO_INTERNAL_NUMERIC_UTILS_H
#include "../global.h"
#include "convert.h"
#include <stddef.h>
/*! Does \p a and \p b compares equals by ULP (Units in the Last Place) ?
*
* ULP = spacing between floating-point numbers
*
* See:
* http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
*/
gmio_bool_t gmio_float32_ulp_equals(
gmio_float32_t a, gmio_float32_t b, uint32_t max_ulp_diff);
/*! Count of ULP between \p a and \p b */
GMIO_INLINE uint32_t gmio_float32_ulp_diff(gmio_float32_t a, gmio_float32_t b);
/*! Portable sign-extraction for int32 */
GMIO_INLINE int gmio_int32_sign(int32_t v);
/*! Portable sign-extraction for float32 */
GMIO_INLINE int gmio_float32_sign(gmio_float32_t v);
/*
* Implementation
*/
uint32_t gmio_float32_ulp_diff(gmio_float32_t a, gmio_float32_t b)
{
const uint32_t ua = gmio_convert_uint32(a);
const uint32_t ub = gmio_convert_uint32(b);
return ua > ub ? ua - ub : ub - ua;
}
int gmio_int32_sign(int32_t v)
{
return (v & 0x80000000) != 0 ? -1 : 1;
}
int gmio_float32_sign(gmio_float32_t v)
{
return gmio_int32_sign(gmio_convert_int32(v));
}
#endif /* GMIO_INTERNAL_NUMERIC_UTILS_H */

View File

@ -48,6 +48,14 @@ GMIO_INLINE gmio_bool_t gmio_ascii_char_iequals(char c1, char c2);
*/ */
GMIO_INLINE int gmio_ascii_stricmp(const char* str1, const char* str2); GMIO_INLINE int gmio_ascii_stricmp(const char* str1, const char* str2);
/*! Returns 0 if the first \p n characreters of \p str1 and \p str2 compare
* equal, non-zero otherwise
*
* Comparison is case-insensitive
*/
GMIO_INLINE int gmio_ascii_strincmp(
const char* str1, const char* str2, size_t n);
/*! Returns true if \p str starts with string \p begin /*! Returns true if \p str starts with string \p begin
* *
* Comparison is case-insensitive * Comparison is case-insensitive
@ -55,6 +63,11 @@ GMIO_INLINE int gmio_ascii_stricmp(const char* str1, const char* str2);
GMIO_INLINE gmio_bool_t gmio_ascii_istarts_with( GMIO_INLINE gmio_bool_t gmio_ascii_istarts_with(
const char* str, const char* begin); const char* str, const char* begin);
/*! Locate substring (insensitive case string matching)
*
* Behaves the same as strstr()
*/
const char* gmio_ascii_istrstr(const char *str1, const char *str2);
/* /*
@ -168,6 +181,16 @@ int gmio_ascii_stricmp(const char* str1, const char* str2)
return *str1 - *str2; return *str1 - *str2;
} }
int gmio_ascii_strincmp(const char* str1, const char* str2, size_t n)
{
while (n > 0 && *str1 != 0 && gmio_ascii_char_iequals(*str1, *str2)) {
++str1;
++str2;
--n;
}
return gmio_ascii_tolower(*str1) - gmio_ascii_tolower(*str2);
}
gmio_bool_t gmio_ascii_istarts_with(const char* str, const char* begin) gmio_bool_t gmio_ascii_istarts_with(const char* str, const char* begin)
{ {
while (*begin != 0) { while (*begin != 0) {

View File

@ -44,8 +44,10 @@ struct gmio_memblock gmio_memblock_realloc(void* ptr, size_t size)
void gmio_memblock_deallocate(struct gmio_memblock *mblock) void gmio_memblock_deallocate(struct gmio_memblock *mblock)
{ {
if (mblock != NULL && mblock->func_deallocate != NULL) if (mblock != NULL && mblock->func_deallocate != NULL) {
mblock->func_deallocate(mblock->ptr); mblock->func_deallocate(mblock->ptr);
mblock->ptr = NULL;
}
} }
static struct gmio_memblock gmio_memblock_default_internal_ctor() static struct gmio_memblock gmio_memblock_default_internal_ctor()

View File

@ -18,11 +18,8 @@
#include "stl_funptr_typedefs.h" #include "stl_funptr_typedefs.h"
#include "stl_rw_common.h" #include "stl_rw_common.h"
#include "../stl_error.h" #include "../stl_error.h"
#include "../stl_io_options.h"
#include "../stl_mesh.h"
#include "../../gmio_core/error.h" #include "../../gmio_core/error.h"
#include "../../gmio_core/rwargs.h"
#include "../../gmio_core/text_format.h" #include "../../gmio_core/text_format.h"
#include "../../gmio_core/internal/helper_rwargs.h" #include "../../gmio_core/internal/helper_rwargs.h"
#include "../../gmio_core/internal/helper_stream.h" #include "../../gmio_core/internal/helper_stream.h"
@ -133,44 +130,41 @@ GMIO_INLINE gmio_bool_t gmio_rwargs_flush_buffer(
return write_count == n; return write_count == n;
} }
int gmio_stla_write( int gmio_stla_write(struct gmio_stl_write_args* args)
struct gmio_rwargs* args,
const struct gmio_stl_mesh* mesh,
const struct gmio_stl_write_options* options)
{ {
/* Constants */ /* Constants */
const uint32_t total_facet_count = mesh != NULL ? mesh->triangle_count : 0; const uint32_t total_facet_count =
args->mesh.triangle_count;
const uint32_t buffer_facet_count = const uint32_t buffer_facet_count =
args != NULL ? gmio_size_to_uint32(args->core.memblock.size / GMIO_STLA_FACET_SIZE_P2);
gmio_size_to_uint32(args->memblock.size / GMIO_STLA_FACET_SIZE_P2)
: 0;
const char* opt_solid_name = const char* opt_solid_name =
options != NULL ? options->stla_solid_name : NULL; args->options.stla_solid_name;
const char* solid_name = const char* solid_name =
opt_solid_name != NULL ? opt_solid_name : ""; opt_solid_name != NULL ? opt_solid_name : "";
const enum gmio_float_text_format float32_format = const enum gmio_float_text_format float32_format =
options != NULL ? args->options.stla_float32_format;
options->stla_float32_format :
GMIO_FLOAT_TEXT_FORMAT_DECIMAL_LOWERCASE;
const uint8_t float32_prec = const uint8_t float32_prec =
options != NULL ? options->stla_float32_prec : 9; args->options.stla_float32_prec != 0 ?
args->options.stla_float32_prec :
9;
const gmio_bool_t write_triangles_only = const gmio_bool_t write_triangles_only =
options != NULL ? options->stl_write_triangles_only : GMIO_FALSE; args->options.stl_write_triangles_only;
/* Variables */ /* Variables */
struct gmio_rwargs* core_args = &args->core;
uint32_t ifacet = 0; uint32_t ifacet = 0;
void* mblock_ptr = args != NULL ? args->memblock.ptr : NULL; void* mblock_ptr = core_args->memblock.ptr;
char* buffc = mblock_ptr; char* buffc = mblock_ptr;
char coords_format[64]; char coords_format[64];
int error = GMIO_ERROR_OK; int error = GMIO_ERROR_OK;
/* Check validity of input parameters */ /* Check validity of input parameters */
if (!gmio_check_rwargs(&error, args)) if (!gmio_check_rwargs(&error, core_args))
return error; return error;
if (!gmio_stl_check_mesh(&error, mesh)) if (!gmio_stl_check_mesh(&error, &args->mesh))
return error; return error;
if (float32_prec == 0 || float32_prec > 9) if (float32_prec == 0 || float32_prec > 9)
return GMIO_STL_ERROR_INVALID_FLOAT32_PREC; return GMIO_STL_ERROR_INVALID_FLOAT32_PREC;
if (args->memblock.size < GMIO_STLA_FACET_SIZE_P2) if (core_args->memblock.size < GMIO_STLA_FACET_SIZE_P2)
return GMIO_ERROR_INVALID_MEMBLOCK_SIZE; return GMIO_ERROR_INVALID_MEMBLOCK_SIZE;
{ /* Create XYZ coords format string (for normal and vertex coords) */ { /* Create XYZ coords format string (for normal and vertex coords) */
@ -190,7 +184,7 @@ int gmio_stla_write(
if (!write_triangles_only) { if (!write_triangles_only) {
buffc = gmio_write_rawstr(buffc, "solid "); buffc = gmio_write_rawstr(buffc, "solid ");
buffc = gmio_write_rawstr_eol(buffc, solid_name); buffc = gmio_write_rawstr_eol(buffc, solid_name);
if (!gmio_rwargs_flush_buffer(args, buffc - (char*)mblock_ptr)) if (!gmio_rwargs_flush_buffer(core_args, buffc - (char*)mblock_ptr))
return GMIO_ERROR_STREAM; return GMIO_ERROR_STREAM;
} }
@ -200,14 +194,15 @@ int gmio_stla_write(
ifacet += buffer_facet_count) ifacet += buffer_facet_count)
{ {
const gmio_stl_mesh_func_get_triangle_t func_get_triangle = const gmio_stl_mesh_func_get_triangle_t func_get_triangle =
mesh->func_get_triangle; args->mesh.func_get_triangle;
const void* mesh_cookie = mesh->cookie; const void* mesh_cookie =
args->mesh.cookie;
const uint32_t clamped_facet_count = const uint32_t clamped_facet_count =
GMIO_MIN(ifacet + buffer_facet_count, total_facet_count); GMIO_MIN(ifacet + buffer_facet_count, total_facet_count);
struct gmio_stl_triangle tri; struct gmio_stl_triangle tri;
uint32_t ibuffer_facet; uint32_t ibuffer_facet;
gmio_rwargs_handle_progress(args, ifacet, total_facet_count); gmio_rwargs_handle_progress(core_args, ifacet, total_facet_count);
/* Writing of facets is buffered */ /* Writing of facets is buffered */
buffc = mblock_ptr; buffc = mblock_ptr;
@ -231,19 +226,19 @@ int gmio_stla_write(
buffc = gmio_write_rawstr(buffc, "\nendfacet\n"); buffc = gmio_write_rawstr(buffc, "\nendfacet\n");
} /* end for (ibuffer_facet) */ } /* end for (ibuffer_facet) */
if (!gmio_rwargs_flush_buffer(args, buffc - (char*)mblock_ptr)) if (!gmio_rwargs_flush_buffer(core_args, buffc - (char*)mblock_ptr))
error = GMIO_ERROR_STREAM; error = GMIO_ERROR_STREAM;
/* Task control */ /* Task control */
if (gmio_no_error(error) && gmio_rwargs_is_stop_requested(args)) if (gmio_no_error(error) && gmio_rwargs_is_stop_requested(core_args))
error = GMIO_ERROR_TRANSFER_STOPPED; error = GMIO_ERROR_TRANSFER_STOPPED;
} /* end for (ifacet) */ } /* end for (ifacet) */
/* Write end of solid */ /* Write end of solid */
if (gmio_no_error(error) && !write_triangles_only) { if (gmio_no_error(error) && !write_triangles_only) {
buffc = gmio_write_rawstr(args->memblock.ptr, "endsolid "); buffc = gmio_write_rawstr(mblock_ptr, "endsolid ");
buffc = gmio_write_rawstr_eol(buffc, solid_name); buffc = gmio_write_rawstr_eol(buffc, solid_name);
if (!gmio_rwargs_flush_buffer(args, buffc - (char*)mblock_ptr)) if (!gmio_rwargs_flush_buffer(core_args, buffc - (char*)mblock_ptr))
error = GMIO_ERROR_STREAM; error = GMIO_ERROR_STREAM;
} }

View File

@ -16,19 +16,14 @@
#ifndef GMIO_INTERNAL_STLA_WRITE_H #ifndef GMIO_INTERNAL_STLA_WRITE_H
#define GMIO_INTERNAL_STLA_WRITE_H #define GMIO_INTERNAL_STLA_WRITE_H
struct gmio_rwargs; #include "../stl_rwargs.h"
struct gmio_stl_mesh;
struct gmio_stl_write_options;
/*! Writes geometry in the STL ascii format /*! Writes geometry in the STL ascii format
* *
* \return Error code (see error.h and stl_error.h) * \return Error code (see gmio_core/error.h and stl_error.h)
* \retval GMIO_ERROR_INVALID_MEMBLOCK_SIZE * \retval GMIO_ERROR_INVALID_MEMBLOCK_SIZE
* if <tt>args->memblock.size < 512</tt> * if <tt>args->core.memblock.size < 512</tt>
*/ */
int gmio_stla_write( int gmio_stla_write(struct gmio_stl_write_args* args);
struct gmio_rwargs* args,
const struct gmio_stl_mesh* mesh,
const struct gmio_stl_write_options* options);
#endif /* GMIO_INTERNAL_STLA_WRITE_H */ #endif /* GMIO_INTERNAL_STLA_WRITE_H */

View File

@ -66,25 +66,23 @@ static void gmio_stlb_write_facets(
} }
int gmio_stlb_write( int gmio_stlb_write(
struct gmio_rwargs* args, struct gmio_stl_write_args* args, enum gmio_endianness byte_order)
const struct gmio_stl_mesh* mesh,
const struct gmio_stl_write_options* options,
enum gmio_endianness byte_order)
{ {
/* Constants */ /* Constants */
const uint32_t facet_count = const uint32_t facet_count =
mesh != NULL ? mesh->triangle_count : 0; args->mesh.triangle_count;
const gmio_bool_t write_triangles_only = const gmio_bool_t write_triangles_only =
options != NULL ? options->stl_write_triangles_only : GMIO_FALSE; args->options.stl_write_triangles_only;
/* Variables */ /* Variables */
void* mblock_ptr = args != NULL ? args->memblock.ptr : NULL; struct gmio_rwargs* core_args = &args->core;
void* mblock_ptr = core_args->memblock.ptr;
struct gmio_stlb_readwrite_helper wparams = {0}; struct gmio_stlb_readwrite_helper wparams = {0};
uint32_t i_facet = 0; uint32_t i_facet = 0;
int error = GMIO_ERROR_OK; int error = GMIO_ERROR_OK;
/* Check validity of input parameters */ /* Check validity of input parameters */
if (!gmio_stl_check_mesh(&error, mesh) if (!gmio_stl_check_mesh(&error, &args->mesh)
|| !gmio_stlb_check_params(&error, args, byte_order)) || !gmio_stlb_check_params(&error, core_args, byte_order))
{ {
return error; return error;
} }
@ -95,13 +93,13 @@ int gmio_stlb_write(
/* Note: trsf != NULL certified by gmio_stlb_check_params() */ /* Note: trsf != NULL certified by gmio_stlb_check_params() */
/* coverity[var_deref_op : FALSE] */ /* coverity[var_deref_op : FALSE] */
wparams.facet_count = gmio_size_to_uint32( wparams.facet_count = gmio_size_to_uint32(
args->memblock.size / GMIO_STLB_TRIANGLE_RAWSIZE); core_args->memblock.size / GMIO_STLB_TRIANGLE_RAWSIZE);
if (!write_triangles_only) { if (!write_triangles_only) {
error = gmio_stlb_write_header( error = gmio_stlb_write_header(
&args->stream, &core_args->stream,
byte_order, byte_order,
options != NULL ? options->stlb_header_data : NULL, args->options.stlb_header_data,
facet_count); facet_count);
if (gmio_error(error)) if (gmio_error(error))
return error; return error;
@ -112,18 +110,18 @@ int gmio_stlb_write(
i_facet < facet_count && gmio_no_error(error); i_facet < facet_count && gmio_no_error(error);
i_facet += wparams.facet_count) i_facet += wparams.facet_count)
{ {
gmio_rwargs_handle_progress(args, i_facet, facet_count); gmio_rwargs_handle_progress(core_args, i_facet, facet_count);
/* Write to memory block */ /* Write to memory block */
wparams.facet_count = GMIO_MIN(wparams.facet_count, wparams.facet_count = GMIO_MIN(wparams.facet_count,
facet_count - wparams.i_facet_offset); facet_count - wparams.i_facet_offset);
gmio_stlb_write_facets(mesh, mblock_ptr, &wparams); gmio_stlb_write_facets(&args->mesh, mblock_ptr, &wparams);
wparams.i_facet_offset += wparams.facet_count; wparams.i_facet_offset += wparams.facet_count;
/* Write memory block to stream */ /* Write memory block to stream */
if (gmio_stream_write( if (gmio_stream_write(
&args->stream, &core_args->stream,
mblock_ptr, mblock_ptr,
GMIO_STLB_TRIANGLE_RAWSIZE, GMIO_STLB_TRIANGLE_RAWSIZE,
wparams.facet_count) wparams.facet_count)
@ -133,7 +131,7 @@ int gmio_stlb_write(
} }
/* Handle stop request */ /* Handle stop request */
if (gmio_no_error(error) && gmio_rwargs_is_stop_requested(args)) if (gmio_no_error(error) && gmio_rwargs_is_stop_requested(core_args))
error = GMIO_ERROR_TRANSFER_STOPPED; error = GMIO_ERROR_TRANSFER_STOPPED;
} /* end for */ } /* end for */

View File

@ -16,22 +16,16 @@
#ifndef GMIO_INTERNAL_STLB_WRITE_H #ifndef GMIO_INTERNAL_STLB_WRITE_H
#define GMIO_INTERNAL_STLB_WRITE_H #define GMIO_INTERNAL_STLB_WRITE_H
#include "../stl_rwargs.h"
#include "../../gmio_core/endian.h" #include "../../gmio_core/endian.h"
struct gmio_rwargs;
struct gmio_stl_mesh;
struct gmio_stl_write_options;
/*! Writes geometry in the STL binary format /*! Writes geometry in the STL binary format
* *
* \return Error code (see error.h and stl_error.h) * \return Error code (see error.h and stl_error.h)
* \retval GMIO_INVALID_MEMBLOCK_SIZE_ERROR * \retval GMIO_INVALID_MEMBLOCK_SIZE_ERROR
* if <tt>trsf->memblock.size < GMIO_STLB_MIN_CONTENTS_SIZE</tt> * if <tt>args->core.memblock.size < GMIO_STLB_MIN_CONTENTS_SIZE</tt>
*/ */
int gmio_stlb_write( int gmio_stlb_write(
struct gmio_rwargs* args, struct gmio_stl_write_args* args, enum gmio_endianness byte_order);
const struct gmio_stl_mesh* mesh,
const struct gmio_stl_write_options* options,
enum gmio_endianness byte_order);
#endif /* GMIO_INTERNAL_STLB_WRITE_H */ #endif /* GMIO_INTERNAL_STLB_WRITE_H */

View File

@ -0,0 +1,47 @@
/****************************************************************************
** gmio
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software is a reusable library whose purpose is to provide complete
** I/O support for various CAD file formats (eg. STL)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
/*! \file stl_constants.h
* STL constants
*
* \addtogroup gmio_stl
* @{
*/
#ifndef GMIO_STL_CONSTANTS_H
#define GMIO_STL_CONSTANTS_H
#include "../gmio_core/global.h"
enum gmio_stl_constants
{
/*! Compact size of a struct gmio_stl_coords object */
GMIO_STL_COORDS_RAWSIZE = (3 * sizeof(gmio_float32_t)),
/*! Compact size of a struct gmio_stl_triangle object, STL ascii format */
GMIO_STLA_TRIANGLE_RAWSIZE = (4 * GMIO_STL_COORDS_RAWSIZE),
/*! Compact size of a struct gmio_stl_triangle object, STL binary format */
GMIO_STLB_TRIANGLE_RAWSIZE = (GMIO_STLA_TRIANGLE_RAWSIZE + sizeof(uint16_t)),
/*! Size(in bytes) of the header data for STL binary */
GMIO_STLB_HEADER_SIZE = 80,
/*! Size(in bytes) of the minimum contents possible with the STL binary
* format */
GMIO_STLB_MIN_CONTENTS_SIZE = 284
};
#endif /* GMIO_STL_CONSTANTS_H */

View File

@ -17,72 +17,144 @@
#include "stl_triangle.h" #include "stl_triangle.h"
#include "stlb_header.h" #include "stlb_header.h"
#include "internal/stlb_byte_swap.h"
#include "../gmio_core/endian.h" #include "../gmio_core/endian.h"
#include "../gmio_core/internal/byte_codec.h" #include "../gmio_core/internal/byte_codec.h"
#include "../gmio_core/internal/byte_swap.h" #include "../gmio_core/internal/byte_swap.h"
#include "../gmio_core/internal/helper_stream.h" #include "../gmio_core/internal/helper_stream.h"
#include "../gmio_core/internal/min_max.h" #include "../gmio_core/internal/min_max.h"
#include "../gmio_core/internal/numeric_utils.h"
#include "../gmio_core/internal/string_utils.h" #include "../gmio_core/internal/string_utils.h"
#include <string.h> #include <string.h>
enum { GMIO_FIXED_BUFFER_SIZE = 512 }; enum { GMIO_FIXED_BUFFER_SIZE = 1024 };
GMIO_INLINE gmio_float32_t gmio_sqrlen(const struct gmio_stl_coords* c)
{
const gmio_float32_t cx = c->x;
const gmio_float32_t cy = c->y;
const gmio_float32_t cz = c->z;
return cx*cx + cy*cy + cz*cz;
}
GMIO_INLINE gmio_streamsize_t gmio_stlb_streamsize(uint32_t facet_count) GMIO_INLINE gmio_streamsize_t gmio_stlb_streamsize(uint32_t facet_count)
{ {
return GMIO_STLB_HEADER_SIZE + 4 + facet_count*GMIO_STLB_TRIANGLE_RAWSIZE; return GMIO_STLB_HEADER_SIZE + 4 + facet_count*GMIO_STLB_TRIANGLE_RAWSIZE;
} }
/* Does \p str contains <SPC>token ? */
static gmio_bool_t gmio_str_has_token(const char* str, const char* token)
{
const char* substr = gmio_ascii_istrstr(str, token);
return substr != NULL
&& gmio_ascii_isspace(*(substr - 1));
}
static enum gmio_stl_format gmio_stlb_format(
struct gmio_stream* stream, const uint8_t* buff, size_t read_size)
{
if (read_size >= (GMIO_STLB_HEADER_SIZE + 4)) {
const uint32_t le_facet_count =
gmio_decode_uint32_le((const uint8_t*)buff + 80);
const uint32_t be_facet_count =
gmio_uint32_bswap(le_facet_count);
/* Assume the stream contains one solid */
{
const gmio_streamsize_t stream_size = gmio_stream_size(stream);
if (gmio_stlb_streamsize(le_facet_count) == stream_size)
return GMIO_STL_FORMAT_BINARY_LE;
if (gmio_stlb_streamsize(be_facet_count) == stream_size)
return GMIO_STL_FORMAT_BINARY_BE;
}
/* Tests failed, maybe the stream is made of multiple solids ?
* Investigate to check if first facet's normal has length ~1.f
*/
if (le_facet_count > 0) {
struct gmio_stl_triangle tri;
memcpy(&tri,
buff + GMIO_STLB_HEADER_SIZE + 4,
GMIO_STLB_TRIANGLE_RAWSIZE);
if (gmio_float32_ulp_equals(gmio_sqrlen(&tri.normal), 1.f, 100)) {
#ifdef GMIO_HOST_IS_BIG_ENDIAN
return GMIO_STL_FORMAT_BINARY_BE;
#else
return GMIO_STL_FORMAT_BINARY_LE;
#endif
}
gmio_stl_triangle_bswap(&tri);
if (gmio_float32_ulp_equals(gmio_sqrlen(&tri.normal), 1.f, 100)) {
#ifdef GMIO_HOST_IS_BIG_ENDIAN
return GMIO_STL_FORMAT_BINARY_LE;
#else
return GMIO_STL_FORMAT_BINARY_BE;
#endif
}
}
}
return GMIO_STL_FORMAT_UNKNOWN;
}
static gmio_bool_t gmio_is_stl_ascii(const char* buff, size_t buff_len)
{
/* Skip spaces at beginning */
size_t pos = 0;
while (pos < buff_len && gmio_ascii_isspace(buff[pos]))
++pos;
/* Next token (if exists) must match "solid\s" */
if ((pos + 6) < buff_len
&& gmio_ascii_istarts_with(buff + pos, "solid")
&& gmio_ascii_isspace(buff[pos + 5]))
{
/* Try to find some STL ascii keyword */
pos += 6;
if (gmio_str_has_token(buff + pos, "facet")
|| gmio_str_has_token(buff + pos, "endsolid"))
{
return GMIO_TRUE;
}
}
return GMIO_FALSE;
}
enum gmio_stl_format gmio_stl_get_format(struct gmio_stream *stream) enum gmio_stl_format gmio_stl_get_format(struct gmio_stream *stream)
{ {
char fixed_buffer[GMIO_FIXED_BUFFER_SIZE] = {0}; char buff[GMIO_FIXED_BUFFER_SIZE] = {0};
size_t read_size = 0; size_t read_size = 0;
struct gmio_streampos stream_start_pos = gmio_streampos_null(); struct gmio_streampos stream_start_pos = {0};
if (stream == NULL) if (stream == NULL)
return GMIO_STL_FORMAT_UNKNOWN; return GMIO_STL_FORMAT_UNKNOWN;
/* Read a chunk of bytes from stream, then try to find format from that /* Read a chunk of bytes from stream, then try to find format from that.
*
* First keep stream start position, it will be restored after read * First keep stream start position, it will be restored after read
*/ */
gmio_stream_get_pos(stream, &stream_start_pos); gmio_stream_get_pos(stream, &stream_start_pos);
read_size = gmio_stream_read(stream, &fixed_buffer, 1, GMIO_FIXED_BUFFER_SIZE); read_size = gmio_stream_read(stream, &buff, 1, GMIO_FIXED_BUFFER_SIZE);
read_size = GMIO_MIN(read_size, GMIO_FIXED_BUFFER_SIZE); read_size = GMIO_MIN(read_size, GMIO_FIXED_BUFFER_SIZE);
gmio_stream_set_pos(stream, &stream_start_pos); gmio_stream_set_pos(stream, &stream_start_pos);
/* Binary STL ? */ /* Binary STL ? */
if (read_size >= (GMIO_STLB_HEADER_SIZE + 4)) { {
const gmio_streamsize_t stream_size = gmio_stream_size(stream); const enum gmio_stl_format format =
gmio_stlb_format(stream, (const uint8_t*)buff, read_size);
/* Try with little-endian format */ if (format != GMIO_STL_FORMAT_UNKNOWN)
uint32_t facet_count = return format;
gmio_decode_uint32_le((const uint8_t*)fixed_buffer + 80);
if (gmio_stlb_streamsize(facet_count) == stream_size)
return GMIO_STL_FORMAT_BINARY_LE;
/* Try with big-endian format */
facet_count = gmio_uint32_bswap(facet_count);
if (gmio_stlb_streamsize(facet_count) == stream_size)
return GMIO_STL_FORMAT_BINARY_BE;
} }
/* ASCII STL ? */ /* ASCII STL ? */
{ if (read_size > 0) {
/* Skip spaces at beginning */ /* End buffer with null char for the sake of gmio_str_has_token() */
size_t pos = 0; const size_t buff_last_i =
while (pos < read_size && gmio_ascii_isspace(fixed_buffer[pos])) GMIO_MIN(read_size, GMIO_FIXED_BUFFER_SIZE - 1);
++pos; buff[buff_last_i] = 0;
if (gmio_is_stl_ascii(buff, read_size))
/* Next token (if exists) must match "solid\s" */
if ((pos + 6) < read_size
&& gmio_ascii_istarts_with(fixed_buffer + pos, "solid")
&& gmio_ascii_isspace(fixed_buffer[pos + 5]))
{
return GMIO_STL_FORMAT_ASCII; return GMIO_STL_FORMAT_ASCII;
}
} }
/* Fallback case */ /* Fallback case */

View File

@ -13,7 +13,7 @@
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html". ** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/ ****************************************************************************/
#include "stla_stats.h" #include "stl_infos.h"
#include "../gmio_core/error.h" #include "../gmio_core/error.h"
#include "../gmio_core/internal/string.h" #include "../gmio_core/internal/string.h"
@ -57,31 +57,39 @@ enum {
BUFF_OVERLAP_SIZE_DIV2 = BUFF_OVERLAP_SIZE / 2 BUFF_OVERLAP_SIZE_DIV2 = BUFF_OVERLAP_SIZE / 2
}; };
struct gmio_stla_stats gmio_stla_stats_get( int gmio_stl_infos_get(
struct gmio_rwargs* args, unsigned stat_flags) struct gmio_stl_infos_get_args* args,
struct gmio_stl_infos* infos,
unsigned flags)
{ {
struct gmio_stla_stats stats = {0}; int error = GMIO_ERROR_OK;
return error;
#if 0
struct gmio_stream* stream = args ? &args->stream : NULL; struct gmio_stream* stream = args ? &args->stream : NULL;
void* mblock_ptr = args != NULL ? args->memblock.ptr : NULL; void* mblock_ptr = args != NULL ? args->memblock.ptr : NULL;
/* Leave one byte to end the string buffer with 0 */ /* Leave one byte to end the string buffer with 0 */
const size_t mblock_size = args != NULL ? args->memblock.size - 1: 0; const size_t mblock_size = args != NULL ? args->memblock.size - 1: 0;
struct gmio_string strbuff = gmio_string(mblock_ptr, 0, mblock_size); struct gmio_string strbuff = gmio_string(mblock_ptr, 0, mblock_size);
const gmio_bool_t flag_size =
(stat_flags & GMIO_STLA_STAT_FLAG_SIZE) != 0;
const gmio_bool_t flag_avg_facet_size =
(stat_flags & GMIO_STLA_STAT_FLAG_AVERAGE_FACET_SIZE) != 0;
const gmio_bool_t flag_facet_count = const gmio_bool_t flag_facet_count =
(stat_flags & GMIO_STLA_STAT_FLAG_FACET_COUNT) != 0; (flags & GMIO_STL_INFO_FLAG_FACET_COUNT) != 0;
const gmio_bool_t flag_size =
(flags & GMIO_STL_INFO_FLAG_SIZE) != 0;
const gmio_bool_t flag_stla_solidname =
(flags & GMIO_STLA_INFO_FLAG_SOLIDNAME) != 0;
const gmio_bool_t flag_stlb_header =
(flags & GMIO_STLB_INFO_FLAG_HEADER) != 0;
int err = GMIO_ERROR_OK; int err = GMIO_ERROR_OK;
/* Check validity of input transfer object */ /* Check validity of input transfer object */
if (!gmio_check_rwargs(&err, args)) if (!gmio_check_rwargs(&err, args))
return stats; return err;
if (stat_flags != 0) { if (stat_flags != 0) {
/* 'overlap' stores the ending/starting bytes of the previous/current
* stream buffers(memblock) */
char overlap[14] = {0}; /* 14 == 2*(strlen("endfacet") - 1) */ char overlap[14] = {0}; /* 14 == 2*(strlen("endfacet") - 1) */
gmio_bool_t endsolid_found = GMIO_FALSE; gmio_bool_t endsolid_found = GMIO_FALSE;
@ -122,11 +130,11 @@ struct gmio_stla_stats gmio_stla_stats_get(
/* Note: strlen("endsolid") == 8 */ /* Note: strlen("endsolid") == 8 */
if (endsolid_found) { if (endsolid_found) {
if (!endsolid_in_overlap) if (!endsolid_in_overlap)
stats.size += (substr_at - strbuff.ptr) + 8; stats->size += (substr_at - strbuff.ptr) + 8;
/* TODO : gérer le cas où "endsolid" se trouve dans overlap */ /* TODO : gérer le cas où "endsolid" se trouve dans overlap */
} }
else { else {
stats.size += read_size; stats->size += read_size;
} }
} }
@ -138,9 +146,9 @@ struct gmio_stla_stats gmio_stla_stats_get(
const gmio_bool_t endfacet_in_overlap = const gmio_bool_t endfacet_in_overlap =
overlap_has_contents overlap_has_contents
&& strstr(overlap, "endfacet") != NULL; && strstr(overlap, "endfacet") != NULL;
stats.facet_count += endfacet_in_overlap ? 1 : 0; stats->facet_count += endfacet_in_overlap ? 1 : 0;
/* Check in memblock */ /* Check in memblock */
stats.facet_count += stla_facet_count(&strbuff, endsolid_ptr); stats->facet_count += stla_facet_count(&strbuff, endsolid_ptr);
} }
/* Copy second half of overlap buffer */ /* Copy second half of overlap buffer */
@ -153,5 +161,6 @@ struct gmio_stla_stats gmio_stla_stats_get(
} }
} }
return stats; return err;
#endif
} }

101
src/gmio_stl/stl_infos.h Normal file
View File

@ -0,0 +1,101 @@
/****************************************************************************
** gmio
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software is a reusable library whose purpose is to provide complete
** I/O support for various CAD file formats (eg. STL)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
/*! \file stl_infos.h
* TODO: description
*
* \addtogroup gmio_stl
* @{
*/
#ifndef GMIO_STL_INFOS_H
#define GMIO_STL_INFOS_H
#include "stl_global.h"
#include "../gmio_core/rwargs.h"
#include "../gmio_core/internal/helper_stream.h"
#include <stddef.h>
#include "stl_format.h"
#include "stlb_header.h"
struct gmio_stl_infos
{
/*! Count of facets(triangles) */
uint32_t facet_count;
/*! Size of the STL data in bytes
*
* For STL ascii it includes the "endsolid" tag */
size_t size;
/*! STL ascii only: name of the solid, the pointer has to be set by the
* caller of gmio_stl_infos_get() */
char* stla_solidname;
/*! STL ascii only: maximum length(capacity) of stla_solidname, it has to be
* set by the caller of gmio_stl_infos_get()
*/
size_t stla_solidname_maxlen;
/*! STL binary only */
struct gmio_stlb_header stlb_header;
};
/*! Flags(OR-combinations) for each STL info */
enum gmio_stl_info_flag
{
/*! -> gmio_stl_infos::facet_count */
GMIO_STL_INFO_FLAG_FACET_COUNT = 0x0001,
/*! -> gmio_stl_infos::size */
GMIO_STL_INFO_FLAG_SIZE = 0x0002,
/*! -> gmio_stl_infos::stla_solidname */
GMIO_STLA_INFO_FLAG_SOLIDNAME = 0x0004,
/*! -> gmio_stl_infos::stlb_header */
GMIO_STLB_INFO_FLAG_HEADER = 0x0008,
/*! -> gmio_stl_infos::stla_solidname or gmio_stl_infos::stlb_header */
GMIO_STL_INFO_FLAG_SOLIDNAME_OR_HEADER =
GMIO_STLA_INFO_FLAG_SOLIDNAME | GMIO_STLB_INFO_FLAG_HEADER,
/*! All infos */
GMIO_STL_INFO_FLAG_ALL = 0xFFFF
};
struct gmio_stl_infos_get_args
{
enum gmio_stl_format format;
struct gmio_stream stream;
struct gmio_memblock memblock;
};
GMIO_C_LINKAGE_BEGIN
GMIO_LIBSTL_EXPORT
int gmio_stl_infos_get(
struct gmio_stl_infos_get_args* args,
struct gmio_stl_infos* infos,
unsigned flags);
GMIO_C_LINKAGE_END
#endif /* GMIO_STL_INFOS_H */
/*! @} */

View File

@ -16,151 +16,126 @@
#include "stl_io.h" #include "stl_io.h"
#include "stl_error.h" #include "stl_error.h"
#include "stl_format.h"
#include "stlb_header.h"
#include "internal/stla_write.h" #include "internal/stla_write.h"
#include "internal/stlb_write.h" #include "internal/stlb_write.h"
#include "../gmio_core/error.h" #include "../gmio_core/error.h"
#include "../gmio_core/rwargs.h"
#include "../gmio_core/stream.h"
#include "../gmio_core/internal/byte_codec.h" #include "../gmio_core/internal/byte_codec.h"
#include "../gmio_core/internal/helper_stream.h" #include "../gmio_core/internal/helper_stream.h"
int gmio_stl_read_file( int gmio_stl_read(struct gmio_stl_read_args* args)
const char* filepath,
struct gmio_rwargs* args,
struct gmio_stl_mesh_creator* creator)
{ {
int error = GMIO_ERROR_OK; int error = GMIO_ERROR_OK;
FILE* file = NULL;
file = fopen(filepath, "rb");
if (file != NULL) {
struct gmio_rwargs local_args =
args != NULL ? *args : gmio_rwargs_null();
const gmio_bool_t memblock_allocated =
local_args.memblock.ptr == NULL;
if (memblock_allocated)
local_args.memblock = gmio_memblock_default();
local_args.stream = gmio_stream_stdio(file);
error = gmio_stl_read(&local_args, creator);
fclose(file);
if (memblock_allocated)
gmio_memblock_deallocate(&local_args.memblock);
}
else {
error = GMIO_ERROR_STDIO;
}
return error;
}
int gmio_stl_read(
struct gmio_rwargs* args,
struct gmio_stl_mesh_creator* creator)
{
int error = GMIO_ERROR_OK;
if (args != NULL) { if (args != NULL) {
const enum gmio_stl_format stl_format = const enum gmio_stl_format stl_format =
gmio_stl_get_format(&args->stream); gmio_stl_get_format(&args->core.stream);
switch (stl_format) { switch (stl_format) {
case GMIO_STL_FORMAT_ASCII: { case GMIO_STL_FORMAT_ASCII: {
error = gmio_stla_read(args, creator); error = gmio_stla_read(args);
break; break;
} }
case GMIO_STL_FORMAT_BINARY_BE: { case GMIO_STL_FORMAT_BINARY_BE: {
error = gmio_stlb_read(args, creator, GMIO_ENDIANNESS_BIG); error = gmio_stlb_read(args, GMIO_ENDIANNESS_BIG);
break; break;
} }
case GMIO_STL_FORMAT_BINARY_LE: { case GMIO_STL_FORMAT_BINARY_LE: {
error = gmio_stlb_read(args, creator, GMIO_ENDIANNESS_LITTLE); error = gmio_stlb_read(args, GMIO_ENDIANNESS_LITTLE);
break; break;
} }
case GMIO_STL_FORMAT_UNKNOWN: { case GMIO_STL_FORMAT_UNKNOWN: {
error = GMIO_STL_ERROR_UNKNOWN_FORMAT; error = GMIO_STL_ERROR_UNKNOWN_FORMAT;
break;
} }
} /* end switch() */ } /* end switch() */
} }
else { else {
error = GMIO_ERROR_NULL_RWARGS; error = GMIO_ERROR_NULL_RWARGS;
} }
return error;
}
int gmio_stl_read_file(
struct gmio_stl_read_args* args, const char* filepath)
{
int error = GMIO_ERROR_OK;
if (args != NULL) {
FILE* file = fopen(filepath, "rb");
if (file != NULL) {
const gmio_bool_t mem_allocated = args->core.memblock.ptr == NULL;
if (mem_allocated)
args->core.memblock = gmio_memblock_default();
args->core.stream = gmio_stream_stdio(file);
error = gmio_stl_read(args);
fclose(file);
if (mem_allocated)
gmio_memblock_deallocate(&args->core.memblock);
}
else {
error = GMIO_ERROR_STDIO;
}
}
else {
error = GMIO_ERROR_NULL_RWARGS;
}
return error;
}
int gmio_stl_write(struct gmio_stl_write_args* args)
{
int error = GMIO_ERROR_OK;
if (args != NULL) {
const gmio_bool_t mem_allocated = args->core.memblock.ptr == NULL;
if (mem_allocated)
args->core.memblock = gmio_memblock_default();
switch (args->format) {
case GMIO_STL_FORMAT_ASCII: {
error = gmio_stla_write(args);
break;
}
case GMIO_STL_FORMAT_BINARY_BE: {
error = gmio_stlb_write(args, GMIO_ENDIANNESS_BIG);
break;
}
case GMIO_STL_FORMAT_BINARY_LE: {
error = gmio_stlb_write(args, GMIO_ENDIANNESS_LITTLE);
break;
}
case GMIO_STL_FORMAT_UNKNOWN: {
error = GMIO_STL_ERROR_UNKNOWN_FORMAT;
}
} /* end switch() */
if (mem_allocated)
gmio_memblock_deallocate(&args->core.memblock);
}
else {
error = GMIO_ERROR_NULL_RWARGS;
}
return error; return error;
} }
int gmio_stl_write_file( int gmio_stl_write_file(
const char* filepath, struct gmio_stl_write_args* args, const char* filepath)
struct gmio_rwargs* args,
const struct gmio_stl_mesh* mesh,
enum gmio_stl_format format,
const struct gmio_stl_write_options* options)
{ {
int error = GMIO_ERROR_OK; int error = GMIO_ERROR_OK;
FILE* file = NULL;
file = fopen(filepath, "wb");
if (file != NULL) {
struct gmio_rwargs local_args =
args != NULL ? *args : gmio_rwargs_null();
const gmio_bool_t memblock_allocated =
local_args.memblock.ptr == NULL;
if (memblock_allocated)
local_args.memblock = gmio_memblock_default();
local_args.stream = gmio_stream_stdio(file);
error = gmio_stl_write(&local_args, mesh, format, options);
fclose(file);
if (memblock_allocated)
gmio_memblock_deallocate(&local_args.memblock);
}
else {
error = GMIO_ERROR_STDIO;
}
return error;
}
int gmio_stl_write(
struct gmio_rwargs* args,
const struct gmio_stl_mesh* mesh,
enum gmio_stl_format format,
const struct gmio_stl_write_options* options)
{
int error = GMIO_ERROR_OK;
if (args != NULL) { if (args != NULL) {
const gmio_bool_t memblock_allocated = args->memblock.ptr == NULL; FILE* file = fopen(filepath, "wb");
if (memblock_allocated) if (file != NULL) {
args->memblock = gmio_memblock_default(); const gmio_bool_t mem_allocated = args->core.memblock.ptr == NULL;
if (mem_allocated)
switch (format) { args->core.memblock = gmio_memblock_default();
case GMIO_STL_FORMAT_ASCII: { args->core.stream = gmio_stream_stdio(file);
error = gmio_stla_write(args, mesh, options); error = gmio_stl_write(args);
break; fclose(file);
if (mem_allocated)
gmio_memblock_deallocate(&args->core.memblock);
} }
case GMIO_STL_FORMAT_BINARY_BE: { else {
error = gmio_stlb_write(args, mesh, options, GMIO_ENDIANNESS_BIG); error = GMIO_ERROR_STDIO;
break;
} }
case GMIO_STL_FORMAT_BINARY_LE: {
error = gmio_stlb_write(args, mesh, options, GMIO_ENDIANNESS_LITTLE);
break;
}
case GMIO_STL_FORMAT_UNKNOWN: {
error = GMIO_STL_ERROR_UNKNOWN_FORMAT;
}
} /* end switch() */
if (memblock_allocated)
gmio_memblock_deallocate(&args->memblock);
} }
else { else {
error = GMIO_ERROR_NULL_RWARGS; error = GMIO_ERROR_NULL_RWARGS;
} }
return error; return error;
} }

View File

@ -24,143 +24,67 @@
#define GMIO_STL_IO_H #define GMIO_STL_IO_H
#include "stl_global.h" #include "stl_global.h"
#include "stl_format.h" #include "stl_rwargs.h"
#include "../gmio_core/endian.h" #include "../gmio_core/endian.h"
struct gmio_rwargs;
struct gmio_stream;
struct gmio_stl_mesh;
struct gmio_stl_mesh_creator;
struct gmio_stl_write_options;
struct gmio_stlb_header;
GMIO_C_LINKAGE_BEGIN GMIO_C_LINKAGE_BEGIN
/*! Reads STL mesh from file, format is automatically guessed
*
* \return Error code (see error.h and stl_error.h)
*/
GMIO_LIBSTL_EXPORT
int gmio_stl_read_file(
/*! Path to the STL file.
* A stream is opened with fopen() so the string shall follow the file
* name specifications of the running environment */
const char* filepath,
/*! Common objects needed for the read operation
* gmio_read_args::stream is internally initialized with the
* builtin stream wrapper around \c FILE* (see gmio_stream_stdio()) */
struct gmio_rwargs* args,
/*! Defines the callbacks for the mesh creation */
struct gmio_stl_mesh_creator* creator
);
/*! Reads STL mesh from stream, format is automatically guessed /*! Reads STL mesh from stream, format is automatically guessed
* *
* \return Error code (see error.h and stl_error.h) * \return Error code (see gmio_core/error.h and stl_error.h)
*/ */
GMIO_LIBSTL_EXPORT GMIO_LIBSTL_EXPORT
int gmio_stl_read( int gmio_stl_read(struct gmio_stl_read_args* args);
/*! Common objects needed for the read operation */
struct gmio_rwargs* args,
/*! Defines the callbacks for the mesh creation */ /*! Reads STL mesh from a file, format is automatically guessed
struct gmio_stl_mesh_creator* creator
);
/*! Writes STL mesh to file
* *
* Internally, it uses: * This is just a facility function over gmio_stl_read(). The stream object
* \li the builtin stream wrapper around FILE* (see gmio_stream_stdio()) * pointed to by \c args->core.stream is automatically initialized to read file
* \li the global default function to construct a temporary gmio_memblock * at \p filepath (see gmio_stream_stdio(FILE*))
* object (see gmio_memblock_default())
* *
* \return Error code (see error.h and stl_error.h) * The file is opened with fopen() so \p filepath shall follow the file name
* specifications of the running environment
*/ */
GMIO_LIBSTL_EXPORT GMIO_LIBSTL_EXPORT
int gmio_stl_write_file( int gmio_stl_read_file(struct gmio_stl_read_args* args, const char* filepath);
/*! Path to the STL file. A stream is opened with fopen() so the string
* shall follow the file name specifications of the running
* environment */
const char* filepath,
/*! Common objects needed for the write operation
* gmio_read_args::stream is internally initialized with the
* builtin stream wrapper around \c FILE* (see gmio_stream_stdio()) */
struct gmio_rwargs* args,
/*! Defines the mesh to output */
const struct gmio_stl_mesh* mesh,
/*! STL format of the output file */
enum gmio_stl_format format,
/*! Options for the write operation, can be safely set to NULL to use
* default values */
const struct gmio_stl_write_options* options
);
/*! Writes STL mesh to stream
*
* \return Error code (see error.h and stl_error.h)
*/
GMIO_LIBSTL_EXPORT
int gmio_stl_write(
/*! Common objects needed for the write operation */
struct gmio_rwargs* args,
/*! Defines the mesh to output */
const struct gmio_stl_mesh* mesh,
/*! STL format of the output */
enum gmio_stl_format format,
/*! Options for the write operation, can be safely set to NULL to use
* default values */
const struct gmio_stl_write_options* options
);
/*! Reads geometry from STL ascii stream /*! Reads geometry from STL ascii stream
* *
* Stream size is passed to gmio_task_iface::func_handle_progress() as the * \return Error code (see gmio_core/error.h and stl_error.h)
* \p max_value argument.
*
* Possible options in a future version could be:
* - flag to force locale ?
* - case sensitive/insensitive ?
*
* \return Error code (see error.h and stl_error.h)
*/ */
GMIO_LIBSTL_EXPORT GMIO_LIBSTL_EXPORT
int gmio_stla_read( int gmio_stla_read(struct gmio_stl_read_args* args);
/*! Common objects needed for the read operation */
struct gmio_rwargs* args,
/*! Defines the callbacks for the mesh creation */
struct gmio_stl_mesh_creator* creator
);
/*! Size(in bytes) of the minimum contents possible with the STL binary format */
enum { GMIO_STLB_MIN_CONTENTS_SIZE = 284 };
/*! Reads geometry from STL binary stream /*! Reads geometry from STL binary stream
* *
* \return Error code (see error.h and stl_error.h) * \return Error code (see gmio_core/error.h and stl_error.h)
* \retval GMIO_ERROR_INVALID_MEMBLOCK_SIZE * \retval GMIO_ERROR_INVALID_MEMBLOCK_SIZE
* if <tt>trsf->memblock.size < GMIO_STLB_MIN_CONTENTS_SIZE</tt> * if <tt>args->core.memblock.size < GMIO_STLB_MIN_CONTENTS_SIZE</tt>
*/ */
GMIO_LIBSTL_EXPORT GMIO_LIBSTL_EXPORT
int gmio_stlb_read( int gmio_stlb_read(
/*! Common objects needed for the read operation */ struct gmio_stl_read_args* args, enum gmio_endianness byte_order);
struct gmio_rwargs* args,
/*! Defines the callbacks for the mesh creation */ /*! Writes STL mesh to stream
struct gmio_stl_mesh_creator* creator, *
* \return Error code (see gmio_core/error.h and stl_error.h)
*/
GMIO_LIBSTL_EXPORT
int gmio_stl_write(struct gmio_stl_write_args* args);
/*! Byte order of the input STL binary data */ /*! Writes STL mesh to stream
enum gmio_endianness byte_order *
); * This is just a facility function over gmio_stl_write(). The stream object
* pointed to by \c args->core.stream is automatically initialized to write
* file to \p filepath (see gmio_stream_stdio(FILE*))
*
* The file is opened with fopen() so \p filepath shall follow the file name
* specifications of the running environment
* \return Error code (see gmio_core/error.h and stl_error.h)
*/
GMIO_LIBSTL_EXPORT
int gmio_stl_write_file(struct gmio_stl_write_args* args, const char* filepath);
/*! Writes STL binary header data to stream /*! Writes STL binary header data to stream
* *

View File

@ -49,7 +49,9 @@ struct gmio_stl_mesh_creator
* stream * stream
*/ */
void (*func_ascii_begin_solid)( void (*func_ascii_begin_solid)(
void* cookie, gmio_streamsize_t stream_size, const char* solid_name); void* cookie,
gmio_streamsize_t stream_size,
const char* solid_name);
/*! Pointer on a function that handles declaration of a mesh with /*! Pointer on a function that handles declaration of a mesh with
* \p tri_count number of triangles * \p tri_count number of triangles
@ -66,7 +68,8 @@ struct gmio_stl_mesh_creator
/*! Pointer on a function that adds a triangle to the user mesh /*! Pointer on a function that adds a triangle to the user mesh
* *
* The argument \p triangle is the triangle to be added, note that * The argument \p triangle is the triangle to be added, note that
* struct gmio_stl_triangle::attribute_byte_count is meaningless for STL ascii. * struct gmio_stl_triangle::attribute_byte_count is meaningless for STL
* ascii.
* *
* The argument \p tri_id is the index of the mesh triangle * The argument \p tri_id is the index of the mesh triangle
*/ */

73
src/gmio_stl/stl_rwargs.h Normal file
View File

@ -0,0 +1,73 @@
/****************************************************************************
** gmio
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software is a reusable library whose purpose is to provide complete
** I/O support for various CAD file formats (eg. STL)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
/*! \file stl_rwargs.h
* Read/write structures for STL
*
* \addtogroup gmio_stl
* @{
*/
#ifndef GMIO_STL_RWARGS_H
#define GMIO_STL_RWARGS_H
#include "stl_format.h"
#include "stl_global.h"
#include "stl_io_options.h"
#include "stl_mesh.h"
#include "stl_mesh_creator.h"
#include "../gmio_core/rwargs.h"
/*! Arguments for STL read functions */
struct gmio_stl_read_args
{
/*! Common(core) objects needed for the read operation */
struct gmio_rwargs core;
/*! Defines the callbacks for the mesh creation */
struct gmio_stl_mesh_creator mesh_creator;
/*! Optional pointer to a function that returns the size(in bytes) of the
* STL ascii data to read
*
* Useful only with STL ascii format. If set to NULL then by default the
* gmio_stream::func_size() is called.
*
* The resulting stream size is passed to
* gmio_task_iface::func_handle_progress() as the \p max_value argument.
*/
gmio_streamsize_t (*func_stla_get_streamsize)(
struct gmio_stream* stream,
struct gmio_memblock* memblock);
};
/*! Arguments for STL write functions */
struct gmio_stl_write_args
{
/*! Common(core) objects needed for the write operation */
struct gmio_rwargs core;
/*! Defines the mesh to output */
struct gmio_stl_mesh mesh;
/*! STL format of the output */
enum gmio_stl_format format;
/*! Options for the write operation, can be safely set to \c {0} to use
* default values */
struct gmio_stl_write_options options;
};
#endif /* GMIO_STL_RWARGS_H */

View File

@ -45,17 +45,5 @@ struct gmio_stl_triangle
uint16_t attribute_byte_count; /*!< Useful only for STL binary format */ uint16_t attribute_byte_count; /*!< Useful only for STL binary format */
}; };
/*! Constants for STL triangles */
enum {
/*! Compact size of a struct gmio_stl_coords object */
GMIO_STL_COORDS_RAWSIZE = (3 * sizeof(gmio_float32_t)),
/*! Compact size of a struct gmio_stl_triangle object for STL ascii format */
GMIO_STLA_TRIANGLE_RAWSIZE = (4 * GMIO_STL_COORDS_RAWSIZE),
/*! Compact size of a struct gmio_stl_triangle object for STL binary format */
GMIO_STLB_TRIANGLE_RAWSIZE = (GMIO_STLA_TRIANGLE_RAWSIZE + sizeof(uint16_t))
};
#endif /* GMIO_STL_TRIANGLE_H */ #endif /* GMIO_STL_TRIANGLE_H */
/*! @} */ /*! @} */

View File

@ -145,41 +145,39 @@ static void gmio_stringstream_stla_read_hook(
/* Root function, parses a whole solid */ /* Root function, parses a whole solid */
static void parse_solid(struct gmio_stla_parse_data* data); static void parse_solid(struct gmio_stla_parse_data* data);
int gmio_stla_read( int gmio_stla_read(struct gmio_stl_read_args* args)
struct gmio_rwargs* args,
struct gmio_stl_mesh_creator* creator)
{ {
struct gmio_rwargs* core_args = &args->core;
char fixed_buffer[GMIO_STLA_READ_STRING_MAX_LEN]; char fixed_buffer[GMIO_STLA_READ_STRING_MAX_LEN];
struct gmio_stla_parse_data parse_data; struct gmio_stla_parse_data parse_data;
{ /* Check validity of input parameters */ { /* Check validity of input parameters */
int error = GMIO_ERROR_OK; int error = GMIO_ERROR_OK;
if (!gmio_check_rwargs(&error, args)) if (!gmio_check_rwargs(&error, core_args))
return error; return error;
} }
parse_data.token = unknown_token; parse_data.token = unknown_token;
parse_data.error = GMIO_FALSE; parse_data.error = GMIO_FALSE;
parse_data.strstream_cookie.rwargs = args; parse_data.strstream_cookie.rwargs = core_args;
parse_data.strstream_cookie.stream_offset = 0; parse_data.strstream_cookie.stream_offset = 0;
parse_data.strstream_cookie.stream_size = parse_data.strstream_cookie.stream_size =
gmio_stream_size(&args->stream); gmio_stream_size(&core_args->stream);
parse_data.strstream_cookie.is_stop_requested = GMIO_FALSE; parse_data.strstream_cookie.is_stop_requested = GMIO_FALSE;
parse_data.strstream.stream = args->stream; parse_data.strstream.stream = core_args->stream;
parse_data.strstream.strbuff.ptr = args->memblock.ptr; parse_data.strstream.strbuff.ptr = core_args->memblock.ptr;
parse_data.strstream.strbuff.max_len = args->memblock.size; parse_data.strstream.strbuff.max_len = core_args->memblock.size;
parse_data.strstream.cookie = &parse_data.strstream_cookie; parse_data.strstream.cookie = &parse_data.strstream_cookie;
parse_data.strstream.func_stream_read_hook = parse_data.strstream.func_stream_read_hook = gmio_stringstream_stla_read_hook;
gmio_stringstream_stla_read_hook;
gmio_stringstream_init(&parse_data.strstream); gmio_stringstream_init(&parse_data.strstream);
parse_data.string_buffer.ptr = &fixed_buffer[0]; parse_data.string_buffer.ptr = &fixed_buffer[0];
parse_data.string_buffer.len = 0; parse_data.string_buffer.len = 0;
parse_data.string_buffer.max_len = GMIO_STLA_READ_STRING_MAX_LEN; parse_data.string_buffer.max_len = GMIO_STLA_READ_STRING_MAX_LEN;
parse_data.creator = creator; parse_data.creator = &args->mesh_creator;
parse_solid(&parse_data); parse_solid(&parse_data);

View File

@ -1,65 +0,0 @@
/****************************************************************************
** gmio
** Copyright Fougue (2 Mar. 2015)
** contact@fougue.pro
**
** This software is a reusable library whose purpose is to provide complete
** I/O support for various CAD file formats (eg. STL)
**
** This software is governed by the CeCILL-B license under French law and
** abiding by the rules of distribution of free software. You can use,
** modify and/ or redistribute the software under the terms of the CeCILL-B
** license as circulated by CEA, CNRS and INRIA at the following URL
** "http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.html".
****************************************************************************/
/*! \file stla_stats.h
* TODO: description
*
* \addtogroup gmio_stl
* @{
*/
#ifndef GMIO_STLA_STATS_H
#define GMIO_STLA_STATS_H
#include "stl_global.h"
#include "../gmio_core/rwargs.h"
#include "../gmio_core/internal/helper_stream.h"
#include <stddef.h>
/*! Statistics of some STL ascii contents(eg. file) */
struct gmio_stla_stats
{
/*! Count of facets(triangles) */
uint32_t facet_count;
/*! Average facet size in bytes(what's in between facet ... endfacet
* including the tags) */
size_t average_facet_size;
/*! Size of the STL ascii contents in bytes, including the "endsolid" tag */
size_t size;
};
/*! Flags(OR-combinations) for each STL ascii statistic */
enum gmio_stla_stat_flag
{
GMIO_STLA_STAT_FLAG_FACET_COUNT = 0x01,
GMIO_STLA_STAT_FLAG_AVERAGE_FACET_SIZE = 0x02,
GMIO_STLA_STAT_FLAG_SIZE = 0x04,
GMIO_STLA_STAT_FLAG_ALL = 0xFF
};
GMIO_C_LINKAGE_BEGIN
GMIO_LIBSTL_EXPORT
struct gmio_stla_stats gmio_stla_stats_get(
struct gmio_rwargs* args, unsigned stat_flags);
GMIO_C_LINKAGE_END
#endif /* GMIO_STLA_STATS_H */
/*! @} */

View File

@ -24,9 +24,7 @@
#define GMIO_STLB_HEADER_H #define GMIO_STLB_HEADER_H
#include "stl_global.h" #include "stl_global.h"
#include "stl_constants.h"
/*! Size(in bytes) of the header data for STL binary */
enum { GMIO_STLB_HEADER_SIZE = 80 };
/*! 80-byte data at the beginning of any STL binary file */ /*! 80-byte data at the beginning of any STL binary file */
struct gmio_stlb_header struct gmio_stlb_header

View File

@ -70,25 +70,28 @@ static void gmio_stlb_read_facets(
} }
int gmio_stlb_read( int gmio_stlb_read(
struct gmio_rwargs* args, struct gmio_stl_read_args* args, enum gmio_endianness byte_order)
struct gmio_stl_mesh_creator *creator,
enum gmio_endianness byte_order)
{ {
/* Constants */ /* Constants */
const uint32_t max_facet_count_per_read = const uint32_t max_facet_count_per_read =
args != NULL ? args != NULL ?
gmio_size_to_uint32( gmio_size_to_uint32(
args->memblock.size / GMIO_STLB_TRIANGLE_RAWSIZE) args->core.memblock.size / GMIO_STLB_TRIANGLE_RAWSIZE)
: 0; : 0;
/* Variables */ /* Variables */
void* mblock_ptr = args != NULL ? args->memblock.ptr : NULL; struct gmio_rwargs* core_args =
args != NULL ? &args->core : NULL;
struct gmio_stl_mesh_creator* mesh_creator =
args != NULL ? &args->mesh_creator : NULL;
void* mblock_ptr =
core_args != NULL ? core_args->memblock.ptr : NULL;
struct gmio_stlb_readwrite_helper rparams = {0}; struct gmio_stlb_readwrite_helper rparams = {0};
struct gmio_stlb_header header; struct gmio_stlb_header header;
uint32_t total_facet_count = 0; /* Facet count, as declared in the stream */ uint32_t total_facet_count = 0; /* Facet count, as declared in the stream */
int error = GMIO_ERROR_OK; /* Helper to store function result error code */ int error = GMIO_ERROR_OK; /* Helper to store function result error code */
/* Check validity of input parameters */ /* Check validity of input parameters */
if (!gmio_stlb_check_params(&error, args, byte_order)) if (!gmio_stlb_check_params(&error, core_args, byte_order))
return error; return error;
/* Initialize rparams */ /* Initialize rparams */
@ -96,14 +99,14 @@ int gmio_stlb_read(
rparams.func_fix_endian = gmio_stl_triangle_bswap; rparams.func_fix_endian = gmio_stl_triangle_bswap;
/* Read header */ /* Read header */
if (gmio_stream_read(&args->stream, &header, GMIO_STLB_HEADER_SIZE, 1) if (gmio_stream_read(&core_args->stream, &header, GMIO_STLB_HEADER_SIZE, 1)
!= 1) != 1)
{ {
return GMIO_STL_ERROR_HEADER_WRONG_SIZE; return GMIO_STL_ERROR_HEADER_WRONG_SIZE;
} }
/* Read facet count */ /* Read facet count */
if (gmio_stream_read(&args->stream, mblock_ptr, sizeof(uint32_t), 1) if (gmio_stream_read(&core_args->stream, mblock_ptr, sizeof(uint32_t), 1)
!= 1) != 1)
{ {
return GMIO_STL_ERROR_FACET_COUNT; return GMIO_STL_ERROR_FACET_COUNT;
@ -115,23 +118,23 @@ int gmio_stlb_read(
/* Callback to notify triangle count and header data */ /* Callback to notify triangle count and header data */
gmio_stl_mesh_creator_binary_begin_solid( gmio_stl_mesh_creator_binary_begin_solid(
creator, total_facet_count, &header); mesh_creator, total_facet_count, &header);
/* Read triangles */ /* Read triangles */
while (gmio_no_error(error) while (gmio_no_error(error)
&& rparams.i_facet_offset < total_facet_count) && rparams.i_facet_offset < total_facet_count)
{ {
gmio_rwargs_handle_progress( gmio_rwargs_handle_progress(
args, rparams.i_facet_offset, total_facet_count); core_args, rparams.i_facet_offset, total_facet_count);
rparams.facet_count = rparams.facet_count =
gmio_size_to_uint32( gmio_size_to_uint32(
gmio_stream_read( gmio_stream_read(
&args->stream, &core_args->stream,
mblock_ptr, mblock_ptr,
GMIO_STLB_TRIANGLE_RAWSIZE, GMIO_STLB_TRIANGLE_RAWSIZE,
max_facet_count_per_read)); max_facet_count_per_read));
if (gmio_stream_error(&args->stream) != 0) if (gmio_stream_error(&core_args->stream) != 0)
error = GMIO_ERROR_STREAM; error = GMIO_ERROR_STREAM;
else if (rparams.facet_count > 0) else if (rparams.facet_count > 0)
error = GMIO_ERROR_OK; error = GMIO_ERROR_OK;
@ -139,15 +142,15 @@ int gmio_stlb_read(
break; /* Exit if no facet to read */ break; /* Exit if no facet to read */
if (gmio_no_error(error)) { if (gmio_no_error(error)) {
gmio_stlb_read_facets(creator, mblock_ptr, &rparams); gmio_stlb_read_facets(mesh_creator, mblock_ptr, &rparams);
rparams.i_facet_offset += rparams.facet_count; rparams.i_facet_offset += rparams.facet_count;
if (gmio_rwargs_is_stop_requested(args)) if (gmio_rwargs_is_stop_requested(core_args))
error = GMIO_ERROR_TRANSFER_STOPPED; error = GMIO_ERROR_TRANSFER_STOPPED;
} }
} /* end while */ } /* end while */
if (gmio_no_error(error)) if (gmio_no_error(error))
gmio_stl_mesh_creator_end_solid(creator); gmio_stl_mesh_creator_end_solid(mesh_creator);
if (gmio_no_error(error) && rparams.i_facet_offset != total_facet_count) if (gmio_no_error(error) && rparams.i_facet_offset != total_facet_count)
error = GMIO_STL_ERROR_FACET_COUNT; error = GMIO_STL_ERROR_FACET_COUNT;

View File

@ -52,7 +52,7 @@ set(GMIO_TEST_STL_SRC
main_test_stl.c main_test_stl.c
test_stl_internal.c test_stl_internal.c
test_stl_io.c test_stl_io.c
test_stla_stats.c test_stl_infos.c
core_utils.c core_utils.c
stl_utils.c) stl_utils.c)
if(GMIO_BUILD_SHARED_LIBS) if(GMIO_BUILD_SHARED_LIBS)

View File

@ -15,32 +15,6 @@
#include "core_utils.h" #include "core_utils.h"
gmio_bool_t gmio_float32_equals_by_ulp(
gmio_float32_t a, gmio_float32_t b, uint32_t max_ulp_diff)
{
const int32_t ia = gmio_convert_int32(a);
const int32_t ib = gmio_convert_int32(b);
const int32_t slp_diff = ia - ib;
const uint32_t ulp_diff = slp_diff > 0 ? slp_diff : -slp_diff;
/* Different signs, we could maybe get difference to 0, but so close to 0
* using epsilons is better */
if (gmio_int32_sign(ia) != gmio_int32_sign(ib)) {
/* Check for equality to make sure +0 == -0 */
return ia == ib;
}
/* Find the difference in ULPs */
return ulp_diff <= max_ulp_diff;
}
uint32_t gmio_float32_ulp_diff(gmio_float32_t a, gmio_float32_t b)
{
const uint32_t ua = gmio_convert_uint32(a);
const uint32_t ub = gmio_convert_uint32(b);
return ua > ub ? ua - ub : ub - ua;
}
void gmio_string_trim_from_end(char *str, size_t len) void gmio_string_trim_from_end(char *str, size_t len)
{ {
if (len > 0) { if (len > 0) {

View File

@ -18,35 +18,9 @@
#include "../src/gmio_core/global.h" #include "../src/gmio_core/global.h"
#include "../src/gmio_core/internal/string_utils.h" #include "../src/gmio_core/internal/string_utils.h"
#include "../src/gmio_core/internal/convert.h"
#include <stddef.h> #include <stddef.h>
/*! Does \p a and \p b compares equals by ULP (Units in the Last Place) ?
*
* ULP = spacing between floating-point numbers
*
* See:
* http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
*/
gmio_bool_t gmio_float32_equals_by_ulp(
gmio_float32_t a, gmio_float32_t b, uint32_t max_ulp_diff);
/*! Count of ULP between \p a and \p b */
uint32_t gmio_float32_ulp_diff(gmio_float32_t a, gmio_float32_t b);
/*! Portable sign-extraction for int32 */
GMIO_INLINE int gmio_int32_sign(int32_t v)
{
return (v & 0x80000000) != 0 ? -1 : 1;
}
/*! Portable sign-extraction for float32 */
GMIO_INLINE int gmio_float32_sign(gmio_float32_t v)
{
return gmio_int32_sign(gmio_convert_int32(v));
}
/*! Trim whitespaces in string \p str from end */ /*! Trim whitespaces in string \p str from end */
void gmio_string_trim_from_end(char* str, size_t len); void gmio_string_trim_from_end(char* str, size_t len);

View File

@ -17,11 +17,11 @@
const char* test_stl_internal__rw_common(); const char* test_stl_internal__rw_common();
const char* test_stl_infos();
const char* test_stl_read(); const char* test_stl_read();
const char* test_stla_write();
const char* test_stlb_write_header(); const char* test_stlb_write_header();
const char* test_stlb_write(); const char* test_stlb_write();
const char* test_stla_write();
const char* test_stla_stats();
const char* all_tests() const char* all_tests()
{ {
@ -29,11 +29,11 @@ const char* all_tests()
UTEST_RUN(test_stl_internal__rw_common); UTEST_RUN(test_stl_internal__rw_common);
UTEST_RUN(test_stl_infos);
UTEST_RUN(test_stl_read); UTEST_RUN(test_stl_read);
UTEST_RUN(test_stla_write);
UTEST_RUN(test_stlb_write_header); UTEST_RUN(test_stlb_write_header);
UTEST_RUN(test_stlb_write); UTEST_RUN(test_stlb_write);
UTEST_RUN(test_stla_write);
UTEST_RUN(test_stla_stats);
return NULL; return NULL;
} }

View File

@ -15,8 +15,8 @@
#include "stl_utils.h" #include "stl_utils.h"
#include "core_utils.h"
#include "../src/gmio_core/internal/min_max.h" #include "../src/gmio_core/internal/min_max.h"
#include "../src/gmio_core/internal/numeric_utils.h"
#include "../src/gmio_core/internal/safe_cast.h" #include "../src/gmio_core/internal/safe_cast.h"
#include <stdlib.h> #include <stdlib.h>
@ -36,7 +36,8 @@ struct gmio_stl_triangle_array gmio_stl_triangle_array_malloc(size_t tri_count)
struct gmio_stl_triangle_array array = {0}; struct gmio_stl_triangle_array array = {0};
if (tri_count > 0) { if (tri_count > 0) {
array.ptr = array.ptr =
(struct gmio_stl_triangle*)calloc(tri_count, sizeof(struct gmio_stl_triangle)); (struct gmio_stl_triangle*)calloc(
tri_count, sizeof(struct gmio_stl_triangle));
} }
array.count = gmio_size_to_uint32(tri_count); array.count = gmio_size_to_uint32(tri_count);
array.capacity = array.count; array.capacity = array.count;
@ -131,9 +132,9 @@ gmio_bool_t gmio_stl_coords_equal(
const struct gmio_stl_coords *rhs, const struct gmio_stl_coords *rhs,
uint32_t max_ulp_diff) uint32_t max_ulp_diff)
{ {
return gmio_float32_equals_by_ulp(lhs->x, rhs->x, max_ulp_diff) return gmio_float32_ulp_equals(lhs->x, rhs->x, max_ulp_diff)
&& gmio_float32_equals_by_ulp(lhs->y, rhs->y, max_ulp_diff) && gmio_float32_ulp_equals(lhs->y, rhs->y, max_ulp_diff)
&& gmio_float32_equals_by_ulp(lhs->z, rhs->z, max_ulp_diff); && gmio_float32_ulp_equals(lhs->z, rhs->z, max_ulp_diff);
} }
gmio_bool_t gmio_stl_triangle_equal( gmio_bool_t gmio_stl_triangle_equal(

View File

@ -15,13 +15,13 @@
#include "utest_assert.h" #include "utest_assert.h"
#include "core_utils.h"
#include "stream_buffer.h" #include "stream_buffer.h"
#include "../src/gmio_core/internal/byte_codec.h" #include "../src/gmio_core/internal/byte_codec.h"
#include "../src/gmio_core/internal/byte_swap.h" #include "../src/gmio_core/internal/byte_swap.h"
#include "../src/gmio_core/internal/convert.h" #include "../src/gmio_core/internal/convert.h"
#include "../src/gmio_core/internal/fast_atof.h" #include "../src/gmio_core/internal/fast_atof.h"
#include "../src/gmio_core/internal/numeric_utils.h"
#include "../src/gmio_core/internal/safe_cast.h" #include "../src/gmio_core/internal/safe_cast.h"
#include "../src/gmio_core/internal/stringstream.h" #include "../src/gmio_core/internal/stringstream.h"
#include "../src/gmio_core/internal/string_utils.h" #include "../src/gmio_core/internal/string_utils.h"
@ -65,7 +65,7 @@ static gmio_bool_t gmio_test_calculation_atof(const char* value_str)
const gmio_float32_t fast_value = fast_atof(value_str); const gmio_float32_t fast_value = fast_atof(value_str);
const gmio_float32_t std_value = (gmio_float32_t)strtod(value_str, NULL); const gmio_float32_t std_value = (gmio_float32_t)strtod(value_str, NULL);
const gmio_bool_t accurate = const gmio_bool_t accurate =
gmio_float32_equals_by_ulp(fast_value, std_value, 1); gmio_float32_ulp_equals(fast_value, std_value, 1);
if (!accurate) { if (!accurate) {
fprintf(stderr, fprintf(stderr,
"*** ERROR: fast_atof() less accurate than strtod()\n" "*** ERROR: fast_atof() less accurate than strtod()\n"
@ -142,7 +142,7 @@ const char* test_internal__gmio_fast_atof()
f2 = gmio_stringstream_fast_atof(&sstream); f2 = gmio_stringstream_fast_atof(&sstream);
UTEST_ASSERT(gmio_float32_equals_by_ulp(f1, f2, 1)); UTEST_ASSERT(gmio_float32_ulp_equals(f1, f2, 1));
} }
return NULL; return NULL;

View File

@ -17,6 +17,7 @@
#include "../src/gmio_core/global.h" #include "../src/gmio_core/global.h"
#include "../src/gmio_core/rwargs.h" #include "../src/gmio_core/rwargs.h"
#include "../src/gmio_stl/stl_constants.h"
#include "../src/gmio_stl/stl_triangle.h" #include "../src/gmio_stl/stl_triangle.h"
#include <stddef.h> #include <stddef.h>

View File

@ -15,28 +15,32 @@
#include "utest_assert.h" #include "utest_assert.h"
#include "../src/gmio_stl/stla_stats.h" #include "../src/gmio_core/error.h"
#include "../src/gmio_stl/stl_infos.h"
#include <stdio.h> #include <stdio.h>
static const char stl_jburkardt_sphere_filepath[] = static const char stl_jburkardt_sphere_filepath[] =
"models/solid_jburkardt_sphere.stla"; "models/solid_jburkardt_sphere.stla";
const char* test_stla_stats() const char* test_stl_infos()
{ {
FILE* stla_file = fopen(stl_jburkardt_sphere_filepath, "rb"); FILE* stla_file = fopen(stl_jburkardt_sphere_filepath, "rb");
struct gmio_rwargs rwargs = {0}; struct gmio_stl_infos_get_args args = {0};
struct gmio_stla_stats stats = {0}; struct gmio_stl_infos infos = {0};
int error = GMIO_ERROR_OK;
rwargs.memblock = gmio_memblock_malloc(8 * 1024); /* 8Ko */ args.format = GMIO_STL_FORMAT_ASCII;
rwargs.stream = gmio_stream_stdio(stla_file); args.memblock = gmio_memblock_malloc(8 * 1024); /* 8Ko */
args.stream = gmio_stream_stdio(stla_file);
stats = gmio_stla_stats_get(&rwargs, GMIO_STLA_STAT_FLAG_ALL); error = gmio_stl_infos_get(&args, &infos, GMIO_STL_INFO_FLAG_ALL);
gmio_memblock_deallocate(&rwargs.memblock); gmio_memblock_deallocate(&args.memblock);
fclose(stla_file); fclose(stla_file);
UTEST_ASSERT(stats.facet_count == 228); UTEST_ASSERT(error == GMIO_ERROR_OK);
/*UTEST_ASSERT(infos.facet_count == 228);*/
/*UTEST_ASSERT(stats.size == 54297);*/ /*UTEST_ASSERT(stats.size == 54297);*/
return NULL; return NULL;

View File

@ -34,12 +34,11 @@ struct stl_testcase_result
{ {
char solid_name[2048]; char solid_name[2048];
}; };
typedef struct stl_testcase_result stl_testcase_result_t;
void stl_testcase_result__ascii_begin_solid( void stl_testcase_result__ascii_begin_solid(
void* cookie, gmio_streamsize_t stream_size, const char* solid_name) void* cookie, gmio_streamsize_t stream_size, const char* solid_name)
{ {
stl_testcase_result_t* res = (stl_testcase_result_t*)cookie; struct stl_testcase_result* res = (struct stl_testcase_result*)cookie;
GMIO_UNUSED(stream_size); GMIO_UNUSED(stream_size);
if (res != NULL) { if (res != NULL) {
res->solid_name[0] = 0; res->solid_name[0] = 0;
@ -55,11 +54,10 @@ struct stl_testcase
enum gmio_stl_format format; enum gmio_stl_format format;
const char* solid_name; const char* solid_name;
}; };
typedef struct stl_testcase stl_testcase;
const char* test_stl_read() const char* test_stl_read()
{ {
const stl_testcase expected[] = { const struct stl_testcase expected[] = {
{ "models/file_empty", { "models/file_empty",
GMIO_STL_ERROR_UNKNOWN_FORMAT, GMIO_STL_ERROR_UNKNOWN_FORMAT,
GMIO_STL_FORMAT_UNKNOWN, GMIO_STL_FORMAT_UNKNOWN,
@ -127,20 +125,20 @@ const char* test_stl_read()
} }
}; };
const size_t expected_count = const size_t expected_count =
sizeof(expected) / sizeof(stl_testcase); sizeof(expected) / sizeof(struct stl_testcase);
size_t i; /* for loop counter */ size_t i; /* for loop counter */
struct gmio_stl_mesh_creator meshc = {0}; struct gmio_stl_read_args read = {0};
stl_testcase_result_t result = {0}; struct stl_testcase_result result = {0};
meshc.cookie = &result; read.mesh_creator.cookie = &result;
meshc.func_ascii_begin_solid = &stl_testcase_result__ascii_begin_solid; read.mesh_creator.func_ascii_begin_solid = &stl_testcase_result__ascii_begin_solid;
meshc.func_add_triangle = &gmio_stl_nop_add_triangle; read.mesh_creator.func_add_triangle = &gmio_stl_nop_add_triangle;
for (i = 0; i < expected_count; ++i) { for (i = 0; i < expected_count; ++i) {
const enum gmio_stl_format format = const enum gmio_stl_format format =
gmio_stl_get_format_file(expected[i].filepath); gmio_stl_get_format_file(expected[i].filepath);
const int err = const int err =
gmio_stl_read_file(expected[i].filepath, NULL, &meshc); gmio_stl_read_file(&read, expected[i].filepath);
/* Check format */ /* Check format */
if (format != expected[i].format) { if (format != expected[i].format) {
@ -203,9 +201,10 @@ const char* test_stlb_write_header()
} }
{ {
struct gmio_stl_read_args read = {0};
struct gmio_stl_data data = {0}; struct gmio_stl_data data = {0};
struct gmio_stl_mesh_creator mesh_creator = gmio_stl_data_mesh_creator(&data); read.mesh_creator = gmio_stl_data_mesh_creator(&data);
error = gmio_stl_read_file(filepath, NULL, &mesh_creator); error = gmio_stl_read_file(&read, filepath);
UTEST_ASSERT(error == GMIO_ERROR_OK); UTEST_ASSERT(error == GMIO_ERROR_OK);
UTEST_ASSERT(gmio_stlb_header_equal(&header, &data.header)); UTEST_ASSERT(gmio_stlb_header_equal(&header, &data.header));
UTEST_ASSERT(data.tri_array.count == 0); UTEST_ASSERT(data.tri_array.count == 0);
@ -233,8 +232,9 @@ const char* test_stlb_write()
/* Read input model file */ /* Read input model file */
{ {
struct gmio_stl_mesh_creator creator = gmio_stl_data_mesh_creator(&data); struct gmio_stl_read_args read = {0};
error = gmio_stl_read_file(model_filepath, NULL, &creator); read.mesh_creator = gmio_stl_data_mesh_creator(&data);
error = gmio_stl_read_file(&read, model_filepath);
UTEST_ASSERT(error == GMIO_ERROR_OK); UTEST_ASSERT(error == GMIO_ERROR_OK);
} }
@ -242,16 +242,16 @@ const char* test_stlb_write()
* Write also the model file in big-endian STL format * Write also the model file in big-endian STL format
*/ */
{ {
struct gmio_stl_write_options options = {0}; struct gmio_stl_write_args write = {0};
const struct gmio_stl_mesh mesh = gmio_stl_data_mesh(&data); write.mesh = gmio_stl_data_mesh(&data);
options.stlb_header_data = &data.header; write.options.stlb_header_data = &data.header;
error = gmio_stl_write_file( write.format = GMIO_STL_FORMAT_BINARY_LE;
model_filepath_out, NULL, &mesh, GMIO_STL_FORMAT_BINARY_LE, &options); error = gmio_stl_write_file(&write, model_filepath_out);
UTEST_ASSERT(error == GMIO_ERROR_OK); UTEST_ASSERT(error == GMIO_ERROR_OK);
/* Big-endian version */ /* Big-endian version */
error = gmio_stl_write_file( write.format = GMIO_STL_FORMAT_BINARY_BE;
model_filepath_out_be, NULL, &mesh, GMIO_STL_FORMAT_BINARY_BE, &options); error = gmio_stl_write_file(&write, model_filepath_out_be);
} }
/* Check input and output models are equal */ /* Check input and output models are equal */
@ -287,8 +287,9 @@ const char* test_stlb_write()
/* Check output LE/BE models are equal */ /* Check output LE/BE models are equal */
{ {
struct gmio_stl_data data_be = {0}; struct gmio_stl_data data_be = {0};
struct gmio_stl_mesh_creator creator = gmio_stl_data_mesh_creator(&data_be); struct gmio_stl_read_args read = {0};
error = gmio_stl_read_file(model_filepath_out_be, NULL, &creator); read.mesh_creator = gmio_stl_data_mesh_creator(&data_be);
error = gmio_stl_read_file(&read, model_filepath_out_be);
UTEST_ASSERT(error == GMIO_ERROR_OK); UTEST_ASSERT(error == GMIO_ERROR_OK);
UTEST_ASSERT(gmio_stlb_header_equal(&data.header, &data_be.header)); UTEST_ASSERT(gmio_stlb_header_equal(&data.header, &data_be.header));
UTEST_ASSERT(data.tri_array.count == data_be.tri_array.count); UTEST_ASSERT(data.tri_array.count == data_be.tri_array.count);
@ -314,21 +315,22 @@ const char* test_stla_write()
/* Read input model file */ /* Read input model file */
{ {
struct gmio_stl_mesh_creator creator = gmio_stl_data_mesh_creator(&data); struct gmio_stl_read_args read = {0};
error = gmio_stl_read_file(model_filepath, NULL, &creator); read.mesh_creator = gmio_stl_data_mesh_creator(&data);
error = gmio_stl_read_file(&read, model_filepath);
UTEST_ASSERT(error == GMIO_ERROR_OK); UTEST_ASSERT(error == GMIO_ERROR_OK);
} }
/* Write the model to STL ascii format */ /* Write the model to STL ascii format */
{ {
struct gmio_stl_write_options options = {0}; struct gmio_stl_write_args write = {0};
const struct gmio_stl_mesh mesh = gmio_stl_data_mesh(&data); write.format = GMIO_STL_FORMAT_ASCII;
write.mesh = gmio_stl_data_mesh(&data);
gmio_stlb_header_to_printable_string(&data.header, &header_str[0], '_'); gmio_stlb_header_to_printable_string(&data.header, &header_str[0], '_');
options.stla_solid_name = &header_str[0]; write.options.stla_solid_name = &header_str[0];
options.stla_float32_prec = 7; write.options.stla_float32_prec = 7;
options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_LOWERCASE; write.options.stla_float32_format = GMIO_FLOAT_TEXT_FORMAT_SHORTEST_LOWERCASE;
error = gmio_stl_write_file( error = gmio_stl_write_file(&write, model_filepath_out);
model_filepath_out, NULL, &mesh, GMIO_STL_FORMAT_ASCII, &options);
UTEST_ASSERT(error == GMIO_ERROR_OK); UTEST_ASSERT(error == GMIO_ERROR_OK);
} }
@ -336,11 +338,12 @@ const char* test_stla_write()
{ {
char trim_header_str[sizeof(header_str)] = {0}; char trim_header_str[sizeof(header_str)] = {0};
struct gmio_stl_data data_stla = {0}; struct gmio_stl_data data_stla = {0};
struct gmio_stl_mesh_creator creator = gmio_stl_data_mesh_creator(&data_stla); struct gmio_stl_read_args read = {0};
size_t i = 0; size_t i = 0;
read.mesh_creator = gmio_stl_data_mesh_creator(&data_stla);
strncpy(&trim_header_str[0], &header_str[0], sizeof(header_str)); strncpy(&trim_header_str[0], &header_str[0], sizeof(header_str));
gmio_string_trim_from_end(trim_header_str, sizeof(header_str)); gmio_string_trim_from_end(trim_header_str, sizeof(header_str));
error = gmio_stl_read_file(model_filepath_out, NULL, &creator); error = gmio_stl_read_file(&read, model_filepath_out);
UTEST_ASSERT(error == GMIO_ERROR_OK); UTEST_ASSERT(error == GMIO_ERROR_OK);
UTEST_ASSERT(data.tri_array.count == data_stla.tri_array.count); UTEST_ASSERT(data.tri_array.count == data_stla.tri_array.count);
UTEST_ASSERT(strcmp(&trim_header_str[0], &data_stla.solid_name[0]) == 0); UTEST_ASSERT(strcmp(&trim_header_str[0], &data_stla.solid_name[0]) == 0);
@ -365,31 +368,23 @@ void generate_stlb_tests_models()
} }
{ {
struct gmio_stl_mesh mesh = {0}; struct gmio_stl_triangle tri = {
struct gmio_stl_triangle tri = { { 0.f, 0.f, 1.f }, /* normal */
{ 0.f, 0.f, 1.f }, /* normal */ { 0.f, 0.f, 0.f }, /* v1 */
{ 0.f, 0.f, 0.f }, /* v1 */ { 10.f, 0.f, 0.f }, /* v2 */
{ 10.f, 0.f, 0.f }, /* v2 */ { 5.f, 10.f, 0.f }, /* v3 */
{ 5.f, 10.f, 0.f }, /* v3 */ 0 /* attr */
0 /* attr */ };
}; struct gmio_stl_data data = {0};
struct gmio_stl_data data = {0}; struct gmio_stl_write_args write = {0};
data.tri_array.ptr = &tri; data.tri_array.ptr = &tri;
data.tri_array.count = 1; data.tri_array.count = 1;
write.mesh = gmio_stl_data_mesh(&data);
mesh = gmio_stl_data_mesh(&data); write.format = GMIO_STL_FORMAT_BINARY_LE;
gmio_stl_write_file( gmio_stl_write_file(&write, "models/solid_one_facet.le_stlb");
"models/solid_one_facet.le_stlb", write.format = GMIO_STL_FORMAT_BINARY_BE;
NULL, gmio_stl_write_file(&write, "models/solid_one_facet.be_stlb");
&mesh,
GMIO_STL_FORMAT_BINARY_LE,
NULL);
gmio_stl_write_file(
"models/solid_one_facet.be_stlb",
NULL,
&mesh,
GMIO_STL_FORMAT_BINARY_BE,
NULL);
} }
} }