Switch to using an intrusive list for the visual tool features

Slightly improves performance and eliminates a bunch of really clunky
passing around and storing of iterators.
This commit is contained in:
Thomas Goyne 2013-09-17 11:13:52 -07:00
parent b4ba31fe45
commit 64ecd29169
17 changed files with 253 additions and 182 deletions

View File

@ -67,6 +67,7 @@
<ClInclude Include="$(SrcDir)include\libaegisub\of_type_adaptor.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\option.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\option_value.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\owning_intrusive_list.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\path.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\scoped_ptr.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\signal.h" />

View File

@ -161,6 +161,9 @@
<ClInclude Include="$(SrcDir)include\libaegisub\kana_table.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\owning_intrusive_list.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp">

View File

@ -0,0 +1,92 @@
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
#pragma once
#include <boost/intrusive/list.hpp>
namespace agi {
template<typename T>
class owning_intrusive_list : private boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>>::type {
typedef typename boost::intrusive::make_list<T, boost::intrusive::constant_time_size<false>>::type base;
public:
using base::back;
using base::begin;
using base::cbegin;
using base::cend;
using base::crbegin;
using base::crend;
using base::empty;
using base::end;
using base::front;
using base::insert;
using base::iterator_to;
using base::merge;
using base::push_back;
using base::push_front;
using base::rbegin;
using base::rend;
using base::reverse;
using base::s_iterator_to;
using base::shift_backwards;
using base::shift_forward;
using base::size;
using base::sort;
using base::splice;
using base::swap;
using typename base::const_node_ptr;
using typename base::const_pointer;
using typename base::const_reverse_iterator;
using typename base::node;
using typename base::node_algorithms;
using typename base::node_ptr;
using typename base::node_traits;
using typename base::pointer;
using typename base::reference;
using typename base::reverse_iterator;
using typename base::size_type;
using typename base::value_type;
using typename base::iterator;
using typename base::const_iterator;
using typename base::const_reference;
using typename base::difference_type;
iterator erase(const_iterator b, const_iterator e) { return this->erase_and_dispose(b, e, [](T *e) { delete e; }); }
iterator erase(const_iterator b, const_iterator e, difference_type n) { return this->erase_and_dispose(b, e, n, [](T *e) { delete e; }); }
iterator erase(const_iterator i) { return this->erase_and_dispose(i, [](T *e) { delete e; }); }
void clear() { this->clear_and_dispose([](T *e) { delete e; }); }
void pop_back() { this->pop_back_and_dispose([](T *e) { delete e; }); }
void pop_front() { this->pop_front_and_dispose([](T *e) { delete e; }); }
void remove(const_reference value) { return this->remove_and_dispose(value, [](T *e) { delete e; }); }
void unique() { this->unique_and_dispose([](T *e) { delete e; }); }
template<class Pred> void remove_if(Pred&& pred) {
this->remove_if_and_dispose(std::forward<Pred>(pred), [](T *e) { delete e; });
}
template<class BinaryPredicate> void unique(BinaryPredicate&& pred) {
this->unique_and_dispose(std::forward<BinaryPredicate>(pred), [](T *e) { delete e; });
}
~owning_intrusive_list() {
clear();
}
};
}

View File

@ -40,7 +40,7 @@
VisualDraggableFeature::VisualDraggableFeature()
: type(DRAG_NONE)
, layer(0)
, line(0)
, line(nullptr)
{
}

View File

@ -36,6 +36,8 @@
#include "vector2d.h"
#include <boost/intrusive/list_hook.hpp>
class OpenGLWrapper;
class AssDialogue;
@ -54,7 +56,7 @@ enum DraggableFeatureType {
///
/// By itself this class doesn't do much. It mostly just draws itself at a
/// specified position and performs hit-testing.
class VisualDraggableFeature {
class VisualDraggableFeature : public boost::intrusive::make_list_base_hook<boost::intrusive::link_mode<boost::intrusive::auto_unlink>>::type {
Vector2D start; ///< position before the last drag operation began
public:

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@ -43,7 +43,12 @@
using std::placeholders::_1;
const wxColour VisualToolBase::colour[4] = {wxColour(106,32,19), wxColour(255,169,40), wxColour(255,253,185), wxColour(187,0,0)};
const wxColour VisualToolBase::colour[] = {
wxColour(106,32,19),
wxColour(255,169,40),
wxColour(255,253,185),
wxColour(187,0,0)
};
VisualToolBase::VisualToolBase(VideoDisplay *parent, agi::Context *context)
: c(context)
@ -165,8 +170,8 @@ template<class FeatureType>
VisualTool<FeatureType>::VisualTool(VideoDisplay *parent, agi::Context *context)
: VisualToolBase(parent, context)
, sel_changed(false)
, active_feature(nullptr)
{
active_feature = features.begin();
}
template<class FeatureType>
@ -179,30 +184,21 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
mouse_pos = event.GetPosition();
bool need_render = false;
if (event.Leaving()) {
mouse_pos = Vector2D();
parent->Render();
return;
}
if (event.Entering() && OPT_GET("Tool/Visual/Autohide")->GetBool())
need_render = true;
if (!dragging) {
feature_iterator prev_feature = active_feature;
int max_layer = INT_MIN;
active_feature = features.end();
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) {
if (cur->IsMouseOver(mouse_pos) && cur->layer >= max_layer) {
active_feature = cur;
max_layer = cur->layer;
active_feature = nullptr;
for (auto& feature : features) {
if (feature.IsMouseOver(mouse_pos) && feature.layer >= max_layer) {
active_feature = &feature;
max_layer = feature.layer;
}
}
need_render |= active_feature != prev_feature;
}
if (dragging) {
@ -213,14 +209,13 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
for (auto sel : sel_features)
UpdateDrag(sel);
Commit();
need_render = true;
}
// end drag
else {
dragging = false;
// mouse didn't move, fiddle with selection
if (active_feature != features.end() && !active_feature->HasMoved()) {
if (active_feature && !active_feature->HasMoved()) {
// Don't deselect stuff that was selected in this click's mousedown event
if (!sel_changed) {
if (ctrl_down)
@ -230,7 +225,7 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
}
}
active_feature = features.end();
active_feature = nullptr;
parent->ReleaseMouse();
parent->SetFocus();
}
@ -244,7 +239,6 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
}
UpdateHold();
need_render = true;
Commit();
}
@ -252,7 +246,7 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
drag_start = mouse_pos;
// start drag
if (active_feature != features.end()) {
if (active_feature) {
if (!sel_features.count(active_feature)) {
sel_changed = true;
SetSelection(active_feature, !ctrl_down);
@ -276,7 +270,6 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
SubtitleSelection sel;
sel.insert(c->selectionController->GetActiveLine());
c->selectionController->SetSelectedSet(sel);
need_render = true;
}
if (active_line && InitializeHold()) {
holding = true;
@ -288,7 +281,6 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
if (active_line && left_double)
OnDoubleClick();
//if (need_render)
parent->Render();
// Only coalesce the changes made in a single drag
@ -299,19 +291,19 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
template<class FeatureType>
void VisualTool<FeatureType>::DrawAllFeatures() {
gl.SetLineColour(colour[0], 1.0f, 2);
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) {
for (auto& feature : features) {
int fill = 1;
if (cur == active_feature)
if (&feature == active_feature)
fill = 2;
else if (sel_features.count(cur))
else if (sel_features.count(&feature))
fill = 3;
gl.SetFillColour(colour[fill], 0.6f);
cur->Draw(gl);
feature.Draw(gl);
}
}
template<class FeatureType>
void VisualTool<FeatureType>::SetSelection(feature_iterator feat, bool clear) {
void VisualTool<FeatureType>::SetSelection(FeatureType *feat, bool clear) {
if (clear)
sel_features.clear();
@ -325,12 +317,10 @@ void VisualTool<FeatureType>::SetSelection(feature_iterator feat, bool clear) {
}
template<class FeatureType>
void VisualTool<FeatureType>::RemoveSelection(feature_iterator feat) {
void VisualTool<FeatureType>::RemoveSelection(FeatureType *feat) {
if (!sel_features.erase(feat) || !feat->line) return;
for (auto sel : sel_features) {
for (auto sel : sel_features)
if (sel->line == feat->line) return;
}
SubtitleSelection sel = c->selectionController->GetSelectedSet();

View File

@ -1,4 +1,4 @@
// Copyright (c) 2011, Thomas Goyne <plorkyeran@aegisub.org>
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
@ -20,22 +20,17 @@
#pragma once
#include <deque>
#include <list>
#include <map>
#include <set>
#include <vector>
#include <boost/container/list.hpp>
#include <wx/event.h>
#include <libaegisub/signal.h>
#include "gl_wrap.h"
#include "selection_controller.h"
#include "vector2d.h"
#include <libaegisub/owning_intrusive_list.h>
#include <libaegisub/signal.h>
#include <deque>
#include <set>
#include <wx/event.h>
class AssDialogue;
class SubtitlesGrid;
class VideoDisplay;
@ -155,21 +150,9 @@ template<class FeatureType>
class VisualTool : public VisualToolBase {
protected:
typedef FeatureType Feature;
typedef typename boost::container::list<FeatureType>::iterator feature_iterator;
typedef typename boost::container::list<FeatureType>::const_iterator feature_const_iterator;
typedef agi::owning_intrusive_list<FeatureType> feature_list;
private:
struct ltaddr {
template<class T>
bool operator()(T lft, T rgt) const {
return &*lft < &*rgt;
}
};
boost::container::list<agi::signal::Connection> slots;
typedef typename std::set<feature_iterator, ltaddr>::iterator selection_iterator;
bool sel_changed; /// Has the selection already been changed in the current click?
/// @brief Called when a hold is begun
@ -181,23 +164,22 @@ private:
/// @brief Called at the beginning of a drag
/// @param feature The visual feature clicked on
/// @return Should the drag happen?
virtual bool InitializeDrag(feature_iterator feature) { return true; }
virtual bool InitializeDrag(FeatureType *feature) { return true; }
/// @brief Called on every mouse event during a drag
/// @param feature The current feature to process; not necessarily the one clicked on
virtual void UpdateDrag(feature_iterator feature) { }
virtual void UpdateDrag(FeatureType *feature) { }
/// @brief Draw stuff
virtual void Draw()=0;
protected:
std::set<feature_iterator, ltaddr> sel_features; ///< Currently selected visual features
typedef typename std::set<feature_iterator, ltaddr>::const_iterator sel_iterator;
std::set<FeatureType *> sel_features; ///< Currently selected visual features
/// Topmost feature under the mouse; generally only valid during a drag
feature_iterator active_feature;
FeatureType *active_feature;
/// List of features which are drawn and can be clicked on
/// List is used here for the iterator invalidation properties
boost::container::list<FeatureType> features;
feature_list features;
/// Draw all of the features in the list
void DrawAllFeatures();
@ -205,11 +187,11 @@ protected:
/// @brief Remove a feature from the selection
/// @param i Index in the feature list
/// Also deselects lines if all features for that line have been deselected
void RemoveSelection(feature_iterator feat);
void RemoveSelection(FeatureType *feat);
/// @brief Set the selection to a single feature, deselecting everything else
/// @param i Index in the feature list
void SetSelection(feature_iterator feat, bool clear);
void SetSelection(FeatureType *feat, bool clear);
public:
/// @brief Handler for all mouse events

View File

@ -36,18 +36,11 @@ VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context)
, cur_2(video_res)
, inverse(false)
{
Feature feat;
feat.type = DRAG_SMALL_CIRCLE;
features.resize(4, feat);
// This is really awkward without being able to just index the list of
// features, so copy them into a temporary array
ClipCorner *feats[4];
feature_iterator cur = features.begin();
feats[0] = &*(cur++);
feats[1] = &*(cur++);
feats[2] = &*(cur++);
feats[3] = &*(cur++);
for (size_t i = 0; i < 4; ++i) {
feats[i] = new ClipCorner;
features.push_back(*feats[i]);
}
// Attach each feature to the two features it shares edges with
// Top-left
@ -123,11 +116,7 @@ void VisualToolClip::CommitHold() {
}
}
bool VisualToolClip::InitializeDrag(feature_iterator) {
return true;
}
void VisualToolClip::UpdateDrag(feature_iterator feature) {
void VisualToolClip::UpdateDrag(ClipCorner *feature) {
// Update features which share an edge with the dragged one
feature->horiz->pos = Vector2D(feature->horiz->pos, feature->pos);
feature->vert->pos = Vector2D(feature->pos, feature->vert->pos);
@ -139,7 +128,7 @@ void VisualToolClip::UpdateDrag(feature_iterator feature) {
}
void VisualToolClip::SetFeaturePositions() {
feature_iterator it = features.begin();
auto it = features.begin();
(it++)->pos = cur_1; // Top-left
(it++)->pos = Vector2D(cur_2, cur_1); // Top-right
(it++)->pos = Vector2D(cur_1, cur_2); // Bottom-left

View File

@ -26,7 +26,7 @@
struct ClipCorner : public VisualDraggableFeature {
ClipCorner *horiz; ///< Other corner on this corner's horizontal line
ClipCorner *vert; ///< Other corner on this corner's vertical line
ClipCorner() : VisualDraggableFeature() , horiz(0) , vert(0) { }
ClipCorner() : VisualDraggableFeature() , horiz(0) , vert(0) { type = DRAG_SMALL_CIRCLE; }
};
class VisualToolClip : public VisualTool<ClipCorner> {
@ -42,8 +42,8 @@ class VisualToolClip : public VisualTool<ClipCorner> {
void DoRefresh();
void SetFeaturePositions();
bool InitializeDrag(feature_iterator feature);
void UpdateDrag(feature_iterator feature);
bool InitializeDrag(ClipCorner *feature) { return true; }
void UpdateDrag(ClipCorner *feature);
void Draw();
public:

View File

@ -32,6 +32,7 @@
#include "video_display.h"
#include <libaegisub/of_type_adaptor.h>
#include <libaegisub/util.h>
#include <algorithm>
#include <boost/format.hpp>
@ -47,7 +48,7 @@ static const DraggableFeatureType DRAG_END = DRAG_BIG_CIRCLE;
VisualToolDrag::VisualToolDrag(VideoDisplay *parent, agi::Context *context)
: VisualTool<VisualToolDragDraggableFeature>(parent, context)
, primary(0)
, primary(nullptr)
, button_is_move(true)
{
c->selectionController->GetSelectedSet(selection);
@ -112,7 +113,7 @@ void VisualToolDrag::OnFileChanged() {
features.clear();
sel_features.clear();
primary = 0;
active_feature = features.end();
active_feature = nullptr;
for (auto diag : c->ass->Line | agi::of_type<AssDialogue>()) {
if (IsDisplayed(diag))
@ -126,8 +127,8 @@ void VisualToolDrag::OnFrameChanged() {
if (primary && !IsDisplayed(primary->line))
primary = 0;
feature_iterator feat = features.begin();
feature_iterator end = features.end();
auto feat = features.begin();
auto end = features.end();
for (auto diag : c->ass->Line | agi::of_type<AssDialogue>()) {
if (IsDisplayed(diag)) {
@ -141,9 +142,9 @@ void VisualToolDrag::OnFrameChanged() {
else {
// Remove all features for this line (if any)
while (feat != end && feat->line == diag) {
if (feat == active_feature) active_feature = features.end();
feat->line = 0;
RemoveSelection(feat);
if (&*feat == active_feature) active_feature = nullptr;
feat->line = nullptr;
RemoveSelection(&*feat);
feat = features.erase(feat);
}
}
@ -151,21 +152,23 @@ void VisualToolDrag::OnFrameChanged() {
}
template<class C, class T> static bool line_not_present(C const& set, T const& it) {
return std::none_of(set.begin(), set.end(), [&](T const& cmp) { return cmp->line == it->line; });
return std::none_of(set.begin(), set.end(), [&](typename C::value_type const& cmp) {
return cmp->line == it->line;
});
}
void VisualToolDrag::OnSelectedSetChanged(const SubtitleSelection &added, const SubtitleSelection &removed) {
c->selectionController->GetSelectedSet(selection);
bool any_changed = false;
for (feature_iterator it = features.begin(); it != features.end(); ) {
for (auto it = features.begin(); it != features.end(); ) {
if (removed.count(it->line)) {
sel_features.erase(it++);
sel_features.erase(&*it++);
any_changed = true;
}
else {
if (added.count(it->line) && it->type == DRAG_START && line_not_present(sel_features, it)) {
sel_features.insert(it);
sel_features.insert(&*it);
any_changed = true;
}
++it;
@ -180,11 +183,11 @@ void VisualToolDrag::Draw() {
DrawAllFeatures();
// Draw connecting lines
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) {
if (cur->type == DRAG_START) continue;
for (auto& feature : features) {
if (feature.type == DRAG_START) continue;
feature_iterator p2 = cur;
feature_iterator p1 = cur->parent;
Feature *p2 = &feature;
Feature *p1 = feature.parent;
// Move end marker has an arrow; origin doesn't
bool has_arrow = p2->type == DRAG_END;
@ -221,51 +224,54 @@ void VisualToolDrag::MakeFeatures(AssDialogue *diag) {
MakeFeatures(diag, features.end());
}
void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_iterator pos) {
void VisualToolDrag::MakeFeatures(AssDialogue *diag, feature_list::iterator pos) {
Vector2D p1 = FromScriptCoords(GetLinePosition(diag));
// Create \pos feature
Feature feat;
feat.pos = p1;
feat.layer = 0;
feat.type = DRAG_START;
feat.time = 0;
feat.line = diag;
feat.parent = features.end();
features.insert(pos, feat);
feature_iterator cur = prev(pos);
feat.parent = cur;
auto feat = agi::util::make_unique<Feature>();
auto parent = feat.get();
feat->pos = p1;
feat->type = DRAG_START;
feat->line = diag;
if (selection.count(diag))
sel_features.insert(cur);
sel_features.insert(feat.get());
features.insert(pos, *feat.release());
Vector2D p2;
int t1, t2;
// Create move destination feature
if (GetLineMove(diag, p1, p2, t1, t2)) {
feat.pos = FromScriptCoords(p2);
feat.layer = 1;
feat.type = DRAG_END;
feat.parent->time = t1;
feat.time = t2;
feat.line = diag;
features.insert(pos, feat);
feat.parent->parent = prev(pos);
feat = agi::util::make_unique<Feature>();
feat->pos = FromScriptCoords(p2);
feat->layer = 1;
feat->type = DRAG_END;
feat->time = t2;
feat->line = diag;
feat->parent = parent;
parent->time = t1;
parent->parent = feat.get();
features.insert(pos, *feat.release());
}
// Create org feature
if (Vector2D org = GetLineOrigin(diag)) {
feat.pos = FromScriptCoords(org);
feat.layer = -1;
feat.type = DRAG_ORIGIN;
feat.time = 0;
feat.line = diag;
features.insert(pos, feat);
feat = agi::util::make_unique<Feature>();
feat->pos = FromScriptCoords(org);
feat->layer = -1;
feat->type = DRAG_ORIGIN;
feat->time = 0;
feat->line = diag;
feat->parent = parent;
features.insert(pos, *feat.release());
}
}
bool VisualToolDrag::InitializeDrag(feature_iterator feature) {
primary = &*feature;
bool VisualToolDrag::InitializeDrag(Feature *feature) {
primary = feature;
// Set time of clicked feature to the current frame and shift all other
// selected features by the same amount
@ -279,17 +285,17 @@ bool VisualToolDrag::InitializeDrag(feature_iterator feature) {
return true;
}
void VisualToolDrag::UpdateDrag(feature_iterator feature) {
void VisualToolDrag::UpdateDrag(Feature *feature) {
if (feature->type == DRAG_ORIGIN) {
SetOverride(feature->line, "\\org", ToScriptCoords(feature->pos).PStr());
return;
}
feature_iterator end_feature = feature->parent;
Feature *end_feature = feature->parent;
if (feature->type == DRAG_END)
std::swap(feature, end_feature);
if (feature->parent == features.end())
if (!feature->parent)
SetOverride(feature->line, "\\pos", ToScriptCoords(feature->pos).PStr());
else
SetOverride(feature->line, "\\move",

View File

@ -27,8 +27,8 @@
class VisualToolDragDraggableFeature : public VisualDraggableFeature {
public:
int time;
boost::container::list<VisualToolDragDraggableFeature>::iterator parent;
VisualToolDragDraggableFeature() : VisualDraggableFeature(), time(0) { }
VisualToolDragDraggableFeature *parent;
VisualToolDragDraggableFeature() : VisualDraggableFeature(), time(0), parent(nullptr) { }
};
class wxBitmapButton;
@ -54,7 +54,7 @@ class VisualToolDrag : public VisualTool<VisualToolDragDraggableFeature> {
/// @brief Create the features for a line
/// @param diag Line to create the features for
/// @param pos Insertion point in the feature list
void MakeFeatures(AssDialogue *diag, feature_iterator pos);
void MakeFeatures(AssDialogue *diag, feature_list::iterator pos);
void MakeFeatures(AssDialogue *diag);
void OnSelectedSetChanged(SubtitleSelection const& lines_added, SubtitleSelection const& lines_removed);
@ -64,8 +64,8 @@ class VisualToolDrag : public VisualTool<VisualToolDragDraggableFeature> {
void OnLineChanged();
void OnCoordinateSystemsChanged() { OnFileChanged(); }
bool InitializeDrag(feature_iterator feature);
void UpdateDrag(feature_iterator feature);
bool InitializeDrag(Feature *feature);
void UpdateDrag(Feature *feature);
void Draw();
void OnDoubleClick();

View File

@ -33,9 +33,9 @@ VisualToolRotateXY::VisualToolRotateXY(VideoDisplay *parent, agi::Context *conte
, orig_x(0)
, orig_y(0)
{
features.resize(1);
org = &features.back();
org = new Feature;
org->type = DRAG_BIG_TRIANGLE;
features.push_back(*org);
}
void VisualToolRotateXY::Draw() {
@ -161,7 +161,7 @@ void VisualToolRotateXY::UpdateHold() {
SetSelectedOverride("\\fry", str(boost::format("%.4g") % angle_y));
}
void VisualToolRotateXY::UpdateDrag(feature_iterator feature) {
void VisualToolRotateXY::UpdateDrag(Feature *feature) {
SetOverride(active_line, "\\org", ToScriptCoords(feature->pos).PStr());
}

View File

@ -34,7 +34,7 @@ class VisualToolRotateXY : public VisualTool<VisualDraggableFeature> {
void DoRefresh();
void Draw();
void UpdateDrag(feature_iterator feature);
void UpdateDrag(Feature *feature);
bool InitializeHold();
void UpdateHold();
public:

View File

@ -36,9 +36,9 @@ VisualToolRotateZ::VisualToolRotateZ(VideoDisplay *parent, agi::Context *context
, orig_angle(0)
, rotation_x(0)
, rotation_y(0)
, org(new Feature)
{
features.resize(1);
org = &features.back();
features.push_back(*org);
org->type = DRAG_BIG_TRIANGLE;
}
@ -118,7 +118,7 @@ void VisualToolRotateZ::UpdateHold() {
SetSelectedOverride("\\frz", str(boost::format("%.4g") % angle));
}
void VisualToolRotateZ::UpdateDrag(feature_iterator feature) {
void VisualToolRotateZ::UpdateDrag(Feature *feature) {
SetOverride(active_line, "\\org", ToScriptCoords(feature->pos).PStr());
}

View File

@ -36,7 +36,7 @@ class VisualToolRotateZ : public VisualTool<VisualDraggableFeature> {
bool InitializeHold();
void UpdateHold();
void UpdateDrag(feature_iterator feature);
void UpdateDrag(Feature *feature);
void DoRefresh();

View File

@ -29,6 +29,8 @@
#include "selection_controller.h"
#include "utils.h"
#include <libaegisub/util.h>
#include <algorithm>
#include <wx/toolbar.h>
@ -111,7 +113,7 @@ void VisualToolVectorClip::Draw() {
spline.GetClosestParametricPoint(mouse_pos, highlighted_curve, t, pt);
// Draw highlighted line
if ((mode == 3 || mode == 4) && active_feature == features.end() && points.size() > 2) {
if ((mode == 3 || mode == 4) && !active_feature && points.size() > 2) {
std::vector<float> highlighted_points;
spline.GetPointList(highlighted_points, highlighted_curve);
if (!highlighted_points.empty()) {
@ -148,42 +150,47 @@ void VisualToolVectorClip::Draw() {
}
void VisualToolVectorClip::MakeFeature(Spline::iterator cur) {
Feature feat;
feat.curve = cur;
auto feat = agi::util::make_unique<Feature>();
feat->curve = cur;
if (cur->type == SplineCurve::POINT) {
feat.pos = cur->p1;
feat.type = DRAG_SMALL_CIRCLE;
feat.point = 0;
feat->pos = cur->p1;
feat->type = DRAG_SMALL_CIRCLE;
feat->point = 0;
}
else if (cur->type == SplineCurve::LINE) {
feat.pos = cur->p2;
feat.type = DRAG_SMALL_CIRCLE;
feat.point = 1;
feat->pos = cur->p2;
feat->type = DRAG_SMALL_CIRCLE;
feat->point = 1;
}
else if (cur->type == SplineCurve::BICUBIC) {
// Control points
feat.pos = cur->p2;
feat.point = 1;
feat.type = DRAG_SMALL_SQUARE;
features.push_back(feat);
feat->pos = cur->p2;
feat->point = 1;
feat->type = DRAG_SMALL_SQUARE;
features.push_back(*feat.release());
feat.pos = cur->p3;
feat.point = 2;
features.push_back(feat);
feat = agi::util::make_unique<Feature>();
feat->curve = cur;
feat->pos = cur->p3;
feat->point = 2;
feat->type = DRAG_SMALL_SQUARE;
features.push_back(*feat.release());
// End point
feat.pos = cur->p4;
feat.type = DRAG_SMALL_CIRCLE;
feat.point = 3;
feat = agi::util::make_unique<Feature>();
feat->curve = cur;
feat->pos = cur->p4;
feat->point = 3;
feat->type = DRAG_SMALL_CIRCLE;
}
features.push_back(feat);
features.push_back(*feat.release());
}
void VisualToolVectorClip::MakeFeatures() {
sel_features.clear();
features.clear();
active_feature = features.end();
active_feature = nullptr;
for (auto it = spline.begin(); it != spline.end(); ++it)
MakeFeature(it);
}
@ -202,12 +209,12 @@ void VisualToolVectorClip::Save() {
}
}
void VisualToolVectorClip::UpdateDrag(feature_iterator feature) {
void VisualToolVectorClip::UpdateDrag(Feature *feature) {
spline.MovePoint(feature->curve, feature->point, feature->pos);
Save();
}
bool VisualToolVectorClip::InitializeDrag(feature_iterator feature) {
bool VisualToolVectorClip::InitializeDrag(Feature *feature) {
if (mode != 5) return true;
if (feature->curve->type == SplineCurve::BICUBIC && (feature->point == 1 || feature->point == 2)) {
@ -229,7 +236,7 @@ bool VisualToolVectorClip::InitializeDrag(feature_iterator feature) {
spline.erase(feature->curve);
}
active_feature = features.end();
active_feature = nullptr;
Save();
MakeFeatures();
@ -313,16 +320,15 @@ bool VisualToolVectorClip::InitializeHold() {
if (mode == 6 || mode == 7) {
sel_features.clear();
features.clear();
active_feature = features.end();
active_feature = nullptr;
spline.clear();
spline.emplace_back(mouse_pos);
return true;
}
/// @todo box selection?
if (mode == 0) {
if (mode == 0)
return false;
}
// Nothing to do for mode 5 (remove)
return false;
@ -398,6 +404,6 @@ void VisualToolVectorClip::DoRefresh() {
void VisualToolVectorClip::SelectAll() {
sel_features.clear();
for (feature_iterator it = features.begin(); it != features.end(); ++it)
sel_features.insert(it);
for (auto& feature : features)
sel_features.insert(&feature);
}

View File

@ -59,8 +59,8 @@ class VisualToolVectorClip : public VisualTool<VisualToolVectorClipDraggableFeat
bool InitializeHold();
void UpdateHold();
void UpdateDrag(feature_iterator feature);
bool InitializeDrag(feature_iterator feature);
void UpdateDrag(Feature *feature);
bool InitializeDrag(Feature *feature);
void DoRefresh();
void Draw();