1
0
mirror of git://projects.qi-hardware.com/cae-tools.git synced 2024-12-23 03:22:25 +02:00
cae-tools/solidify/level.c

201 lines
4.1 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 "util.h"
#include "array.h"
#include "face.h"
#include "style.h"
#include "level.h"
#define NEAR 1
static int has_osd;
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 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 */;
}
return TRUE;
}
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event,
gpointer user_data)
{
draw_image(widget, user_data, has_osd);
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);
return FALSE;
}
void level(GtkWidget *canvas, struct face *f)
{
GtkWidget *evbox, *darea;
evbox = gtk_event_box_new();
darea = gtk_drawing_area_new();
gtk_widget_set_events(darea,
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(darea, f->sx, f->sy);
gtk_container_add(GTK_CONTAINER(canvas), evbox);
gtk_container_add(GTK_CONTAINER(evbox), darea);
draw_image(darea, f, 0);
g_signal_connect(G_OBJECT(evbox), "scroll-event",
G_CALLBACK(scroll_event), f);
g_signal_connect(G_OBJECT(darea), "expose-event",
G_CALLBACK(expose_event), f);
g_signal_connect(G_OBJECT(darea), "motion-notify-event",
G_CALLBACK(motion_notify_event), f);
}