Tweaks in generation of rendering geometry for preview toolpaths. Fixes #240 and #348

This commit is contained in:
Enrico Turri 2018-04-17 15:04:14 +02:00
parent d7dc04eb57
commit 3bedcf4413

View File

@ -788,15 +788,14 @@ static void thick_lines_to_indexed_vertex_array(
#define TOP 2
#define BOTTOM 3
Line prev_line;
// right, left, top, bottom
int idx_prev[4] = { -1, -1, -1, -1 };
double bottom_z_prev = 0.;
Pointf b1_prev;
Pointf b2_prev;
Vectorf v_prev;
int idx_initial[4] = { -1, -1, -1, -1 };
double width_initial = 0.;
double bottom_z_initial = 0.0;
// loop once more in case of closed loops
size_t lines_end = closed ? (lines.size() + 1) : lines.size();
@ -804,13 +803,18 @@ static void thick_lines_to_indexed_vertex_array(
size_t i = (ii == lines.size()) ? 0 : ii;
const Line &line = lines[i];
double len = unscale(line.length());
double inv_len = 1.0 / len;
double bottom_z = top_z - heights[i];
double middle_z = (top_z + bottom_z) / 2.;
double middle_z = 0.5 * (top_z + bottom_z);
double width = widths[i];
bool is_first = (ii == 0);
bool is_last = (ii == lines_end - 1);
bool is_closing = closed && is_last;
Vectorf v = Vectorf::new_unscale(line.vector());
v.scale(1. / len);
v.scale(inv_len);
Pointf a = Pointf::new_unscale(line.a);
Pointf b = Pointf::new_unscale(line.b);
Pointf a1 = a;
@ -818,17 +822,19 @@ static void thick_lines_to_indexed_vertex_array(
Pointf b1 = b;
Pointf b2 = b;
{
double dist = width / 2.; // scaled
a1.translate(+dist*v.y, -dist*v.x);
a2.translate(-dist*v.y, +dist*v.x);
b1.translate(+dist*v.y, -dist*v.x);
b2.translate(-dist*v.y, +dist*v.x);
double dist = 0.5 * width; // scaled
double dx = dist * v.x;
double dy = dist * v.y;
a1.translate(+dy, -dx);
a2.translate(-dy, +dx);
b1.translate(+dy, -dx);
b2.translate(-dy, +dx);
}
// calculate new XY normals
Vector n = line.normal();
Vectorf3 xy_right_normal = Vectorf3::new_unscale(n.x, n.y, 0);
xy_right_normal.scale(1.f / len);
xy_right_normal.scale(inv_len);
int idx_a[4];
int idx_b[4];
@ -837,14 +843,21 @@ static void thick_lines_to_indexed_vertex_array(
bool bottom_z_different = bottom_z_prev != bottom_z;
bottom_z_prev = bottom_z;
if (!is_first && bottom_z_different)
{
// Found a change of the layer thickness -> Add a cap at the end of the previous segment.
volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]);
}
// Share top / bottom vertices if possible.
if (ii == 0) {
idx_a[TOP] = idx_last ++;
if (is_first) {
idx_a[TOP] = idx_last++;
volume.push_geometry(a.x, a.y, top_z , 0., 0., 1.);
} else {
idx_a[TOP] = idx_prev[TOP];
}
if (ii == 0 || bottom_z_different) {
if (is_first || bottom_z_different) {
// Start of the 1st line segment or a change of the layer thickness while maintaining the print_z.
idx_a[BOTTOM] = idx_last ++;
volume.push_geometry(a.x, a.y, bottom_z, 0., 0., -1.);
@ -852,13 +865,15 @@ static void thick_lines_to_indexed_vertex_array(
volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z);
idx_a[RIGHT] = idx_last ++;
volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z);
} else {
}
else {
idx_a[BOTTOM] = idx_prev[BOTTOM];
}
if (ii == 0) {
if (is_first) {
// Start of the 1st line segment.
width_initial = width;
bottom_z_initial = bottom_z;
memcpy(idx_initial, idx_a, sizeof(int) * 4);
} else {
// Continuing a previous segment.
@ -866,43 +881,54 @@ static void thick_lines_to_indexed_vertex_array(
double v_dot = dot(v_prev, v);
bool sharp = v_dot < 0.707; // sin(45 degrees)
if (sharp) {
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
idx_a[RIGHT] = idx_last ++;
volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z);
idx_a[LEFT ] = idx_last ++;
volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z);
if (!bottom_z_different)
{
// Allocate new left / right points for the start of this segment as these points will receive their own normals to indicate a sharp turn.
idx_a[RIGHT] = idx_last++;
volume.push_geometry(a1.x, a1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z);
idx_a[LEFT] = idx_last++;
volume.push_geometry(a2.x, a2.y, middle_z, -xy_right_normal.x, -xy_right_normal.y, -xy_right_normal.z);
}
}
if (v_dot > 0.9) {
// The two successive segments are nearly collinear.
idx_a[LEFT ] = idx_prev[LEFT];
idx_a[RIGHT] = idx_prev[RIGHT];
} else if (! sharp) {
// Create a sharp corner with an overshot and average the left / right normals.
// At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
Pointf intersection;
Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection);
a1 = intersection;
a2 = 2. * a - intersection;
assert(length(a1.vector_to(a)) < width);
assert(length(a2.vector_to(a)) < width);
float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6;
float *p_left_prev = n_left_prev + 3;
float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6;
float *p_right_prev = n_right_prev + 3;
p_left_prev [0] = float(a2.x);
p_left_prev [1] = float(a2.y);
p_right_prev[0] = float(a1.x);
p_right_prev[1] = float(a1.y);
xy_right_normal.x += n_right_prev[0];
xy_right_normal.y += n_right_prev[1];
xy_right_normal.scale(1. / length(xy_right_normal));
n_left_prev [0] = float(-xy_right_normal.x);
n_left_prev [1] = float(-xy_right_normal.y);
n_right_prev[0] = float( xy_right_normal.x);
n_right_prev[1] = float( xy_right_normal.y);
idx_a[LEFT ] = idx_prev[LEFT ];
idx_a[RIGHT] = idx_prev[RIGHT];
} else if (cross(v_prev, v) > 0.) {
if (!bottom_z_different)
{
// The two successive segments are nearly collinear.
idx_a[LEFT ] = idx_prev[LEFT];
idx_a[RIGHT] = idx_prev[RIGHT];
}
}
else if (!sharp) {
if (!bottom_z_different)
{
// Create a sharp corner with an overshot and average the left / right normals.
// At the crease angle of 45 degrees, the overshot at the corner will be less than (1-1/cos(PI/8)) = 8.2% over an arc.
Pointf intersection;
Geometry::ray_ray_intersection(b1_prev, v_prev, a1, v, intersection);
a1 = intersection;
a2 = 2. * a - intersection;
assert(length(a1.vector_to(a)) < width);
assert(length(a2.vector_to(a)) < width);
float *n_left_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6;
float *p_left_prev = n_left_prev + 3;
float *n_right_prev = volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6;
float *p_right_prev = n_right_prev + 3;
p_left_prev [0] = float(a2.x);
p_left_prev [1] = float(a2.y);
p_right_prev[0] = float(a1.x);
p_right_prev[1] = float(a1.y);
xy_right_normal.x += n_right_prev[0];
xy_right_normal.y += n_right_prev[1];
xy_right_normal.scale(1. / length(xy_right_normal));
n_left_prev [0] = float(-xy_right_normal.x);
n_left_prev [1] = float(-xy_right_normal.y);
n_right_prev[0] = float( xy_right_normal.x);
n_right_prev[1] = float( xy_right_normal.y);
idx_a[LEFT ] = idx_prev[LEFT ];
idx_a[RIGHT] = idx_prev[RIGHT];
}
}
else if (cross(v_prev, v) > 0.) {
// Right turn. Fill in the right turn wedge.
volume.push_triangle(idx_prev[RIGHT], idx_a [RIGHT], idx_prev[TOP] );
volume.push_triangle(idx_prev[RIGHT], idx_prev[BOTTOM], idx_a [RIGHT] );
@ -911,18 +937,21 @@ static void thick_lines_to_indexed_vertex_array(
volume.push_triangle(idx_prev[LEFT], idx_prev[TOP], idx_a [LEFT] );
volume.push_triangle(idx_prev[LEFT], idx_a [LEFT], idx_prev[BOTTOM]);
}
if (ii == lines.size()) {
if (! sharp) {
// Closing a loop with smooth transition. Unify the closing left / right vertices.
memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT ] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6, sizeof(float) * 6);
memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6);
volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end());
// Replace the left / right vertex indices to point to the start of the loop.
for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++ u) {
if (volume.quad_indices[u] == idx_prev[LEFT])
volume.quad_indices[u] = idx_initial[LEFT];
else if (volume.quad_indices[u] == idx_prev[RIGHT])
volume.quad_indices[u] = idx_initial[RIGHT];
if (is_closing) {
if (!sharp) {
if (!bottom_z_different)
{
// Closing a loop with smooth transition. Unify the closing left / right vertices.
memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[LEFT ] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[LEFT ] * 6, sizeof(float) * 6);
memcpy(volume.vertices_and_normals_interleaved.data() + idx_initial[RIGHT] * 6, volume.vertices_and_normals_interleaved.data() + idx_prev[RIGHT] * 6, sizeof(float) * 6);
volume.vertices_and_normals_interleaved.erase(volume.vertices_and_normals_interleaved.end() - 12, volume.vertices_and_normals_interleaved.end());
// Replace the left / right vertex indices to point to the start of the loop.
for (size_t u = volume.quad_indices.size() - 16; u < volume.quad_indices.size(); ++ u) {
if (volume.quad_indices[u] == idx_prev[LEFT])
volume.quad_indices[u] = idx_initial[LEFT];
else if (volume.quad_indices[u] == idx_prev[RIGHT])
volume.quad_indices[u] = idx_initial[RIGHT];
}
}
}
// This is the last iteration, only required to solve the transition.
@ -931,13 +960,14 @@ static void thick_lines_to_indexed_vertex_array(
}
// Only new allocate top / bottom vertices, if not closing a loop.
if (closed && ii + 1 == lines.size()) {
if (is_closing) {
idx_b[TOP] = idx_initial[TOP];
} else {
idx_b[TOP] = idx_last ++;
volume.push_geometry(b.x, b.y, top_z , 0., 0., 1.);
}
if (closed && ii + 1 == lines.size() && width == width_initial) {
if (is_closing && (width == width_initial) && (bottom_z == bottom_z_initial)) {
idx_b[BOTTOM] = idx_initial[BOTTOM];
} else {
idx_b[BOTTOM] = idx_last ++;
@ -949,22 +979,26 @@ static void thick_lines_to_indexed_vertex_array(
idx_b[RIGHT ] = idx_last ++;
volume.push_geometry(b1.x, b1.y, middle_z, xy_right_normal.x, xy_right_normal.y, xy_right_normal.z);
prev_line = line;
memcpy(idx_prev, idx_b, 4 * sizeof(int));
bottom_z_prev = bottom_z;
b1_prev = b1;
b2_prev = b2;
v_prev = v;
v_prev = v;
if (bottom_z_different)
{
// Found a change of the layer thickness -> Add a cap at the beginning of this segment.
volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
}
if (! closed) {
// Terminate open paths with caps.
if (i == 0)
if (is_first && !bottom_z_different)
volume.push_quad(idx_a[BOTTOM], idx_a[RIGHT], idx_a[TOP], idx_a[LEFT]);
// We don't use 'else' because both cases are true if we have only one line.
if (i + 1 == lines.size())
if (is_last && !bottom_z_different)
volume.push_quad(idx_b[BOTTOM], idx_b[LEFT], idx_b[TOP], idx_b[RIGHT]);
}
// Add quads for a straight hollow tube-like segment.
// bottom-right face
volume.push_quad(idx_a[BOTTOM], idx_b[BOTTOM], idx_b[RIGHT], idx_a[RIGHT]);