mirror of
git://projects.qi-hardware.com/cae-tools.git
synced 2024-12-23 03:02:25 +02:00
f2963b9e2a
- solidify/style.h (PROJECTION_BORDER): number of pixels to leave dark above and below the face - solidify/level.c (draw_xz, draw_zy, level): increase projection z size by twice the border width - solidify/level.c (draw_xz, draw_zy): offset z coordinates by the border
336 lines
7.6 KiB
C
336 lines
7.6 KiB
C
/*
|
|
* level.c - Interactively align a nearly horizontal plane with a face
|
|
*
|
|
* Written 2010 by Werner Almesberger
|
|
* Copyright 2010 by Werner Almesberger
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "array.h"
|
|
#include "face.h"
|
|
#include "gui_util.h"
|
|
#include "style.h"
|
|
#include "level.h"
|
|
|
|
|
|
#define NEAR 1
|
|
|
|
|
|
static int has_osd;
|
|
static GtkWidget *xz, *zy;
|
|
|
|
|
|
static double r_center(const struct face *f)
|
|
{
|
|
return hypot(f->sx, f->sy)/LEVEL_CENTER_DIV;
|
|
}
|
|
|
|
|
|
static void draw_map(GtkWidget *widget, struct face *f)
|
|
{
|
|
int x, y, z;
|
|
double z0;
|
|
guchar *rgbbuf, *p;
|
|
|
|
rgbbuf = p = calloc(f->sx*f->sy, 3);
|
|
if (!rgbbuf) {
|
|
perror("calloc");
|
|
exit(1);
|
|
}
|
|
for (y = f->sy-1; y >= 0; y--)
|
|
for (x = 0; x != f->sx ; x++) {
|
|
z = get(f->a, x+f->a->min_x, y+f->a->min_y);
|
|
if (z == UNDEF) {
|
|
p += 3;
|
|
continue;
|
|
}
|
|
z0 = face_z0(f, x, y);
|
|
if (fabs(z-z0) < NEAR) {
|
|
*p++ = 255*fabs(z-z0);
|
|
*p++ = 255*fabs(z-z0);
|
|
*p++ = 255;
|
|
continue;
|
|
}
|
|
if (z < z0) {
|
|
z = z > z0-2*NEAR ? 255*(z-z0)/NEAR :
|
|
255.0*(z-z0)/(z0-f->a->min_z);
|
|
*p++ = 255;
|
|
*p++ = z;
|
|
*p++ = z;
|
|
} else {
|
|
z = z < z0+2*NEAR ? 255*(z0-z)/NEAR :
|
|
255.0*(z0-z)/(f->a->max_z-z0);
|
|
*p++ = z;
|
|
*p++ = 255;
|
|
*p++ = z;
|
|
}
|
|
}
|
|
gdk_draw_rgb_image(widget->window,
|
|
widget->style->fg_gc[GTK_STATE_NORMAL],
|
|
0, 0, f->sx, f->sy, GDK_RGB_DITHER_MAX, rgbbuf, f->sx*3);
|
|
free(rgbbuf);
|
|
}
|
|
|
|
|
|
static void draw_image(GtkWidget *widget, struct face *f, int osd)
|
|
{
|
|
draw_map(widget, f);
|
|
has_osd = osd;
|
|
if (osd)
|
|
draw_circle(widget->window, gc_osd,
|
|
f->sx/2, f->sy/2, r_center(f));
|
|
}
|
|
|
|
|
|
static void draw_xz(GtkWidget *widget, struct face *f, int y)
|
|
{
|
|
int zm = f->a->max_z;
|
|
int sz = f->sz*z0_scale(f)+2*PROJECTION_BORDER;
|
|
int x, z;
|
|
double z0;
|
|
guchar *rgbbuf, *p;
|
|
|
|
rgbbuf = p = calloc(f->sx*sz, 3);
|
|
if (!rgbbuf) {
|
|
perror("calloc");
|
|
exit(1);
|
|
}
|
|
if (y != -1)
|
|
for (x = 0; x != f->sx ; x++) {
|
|
z = get(f->a, x+f->a->min_x, y+f->a->min_y);
|
|
z0 = face_z0(f, x, y);
|
|
if (z == UNDEF || z == z0) {
|
|
aa_line(rgbbuf, x,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
sz-1, f->sx,
|
|
(uint8_t *) "\0\0\xff", vpoint);
|
|
continue;
|
|
}
|
|
if (z > z0) {
|
|
aa_line(rgbbuf, x,
|
|
(zm-z)*z0_scale(f)+PROJECTION_BORDER,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
sz-1, f->sx,
|
|
(uint8_t *) "\0\xff\0", vpoint);
|
|
} else {
|
|
aa_line(rgbbuf, x,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
(zm-z)*z0_scale(f)+PROJECTION_BORDER,
|
|
sz-1, f->sx,
|
|
(uint8_t *) "\xff\0\0", vpoint);
|
|
}
|
|
}
|
|
gdk_draw_rgb_image(widget->window,
|
|
widget->style->fg_gc[GTK_STATE_NORMAL],
|
|
0, 0, f->sx, sz, GDK_RGB_DITHER_MAX, rgbbuf, f->sx*3);
|
|
free(rgbbuf);
|
|
|
|
}
|
|
|
|
|
|
static void draw_zy(GtkWidget *widget, struct face *f, int x)
|
|
{
|
|
int zm = f->a->max_z;
|
|
int sz = f->sz*z0_scale(f)+2*PROJECTION_BORDER;
|
|
int y, z;
|
|
double z0;
|
|
guchar *rgbbuf, *p;
|
|
|
|
rgbbuf = p = calloc(f->sy*sz, 3);
|
|
if (!rgbbuf) {
|
|
perror("calloc");
|
|
exit(1);
|
|
}
|
|
if (x != -1)
|
|
for (y = 0; y != f->sy ; y++) {
|
|
z = get(f->a, x+f->a->min_x, f->a->max_y-y);
|
|
z0 = face_z0(f, x, f->sy-y-1);
|
|
if (z == UNDEF || z == z0) {
|
|
aa_line(rgbbuf, y,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
sz-1, sz,
|
|
(uint8_t *) "\0\0\xff", hpoint);
|
|
continue;
|
|
}
|
|
if (z > z0) {
|
|
aa_line(rgbbuf, y,
|
|
(zm-z)*z0_scale(f)+PROJECTION_BORDER,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
sz-1, sz,
|
|
(uint8_t *) "\0\xff\0", hpoint);
|
|
} else {
|
|
aa_line(rgbbuf, y,
|
|
(zm-z0)*z0_scale(f)+PROJECTION_BORDER,
|
|
(zm-z)*z0_scale(f)+PROJECTION_BORDER,
|
|
sz-1, sz,
|
|
(uint8_t *) "\xff\0\0", hpoint);
|
|
}
|
|
}
|
|
gdk_draw_rgb_image(widget->window,
|
|
widget->style->fg_gc[GTK_STATE_NORMAL],
|
|
0, 0, sz, f->sy, GDK_RGB_DITHER_MAX, rgbbuf, sz*3);
|
|
free(rgbbuf);
|
|
|
|
}
|
|
|
|
|
|
static void scroll_z(GtkWidget *darea, struct face *f, int up, int osd)
|
|
{
|
|
if (up) {
|
|
if (f->z_ref < f->a->max_z)
|
|
f->z_ref++;
|
|
} else {
|
|
if (f->z_ref > f->a->min_z)
|
|
f->z_ref--;
|
|
}
|
|
draw_image(darea, f, osd);
|
|
}
|
|
|
|
|
|
static void scroll_xy(GtkWidget *darea, struct face *f, int dx, int dy, int up,
|
|
int osd)
|
|
{
|
|
double d;
|
|
|
|
d = (double) (up ? 1 : -1)/(dx*dx+dy*dy)/2.0;
|
|
f->fx += d*dx;
|
|
f->fy += d*dy;
|
|
draw_image(darea, f, osd);
|
|
}
|
|
|
|
|
|
static gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event,
|
|
gpointer data)
|
|
{
|
|
GtkWidget *darea = gtk_bin_get_child(GTK_BIN(widget));
|
|
struct face *f = data;
|
|
int dx = event->x-f->sx/2;
|
|
int dy = event->y-f->sy/2;
|
|
double r = hypot(dx, dy);
|
|
double rc = r_center(f);
|
|
int center = r < rc;
|
|
int osd = fabs(r-rc) < OSD_PROXIMITY;
|
|
|
|
switch (event->direction) {
|
|
case GDK_SCROLL_UP:
|
|
if (center)
|
|
scroll_z(darea, f, 0, osd);
|
|
else
|
|
scroll_xy(darea, f, dx, dy, 1, osd);
|
|
break;
|
|
case GDK_SCROLL_DOWN:
|
|
if (center)
|
|
scroll_z(darea, f, 1, osd);
|
|
else
|
|
scroll_xy(darea, f, dx, dy, 0, osd);
|
|
break;
|
|
default:
|
|
/* ignore */;
|
|
}
|
|
draw_xz(xz, f, f->sy-1-event->y);
|
|
draw_zy(zy, f, event->x);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean expose_event_xy(GtkWidget *widget, GdkEventExpose *event,
|
|
gpointer user_data)
|
|
{
|
|
draw_image(widget, user_data, has_osd);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean expose_event_xz(GtkWidget *widget, GdkEventExpose *event,
|
|
gpointer user_data)
|
|
{
|
|
draw_xz(widget, user_data, -1);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean expose_event_zy(GtkWidget *widget, GdkEventExpose *event,
|
|
gpointer user_data)
|
|
{
|
|
draw_zy(widget, user_data, -1);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean motion_notify_event(GtkWidget *widget, GdkEventMotion *event,
|
|
gpointer data)
|
|
{
|
|
struct face *f = data;
|
|
int dx = event->x-f->sx/2;
|
|
int dy = event->y-f->sy/2;
|
|
double r = hypot(dx, dy);
|
|
double rc = r_center(f);
|
|
int osd = fabs(r-rc) < OSD_PROXIMITY;
|
|
|
|
if (osd != has_osd)
|
|
draw_image(widget, f, osd);
|
|
draw_xz(xz, f, f->sy-1-event->y);
|
|
draw_zy(zy, f, event->x);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void level(GtkWidget *canvas, struct face *f)
|
|
{
|
|
GtkWidget *evbox, *tab, *xy;
|
|
|
|
evbox = gtk_event_box_new();
|
|
tab = gtk_table_new(2, 2, FALSE);
|
|
xy = gtk_drawing_area_new();
|
|
xz = gtk_drawing_area_new();
|
|
zy = gtk_drawing_area_new();
|
|
|
|
gtk_widget_set_events(xy,
|
|
GDK_EXPOSE | GDK_KEY_PRESS_MASK |
|
|
GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
|
|
GDK_SCROLL |
|
|
GDK_POINTER_MOTION_MASK);
|
|
|
|
gtk_widget_set_size_request(xy, f->sx, f->sy);
|
|
gtk_widget_set_size_request(xz,
|
|
f->sx, f->sz*z0_scale(f)+2*PROJECTION_BORDER);
|
|
gtk_widget_set_size_request(zy,
|
|
f->sz*z0_scale(f)+2*PROJECTION_BORDER, f->sy);
|
|
|
|
gtk_table_set_row_spacings(GTK_TABLE(tab), 2);
|
|
gtk_table_set_col_spacings(GTK_TABLE(tab), 2);
|
|
|
|
gtk_table_attach_defaults(GTK_TABLE(tab), evbox, 0, 1, 0, 1);
|
|
gtk_table_attach_defaults(GTK_TABLE(tab), xz, 0, 1, 1, 2);
|
|
gtk_table_attach_defaults(GTK_TABLE(tab), zy, 1, 2, 0, 1);
|
|
|
|
gtk_container_add(GTK_CONTAINER(canvas), tab);
|
|
gtk_container_add(GTK_CONTAINER(evbox), xy);
|
|
|
|
gtk_widget_show_all(canvas);
|
|
has_osd = 0;
|
|
|
|
g_signal_connect(G_OBJECT(evbox), "scroll-event",
|
|
G_CALLBACK(scroll_event), f);
|
|
g_signal_connect(G_OBJECT(xy), "expose-event",
|
|
G_CALLBACK(expose_event_xy), f);
|
|
g_signal_connect(G_OBJECT(xz), "expose-event",
|
|
G_CALLBACK(expose_event_xz), f);
|
|
g_signal_connect(G_OBJECT(zy), "expose-event",
|
|
G_CALLBACK(expose_event_zy), f);
|
|
g_signal_connect(G_OBJECT(xy), "motion-notify-event",
|
|
G_CALLBACK(motion_notify_event), f);
|
|
}
|