vanishing-point.cpp revision b45b3ca12c271745b18a142d10a6ac8efd9f79cc
#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 "desktop.h"
#include "event-context.h"
#include "perspective-line.h"
#include "shape-editor.h"
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
};
static void
{
drag->updateDraggers();
drag->updateLines();
drag->updateBoxReprs();
}
static void
{
drag->updateLines ();
//drag->updateBoxReprs();
drag->updateDraggers ();
}
static bool
{
return true;
}
}
return false;
}
static void
{
// FIXME: take from prefs
/*
* We use dragging_started to indicate if we have already checked for the need to split Draggers up.
* This only has the purpose of avoiding costly checks in the routine below.
*/
/* with Shift; if there is more than one box linked to this VP
we need to split it and create a new perspective */
for (std::set<VanishingPoint*, less_ptr>::iterator vp = sel_vps.begin(); vp != sel_vps.end(); ++vp) {
// for each VP that has selected boxes:
// we create a new perspective ...
/* ... unlink the boxes from the old one and
FIXME: We need to unlink the _un_selected boxes of each VP so that
the correct boxes are kept with the VP being moved */
/* if a box in the VP is unselected, move it to the
newly created perspective so that it doesn't get dragged **/
}
}
}
// FIXME: Do we need to create a new dragger as well?
dragger->updateZOrders ();
_("Split vanishing points"));
return;
}
}
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 ... (this is needed so that the perspectives are detected as identical)
// FIXME: This is called a bit too often, isn't it?
}
// ... join lists of VPs ...
// ... delete old dragger ...
delete dragger;
// ... and merge any duplicate perspectives
// TODO: Update the new merged dragger
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.
_("Merge vanishing points"));
return;
}
}
}
dragger->parent->updateBoxHandles (); // FIXME: Only update the handles of boxes on this dragger (not on all)
dragger->updateZOrders();
drag->updateLines();
dragger->dragging_started = true;
}
void
{
}
static void
{
dragger->dragging_started = false;
(*i).updateBoxReprs();
(*i).updatePerspRepr();
}
// TODO: Update box's paths and svg representation
// TODO: Undo machinery!!
_("3D box: Move vanishing point"));
}
unsigned int VanishingPoint::global_counter = 0;
// FIXME: Rename to something more meaningful!
void
}
if (!SP_IS_BOX3D(i->data))
continue;
}
}
return sel_boxes;
}
{
this->point = p;
this->point_original = p;
this->dragging_started = false;
// 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), "ungrabbed", G_CALLBACK (vp_knot_ungrabbed_handler), this);
// add the initial VP (which may be NULL!)
}
}
{
// 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_grabbed_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (vp_knot_ungrabbed_handler), this);
/* unref should call destroy */
}
/**
Updates the statusbar tip of the dragger knot, based on its draggables
*/
void
{
}
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);
} else {
// 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);
}
} 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 if necessary);
* the perspective is stored separately, too, for efficiency in updating boxes.
*/
void
{
// don't add infinite VPs; don't add the same VP twice
return;
}
if (update_pos) {
}
this->updateTip();
}
void
{
}
this->updateTip();
}
return &(*vp);
}
}
return NULL;
}
// FIXME: Should we take the selection from the parent VPDrag? I guess it shouldn't make a difference.
if (!SP_IS_BOX3D(i->data))
continue;
if (vp) {
}
}
return sel_vps;
}
{
}
return num;
}
bool
{
return true;
}
}
return false;
}
void
{
persp1 = (*i).get_perspective();
persp2 = (*j).get_perspective();
/* don't merge a perspective with itself */
continue;
}
/* if perspectives coincide but are not the same, merge them */
}
}
}
}
void
{
(*i).updateBoxDisplays();
}
}
void
{
}
}
void
{
persp3d_update_z_orders((*i).get_perspective());
}
}
void
}
}
{
this->show_lines = true;
this->front_or_rear_lines = 0x1;
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 (*j == vp) {
return (dragger);
}
}
}
return NULL;
}
void
VPDrag::printDraggers ()
{
g_print ("=== VPDrag info: =================================\n");
g_print ("========\n");
}
g_print ("=================================================\n");
}
/**
* Regenerates the draggers list from the current selection; is called when selection is changed or modified
*/
void
VPDrag::updateDraggers ()
{
if (this->dragging)
return;
// delete old draggers
}
g_list_free (this->draggers);
if (!SP_IS_BOX3D (item)) continue;
for (int i = 0; i < 3; ++i) {
addDragger (vp);
}
}
}
/**
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_BOX3D(i->data)) continue;
}
}
void
{
// FIXME: Is there a way to update the knots without accessing the
// (previously) statically linked function KnotHolder::update_knots?
if (!sel)
return; // no selection
// Currently we only show handles if a single box is selected
return;
}
}
}
void
VPDrag::updateBoxReprs ()
{
(*i).updateBoxReprs();
}
}
}
void
{
(*i).updateBoxDisplays();
}
}
}
/**
* Depending on the value of all_lines, draw the front and/or rear perspective lines starting from the given corners.
*/
void
VPDrag::drawLinesForFace (const SPBox3D *box, Proj::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
}
}
}
/**
* 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
* We also store the corresponding perspective in case it is not already present.
*/
void
{
// don't create draggers for infinite vanishing points
return;
}
// distance is small, merge this draggable into dragger, no need to create new dragger
return;
}
}
// fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
}
void
{
// iterate over all VP in all draggers and replace persp2 with persp1
if ((*j).get_perspective() == persp2) {
(*j).set_perspective(persp1);
}
}
}
}
/**
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 :