Better 2D Snapping

Consolidated duplicate snapping functions into CanvasItemEditor.
Allow non-square grids.
Add grid origin offsets.
Allow seperate toggling of grid display.
Add rotation snapping.
Add offset snapping.
This commit is contained in:
Carl Olsson 2015-02-15 18:00:55 +10:00
parent 2185c018f6
commit e2c8aecb3d
13 changed files with 294 additions and 254 deletions

View file

@ -36,6 +36,110 @@
#include "globals.h"
#include "os/input.h"
#include "tools/editor/editor_settings.h"
#include "scene/gui/grid_container.h"
class SnapDialog : public ConfirmationDialog {
OBJ_TYPE(SnapDialog,ConfirmationDialog);
protected:
friend class CanvasItemEditor;
SpinBox *grid_offset_x;
SpinBox *grid_offset_y;
SpinBox *grid_step_x;
SpinBox *grid_step_y;
SpinBox *rotation_offset;
SpinBox *rotation_step;
public:
SnapDialog() : ConfirmationDialog() {
const int SPIN_BOX_GRID_RANGE = 256;
const int SPIN_BOX_ROTATION_RANGE = 360;
Label *label;
VBoxContainer *container;
GridContainer *child_container;
set_title("Configure Snap");
get_ok()->set_text("Close");
container = memnew(VBoxContainer);
add_child(container);
child_container = memnew(GridContainer);
child_container->set_columns(3);
container->add_child(child_container);
label = memnew(Label);
label->set_text("Grid Offset:");
child_container->add_child(label);
grid_offset_x=memnew(SpinBox);
grid_offset_x->set_min(-SPIN_BOX_GRID_RANGE);
grid_offset_x->set_max(SPIN_BOX_GRID_RANGE);
grid_offset_x->set_suffix("px");
child_container->add_child(grid_offset_x);
grid_offset_y=memnew(SpinBox);
grid_offset_y->set_min(-SPIN_BOX_GRID_RANGE);
grid_offset_y->set_max(SPIN_BOX_GRID_RANGE);
grid_offset_y->set_suffix("px");
child_container->add_child(grid_offset_y);
label = memnew(Label);
label->set_text("Grid Step:");
child_container->add_child(label);
grid_step_x=memnew(SpinBox);
grid_step_x->set_min(-SPIN_BOX_GRID_RANGE);
grid_step_x->set_max(SPIN_BOX_GRID_RANGE);
grid_step_x->set_suffix("px");
child_container->add_child(grid_step_x);
grid_step_y=memnew(SpinBox);
grid_step_y->set_min(-SPIN_BOX_GRID_RANGE);
grid_step_y->set_max(SPIN_BOX_GRID_RANGE);
grid_step_y->set_suffix("px");
child_container->add_child(grid_step_y);
container->add_child(memnew(HSeparator));
child_container = memnew(GridContainer);
child_container->set_columns(2);
container->add_child(child_container);
label = memnew(Label);
label->set_text("Rotation Offset:");
child_container->add_child(label);
rotation_offset=memnew(SpinBox);
rotation_offset->set_min(-SPIN_BOX_ROTATION_RANGE);
rotation_offset->set_max(SPIN_BOX_ROTATION_RANGE);
rotation_offset->set_suffix("deg");
child_container->add_child(rotation_offset);
label = memnew(Label);
label->set_text("Rotation Step:");
child_container->add_child(label);
rotation_step=memnew(SpinBox);
rotation_step->set_min(-SPIN_BOX_ROTATION_RANGE);
rotation_step->set_max(SPIN_BOX_ROTATION_RANGE);
rotation_step->set_suffix("deg");
child_container->add_child(rotation_step);
}
void set_fields(const Point2 p_grid_offset, const Size2 p_grid_step, const float p_rotation_offset, const float p_rotation_step) {
grid_offset_x->set_val(p_grid_offset.x);
grid_offset_y->set_val(p_grid_offset.y);
grid_step_x->set_val(p_grid_step.x);
grid_step_y->set_val(p_grid_step.y);
rotation_offset->set_val(p_rotation_offset * (180 / Math_PI));
rotation_step->set_val(p_rotation_step * (180 / Math_PI));
}
void get_fields(Point2 &p_grid_offset, Size2 &p_grid_step, float &p_rotation_offset, float &p_rotation_step) {
p_grid_offset.x = grid_offset_x->get_val();
p_grid_offset.y = grid_offset_y->get_val();
p_grid_step.x = grid_step_x->get_val();
p_grid_step.y = grid_step_y->get_val();
p_rotation_offset = rotation_offset->get_val() / (180 / Math_PI);
p_rotation_step = rotation_step->get_val() / (180 / Math_PI);
}
};
void CanvasItemEditor::_unhandled_key_input(const InputEvent& p_ev) {
if (!is_visible())
@ -75,9 +179,24 @@ Object *CanvasItemEditor::_get_editor_data(Object *p_what) {
return memnew( CanvasItemEditorSelectedItem );
}
bool CanvasItemEditor::is_snap_active() const {
inline float _snap_scalar(float p_offset, float p_step, bool p_snap_to_offset, float p_target, float p_start) {
float offset = p_snap_to_offset ? p_start : p_offset;
return p_step != 0 ? Math::stepify(p_target - offset, p_step) + offset : p_target;
}
return edit_menu->get_popup()->is_item_checked(edit_menu->get_popup()->get_item_index(SNAP_USE));
Vector2 CanvasItemEditor::snap_point(Vector2 p_target, Vector2 p_start) const {
if (snap_grid) {
p_target.x = _snap_scalar(snap_offset.x, snap_step.x, snap_to_offset, p_target.x, p_start.x);
p_target.y = _snap_scalar(snap_offset.y, snap_step.y, snap_to_offset, p_target.y, p_start.y);
}
if (snap_pixel)
p_target = p_target.snapped(Size2(1, 1));
return p_target;
}
float CanvasItemEditor::snap_angle(float p_target, float p_start) const {
return snap_rotation ? _snap_scalar(snap_rotation_offset, snap_rotation_step, snap_to_offset, p_target, p_start) : p_target;
}
Dictionary CanvasItemEditor::get_state() const {
@ -86,9 +205,15 @@ Dictionary CanvasItemEditor::get_state() const {
state["zoom"]=zoom;
state["ofs"]=Point2(h_scroll->get_val(),v_scroll->get_val());
// state["ofs"]=-transform.get_origin();
state["use_snap"]=is_snap_active();
state["snap"]=snap;
state["pixel_snap"]=pixel_snap;
state["snap_offset"]=snap_offset;
state["snap_step"]=snap_step;
state["snap_rotation_offset"]=snap_rotation_offset;
state["snap_rotation_step"]=snap_rotation_step;
state["snap_grid"]=snap_grid;
state["snap_show_grid"]=snap_show_grid;
state["snap_rotation"]=snap_rotation;
state["snap_to_offset"]=snap_to_offset;
state["snap_pixel"]=snap_pixel;
return state;
}
void CanvasItemEditor::set_state(const Dictionary& p_state){
@ -105,19 +230,50 @@ void CanvasItemEditor::set_state(const Dictionary& p_state){
v_scroll->set_val(ofs.y);
}
if (state.has("use_snap")) {
if (state.has("snap_step")) {
snap_step=state["snap_step"];
}
if (state.has("snap_offset")) {
snap_offset=state["snap_offset"];
}
if (state.has("snap_rotation_step")) {
snap_rotation_step=state["snap_rotation_step"];
}
if (state.has("snap_rotation_offset")) {
snap_rotation_offset=state["snap_rotation_offset"];
}
if (state.has("snap_grid")) {
snap_grid=state["snap_grid"];
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE);
edit_menu->get_popup()->set_item_checked(idx,state["use_snap"]);
edit_menu->get_popup()->set_item_checked(idx,snap_grid);
}
if (state.has("snap")) {
snap=state["snap"];
if (state.has("snap_show_grid")) {
snap_show_grid=state["snap_show_grid"];
int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID);
edit_menu->get_popup()->set_item_checked(idx,snap_show_grid);
}
if (state.has("pixel_snap")) {
pixel_snap=state["pixel_snap"];
if (state.has("snap_rotation")) {
snap_rotation=state["snap_rotation"];
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
edit_menu->get_popup()->set_item_checked(idx,snap_rotation);
}
if (state.has("snap_to_offset")) {
snap_to_offset=state["snap_to_offset"];
int idx = edit_menu->get_popup()->get_item_index(SNAP_TO_OFFSET);
edit_menu->get_popup()->set_item_checked(idx,snap_to_offset);
}
if (state.has("snap_pixel")) {
snap_pixel=state["snap_pixel"];
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
edit_menu->get_popup()->set_item_checked(idx,pixel_snap);
edit_menu->get_popup()->set_item_checked(idx,snap_pixel);
}
}
@ -286,9 +442,7 @@ void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@ -300,7 +454,7 @@ void CanvasItemEditor::_key_move(const Vector2& p_dir, bool p_snap, KeyMoveMODE
Vector2 drag = p_dir;
if (p_snap)
drag*=snap;
drag*=snap_step;
undo_redo->add_undo_method(canvas_item,"edit_set_state",canvas_item->edit_get_state());
@ -347,9 +501,7 @@ Point2 CanvasItemEditor::_find_topleftmost_point() {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
@ -377,9 +529,7 @@ int CanvasItemEditor::get_item_count() {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
ic++;
@ -398,9 +548,7 @@ CanvasItem *CanvasItemEditor::get_single_item() {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
if (single_item)
@ -554,6 +702,11 @@ void CanvasItemEditor::_append_canvas_item(CanvasItem *c) {
}
void CanvasItemEditor::_snap_changed() {
((SnapDialog *)snap_dialog)->get_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step);
viewport->update();
}
void CanvasItemEditor::_dialog_value_changed(double) {
if (updating_value_dialog)
@ -561,11 +714,6 @@ void CanvasItemEditor::_dialog_value_changed(double) {
switch(last_option) {
case SNAP_CONFIGURE: {
snap=dialog_val->get_val();
viewport->update();
} break;
case ZOOM_SET: {
zoom=dialog_val->get_val()/100.0;
@ -661,9 +809,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
@ -735,9 +881,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@ -943,9 +1087,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@ -1068,9 +1210,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@ -1126,9 +1266,7 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@ -1153,39 +1291,21 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
if (drag==DRAG_ROTATE) {
Vector2 center = canvas_item->get_global_transform_with_canvas().get_origin();
Matrix32 rot;
rot.elements[1] = (dfrom - center).normalized();
rot.elements[0] = rot.elements[1].tangent();
float ang = rot.xform_inv(dto-center).atan2();
canvas_item->edit_rotate(ang);
display_rotate_to = dto;
display_rotate_from = center;
if (Node2D *node = canvas_item->cast_to<Node2D>()) {
Matrix32 rot;
rot.elements[1] = (dfrom - center).normalized();
rot.elements[0] = rot.elements[1].tangent();
node->set_rot(snap_angle(rot.xform_inv(dto-center).atan2(), node->get_rot()));
display_rotate_to = dto;
display_rotate_from = center;
viewport->update();
}
continue;
}
if (pixel_snap || (is_snap_active() && snap>0)) {
if (drag!=DRAG_ALL) {
dfrom=drag_point_from;
dto=snapify(dto);
} else {
Vector2 newpos = drag_point_from + (dto-dfrom);
Vector2 disp;
if (!is_snap_active() || snap<1) {
disp.x = Math::fposmod(newpos.x,1);
disp.y = Math::fposmod(newpos.y,1);
} else {
disp.x = Math::fposmod(newpos.x,snap);
disp.y = Math::fposmod(newpos.y,snap);
}
dto-=disp;
}
}
dfrom = drag_point_from;
dto = snap_point(dto - (drag == DRAG_ALL ? drag_from - drag_point_from : Vector2(0, 0)), drag_point_from);
Vector2 drag_vector =
canvas_item->get_global_transform_with_canvas().affine_inverse().xform(dto) -
@ -1293,8 +1413,6 @@ void CanvasItemEditor::_viewport_input_event(const InputEvent& p_event) {
if (!dragging_bone) {
local_rect.pos=begin;
@ -1477,32 +1595,32 @@ void CanvasItemEditor::_viewport_draw() {
_update_scrollbars();
RID ci=viewport->get_canvas_item();
if (snap>0 && is_snap_active() && true ) {
if (snap_show_grid) {
Size2 s = viewport->get_size();
int last_cell;
Matrix32 xform = transform.affine_inverse();
for(int i=0;i<s.width;i++) {
int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(i,0)).x/snap));
if (i==0)
if (snap_step.x!=0) {
for(int i=0;i<s.width;i++) {
int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i,0)).x-snap_offset.x)/snap_step.x));
if (i==0)
last_cell=cell;
if (last_cell!=cell)
viewport->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
last_cell=cell;
if (last_cell!=cell)
viewport->draw_line(Point2(i,0),Point2(i,s.height),Color(0.3,0.7,1,0.3));
last_cell=cell;
}
}
for(int i=0;i<s.height;i++) {
int cell = Math::fast_ftoi(Math::floor(xform.xform(Vector2(0,i)).y/snap));
if (i==0)
if (snap_step.y!=0) {
for(int i=0;i<s.height;i++) {
int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(0,i)).y-snap_offset.y)/snap_step.y));
if (i==0)
last_cell=cell;
if (last_cell!=cell)
viewport->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
last_cell=cell;
if (last_cell!=cell)
viewport->draw_line(Point2(0,i),Point2(s.width,i),Color(0.3,0.7,1,0.3));
last_cell=cell;
}
}
}
if (viewport->has_focus()) {
@ -1530,9 +1648,7 @@ void CanvasItemEditor::_viewport_draw() {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
if (!se)
@ -1749,9 +1865,7 @@ void CanvasItemEditor::_notification(int p_what) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
CanvasItemEditorSelectedItem *se=editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(canvas_item);
@ -1996,59 +2110,40 @@ void CanvasItemEditor::_update_scroll(float) {
}
Point2 CanvasItemEditor::snapify(const Point2& p_pos) const {
bool active=is_snap_active();
Vector2 pos = p_pos;
if (!active || snap<1) {
if (pixel_snap) {
pos.x=Math::stepify(pos.x,1);
pos.y=Math::stepify(pos.y,1);
}
return pos;
}
pos.x=Math::stepify(pos.x,snap);
pos.y=Math::stepify(pos.y,snap);
return pos;
}
void CanvasItemEditor::_popup_callback(int p_op) {
last_option=MenuOption(p_op);
switch(p_op) {
case SNAP_USE: {
snap_grid = !snap_grid;
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE);
edit_menu->get_popup()->set_item_checked( idx,!edit_menu->get_popup()->is_item_checked(0));
edit_menu->get_popup()->set_item_checked(idx,snap_grid);
} break;
case SNAP_SHOW_GRID: {
snap_show_grid = !snap_show_grid;
int idx = edit_menu->get_popup()->get_item_index(SNAP_SHOW_GRID);
edit_menu->get_popup()->set_item_checked(idx,snap_show_grid);
viewport->update();
} break;
case SNAP_USE_ROTATION: {
snap_rotation = !snap_rotation;
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_ROTATION);
edit_menu->get_popup()->set_item_checked(idx,snap_rotation);
} break;
case SNAP_TO_OFFSET: {
snap_to_offset = !snap_to_offset;
int idx = edit_menu->get_popup()->get_item_index(SNAP_TO_OFFSET);
edit_menu->get_popup()->set_item_checked(idx,snap_to_offset);
} break;
case SNAP_USE_PIXEL: {
pixel_snap = ! pixel_snap;
snap_pixel = !snap_pixel;
int idx = edit_menu->get_popup()->get_item_index(SNAP_USE_PIXEL);
edit_menu->get_popup()->set_item_checked(idx,pixel_snap);
edit_menu->get_popup()->set_item_checked(idx,snap_pixel);
} break;
case SNAP_CONFIGURE: {
updating_value_dialog=true;
dialog_label->set_text("Snap (Pixels):");
dialog_val->set_min(1);
dialog_val->set_step(1);
dialog_val->set_max(4096);
dialog_val->set_val(snap);
value_dialog->popup_centered(Size2(200,85));
updating_value_dialog=false;
((SnapDialog *)snap_dialog)->set_fields(snap_offset, snap_step, snap_rotation_offset, snap_rotation_step);
snap_dialog->popup_centered(Size2(200,160));
} break;
case ZOOM_IN: {
zoom=zoom*(1.0/0.5);
@ -2092,9 +2187,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
canvas_item->set_meta("_edit_lock_",true);
@ -2109,9 +2202,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
@ -2129,9 +2220,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
canvas_item->set_meta("_edit_group_",true);
@ -2146,9 +2235,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
canvas_item->set_meta("_edit_group_",Variant());
@ -2166,9 +2253,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
@ -2236,9 +2321,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
if (canvas_item->cast_to<Node2D>()) {
@ -2348,9 +2431,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
@ -2400,9 +2481,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(Map<Node*,Object*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->key()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
if (canvas_item->cast_to<Node2D>()) {
@ -2528,9 +2607,7 @@ void CanvasItemEditor::_popup_callback(int p_op) {
for(List<Node*>::Element *E=selection.front();E;E=E->next()) {
CanvasItem *canvas_item = E->get()->cast_to<CanvasItem>();
if (!canvas_item)
continue;
if (!canvas_item->is_visible())
if (!canvas_item || !canvas_item->is_visible())
continue;
@ -2604,6 +2681,7 @@ void CanvasItemEditor::_bind_methods() {
ObjectTypeDB::bind_method("_unhandled_key_input",&CanvasItemEditor::_unhandled_key_input);
ObjectTypeDB::bind_method("_viewport_draw",&CanvasItemEditor::_viewport_draw);
ObjectTypeDB::bind_method("_viewport_input_event",&CanvasItemEditor::_viewport_input_event);
ObjectTypeDB::bind_method("_snap_changed",&CanvasItemEditor::_snap_changed);
ADD_SIGNAL( MethodInfo("item_lock_status_changed") );
ADD_SIGNAL( MethodInfo("item_group_status_changed") );
@ -2809,6 +2887,9 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
PopupMenu *p;
p = edit_menu->get_popup();
p->add_check_item("Use Snap",SNAP_USE);
p->add_check_item("Show Grid",SNAP_SHOW_GRID);
p->add_check_item("Use Rotation Snap",SNAP_USE_ROTATION);
p->add_check_item("Offset Snap",SNAP_TO_OFFSET);
p->add_item("Configure Snap..",SNAP_CONFIGURE);
p->add_separator();
p->add_check_item("Use Pixel Snap",SNAP_USE_PIXEL);
@ -2899,6 +2980,10 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
p->add_item("Paste Pose",ANIM_PASTE_POSE);
p->add_item("Clear Pose",ANIM_CLEAR_POSE,KEY_MASK_SHIFT|KEY_K);
snap_dialog = memnew(SnapDialog);
snap_dialog->connect("confirmed",this,"_snap_changed");
add_child(snap_dialog);
value_dialog = memnew( AcceptDialog );
value_dialog->set_title("Set a Value");
value_dialog->get_ok()->set_text("Close");
@ -2923,7 +3008,14 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
key_scale=false;
zoom=1;
snap=10;
snap_offset=Vector2(0, 0);
snap_step=Vector2(10, 10);
snap_rotation_offset=0;
snap_rotation_step=15 / (180 / Math_PI);
snap_grid=false;
snap_show_grid=false;
snap_rotation=false;
snap_pixel=false;
updating_value_dialog=false;
box_selecting=false;
//zoom=0.5;
@ -2931,7 +3023,6 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) {
editor->get_animation_editor()->connect("keying_changed",this,"_keying_changed");
set_process_unhandled_key_input(true);
can_move_pivot=false;
pixel_snap=false;
drag=DRAG_NONE;
}

View file

@ -75,6 +75,9 @@ class CanvasItemEditor : public VBoxContainer {
enum MenuOption {
SNAP_USE,
SNAP_SHOW_GRID,
SNAP_USE_ROTATION,
SNAP_TO_OFFSET,
SNAP_CONFIGURE,
SNAP_USE_PIXEL,
ZOOM_IN,
@ -143,8 +146,15 @@ class CanvasItemEditor : public VBoxContainer {
Matrix32 transform;
float zoom;
int snap;
bool pixel_snap;
Vector2 snap_offset;
Vector2 snap_step;
float snap_rotation_step;
float snap_rotation_offset;
bool snap_grid;
bool snap_show_grid;
bool snap_rotation;
bool snap_to_offset;
bool snap_pixel;
bool box_selecting;
Point2 box_selecting_to;
bool key_pos;
@ -247,6 +257,8 @@ class CanvasItemEditor : public VBoxContainer {
CanvasItem* _select_canvas_item_at_pos(const Point2 &p_pos,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform);
void _find_canvas_items_at_rect(const Rect2& p_rect,Node* p_node,const Matrix32& p_parent_xform,const Matrix32& p_canvas_xform,List<CanvasItem*> *r_items);
ConfirmationDialog *snap_dialog;
AcceptDialog *value_dialog;
Label *dialog_label;
SpinBox *dialog_val;
@ -261,7 +273,6 @@ class CanvasItemEditor : public VBoxContainer {
DragType _find_drag_type(const Matrix32& p_xform, const Rect2& p_local_rect, const Point2& p_click, Vector2& r_point);
Point2 snapify(const Point2& p_pos) const;
void _popup_callback(int p_op);
bool updating_scroll;
void _update_scroll(float);
@ -271,6 +282,7 @@ class CanvasItemEditor : public VBoxContainer {
void _append_canvas_item(CanvasItem *p_item);
void _dialog_value_changed(double);
void _snap_changed();
UndoRedo *undo_redo;
Point2 _find_topleftmost_point();
@ -334,8 +346,8 @@ protected:
static CanvasItemEditor *singleton;
public:
bool is_snap_active() const;
int get_snap() const { return snap; }
Vector2 snap_point(Vector2 p_target, Vector2 p_start = Vector2(0, 0)) const;
float snap_angle(float p_target, float p_start = 0) const;
Matrix32 get_canvas_transform() const { return transform; }

View file

@ -34,17 +34,6 @@ void CollisionPolygon2DEditor::_node_removed(Node *p_node) {
}
Vector2 CollisionPolygon2DEditor::snap_point(const Vector2& p_point) const {
if (canvas_item_editor->is_snap_active()) {
return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
} else {
return p_point;
}
}
void CollisionPolygon2DEditor::_menu_option(int p_option) {
switch(p_option) {
@ -98,7 +87,7 @@ bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mb.x,mb.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
cpoint=snap_point(cpoint);
cpoint=canvas_item_editor->snap_point(cpoint);
cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
Vector<Vector2> poly = node->get_polygon();
@ -301,7 +290,7 @@ bool CollisionPolygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mm.x,mm.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
cpoint=snap_point(cpoint);
cpoint=canvas_item_editor->snap_point(cpoint);
edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
canvas_item_editor->get_viewport_control()->update();

View file

@ -53,7 +53,6 @@ protected:
static void _bind_methods();
public:
Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_collision_polygon);
CollisionPolygon2DEditor(EditorNode *p_editor);

View file

@ -70,19 +70,6 @@ void CollisionPolygonEditor::_node_removed(Node *p_node) {
}
Vector2 CollisionPolygonEditor::snap_point(const Vector2& p_point) const {
return p_point;
if (CanvasItemEditor::get_singleton()->is_snap_active()) {
return p_point.snapped(Vector2(1,1)*CanvasItemEditor::get_singleton()->get_snap());
} else {
return p_point;
}
}
void CollisionPolygonEditor::_menu_option(int p_option) {
switch(p_option) {
@ -150,7 +137,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const
Vector2 cpoint(spoint.x,spoint.y);
cpoint=snap_point(cpoint);
cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint);
Vector<Vector2> poly = node->get_polygon();
@ -364,7 +351,7 @@ bool CollisionPolygonEditor::forward_spatial_input_event(Camera* p_camera,const
Vector2 cpoint(spoint.x,spoint.y);
cpoint=snap_point(cpoint);
cpoint=CanvasItemEditor::get_singleton()->snap_point(cpoint);
edited_point_pos = cpoint;
_polygon_draw();

View file

@ -90,7 +90,6 @@ protected:
static void _bind_methods();
public:
Vector2 snap_point(const Vector2& p_point) const;
virtual bool forward_spatial_input_event(Camera* p_camera,const InputEvent& p_event);
void edit(Node *p_collision_polygon);
CollisionPolygonEditor(EditorNode *p_editor);

View file

@ -42,17 +42,6 @@ void NavigationPolygonEditor::_create_nav() {
undo_redo->commit_action();
}
Vector2 NavigationPolygonEditor::snap_point(const Vector2& p_point) const {
if (canvas_item_editor->is_snap_active()) {
return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
} else {
return p_point;
}
}
void NavigationPolygonEditor::_menu_option(int p_option) {
switch(p_option) {
@ -122,7 +111,7 @@ bool NavigationPolygonEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mb.x,mb.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
cpoint=snap_point(cpoint);
cpoint=canvas_item_editor->snap_point(cpoint);
cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
@ -370,7 +359,7 @@ bool NavigationPolygonEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mm.x,mm.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
cpoint=snap_point(cpoint);
cpoint=canvas_item_editor->snap_point(cpoint);
edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
canvas_item_editor->get_viewport_control()->update();

View file

@ -59,7 +59,6 @@ protected:
static void _bind_methods();
public:
Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_collision_polygon);
NavigationPolygonEditor(EditorNode *p_editor);

View file

@ -62,17 +62,6 @@ void Path2DEditor::_node_removed(Node *p_node) {
}
Vector2 Path2DEditor::snap_point(const Vector2& p_point) const {
if (canvas_item_editor->is_snap_active()) {
return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
} else {
return p_point;
}
}
bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
if (!node)
@ -93,8 +82,8 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Vector2 gpoint = Point2(mb.x,mb.y);
Vector2 cpoint = !mb.mod.alt? snap_point(xform.affine_inverse().xform(gpoint))
: node->get_global_transform().affine_inverse().xform( snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
Vector2 cpoint = !mb.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint))
: node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
//first check if a point is to be added (segment split)
real_t grab_treshold=EDITOR_DEF("poly_editor/point_grab_radius",8);
@ -250,9 +239,9 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
if (!wip_active) {
wip.clear();
wip.push_back( snap_point(cpoint) );
wip.push_back( canvas_item_editor->snap_point(cpoint) );
wip_active=true;
edited_point_pos=snap_point(cpoint);
edited_point_pos=canvas_item_editor->snap_point(cpoint);
canvas_item_editor->update();
edited_point=1;
return true;
@ -265,7 +254,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
return true;
} else {
wip.push_back( snap_point(cpoint) );
wip.push_back( canvas_item_editor->snap_point(cpoint) );
edited_point=wip.size();
canvas_item_editor->update();
return true;
@ -327,9 +316,9 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
if (closest_idx>=0) {
pre_move_edit=poly;
poly.insert(closest_idx+1,snap_point(xform.affine_inverse().xform(closest_pos)));
poly.insert(closest_idx+1,canvas_item_editor->snap_point(xform.affine_inverse().xform(closest_pos)));
edited_point=closest_idx+1;
edited_point_pos=snap_point(xform.affine_inverse().xform(closest_pos));
edited_point_pos=canvas_item_editor->snap_point(xform.affine_inverse().xform(closest_pos));
node->set_polygon(poly);
canvas_item_editor->update();
return true;
@ -434,8 +423,8 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Vector2 gpoint = Point2(mm.x,mm.y);
Vector2 cpoint = !mm.mod.alt? snap_point(xform.affine_inverse().xform(gpoint))
: node->get_global_transform().affine_inverse().xform( snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
Vector2 cpoint = !mm.mod.alt? canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint))
: node->get_global_transform().affine_inverse().xform( canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint)) );
Ref<Curve2D> curve = node->get_curve();
@ -471,7 +460,7 @@ bool Path2DEditor::forward_input_event(const InputEvent& p_event) {
Matrix32 xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
Vector2 gpoint = Point2(mm.x,mm.y);
edited_point_pos = snap_point(xform.affine_inverse().xform(gpoint));
edited_point_pos = canvas_item_editor->snap_point(xform.affine_inverse().xform(gpoint));
canvas_item_editor->update();
}

View file

@ -94,7 +94,6 @@ protected:
static void _bind_methods();
public:
Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_path2d);
Path2DEditor(EditorNode *p_editor);

View file

@ -69,17 +69,6 @@ void Polygon2DEditor::_node_removed(Node *p_node) {
}
Vector2 Polygon2DEditor::snap_point(const Vector2& p_point) const {
if (canvas_item_editor->is_snap_active()) {
return p_point.snapped(Vector2(1,1)*canvas_item_editor->get_snap());
} else {
return p_point;
}
}
void Polygon2DEditor::_menu_option(int p_option) {
switch(p_option) {
@ -201,7 +190,7 @@ bool Polygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mb.x,mb.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
cpoint=snap_point(cpoint);
cpoint=canvas_item_editor->snap_point(cpoint);
cpoint = node->get_global_transform().affine_inverse().xform(cpoint);
@ -405,7 +394,7 @@ bool Polygon2DEditor::forward_input_event(const InputEvent& p_event) {
Vector2 gpoint = Point2(mm.x,mm.y);
Vector2 cpoint = canvas_item_editor->get_canvas_transform().affine_inverse().xform(gpoint);
cpoint=snap_point(cpoint);
cpoint=canvas_item_editor->snap_point(cpoint);
edited_point_pos = node->get_global_transform().affine_inverse().xform(cpoint);
canvas_item_editor->get_viewport_control()->update();

View file

@ -92,7 +92,6 @@ protected:
static void _bind_methods();
public:
Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_collision_polygon);
Polygon2DEditor(EditorNode *p_editor);

View file

@ -109,7 +109,6 @@ protected:
public:
HBoxContainer *get_canvas_item_editor_hb() const { return canvas_item_editor_hb; }
Vector2 snap_point(const Vector2& p_point) const;
bool forward_input_event(const InputEvent& p_event);
void edit(Node *p_tile_map);
TileMapEditor(EditorNode *p_editor);