vanishing-point.cpp revision bedbeec8a0241f2d83052b4c9e3f40510b1edb73
#define __VANISHING_POINT_C__
/*
* Vanishing point for 3D perspectives
*
* Authors:
* bulia byak <buliabyak@users.sf.net>
* Johan Engelen <j.b.c.engelen@ewi.utwente.nl>
* Maximilian Albert <Anhalter42@gmx.de>
*
* Copyright (C) 2005-2007 authors
*
* Released under GNU GPL, read the file 'COPYING' for more information
*/
#include "vanishing-point.h"
#include "desktop-handles.h"
#include "box3d.h"
#include "knotholder.h" // FIXME: can we avoid direct access to knotholder_update_knots?
namespace Box3D {
#define VP_KNOT_COLOR_NORMAL 0xffffff00
#define VP_KNOT_COLOR_SELECTED 0x0000ff00
#define VP_LINE_COLOR_FILL 0x0000ff7f
#define VP_LINE_COLOR_STROKE_X 0xff00007f
#define VP_LINE_COLOR_STROKE_Y 0x0000ff7f
#define VP_LINE_COLOR_STROKE_Z 0xffff007f
// screen pixels between knots when they snap:
#define SNAP_DIST 5
// absolute distance between gradient points for them to become a single dragger when the drag is created:
#define MERGE_DIST 0.1
// knot shapes corresponding to GrPointType enum
SPKnotShapeType vp_knot_shapes [] = {
SP_KNOT_SHAPE_SQUARE, // VP_FINITE
SP_KNOT_SHAPE_CIRCLE //VP_INFINITE
};
// FIXME: We should always require to have both the point (for finite VPs)
// and the direction (for infinite VPs) set. Otherwise toggling
// shows very unexpected behaviour.
// Later on we can maybe infer the infinite direction from the finite point
// and a suitable center of the scene. How to go in the other direction?
{
//this->ref_pt = rhs.ref_pt;
}
VanishingPoint::~VanishingPoint () {}
{
// Should we compare the parent perspectives, too? Probably not.
return true;
}
return false;
}
bool VanishingPoint::is_finite() const
{
}
{
this->state = VP_INFINITE;
} else {
}
return this->state;
}
{
switch (axis) {
case X:
else
break;
case Y:
else
break;
case Z:
else
break;
default:
break;
}
}
static void
{
drag->updateDraggers ();
drag->updateLines ();
}
static void
{
/***
if (drag->local_change) {
drag->local_change = false;
} else {
drag->updateDraggers ();
}
***/
drag->updateLines ();
}
// auxiliary function
static GSList *
eliminate_remaining_boxes_of_persp_starting_from_list_position (GSList *boxes_to_do, const SP3DBox *start_box, const Perspective3D *persp)
{
i = i->next;
while (i != NULL) {
}
i = successor;
}
return boxes_to_do;
}
static bool
{
return true;
}
}
return false;
}
static void
{
// FIXME: take from prefs
if (!(state & GDK_SHIFT_MASK)) {
// without Shift; see if we need to snap to another dragger
// this would result in degenerate boxes, which we disallow for the time being
continue;
}
// update positions ...
}
// ... join lists of VPs ...
// FIXME: Do we have to copy the second list (i.e, is it invalidated when dragger is deleted below)?
// ... delete old dragger ...
delete dragger;
// ... and merge any duplicate perspectives
// TODO: Update the new merged dragger
//d_new->updateKnotShape ();
d_new->updateBoxReprs ();
d_new->updateZOrders ();
drag->updateLines ();
// TODO: Undo machinery; this doesn't work yet because perspectives must be created and
// deleted according to changes in the svg representation, not based on any user input
// as is currently the case.
//sp_document_done (sp_desktop_document (drag->desktop), SP_VERB_CONTEXT_3DBOX,
// _("Merge vanishing points"));
return;
}
}
}
dragger->updateBoxReprs ();
dragger->updateZOrders ();
drag->updateLines ();
//drag->local_change = false;
}
/***
static void
vp_knot_clicked_handler(SPKnot *knot, guint state, gpointer data)
{
VPDragger *dragger = (VPDragger *) data;
}
***/
void
{
//sp_canvas_force_full_redraw_after_interruptions(dragger->parent->desktop->canvas, 5);
if ((state & GDK_SHIFT_MASK) && !drag->hasEmptySelection()) { // FIXME: Is the second check necessary?
// if all of the boxes linked to dragger are selected, we don't need to split it
return;
}
// we are Shift-dragging; unsnap if we carry more than one VP
// FIXME: Should we distinguish between the following cases:
// 1) there are several VPs in a dragger
// 2) there is only a single VP but several boxes linked to it
// ?
// Or should we simply unlink all selected boxes? Currently we do the latter.
// create a new dragger
// move all the VPs from dragger to dr_new
/* now we move all selected boxes back to the current dragger (splitting perspectives
if they also have unselected boxes) so that they are further reshaped during dragging */
g_warning ("VP is NULL. We should be okay, though.\n");
}
// if all boxes of persp are selected, we can simply move the VP from dr_new back to dragger
// some cleaning up for efficiency
boxes_to_do = eliminate_remaining_boxes_of_persp_starting_from_list_position (boxes_to_do, box, persp);
} else {
/* otherwise the unselected boxes need to stay linked to dr_new; thus we
create a new perspective and link the VPs to the correct draggers */
// now we move the selected boxes from persp to persp_new
}
// cleaning up
boxes_to_do = eliminate_remaining_boxes_of_persp_starting_from_list_position (boxes_to_do, box, persp);
}
}
// TODO: Something is still wrong with updating the boxes' representations after snapping
//dr_new->updateBoxReprs ();
}
}
}
static void
{
//sp_canvas_end_forced_full_redraws(dragger->parent->desktop->canvas);
/***
VanishingPoint *vp;
for (GSList *i = dragger->vps; i != NULL; i = i->next) {
vp = (VanishingPoint *) i->data;
vp->set_pos (knot->pos);
}
***/
dragger->updateBoxReprs ();
// TODO: Update box's paths and svg representation
// TODO: Undo machinery!!
_("3D box: Move vanishing point"));
}
{
this->point = p;
this->point_original = p;
// create the knot
sp_knot_update_ctrl(this->knot);
// move knot to the given point
sp_knot_show (this->knot);
// connect knot's signals
/***
g_signal_connect (G_OBJECT (this->knot), "clicked", G_CALLBACK (vp_knot_clicked_handler), this);
***/
g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
/***
g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (vp_knot_doubleclicked_handler), this);
***/
// add the initial VP (which may be NULL!)
//updateKnotShape();
}
}
{
// unselect if it was selected
//this->parent->setDeselected(this);
// disconnect signals
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_moved_handler), this);
/***
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_clicked_handler), this);
***/
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_grabbed_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
/***
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_doubleclicked_handler), this);
***/
/* unref should call destroy */
g_slist_free (this->vps);
}
/**
Updates the statusbar tip of the dragger knot, based on its draggables
*/
void
{
}
case VP_FINITE:
this->knot->tip = g_strdup_printf (ngettext("<b>Finite</b> vanishing point shared by <b>%d</b> box",
"<b>Finite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
num),
num);
break;
case VP_INFINITE:
// This won't make sense any more when infinite VPs are not shown on the canvas,
// but currently we update the status message anyway
this->knot->tip = g_strdup_printf (ngettext("<b>Infinite</b> vanishing point shared by <b>%d</b> box",
"<b>Infinite</b> vanishing point shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
num),
num);
break;
}
} else {
char *desc2 = g_strdup_printf (ngettext("shared by <b>%d</b> box; drag with <b>Shift</b> to separate selected box(es)",
"shared by <b>%d</b> boxes; drag with <b>Shift</b> to separate selected box(es)",
num),
num);
}
}
/**
* Adds a vanishing point to the dragger (also updates the position)
*/
void
{
return;
}
// don't add infinite VPs, and don't add the same VP twice
return;
}
this->updateTip();
}
void
{
g_print ("NULL vanishing point will not be removed.\n");
return;
}
this->updateTip();
}
// returns the VP contained in the dragger that belongs to persp
{
return ((VanishingPoint *) i->data);
}
}
return NULL;
}
bool
{
}
return false;
}
{
}
return num;
}
bool
{
return true;
}
}
return false;
}
void
{
// if the perspective is deleted, the VP is invalidated, too, so we must store its successor beforehand
persp1->absorb (persp2); // persp2 is deleted; hopefully this doesn't screw up the list of vanishing points and thus the loops
}
}
}
}
void
{
// TODO: We can extract the VP directly from the box's perspective. Is that vanishing point identical to 'vp'?
// Or is there duplicated information? If so, remove it and simplify the whole construction!
parent->document->get_persp_of_VP (vp)->reshape_boxes (axis); // FIXME: we should only update the direction of the VP
}
}
void
{
}
}
void
{
}
}
{
this->show_lines = true;
this->front_or_rear_lines = 0x1;
//this->selected = NULL;
this->local_change = false;
this->dragging = false;
(gpointer)this )
);
(gpointer)this )
);
this->updateDraggers ();
this->updateLines ();
}
{
this->sel_changed_connection.disconnect();
this->sel_modified_connection.disconnect();
}
g_list_free (this->draggers);
}
g_slist_free (this->lines);
}
/**
* Select the dragger that has the given VP.
*/
{
// TODO: Should we compare the pointers or the VPs themselves!?!?!?!
//if ((*vp2) == vp) {
return (dragger);
}
}
}
return NULL;
}
/**
* Regenerates the draggers list from the current selection; is called when selection is changed or modified
*/
void
VPDrag::updateDraggers ()
{
if (this->dragging)
return;
/***
while (selected) {
selected = g_list_remove(selected, selected->data);
}
***/
// delete old draggers
}
g_list_free (this->draggers);
//SPStyle *style = SP_OBJECT_STYLE (item);
if (!SP_IS_3DBOX (item)) continue;
}
}
/**
Regenerates the lines list from the current selection; is called on each move
of a dragger, so that lines are always in sync with the actual perspective
*/
void
VPDrag::updateLines ()
{
// delete old lines
}
g_slist_free (this->lines);
// do nothing if perspective lines are currently disabled
if (this->show_lines == 0) return;
if (!SP_IS_3DBOX(i->data)) continue;
}
}
void
{
// FIXME: Is there a way to update the knots without accessing the
// statically linked function knotholder_update_knots?
// Currently we only show handles if a single box is selected
return;
}
return;
}
}
/**
* Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
*/
void
VPDrag::drawLinesForFace (const SP3DBox *box, Box3D::Axis axis) //, guint corner1, guint corner2, guint corner3, guint corner4)
{
switch (axis) {
// TODO: Make color selectable by user
default: g_assert_not_reached();
}
// draw perspective lines for finite VPs
if (this->front_or_rear_lines & 0x1) {
// draw 'front' perspective lines
}
if (this->front_or_rear_lines & 0x2) {
// draw 'rear' perspective lines
}
} else {
// draw perspective lines for infinite VPs
// some perspective lines s are outside the canvas; currently we don't draw any of them
return;
}
if (this->front_or_rear_lines & 0x1) {
// draw 'front' perspective lines
}
if (this->front_or_rear_lines & 0x2) {
// draw 'rear' perspective lines
}
}
}
/**
* Returns true if all boxes that are linked to a VP in the dragger are selected
*/
bool
if (!document->get_persp_of_VP ((VanishingPoint *) i->data)->all_boxes_occur_in_list (selected_boxes)) {
return false;
}
}
return true;
}
GSList *
{
}
}
return sel_boxes;
}
/**
* If there already exists a dragger within MERGE_DIST of p, add the VP to it;
* otherwise create new dragger and add it to draggers list
*/
void
{
g_print ("Warning: The VP in addDragger is already NULL. Aborting.\n)");
}
// don't create draggers for infinite vanishing points
return;
}
// distance is small, merge this draggable into dragger, no need to create new dragger
//dragger->updateKnotShape();
return;
}
}
// fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
}
/**
Create a line from p1 to p2 and add it to the lines list
*/
void
{
SPCanvasItem *line = sp_canvas_item_new(sp_desktop_controls(inkscape_active_desktop()), SP_TYPE_CTRLLINE, NULL);
if (rgba != VP_LINE_COLOR_FILL) // fill is the default, so don't set color for it to speed up redraw
}
} // namespace Box3D
/*
Local Variables:
mode:c++
c-file-style:"stroustrup"
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
indent-tabs-mode:nil
fill-column:99
End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :