8335a271af
Added some smoothing for spheres and cylinders
350 lines
6.8 KiB
C
350 lines
6.8 KiB
C
/** \file
|
|
* Generate an OpenSCAD with cubes for each edge
|
|
*
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <err.h>
|
|
#include <assert.h>
|
|
#include "v3.h"
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.1415926535897932384
|
|
#endif
|
|
|
|
static int debug = 0;
|
|
static int draw_labels = 0;
|
|
|
|
typedef struct
|
|
{
|
|
char header[80];
|
|
uint32_t num_triangles;
|
|
} __attribute__((__packed__))
|
|
stl_header_t;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
v3_t normal;
|
|
v3_t p[3];
|
|
uint16_t attr;
|
|
} __attribute__((__packed__))
|
|
stl_face_t;
|
|
|
|
|
|
#define MAX_VERTEX 64
|
|
|
|
typedef struct stl_vertex stl_vertex_t;
|
|
|
|
struct stl_vertex
|
|
{
|
|
v3_t p;
|
|
int num_edges;
|
|
stl_vertex_t * edges[MAX_VERTEX];
|
|
};
|
|
|
|
|
|
|
|
/* Returns 1 for ever edge in f1 that is shared with f2.
|
|
*/
|
|
int
|
|
coplanar_check(
|
|
const stl_face_t * const f1,
|
|
const stl_face_t * const f2
|
|
)
|
|
{
|
|
// Verify that there are three matching points
|
|
int match[3] = {0,0,0};
|
|
for (int i = 0 ; i < 3 ; i++)
|
|
{
|
|
for (int j = 0 ; j < 3 ; j++)
|
|
if (v3_eq(f1->p[i], f2->p[j]))
|
|
match[i] = 1;
|
|
}
|
|
|
|
uint8_t mask = 0;
|
|
|
|
if (match[0] && match[1])
|
|
mask = 1;
|
|
if (match[1] && match[2])
|
|
mask = 2;
|
|
if (match[2] && match[0])
|
|
mask = 4;
|
|
|
|
if (debug)
|
|
fprintf(stderr, "%p %p: %d %d %d\n",
|
|
f1,
|
|
f2,
|
|
match[0], match[1], match[2]
|
|
);
|
|
|
|
// otherwise they do not share enough points
|
|
if (mask == 0)
|
|
return 0;
|
|
|
|
#if 0
|
|
// find the four distinct points
|
|
v3_t x1 = f1->p[0];
|
|
v3_t x2 = f1->p[1];
|
|
v3_t x3 = f1->p[2];
|
|
v3_t x4;
|
|
|
|
for (int i = 0 ; i < 3 ; i++)
|
|
{
|
|
x4 = f2->p[i];
|
|
if (v3_eq(&x1, &x4))
|
|
continue;
|
|
if (v3_eq(&x2, &x4))
|
|
continue;
|
|
if (v3_eq(&x3, &x4))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
// (x3-x1) . ((x2-x1) X (x4-x3)) == 0
|
|
v3_t dx31 = v3_sub(x3, x1);
|
|
v3_t dx21 = v3_sub(x2, x1);
|
|
v3_t dx43 = v3_sub(x4, x3);
|
|
v3_t cross = v3_cross(dx21, dx43);
|
|
float dot = v3_dot(dx31, cross);
|
|
|
|
if (debug)
|
|
fprintf(stderr, "dot %f:\n %f,%f,%f\n %f,%f,%f\n %f,%f,%f\n %f,%f,%f\n",
|
|
dot,
|
|
x1.p[0], x1.p[1], x1.p[2],
|
|
x2.p[0], x2.p[1], x2.p[2],
|
|
x3.p[0], x3.p[1], x3.p[2],
|
|
x4.p[0], x4.p[1], x4.p[2]
|
|
);
|
|
|
|
int check = -EPS < dot && dot < +EPS;
|
|
|
|
// if the dot product is not close enough to zero, they
|
|
// are not coplanar.
|
|
if (!check)
|
|
return 0;
|
|
|
|
// coplanar! return the shared edge mask
|
|
return mask;
|
|
#else
|
|
// if the normals are close enough, then it is coplanner
|
|
if (v3_eq(f1->normal, f2->normal))
|
|
return mask;
|
|
else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Add a vector to the list of edges if it is not already present
|
|
* and if it is not coplanar with other ones.
|
|
* Note that if it is coplanar, but "outside" the other edges then it
|
|
* will replace the inside one.
|
|
*/
|
|
void
|
|
stl_edge_insert(
|
|
stl_vertex_t * const v1,
|
|
stl_vertex_t * const v2
|
|
)
|
|
{
|
|
for (int i = 0 ; i < v1->num_edges ; i++)
|
|
{
|
|
// if v2 already exists in the edges, discard it
|
|
if (v1->edges[i] == v2)
|
|
return;
|
|
}
|
|
|
|
// if we reach this point, we need to insert the edge
|
|
if (debug)
|
|
fprintf(stderr, "%p: edge %d -> %p\n",
|
|
v1,
|
|
v1->num_edges,
|
|
v2
|
|
);
|
|
v1->edges[v1->num_edges++] = v2;
|
|
}
|
|
|
|
|
|
stl_vertex_t *
|
|
stl_vertex_find(
|
|
stl_vertex_t ** const vertices,
|
|
int * num_vertex_ptr,
|
|
const v3_t * const p
|
|
)
|
|
{
|
|
const int num_vertex = *num_vertex_ptr;
|
|
|
|
for (int x = 0 ; x < num_vertex ; x++)
|
|
{
|
|
stl_vertex_t * const v = vertices[x];
|
|
|
|
if (v3_eq(v->p, *p))
|
|
return v;
|
|
}
|
|
|
|
if (debug)
|
|
fprintf(stderr, "%d: %f,%f,%f\n",
|
|
num_vertex,
|
|
p->p[0],
|
|
p->p[1],
|
|
p->p[2]
|
|
);
|
|
|
|
if (num_vertex == 6)
|
|
fprintf(stderr, "%f %f %f\n",
|
|
p->p[0],
|
|
p->p[1],
|
|
p->p[2]
|
|
);
|
|
|
|
stl_vertex_t * const v = vertices[(*num_vertex_ptr)++] = calloc(1, sizeof(*v));
|
|
v->p = *p;
|
|
return v;
|
|
}
|
|
|
|
|
|
|
|
int main(void)
|
|
{
|
|
const size_t max_len = 1 << 20;
|
|
uint8_t * const buf = calloc(max_len, 1);
|
|
|
|
ssize_t rc = read(0, buf, max_len);
|
|
if (rc == -1)
|
|
return EXIT_FAILURE;
|
|
|
|
const stl_header_t * const hdr = (const void*) buf;
|
|
const stl_face_t * const stl_faces = (const void*)(hdr+1);
|
|
const int num_triangles = hdr->num_triangles;
|
|
const int do_square = 1;
|
|
|
|
fprintf(stderr, "header: '%s'\n", hdr->header);
|
|
fprintf(stderr, "num: %d\n", num_triangles);
|
|
|
|
// generate the unique list of vertices and their
|
|
// correponding edges
|
|
stl_vertex_t ** const vertices = calloc(3*num_triangles, sizeof(*vertices));
|
|
|
|
int num_vertex = 0;
|
|
|
|
for(int i = 0 ; i < num_triangles ; i++)
|
|
{
|
|
if (debug) fprintf(stderr, "---------- triangle %d (%d)\n", i, num_vertex);
|
|
|
|
stl_vertex_t * vp[3] = {};
|
|
|
|
for (int j = 0 ; j < 3 ; j++)
|
|
{
|
|
const v3_t * const p = &stl_faces[i].p[j];
|
|
vp[j] = stl_vertex_find(vertices, &num_vertex, p);
|
|
}
|
|
|
|
// walk all of other triangles to figure out if
|
|
// any of the triangles are coplanar and have shared
|
|
// edges.
|
|
|
|
uint8_t coplanar_mask = 0;
|
|
for (int j = 0 ; j < num_triangles ; j++)
|
|
{
|
|
if (j == i)
|
|
continue;
|
|
|
|
if (debug)
|
|
fprintf(stderr, "%d: check %d -> %d\n", num_vertex, i, j);
|
|
|
|
coplanar_mask |= coplanar_check(
|
|
&stl_faces[i], &stl_faces[j]);
|
|
}
|
|
|
|
if (debug)
|
|
fprintf(stderr, "mask %d\n", coplanar_mask);
|
|
|
|
// all three vertices are mapped; generate the
|
|
// connections
|
|
for (int j = 0 ; j < 3 ; j++)
|
|
{
|
|
stl_vertex_t * const v = vp[j];
|
|
|
|
// if the edge from j to j+1 is not coplanar,
|
|
// add it to the list
|
|
if ((coplanar_mask & (1 << j)) == 0)
|
|
{
|
|
if (debug)
|
|
fprintf(stderr, "%p: %d insert\n", v, j);
|
|
stl_edge_insert(v, vp[(j+1) % 3]);
|
|
}
|
|
|
|
/*
|
|
// if the edge from j+2 to j is not coplanar
|
|
const uint8_t j2 = (j + 2) % 3;
|
|
if ((coplanar_mask & (1 << j2)) == 0)
|
|
{
|
|
if (debug)
|
|
fprintf(stderr, "%p: %d insert back\n", v, j2);
|
|
stl_edge_insert(v, vp[j2]);
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
fprintf(stderr, "%d unique vertices\n", num_vertex);
|
|
printf("thick=1.0;\n"
|
|
"radius=1.0;\n"
|
|
"leg_height=1.0;\n"
|
|
"wall=0.5;\n"
|
|
"smooth=100;\n"
|
|
"leglen_percentage=0.48;\n"
|
|
"module connector(len) {\n"
|
|
" render() difference() {\n"
|
|
" cylinder(r=thick/2+wall, h=leg_height*thick, $fn=smooth);\n"
|
|
//" translate([0,0,len/2+2]) cube([thick,thick,2*thick]);\n"
|
|
" translate([0,0,thick/2+2]) cylinder(r=thick/2, h=2*thick, $fn=smooth);\n"
|
|
" }\n"
|
|
//" %%translate([0,0,len*0.48/2]) cube([thick,thick,len*0.48], center=true);\n"
|
|
" %%translate([0,0,0]) cylinder(r=thick/2, h=len*leglen_percentage, $fn=smooth);\n"
|
|
"}\n"
|
|
);
|
|
|
|
for (int i = 0 ; i < num_vertex ; i++)
|
|
{
|
|
stl_vertex_t * const v = vertices[i];
|
|
printf("translate([%f,%f,%f]) {\n",
|
|
v->p.p[0],
|
|
v->p.p[1],
|
|
v->p.p[2]
|
|
);
|
|
|
|
printf("sphere(radius, $fn=smooth); // %d %p\n", i, v);
|
|
|
|
for (int j = 0 ; j < v->num_edges ; j++)
|
|
{
|
|
stl_vertex_t * const v2 = v->edges[j];
|
|
const v3_t d = v3_sub(v2->p, v->p);
|
|
const float len = v3_len(&v2->p, &v->p);
|
|
|
|
const float b = acos(d.p[2] / len) * 180/M_PI;
|
|
const float c = d.p[0] == 0 ? sign(d.p[1]) * 90 : atan2(d.p[1], d.p[0]) * 180/M_PI;
|
|
//
|
|
printf("rotate([0,%f,%f]) ", b, c);
|
|
|
|
if (do_square)
|
|
printf("connector(%f);\n", len);
|
|
else
|
|
printf(" cylinder(r=1, h=%f, $fn=smooth); // %p\n",
|
|
len*.45,
|
|
v2
|
|
);
|
|
}
|
|
|
|
printf("}\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|