Files
QuelSolaar/la_projection.c
2026-02-28 11:12:52 +01:00

1198 lines
40 KiB
C

#include "la_includes.h"
#include "la_projection.h"
#include "la_geometry_undo.h"
#include "la_particle_fx.h"
#include "la_tool.h"
#ifdef LO_NO_WAY_NO_DEF
extern void point_threw_matrix4(double *matrix, double *pos_x, double *pos_y, double *pos_z, double *pos_w);
extern void point_threw_matrix3(double *matrix, double *pos_x, double *pos_y, double *pos_z);
extern void matrix_multiply(const double *a,const double *b, double *output);
extern void matrix_rotate_x(double *matrix, double degree);
extern void matrix_rotate_y(double *matrix, double degree);
extern void matrix_rotate_z(double *matrix, double degree);
extern void transform_matrix(double *matrix, double x, double y, double z);
extern void scale_matrix(double *matrix, double x, double y, double z);
extern void negate_matrix(double *matrix);
extern void reverse_matrix(double *matrix);
extern void matrix_print(const double *matrix);
extern void la_draw_force_update_persuade(void);
struct{
double matrix[16];
double model[16];
double pitch;
double yaw;
double pitch_target;
double yaw_target;
double target[3];
double position[3];
double speed;
double distance;
double distance_target;
double grid_size;
float grab[2];
uint view_axis;
uint axis;
double direction;
}ProjectionData;
void p_init(void)
{
ProjectionData.pitch = 0;
ProjectionData.yaw = 0;
ProjectionData.pitch_target = 0;
ProjectionData.yaw_target = 0;
ProjectionData.position[0] = 0;
ProjectionData.position[1] = 0;
ProjectionData.position[2] = 0;
ProjectionData.target[0] = 0;
ProjectionData.target[1] = 0;
ProjectionData.target[2] = 0;
ProjectionData.distance = 1.0;
ProjectionData.distance_target = 1.0;
ProjectionData.view_axis = 3;
ProjectionData.speed = 0.8;
ProjectionData.grid_size = 1000;
ProjectionData.axis = 3;
}
void p_view_change_start(BInputState *input)
{
ProjectionData.grab[0] = input->pointers[0].pointer_x;
ProjectionData.grab[1] = input->pointers[0].pointer_y;
ProjectionData.speed = 0.8;
}
void p_view_change(BInputState *input)
{
float a, b;
if(input->mode == BAM_EVENT)
{
if(input->pointers[0].last_button[1] == TRUE)
{
if(input->pointers[0].button[2] == TRUE && input->pointers[0].button[0] == TRUE)
{
p_init();
}else if(input->pointers[0].button[2] == TRUE)
{
ProjectionData.position[0] += ProjectionData.model[0] * ProjectionData.distance * 2 * (ProjectionData.grab[0] - input->pointers[0].pointer_x);
ProjectionData.position[1] += ProjectionData.model[4] * ProjectionData.distance * 2 * (ProjectionData.grab[0] - input->pointers[0].pointer_x);
ProjectionData.position[2] += ProjectionData.model[8] * ProjectionData.distance * 2 * (ProjectionData.grab[0] - input->pointers[0].pointer_x);
ProjectionData.position[0] += ProjectionData.model[1] * ProjectionData.distance * 2 * (ProjectionData.grab[1] - input->pointers[0].pointer_y);
ProjectionData.position[1] += ProjectionData.model[5] * ProjectionData.distance * 2 * (ProjectionData.grab[1] - input->pointers[0].pointer_y);
ProjectionData.position[2] += ProjectionData.model[9] * ProjectionData.distance * 2 * (ProjectionData.grab[1] - input->pointers[0].pointer_y);
ProjectionData.target[0] = ProjectionData.position[0];
ProjectionData.target[1] = ProjectionData.position[1];
ProjectionData.target[2] = ProjectionData.position[2];
}else if(input->pointers[0].button[0] == TRUE)
{
a = ProjectionData.distance * (1 + (input->delta_pointer_x - input->delta_pointer_y) * 4);
if(a > 90000)
a = 90000;
if(a < 0.01)
a = 0.01;
ProjectionData.distance_target = a;
}else
{
a = ProjectionData.yaw_target + (input->pointers[0].pointer_x - ProjectionData.grab[0]) * 400;
b = ProjectionData.pitch_target - (input->pointers[0].pointer_y - ProjectionData.grab[1]) * 400;
if(b > 90)
b = 90;
if(b < -90)
b = -90;
// ProjectionData.pitch = b;
ProjectionData.pitch_target = b;
// ProjectionData.yaw = a;
ProjectionData.yaw_target = a;
if(ProjectionData.yaw > 180 && ProjectionData.yaw_target > 180)
{
ProjectionData.yaw -= 360;
ProjectionData.yaw_target -= 360;
}else if(ProjectionData.yaw < -180 && ProjectionData.yaw_target < -180)
{
ProjectionData.yaw += 360;
ProjectionData.yaw_target += 360;
}
}
}
ProjectionData.grab[0] = input->pointers[0].pointer_x;
ProjectionData.grab[1] = input->pointers[0].pointer_y;
/* a = sqrt(ProjectionData.model[2] * ProjectionData.model[2] + ProjectionData.model[6] * ProjectionData.model[6] + ProjectionData.model[10] * ProjectionData.model[10]);
if(ProjectionData.model[2] / a > 0.95 || ProjectionData.model[2] / a < -0.95)
ProjectionData.view_axis = 0;
else if(ProjectionData.model[6] / a > 0.95 || ProjectionData.model[6] / a < -0.95)
ProjectionData.view_axis = 1;
else if(ProjectionData.model[10] / a > 0.95 || ProjectionData.model[10] / a < -0.95)
ProjectionData.view_axis = 2;
else*/
ProjectionData.view_axis = 3;
/* if(input->pointers[0].button[1] == TRUE && (input->delta_pointer_x > 0.0001 || input->delta_pointer_x < 0.0001 || input->delta_pointer_y > 0.0001 || input->delta_pointer_y < 0.0001))
la_draw_force_update_persuade();*/
// if(input->pointers[0].button[1] == FALSE && input->pointers[0].last_button[1] == TRUE)
// la_draw_force_update_persuade();
}
}
void p_projection_update(void)
{
double proj[16];
glGetDoublev(GL_PROJECTION_MATRIX, ProjectionData.matrix);
glGetDoublev(GL_MODELVIEW_MATRIX, ProjectionData.model);
}
void p_set_grid_size(double grid_size)
{
ProjectionData.grid_size = 1.0 / grid_size;
}
void p_get_view_center(double *center)
{
center[0] = ProjectionData.position[0];
center[1] = ProjectionData.position[1];
center[2] = ProjectionData.position[2];
}
void p_get_view_camera(double *camera)
{
camera[0] = ProjectionData.model[2] * ProjectionData.distance + ProjectionData.position[0];
camera[1] = ProjectionData.model[6] * ProjectionData.distance + ProjectionData.position[1];
camera[2] = ProjectionData.model[10] * ProjectionData.distance + ProjectionData.position[2];
}
double p_get_distance_camera(void)
{
return ProjectionData.distance;
}
void p_view_set(void)
{
static float rotate = 0;
rotate++;
ProjectionData.position[0] = ProjectionData.target[0] * (1 - ProjectionData.speed) + ProjectionData.position[0] * ProjectionData.speed;
ProjectionData.position[1] = ProjectionData.target[1] * (1 - ProjectionData.speed) + ProjectionData.position[1] * ProjectionData.speed;
ProjectionData.position[2] = ProjectionData.target[2] * (1 - ProjectionData.speed) + ProjectionData.position[2] * ProjectionData.speed;
ProjectionData.pitch = ProjectionData.pitch_target * (1 - ProjectionData.speed) + ProjectionData.pitch * ProjectionData.speed;
ProjectionData.yaw = ProjectionData.yaw_target * (1 - ProjectionData.speed) + ProjectionData.yaw * ProjectionData.speed;
ProjectionData.distance = ProjectionData.distance_target * (1 - ProjectionData.speed) + ProjectionData.distance * ProjectionData.speed;
glTranslated(0, 0, -ProjectionData.distance);
glRotated(ProjectionData.pitch, 1, 0, 0); /* the rotate functions now handels radians too */
glRotated(ProjectionData.yaw, 0, 1, 0); /* the rotate functions now handels radians too */
glTranslated(-ProjectionData.position[0], -ProjectionData.position[1], -ProjectionData.position[2]);
seduce_view_update(NULL);
}
void p_get_projection(double *output, float x, float y)
{
output[0] = ProjectionData.model[0] * x * ProjectionData.distance;
output[1] = ProjectionData.model[4] * x * ProjectionData.distance;
output[2] = ProjectionData.model[8] * x * ProjectionData.distance;
output[0] += ProjectionData.model[1] * y * ProjectionData.distance;
output[1] += ProjectionData.model[5] * y * ProjectionData.distance;
output[2] += ProjectionData.model[9] * y * ProjectionData.distance;
output[0] += ProjectionData.position[0];
output[1] += ProjectionData.position[1];
output[2] += ProjectionData.position[2];
}
void p_get_projection_vertex(double *output, double *vertex, double x, double y)
{
double dist, z;
z = ProjectionData.model[2] * (vertex[0] - ProjectionData.position[0]) + ProjectionData.model[6] * (vertex[1] - ProjectionData.position[1]) + ProjectionData.model[10] * (vertex[2] - ProjectionData.position[2]);
dist = (ProjectionData.distance - z);
if(dist < 0)
dist = ProjectionData.distance;
output[0] = ProjectionData.model[0] * x * dist;
output[1] = ProjectionData.model[4] * x * dist;
output[2] = ProjectionData.model[8] * x * dist;
output[0] += ProjectionData.model[1] * y * dist;
output[1] += ProjectionData.model[5] * y * dist;
output[2] += ProjectionData.model[9] * y * dist;
output[0] += ProjectionData.model[2] * z;
output[1] += ProjectionData.model[6] * z;
output[2] += ProjectionData.model[10] * z;
output[0] += ProjectionData.position[0];
output[1] += ProjectionData.position[1];
output[2] += ProjectionData.position[2];
}
void p_get_projection_screen(double *output, double x, double y, double z)
{
double out[3];
f_transform3d(out, ProjectionData.model, x, y, z);
output[0] = out[0] / out[2];
output[1] = out[1] / out[2];
output[2] = out[2];
}
/*
p_get_projection_screen(pos, vertex[i].x, vertex[i].y, vertex[i].z);
pos[0] += x;
pos[1] += y;
r = pos[0] * pos[0] + pos[1] * pos[1];
if(r < *selected_distance)
*/
double p_get_projection_screen_distance(double space_x, double space_y, double space_z, double screen_x, double screen_y)
{
double out[3];
f_transform3d(out, ProjectionData.model, space_x, space_y, space_z);
out[0] = (out[0] / out[2]) + screen_x ;
out[1] = (out[1] / out[2]) + screen_y;
return out[0] * out[0] + out[2] * out[2];
}
/* r = (b[axis] - depth) / (b[axis] - a[axis]); */
void p_get_projection_plane(double *dist, uint axis, double pointer_x, double pointer_y , double depth)
{
double a[3], b[3], r;
a[0] = ProjectionData.model[0] * ProjectionData.distance * pointer_x;
a[1] = ProjectionData.model[4] * ProjectionData.distance * pointer_x;
a[2] = ProjectionData.model[8] * ProjectionData.distance * pointer_x;
a[0] += ProjectionData.model[1] * ProjectionData.distance * pointer_y;
a[1] += ProjectionData.model[5] * ProjectionData.distance * pointer_y;
a[2] += ProjectionData.model[9] * ProjectionData.distance * pointer_y;
a[0] += ProjectionData.position[0];
a[1] += ProjectionData.position[1];
a[2] += ProjectionData.position[2];
b[0] = ProjectionData.model[2] * ProjectionData.distance;
b[1] = ProjectionData.model[6] * ProjectionData.distance;
b[2] = ProjectionData.model[10] * ProjectionData.distance;
b[0] += ProjectionData.position[0];
b[1] += ProjectionData.position[1];
b[2] += ProjectionData.position[2];
r = (b[axis] - depth) / (b[axis] - a[axis]);
dist[0] = b[0] - (b[0] - a[0]) * r;
dist[1] = b[1] - (b[1] - a[1]) * r;
dist[2] = b[2] - (b[2] - a[2]) * r;
dist[axis] = depth;
}
double p_get_projection_line(double *dist, uint axis, double pointer_x, double pointer_y, double *pos)
{
double a[3], b[3], r, r2, r3;
a[0] = ProjectionData.model[0] * ProjectionData.distance * pointer_x;
a[1] = ProjectionData.model[4] * ProjectionData.distance * pointer_x;
a[2] = ProjectionData.model[8] * ProjectionData.distance * pointer_x;
a[0] += ProjectionData.model[1] * ProjectionData.distance * pointer_y;
a[1] += ProjectionData.model[5] * ProjectionData.distance * pointer_y;
a[2] += ProjectionData.model[9] * ProjectionData.distance * pointer_y;
b[0] = ProjectionData.model[2] * ProjectionData.distance;
b[1] = ProjectionData.model[6] * ProjectionData.distance;
b[2] = ProjectionData.model[10] * ProjectionData.distance;
a[0] -= b[0];
a[1] -= b[1];
a[2] -= b[2];
b[0] += ProjectionData.position[0] - pos[0];
b[1] += ProjectionData.position[1] - pos[1];
b[2] += ProjectionData.position[2] - pos[2];
r = sqrt(b[(axis + 1) % 3] * b[(axis + 1) % 3] + b[(axis + 2) % 3] * b[(axis + 2) % 3]);
r2 = sqrt(a[(axis + 1) % 3] * a[(axis + 1) % 3] + a[(axis + 2) % 3] * a[(axis + 2) % 3]);
r3 = b[axis] + (a[axis] * r / r2);
if(dist != NULL)
{
a[0] = pos[0];
a[1] = pos[1];
a[2] = pos[2];
a[axis] += r3;
f_transform3d(a, ProjectionData.model, a[0], a[1], a[2]);
a[0] = (a[0] / a[2]) + pointer_x;
a[1] = (a[1] / a[2]) + pointer_y;
*dist = a[0] * a[0] + a[1] * a[1];
}
return r3;
}
void p_get_projection_line_snap(double *output, uint axis, double direction, double *start, double *snap, LASnapType snap_type)
{
output[0] = start[0];
output[1] = start[1];
output[2] = start[2];
if(snap_type == SUI_ST_VERTEX)
{
output[axis] = snap[axis];
}else if(snap_type == SUI_ST_LENGTH)
{
if(direction > 0)
output[axis] += snap[3];
else
output[axis] -= snap[3];
printf("HH p_get_projection_line_snap %u %f \n", axis, snap[3]);
printf("HH start %f %f %f\n", start[0], start[1], start[2]);
printf("HH out %f %f %f\n", output[0], output[1], output[2]);
}else if(snap_type == SUI_ST_TANGENT)
{
double r, vector[3] = {0, 0, 0};
vector[axis] = 1;
r = vector[0] * snap[3] + vector[1] * snap[4] + vector[2] * snap[5];
if(r < 0.1 && r > -0.1)
;//output[axis] += direction;
else
{
r = (snap[3] * (start[0] - snap[0]) + snap[4] * (start[1] - snap[1]) + snap[5] * (start[2] - snap[2])) / r;
output[0] = start[0] - r * vector[0];
output[1] = start[1] - r * vector[1];
output[2] = start[2] - r * vector[2];
}
}
}
void p_get_projection_vertex_with_axis(double *output, double *start, double pointer_x, double pointer_y, boolean snap, double *closest, LASnapType snap_type)
{
double dist, best_dist, pos;
//static uint axis;
uint i;
// printf("*closest = %f\n", *closest);
if(snap != TRUE)
{
ProjectionData.direction = p_get_projection_line(&best_dist, 0, pointer_x, pointer_y, start);
ProjectionData.axis = 0;
for(i = 1; i < 3; i++)
{
pos = p_get_projection_line(&dist, i, pointer_x, pointer_y, start);
if(dist < best_dist)
{
ProjectionData.direction = pos;
best_dist = dist;
ProjectionData.axis = i;
}
}
if(ProjectionData.view_axis < 3)
{
p_get_projection_plane(output, ProjectionData.view_axis, pointer_x, pointer_y, start[ProjectionData.view_axis]);
return;
}
if(best_dist > 0.0001)
{
p_get_projection_vertex(output, start, pointer_x, pointer_y);
return;
}
}
output[0] = start[0];
output[1] = start[1];
output[2] = start[2];
if(snap != TRUE)
output[ProjectionData.axis] += ProjectionData.direction;
else
{
/* p_get_projection_line(&best_dist, 0, pointer_x, pointer_y, start);
if(best_dist < 0.0001)
{
output[0] = p_get_projection_line(&best_dist, ProjectionData.axis, pointer_x, pointer_y, start);
output[ProjectionData.axis] += (double)((int)(output[0] * ProjectionData.grid_size)) / ProjectionData.grid_size;
}
else
{*/
p_get_projection_line_snap(output, ProjectionData.axis, ProjectionData.direction, start, closest, snap_type);
/* }*/
}
}
uint p_get_projection_axis(void)
{
return ProjectionData.axis;
}
boolean p_find_closest_tag(double *pos, double distance, double x, double y)
{
boolean output = FALSE;
uint32 count, i;
double r;
UNDOTag *tag;
tag = udg_get_tags(&count);
for(i = 0; i < count; i++)
{
p_get_projection_screen(pos, tag[i].vec[0], tag[i].vec[1], tag[i].vec[2]);
if(pos[2] < 0)
{
pos[0] += x;
pos[1] += y;
r = pos[0] * pos[0] + pos[1] * pos[1];
if(r < distance && tag[i].select < 0.001)
{
distance = r;
pos[0] = tag[i].vec[0];
pos[1] = tag[i].vec[1];
pos[2] = tag[i].vec[2];
output = TRUE;
}
}
}
return output;
}
#endif
struct{
float *vertex_array;
uint vertex_array_alloc;
uint vertex_count;
uint version;
uint node_id;
uint updateversion;
}LATransformCache;
void lo_projection_cache_init()
{
LATransformCache.vertex_array = NULL;
LATransformCache.vertex_array_alloc = 0;
LATransformCache.vertex_count = 0;
LATransformCache.version = 0;
LATransformCache.node_id = 0;
}
void lo_projection_cache_update(RMatrix *matrix)
{
double *vertex, tmp[3];
uint32 vertex_count, i;
udg_get_geometry(&vertex_count, NULL, &vertex, NULL, NULL);
LATransformCache.vertex_count = vertex_count;
if(LATransformCache.vertex_array_alloc < vertex_count ||
LATransformCache.vertex_array_alloc > vertex_count * 2)
{
LATransformCache.vertex_array_alloc = vertex_count;
if(LATransformCache.vertex_array != NULL)
free(LATransformCache.vertex_array);
LATransformCache.vertex_array = malloc((sizeof *LATransformCache.vertex_array) * LATransformCache.vertex_array_alloc * 2);
}
for(i = 0; i < vertex_count; i++)
{
if(vertex[i * 3] != V_REAL64_MAX)
{
seduce_view_projection_screend(NULL, tmp, vertex[i * 3], vertex[i * 3 + 1], vertex[i * 3 + 2]);
if(tmp[2] < 0)
{
LATransformCache.vertex_array[i * 2] = tmp[0];
LATransformCache.vertex_array[i * 2 + 1] = tmp[1];
}else
LATransformCache.vertex_array[i * 2] = 1000000;
}else
LATransformCache.vertex_array[i * 2] = 1000000;
}
LATransformCache.version = udg_get_version(FALSE, TRUE, FALSE, FALSE, FALSE);
LATransformCache.node_id = udg_get_modeling_node();
LATransformCache.updateversion++;
}
void lo_projection_cache_update_test(RMatrix *matrix)
{
// if(LATransformCache.version != udg_get_version(FALSE, TRUE, FALSE, FALSE, FALSE) ||
// LATransformCache.node_id != udg_get_modeling_node())
lo_projection_cache_update(matrix);
}
void lo_projection_cache_draw()
{
float color[3];
uint32 i, vertex_count;
return;
color[0] = f_randf(LATransformCache.updateversion);
color[1] = f_randf(LATransformCache.updateversion + 1);
color[2] = f_randf(LATransformCache.updateversion + 2);
for(i = 0; i < LATransformCache.vertex_count; i++)
{
r_primitive_line_2d(LATransformCache.vertex_array[i * 2] + 0.01,
LATransformCache.vertex_array[i * 2 + 1],
LATransformCache.vertex_array[i * 2] - 0.01,
LATransformCache.vertex_array[i * 2 + 1], color[0], color[1], color[2], 1);
r_primitive_line_2d(LATransformCache.vertex_array[i * 2],
LATransformCache.vertex_array[i * 2 + 1] + 0.01,
LATransformCache.vertex_array[i * 2],
LATransformCache.vertex_array[i * 2 + 1] - 0.01, color[0], color[1], color[2], 1);
}
r_primitive_line_flush();
}
boolean p_find_closest_edge_test(float *a, float *b, float x, float y)
{
float temp, r;
if((a[0] - b[0]) * (x - b[0]) + (a[1] - b[1]) * (y - b[1]) < 0)
return FALSE;
if((b[0] - a[0]) * (x - a[0]) + (b[1] - a[1]) * (y - a[1]) < 0)
return FALSE;
if(a[0] > 100000 || b[0] > 100000)
return FALSE;
r = sqrt((b[1] - a[1]) * (b[1] - a[1]) + -(b[0] - a[0]) * -(b[0] - a[0]));
temp = (x - a[0]) * ((b[1] - a[1]) / r) + (y - a[1]) * (-(b[0] - a[0]) / r);
if(temp > 0.008 || temp < -0.008 || r < 0.0001)
return FALSE;
return TRUE;
}
boolean p_find_closest_edge(uint *edge, double *snap, double x, double y)
{
float *a, *b, *c, *d, pointer_x, pointer_y;
uint32 vertex_count, ref_count, *ref, i;
double *vertex;
udg_get_geometry(&vertex_count, &ref_count, &vertex, &ref, NULL);
ref_count *= 4;
edge[0] = -1;
edge[1] = -1;
pointer_x = x;
pointer_y = y;
if(LATransformCache.vertex_count < vertex_count)
vertex_count = LATransformCache.vertex_count;
for(i = 0; i < ref_count; i += 4)
{
if(ref[i] < vertex_count &&
ref[i + 1] < vertex_count &&
ref[i + 2] < vertex_count)
{
a = &LATransformCache.vertex_array[ref[i] * 2];
b = &LATransformCache.vertex_array[ref[i + 1] * 2];
c = &LATransformCache.vertex_array[ref[i + 2] * 2];
// if((a[0] - b[0]) * (c[1] - b[1]) + (a[1] - b[1]) * (b[0] - c[0]) > 0)
{
if(p_find_closest_edge_test(a, b, pointer_x, pointer_y))
{
edge[0] = ref[i];
edge[1] = ref[i + 1];
break;
}
if(p_find_closest_edge_test(b, c, pointer_x, pointer_y))
{
edge[0] = ref[i + 1];
edge[1] = ref[i + 2];
break;
}
if(ref[i + 3] < vertex_count)
{
d = &LATransformCache.vertex_array[ref[i + 3] * 2];
if(p_find_closest_edge_test(c, d, pointer_x, pointer_y))
{
edge[0] = ref[i + 2];
edge[1] = ref[i + 3];
break;
}
if(p_find_closest_edge_test(a, d, pointer_x, pointer_y))
{
edge[0] = ref[i + 3];
edge[1] = ref[i + 0];
break;
}
}else if(p_find_closest_edge_test(c, a, pointer_x, pointer_y))
{
edge[0] = ref[i + 2];
edge[1] = ref[i + 0];
break;
}
}
}
}
if(edge[0] == -1)
{
ref = udg_get_edge_data(&ref_count);
for(i = 0; i < ref_count; i++)
{
a = &LATransformCache.vertex_array[ref[i * 2] * 2];
b = &LATransformCache.vertex_array[ref[i * 2 + 1] * 2];
if(p_find_closest_edge_test(a, b, pointer_x, pointer_y))
{
edge[0] = ref[i * 2];
edge[1] = ref[i * 2 + 1];
break;
}
}
}
if(edge[0] != -1)
{
snap[0] = vertex[edge[0] * 3 + 0];
snap[1] = vertex[edge[0] * 3 + 1];
snap[2] = vertex[edge[0] * 3 + 2];
snap[3] = vertex[edge[1] * 3 + 0];
snap[4] = vertex[edge[1] * 3 + 1];
snap[5] = vertex[edge[1] * 3 + 2];
return TRUE;
}
return FALSE;
}
/*
boolean p_find_closest_edge_old(uint *edge, double *snap, double x, double y)
{
double *vertex;
double a[3], b[3], c[3], d[3];
uint32 vertex_count, ref_count, *ref, i;
udg_get_geometry(&vertex_count, &ref_count, &vertex, &ref, NULL);
ref_count *= 4;
edge[0] = -1;
edge[1] = -1;
for(i = 0; i < ref_count; i += 4)
{
if(ref[i] < vertex_count && vertex[ref[i] * 3] != V_REAL64_MAX &&
ref[i + 1] < vertex_count && vertex[ref[i + 1] * 3] != V_REAL64_MAX &&
ref[i + 2] < vertex_count && vertex[ref[i + 2] * 3] != V_REAL64_MAX)
{
seduce_view_projection_screend(NULL, a, vertex[ref[i] * 3], vertex[ref[i] * 3 + 1], vertex[ref[i] * 3 + 2]);
seduce_view_projection_screend(NULL, b, vertex[ref[i + 1] * 3], vertex[ref[i + 1] * 3 + 1], vertex[ref[i + 1] * 3 + 2]);
seduce_view_projection_screend(NULL, c, vertex[ref[i + 2] * 3], vertex[ref[i + 2] * 3 + 1], vertex[ref[i + 2] * 3 + 2]);
if((a[0] - b[0]) * (c[1] - b[1]) + (a[1] - b[1]) * (b[0] - c[0]) > 0)
{
if(p_find_closest_edge_test(a, b, x, y))
{
edge[0] = ref[i];
edge[1] = ref[i + 1];
break;
}
if(p_find_closest_edge_test(b, c, x, y))
{
edge[0] = ref[i + 1];
edge[1] = ref[i + 2];
break;
}
if(ref[i + 3] < vertex_count && vertex[ref[i + 3] * 3] != V_REAL64_MAX)
{
seduce_view_projection_screend(NULL, d, vertex[ref[i + 3] * 3], vertex[ref[i + 3] * 3 + 1], vertex[ref[i + 3] * 3 + 2]);
if(p_find_closest_edge_test(c, d, x, y))
{
edge[0] = ref[i + 2];
edge[1] = ref[i + 3];
break;
}
if(p_find_closest_edge_test(a, d, x, y))
{
edge[0] = ref[i + 3];
edge[1] = ref[i + 0];
break;
}
}else if(p_find_closest_edge_test(c, a, x, y))
{
edge[0] = ref[i + 2];
edge[1] = ref[i + 0];
break;
}
}
}
}
if(edge[0] == -1)
{
ref = udg_get_edge_data(&ref_count);
for(i = 0; i < ref_count; i++)
{
seduce_view_projection_screend(NULL, a, vertex[ref[i * 2] * 3], vertex[ref[i * 2] * 3 + 1], vertex[ref[i * 2] * 3 + 2]);
seduce_view_projection_screend(NULL, b, vertex[ref[i * 2 + 1] * 3], vertex[ref[i * 2 + 1] * 3 + 1], vertex[ref[i * 2 + 1] * 3 + 2]);
if(p_find_closest_edge_test(a, b, x, y))
{
edge[0] = ref[i * 2];
edge[1] = ref[i * 2 + 1];
break;
}
}
}
if(edge[0] != -1)
{
snap[0] = vertex[edge[0] * 3 + 0];
snap[1] = vertex[edge[0] * 3 + 1];
snap[2] = vertex[edge[0] * 3 + 2];
snap[3] = vertex[edge[1] * 3 + 0];
snap[4] = vertex[edge[1] * 3 + 1];
snap[5] = vertex[edge[1] * 3 + 2];
return TRUE;
}
return FALSE;
}
*/
extern void la_t_poly_compute_normal(double *normal, egreal *vertex, uint *ref);
uint p_find_closest_polygon(BInputState *input, double *mid, double *normal, boolean *selected)
{
uint i, j, j2, poly, found, ref_count, *ref, vertex_count;
double *vertex;
float *temp[4], mouse[2], value;
boolean output = FALSE;
udg_get_geometry(&vertex_count, &ref_count, &vertex, &ref, NULL);
ref_count *= 4;
if(vertex_count > LATransformCache.vertex_count)
vertex_count = LATransformCache.vertex_count;
mouse[0] = input->pointers[0].pointer_x;
mouse[1] = input->pointers[0].pointer_y;
for(i = 0; i < ref_count; i += 4)
{
if(ref[i] < vertex_count &&
ref[i + 1] < vertex_count &&
ref[i + 2] < vertex_count)
{
temp[0] = &LATransformCache.vertex_array[ref[i] * 2];
temp[1] = &LATransformCache.vertex_array[ref[i + 1] * 2];
temp[2] = &LATransformCache.vertex_array[ref[i + 2] * 2];
if(ref[i + 3] < vertex_count)
{
temp[3] = &LATransformCache.vertex_array[ref[i + 3] * 2];
poly = 4;
}else
poly = 3;
found = 0;
for(j = 0; j < poly; j++)
{
j2 = (j + 1) % poly;
value = (mouse[0] - temp[j][0]) * (temp[j][1] - temp[j2][1]) + (mouse[1] - temp[j][1]) * (temp[j2][0] - temp[j][0]);
if(value >= 0 || temp[j][0] > 100000)
break;
/* value = (temp[j][0] - temp[j2][0]) * (temp[j][0] - temp[j2][0]) + (temp[j][1] - temp[j2][1]) * (temp[j][1] - temp[j2][1]);
if(value < 0.0001)
break;*/
}
if(j == poly)
{
if(mid != NULL)
{
mid[0] = 0;
mid[1] = 0;
mid[2] = 0;
for(j = 0; j < poly; j++)
{
mid[0] += vertex[ref[i + j] * 3 + 0] / (double)poly;
mid[1] += vertex[ref[i + j] * 3 + 1] / (double)poly;
mid[2] += vertex[ref[i + j] * 3 + 2] / (double)poly;
}
}
if(normal != NULL)
la_t_poly_compute_normal(normal, vertex, &ref[i]);
if(selected)
{
for(j = 0; j < poly; j++)
if(udg_get_select(ref[i + j]) > 0.01)
break;
*selected = j < poly;
}
return i / 4;
}
}
}
return -1;
}
boolean p_find_line_intersect_test(float *a, float *b, float *c, float *d, boolean *del)
{
float start, end;
if(a[2] > 100000 || b[1] > 100000)
return FALSE;
start = (a[0] - b[0]) * (c[1] - b[1]) + (a[1] - b[1]) * (b[0] - c[0]);
end = (a[0] - b[0]) * (d[1] - b[1]) + (a[1] - b[1]) * (b[0] - d[0]);
if((start > 0 && end < 0) || (start < 0 && end > 0))
{
start = (c[0] - d[0]) * (a[1] - d[1]) + (c[1] - d[1]) * (d[0] - a[0]);
end = (c[0] - d[0]) * (b[1] - d[1]) + (c[1] - d[1]) * (d[0] - b[0]);
if((start > 0 && end < 0) || (start < 0 && end > 0))
{
*del = TRUE;
return TRUE;
}
}
return FALSE;
}
boolean p_find_line_intersect(float *start, float *end, void (*func)(uint id))
{
ENode *node;
float *vertex, *a, *b, *c, *d;
boolean del = FALSE;
uint vertex_length, *ref, ref_length, i;
udg_get_geometry(&vertex_length, &ref_length, NULL, &ref, NULL);
if(vertex_length > LATransformCache.vertex_count)
vertex_length = LATransformCache.vertex_count;
ref_length *= 4;
for(i = 0; i < ref_length; i+= 4)
{
if(ref[i] < vertex_length && ref[i + 1] < vertex_length && ref[i + 2] < vertex_length)
{
a = &LATransformCache.vertex_array[ref[i] * 2];
b = &LATransformCache.vertex_array[ref[i + 1] * 2];
c = &LATransformCache.vertex_array[ref[i + 2] * 2];
if((a[0] - b[0]) * (c[1] - b[1]) + (a[1] - b[1]) * (b[0] - c[0]) > 0)
{
if(ref[i + 3] > vertex_length)
{
if(p_find_line_intersect_test(a, b, start, end, &del) ||
p_find_line_intersect_test(b, c, start, end, &del) ||
p_find_line_intersect_test(c, a, start, end, &del))
func(i / 4);
}
else
{
d = &LATransformCache.vertex_array[ref[i + 2] * 2];
if(p_find_line_intersect_test(a, b, start, end, &del) ||
p_find_line_intersect_test(b, c, start, end, &del) ||
p_find_line_intersect_test(b, d, start, end, &del) ||
p_find_line_intersect_test(d, a, start, end, &del))
func(i / 4);
}
}
}
}
ref = udg_get_edge_data(&i);
while(i != 0)
{
i--;
a = &LATransformCache.vertex_array[ref[i * 2] * 2];
b = &LATransformCache.vertex_array[ref[i * 2 + 1] * 2];
/* if(draw_line_delete_test(vertex, a, b, &del))
la_do_edge_delete_air(a, b);*/
}
return del;
}
uint p_find_click_tag(double x, double y)
{
uint32 count, i, output = -1;
double r, pos[3];
UNDOTag *tag;
tag = udg_get_tags(&count);
for(i = 0; i < count; i++)
{
seduce_view_projection_screend(NULL, pos, tag[i].vec[0], tag[i].vec[1], tag[i].vec[2]);
if(pos[2] < 0)
{
pos[0] += x;
pos[1] += y;
r = pos[0] * pos[0] + pos[1] * pos[1];
if(r < 0.001)
{
output = i;
}
}
}
return output;
}
boolean p_find_click_tag_lable(double x, double y)
{
uint32 count, i;
double f, f2, pos[3];
UNDOTag *tag;
tag = udg_get_tags(&count);
for(i = 0; i < count; i++)
{
seduce_view_projection_screend(NULL, pos, tag[i].vec[0], tag[i].vec[1], tag[i].vec[2]);
if(pos[2] < 0 && pos[1] - 0.02 < -y && pos[1] + 0.02 > -y)
{
f = seduce_text_line_length(NULL, SEDUCE_T_SIZE, SEDUCE_T_SPACE, tag[i].group, -1);
f2 = seduce_text_line_length(NULL, SEDUCE_T_SIZE, SEDUCE_T_SPACE, tag[i].tag, -1);
if(pos[0] > -x && pos[0] - (f + f2 + 0.06) < -x)
// if(sui_box_click_test(pos[0] - f + 0.04, pos[1] + 0.03, -f - 0.04 , -0.06))
// if(sui_box_click_test(pos[0] - f + 0.04, pos[1] + 0.03, -f - 0.04 , -0.06))
{
udg_rename_tag(i);
return TRUE;
}
/* if(sui_box_click_test(pos[0] + f + 0.04, pos[1] + 0.03, sui_compute_text_length(SEDUCE_T_SIZE, SEDUCE_T_SPACE, tag[i].tag) + 0.02 , -0.06))
{
exit(0);
udg_rename_tag_group(i);
return TRUE;
}*/
}
}
return FALSE;
}
boolean p_find_closest_vertex(uint *closest, uint *selected, double *distance, double *selected_distance, double x, double y, boolean include_selected)
{
uint32 vertex_count, i;
float pointer_x, pointer_y, pos_x, pos_y, r;
boolean closest_found = FALSE;
pointer_x = (float)x;
pointer_y = (float)y;
vertex_count = LATransformCache.vertex_count * 2;
for(i = 0; i < vertex_count; i += 2)
{
pos_x = LATransformCache.vertex_array[i] - x;
pos_y = LATransformCache.vertex_array[i + 1] - y;
r = pos_x * pos_x + pos_y * pos_y;
if(r < *selected_distance && r >= 0.0)
{
*selected_distance = r;
*selected = i / 2;
if(r <= *distance && (include_selected || udg_get_select(i / 2) < 0.001))
{
*distance = r;
*closest = i / 2;
closest_found = TRUE;
}
}
}
return closest_found;
}
/*
boolean p_find_closest_vertex_old(uint *closest, uint *selected, double *distance, double *selected_distance, double x, double y, boolean include_selected)
{
uint32 vertex_count, i;
double *vertex;
double pos[3], r;
boolean closest_found = FALSE;
udg_get_geometry(&vertex_count, NULL, &vertex, NULL, NULL);
for(i = 0; i < vertex_count; i++)
{
if(vertex[i * 3] != V_REAL64_MAX)
{
seduce_view_projection_screend(NULL, pos, vertex[i * 3], vertex[i * 3 + 1], vertex[i * 3 + 2]);
if(pos[2] < 0)
{
pos[0] -= x;
pos[1] -= y;
r = pos[0] * pos[0] + pos[1] * pos[1];
if(r < *selected_distance && r >= 0.0)
{
*selected_distance = r;
*selected = i;
if(r <= *distance && (include_selected || udg_get_select(i) < 0.001))
{
*distance = r;
*closest = i;
closest_found = TRUE;
}
}
}
}
}
return closest_found;
}*/
void p_set_view_center(double *center)
{
uint32 vertex_count, polygon_count, *ref, i;
double *vertex;
boolean rotate = FALSE;
double normal[3] = {0, 0, 0}, temp, size = 0.001;
seduce_view_center_set(NULL, center[0], center[1], center[2]);
udg_get_geometry(&vertex_count, &polygon_count, &vertex, &ref, NULL);
for(i = 0; i < polygon_count; i++)
{
if(ref[i * 4] < vertex_count && vertex[ref[i * 4] * 3] != V_REAL64_MAX && 0.01 < udg_get_select(ref[i * 4]) && ref[i * 4 + 1] < vertex_count && vertex[ref[i * 4 + 1] * 3] != V_REAL64_MAX && 0.01 < udg_get_select(ref[i * 4 + 1]) && ref[i * 4 + 2] < vertex_count && vertex[ref[i * 4 + 2] * 3] != V_REAL64_MAX && 0.01 < udg_get_select(ref[i * 4 + 2]) && (ref[i * 4 + 3] >= vertex_count || vertex[ref[i * 4 + 3] * 3] == V_REAL64_MAX || 0.01 < udg_get_select(ref[i * 4 + 3])))
{
normal[0] += ((vertex[ref[i * 4] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]) * (vertex[ref[i * 4 + 2] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]) - (vertex[ref[i * 4] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]) * (vertex[ref[i * 4 + 2] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]));
normal[1] += ((vertex[ref[i * 4] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]) * (vertex[ref[i * 4 + 2] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]) - (vertex[ref[i * 4] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]) * (vertex[ref[i * 4 + 2] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]));
normal[2] += ((vertex[ref[i * 4] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]) * (vertex[ref[i * 4 + 2] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]) - (vertex[ref[i * 4] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]) * (vertex[ref[i * 4 + 2] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]));
rotate = TRUE;
}
}
if(rotate != TRUE)
{
for(i = 0; i < polygon_count; i++)
{
if(ref[i * 4] < vertex_count && vertex[ref[i * 4] * 3] != V_REAL64_MAX && ref[i * 4 + 1] < vertex_count && vertex[ref[i * 4 + 1] * 3] != V_REAL64_MAX && ref[i * 4 + 2] < vertex_count && vertex[ref[i * 4 + 2] * 3] != V_REAL64_MAX)
{
if(0.01 < udg_get_select(ref[i * 4]) || 0.01 < udg_get_select(ref[i * 4 + 1]) || 0.01 < udg_get_select(ref[i * 4 + 2]) || (ref[i * 4 + 3] < vertex_count && vertex[ref[i * 4 + 3] * 3] != V_REAL64_MAX && 0.01 < udg_get_select(ref[i * 4 + 3])))
{
normal[0] += ((vertex[ref[i * 4] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]) * (vertex[ref[i * 4 + 2] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]) - (vertex[ref[i * 4] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]) * (vertex[ref[i * 4 + 2] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]));
normal[1] += ((vertex[ref[i * 4] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]) * (vertex[ref[i * 4 + 2] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]) - (vertex[ref[i * 4] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]) * (vertex[ref[i * 4 + 2] * 3 + 2] - vertex[ref[i * 4 + 1] * 3 + 2]));
normal[2] += ((vertex[ref[i * 4] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]) * (vertex[ref[i * 4 + 2] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]) - (vertex[ref[i * 4] * 3 + 1] - vertex[ref[i * 4 + 1] * 3 + 1]) * (vertex[ref[i * 4 + 2] * 3 + 0] - vertex[ref[i * 4 + 1] * 3 + 0]));
rotate = TRUE;
}
}
}
}
if(rotate == TRUE)
seduce_view_direction_set(NULL, normal[0], normal[1], normal[2]);
for(i = 0; i < vertex_count; i++)
{
if(vertex[i * 3] != V_REAL64_MAX && 0.01 < udg_get_select(i))
{
temp = (vertex[i * 3] - center[0]) * (vertex[i * 3] - center[0]) + (vertex[i * 3 + 1] - center[1]) * (vertex[i * 3 + 1] - center[1]) + (vertex[i * 3 + 2] - center[2]) * (vertex[i * 3 + 2] - center[2]);
if(temp > size)
size = temp;
}
}
size = sqrt(size) * 2;
seduce_view_distance_camera_set(NULL, size);
}
/*
void seduce_view_direction_set(SViewData *v, double normal_x, double normal_y, double normal_z)
{
double f;
if(v == NULL)
v = &sui_default_view;
f = 360 * atan2(-normal_x, normal_z) / PI * 2.0;
if(f > 0 && v->yaw < f - 180.0)
v->yaw += 360;
if(f < 0 && v->yaw > f + 180.0)
v->yaw -= 360;
v->yaw_target = temp;
temp = (normal_x * normal_x) + (normal_y * normal_y) + (normal_z * normal_z);
if(0.0 != temp)
v->pitch_target = 360.0 * atan(normal_y / sqrt(f)) / (PI * 2.0);
}
void
*/
double la_axis_matrix[16] = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1};
boolean la_axis_visible = FALSE;
void la_view_axis_matrix_poly_set(uint polygon, uint edge)
{
uint32 vertex_count, polygon_count, *ref;
double *vertex;
la_axis_visible = TRUE;
udg_get_geometry(&vertex_count, &polygon_count, &vertex, &ref, NULL);
if(polygon >= polygon_count)
return;
if(ref[polygon * 4 + 3] > vertex_count || vertex[ref[polygon * 4 + 3] * 3] == V_REAL64_MAX)
{
f_matrixxyd(la_axis_matrix, &vertex[ref[polygon * 4 + edge] * 3],
&vertex[ref[polygon * 4 + (edge + 1) % 3] * 3],
&vertex[ref[polygon * 4 + (edge + 2) % 3] * 3]);
}else
{
f_matrixxyd(la_axis_matrix, &vertex[ref[polygon * 4 + edge] * 3],
&vertex[ref[polygon * 4 + (edge + 1) % 4] * 3],
&vertex[ref[polygon * 4 + (edge + 3) % 4] * 3]);
}
}
void la_view_axis_matrix_edge_set(uint edge_a, uint edge_b)
{
uint result;
la_axis_visible = TRUE;
result = la_t_edge_fill_find_edge_poly(edge_a, edge_b, FALSE);
if(result != -1)
la_view_axis_matrix_poly_set(result / 4, result % 4);
}
void la_view_axis_matrix_reset()
{
la_axis_visible = FALSE;
la_axis_matrix[0] = 1;
la_axis_matrix[1] = 0;
la_axis_matrix[2] = 0;
la_axis_matrix[3] = 0;
la_axis_matrix[4] = 0;
la_axis_matrix[5] = 1;
la_axis_matrix[6] = 0;
la_axis_matrix[7] = 0;
la_axis_matrix[8] = 0;
la_axis_matrix[9] = 0;
la_axis_matrix[10] = 1;
la_axis_matrix[11] = 0;
la_axis_matrix[12] = 0;
la_axis_matrix[13] = 0;
la_axis_matrix[14] = 0;
la_axis_matrix[15] = 1;
}
extern void lo_pfx_wiggle_line_add(float ax, float ay, float az, float bx, float by, float bz, boolean major);
void la_view_projection_vertex_with_axis(BInputState *input, RMatrix *matrix, double *output, double *start, double pointer_x, double pointer_y, boolean snap, double *closest, SUISnapType snap_type)
{
static uint best_axis = 0;
static double direction = 1;
double pos[3], dout[3];
float out[3], v[3], screen[3], f, best = 10000000;
uint i;
v[0] = start[0];
v[1] = start[1];
v[2] = start[2];
r_matrix_projection_vertexf(&la_world_matrix, out, v, pointer_x, pointer_y);
output[0] = out[0];
output[1] = out[1];
output[2] = out[2];
if(!snap || snap_type == SUI_ST_NONE)
{
for(i = 0; i < 3; i++)
{
float axis[3];
axis[0] = la_axis_matrix[i * 4];
axis[1] = la_axis_matrix[i * 4 + 1];
axis[2] = la_axis_matrix[i * 4 + 2];
r_matrix_projection_vectorf(&la_world_matrix, out, v, axis, pointer_x, pointer_y);
r_matrix_projection_screenf(&la_world_matrix, screen, out[0], out[1], out[2]);
screen[0] -= pointer_x;
screen[1] -= pointer_y;
f = screen[0] * screen[0] + screen[1] * screen[1];
if(0.01 * 0.01 > f)
{
output[0] = out[0];
output[1] = out[1];
output[2] = out[2];
}
if(f < best)
{
best = f;
best_axis = i;
f_transforminv3d(dout, la_axis_matrix, output[0], output[1], output[2]);
f_transforminv3d(pos, la_axis_matrix, start[0], start[1], start[2]);
if(pos[i] < dout[i])
direction = 1;
else
direction = -1;
}
}
}else
{
float fpos[3];
if(snap_type == SUI_ST_VERTEX_CLOSE)
fpos[0] = 0;
switch(snap_type)
{
case SUI_ST_VERTEX_FAR :
case SUI_ST_VERTEX_CLOSE :
f_transforminv3d(dout, la_axis_matrix, closest[0], closest[1], closest[2]);
f_transforminv3d(pos, la_axis_matrix, start[0], start[1], start[2]);
pos[best_axis] = dout[best_axis];
f_transform3d(output, la_axis_matrix, pos[0], pos[1], pos[2]);
break;
case SUI_ST_LINE :
f_transforminv3d(dout, la_axis_matrix, out[0], out[1], out[2]);
f_transforminv3d(pos, la_axis_matrix, start[0], start[1], start[2]);
pos[best_axis] += direction * sqrt((closest[0] - closest[3]) * (closest[0] - closest[3]) +
(closest[1] - closest[4]) * (closest[1] - closest[4]) +
(closest[2] - closest[5]) * (closest[2] - closest[5]));
f_transform3d(output, la_axis_matrix, pos[0], pos[1], pos[2]);
break;
case SUI_ST_TANGENT :
{
double normal[3], vector[5] = {0, 0, 1, 0, 0}, t_start[3];
f_transforminv3d(pos, la_axis_matrix, closest[0], closest[1], closest[2]);
f_transforminv3d(normal, la_axis_matrix, closest[3], closest[4], closest[5]);
f_transforminv3d(t_start, la_axis_matrix, start[0], start[1], start[2]);
dout[0] = t_start[0];
dout[1] = t_start[1];
dout[2] = t_start[2];
f_project3d(output, pos, normal, t_start, &vector[2 - best_axis]);
dout[best_axis] = output[best_axis];
f_transform3d(output, la_axis_matrix, dout[0], dout[1], dout[2]);
}
break;
}
lo_pfx_draw_snap(input, snap_type, closest, output, !input->pointers[0].button[0]);
}
}