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\of_type_adaptor.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\option.h" /> <ClInclude Include="$(SrcDir)include\libaegisub\option.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\option_value.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\path.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\scoped_ptr.h" /> <ClInclude Include="$(SrcDir)include\libaegisub\scoped_ptr.h" />
<ClInclude Include="$(SrcDir)include\libaegisub\signal.h" /> <ClInclude Include="$(SrcDir)include\libaegisub\signal.h" />

View File

@ -161,6 +161,9 @@
<ClInclude Include="$(SrcDir)include\libaegisub\kana_table.h"> <ClInclude Include="$(SrcDir)include\libaegisub\kana_table.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="$(SrcDir)include\libaegisub\owning_intrusive_list.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="$(SrcDir)windows\lagi_pre.cpp"> <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() VisualDraggableFeature::VisualDraggableFeature()
: type(DRAG_NONE) : type(DRAG_NONE)
, layer(0) , layer(0)
, line(0) , line(nullptr)
{ {
} }

View File

@ -36,6 +36,8 @@
#include "vector2d.h" #include "vector2d.h"
#include <boost/intrusive/list_hook.hpp>
class OpenGLWrapper; class OpenGLWrapper;
class AssDialogue; class AssDialogue;
@ -54,7 +56,7 @@ enum DraggableFeatureType {
/// ///
/// By itself this class doesn't do much. It mostly just draws itself at a /// By itself this class doesn't do much. It mostly just draws itself at a
/// specified position and performs hit-testing. /// 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 Vector2D start; ///< position before the last drag operation began
public: 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 // Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // purpose with or without fee is hereby granted, provided that the above
@ -43,7 +43,12 @@
using std::placeholders::_1; 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) VisualToolBase::VisualToolBase(VideoDisplay *parent, agi::Context *context)
: c(context) : c(context)
@ -165,8 +170,8 @@ template<class FeatureType>
VisualTool<FeatureType>::VisualTool(VideoDisplay *parent, agi::Context *context) VisualTool<FeatureType>::VisualTool(VideoDisplay *parent, agi::Context *context)
: VisualToolBase(parent, context) : VisualToolBase(parent, context)
, sel_changed(false) , sel_changed(false)
, active_feature(nullptr)
{ {
active_feature = features.begin();
} }
template<class FeatureType> template<class FeatureType>
@ -179,30 +184,21 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
mouse_pos = event.GetPosition(); mouse_pos = event.GetPosition();
bool need_render = false;
if (event.Leaving()) { if (event.Leaving()) {
mouse_pos = Vector2D(); mouse_pos = Vector2D();
parent->Render(); parent->Render();
return; return;
} }
if (event.Entering() && OPT_GET("Tool/Visual/Autohide")->GetBool())
need_render = true;
if (!dragging) { if (!dragging) {
feature_iterator prev_feature = active_feature;
int max_layer = INT_MIN; int max_layer = INT_MIN;
active_feature = features.end(); active_feature = nullptr;
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { for (auto& feature : features) {
if (cur->IsMouseOver(mouse_pos) && cur->layer >= max_layer) { if (feature.IsMouseOver(mouse_pos) && feature.layer >= max_layer) {
active_feature = cur; active_feature = &feature;
max_layer = cur->layer; max_layer = feature.layer;
} }
} }
need_render |= active_feature != prev_feature;
} }
if (dragging) { if (dragging) {
@ -213,14 +209,13 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
for (auto sel : sel_features) for (auto sel : sel_features)
UpdateDrag(sel); UpdateDrag(sel);
Commit(); Commit();
need_render = true;
} }
// end drag // end drag
else { else {
dragging = false; dragging = false;
// mouse didn't move, fiddle with selection // 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 // Don't deselect stuff that was selected in this click's mousedown event
if (!sel_changed) { if (!sel_changed) {
if (ctrl_down) if (ctrl_down)
@ -230,7 +225,7 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
} }
} }
active_feature = features.end(); active_feature = nullptr;
parent->ReleaseMouse(); parent->ReleaseMouse();
parent->SetFocus(); parent->SetFocus();
} }
@ -244,7 +239,6 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
} }
UpdateHold(); UpdateHold();
need_render = true;
Commit(); Commit();
} }
@ -252,7 +246,7 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
drag_start = mouse_pos; drag_start = mouse_pos;
// start drag // start drag
if (active_feature != features.end()) { if (active_feature) {
if (!sel_features.count(active_feature)) { if (!sel_features.count(active_feature)) {
sel_changed = true; sel_changed = true;
SetSelection(active_feature, !ctrl_down); SetSelection(active_feature, !ctrl_down);
@ -276,7 +270,6 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
SubtitleSelection sel; SubtitleSelection sel;
sel.insert(c->selectionController->GetActiveLine()); sel.insert(c->selectionController->GetActiveLine());
c->selectionController->SetSelectedSet(sel); c->selectionController->SetSelectedSet(sel);
need_render = true;
} }
if (active_line && InitializeHold()) { if (active_line && InitializeHold()) {
holding = true; holding = true;
@ -288,8 +281,7 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
if (active_line && left_double) if (active_line && left_double)
OnDoubleClick(); OnDoubleClick();
//if (need_render) parent->Render();
parent->Render();
// Only coalesce the changes made in a single drag // Only coalesce the changes made in a single drag
if (!event.LeftIsDown()) if (!event.LeftIsDown())
@ -299,19 +291,19 @@ void VisualTool<FeatureType>::OnMouseEvent(wxMouseEvent &event) {
template<class FeatureType> template<class FeatureType>
void VisualTool<FeatureType>::DrawAllFeatures() { void VisualTool<FeatureType>::DrawAllFeatures() {
gl.SetLineColour(colour[0], 1.0f, 2); gl.SetLineColour(colour[0], 1.0f, 2);
for (feature_iterator cur = features.begin(); cur != features.end(); ++cur) { for (auto& feature : features) {
int fill = 1; int fill = 1;
if (cur == active_feature) if (&feature == active_feature)
fill = 2; fill = 2;
else if (sel_features.count(cur)) else if (sel_features.count(&feature))
fill = 3; fill = 3;
gl.SetFillColour(colour[fill], 0.6f); gl.SetFillColour(colour[fill], 0.6f);
cur->Draw(gl); feature.Draw(gl);
} }
} }
template<class FeatureType> template<class FeatureType>
void VisualTool<FeatureType>::SetSelection(feature_iterator feat, bool clear) { void VisualTool<FeatureType>::SetSelection(FeatureType *feat, bool clear) {
if (clear) if (clear)
sel_features.clear(); sel_features.clear();
@ -325,12 +317,10 @@ void VisualTool<FeatureType>::SetSelection(feature_iterator feat, bool clear) {
} }
template<class FeatureType> template<class FeatureType>
void VisualTool<FeatureType>::RemoveSelection(feature_iterator feat) { void VisualTool<FeatureType>::RemoveSelection(FeatureType *feat) {
if (!sel_features.erase(feat) || !feat->line) return; if (!sel_features.erase(feat) || !feat->line) return;
for (auto sel : sel_features)
for (auto sel : sel_features) {
if (sel->line == feat->line) return; if (sel->line == feat->line) return;
}
SubtitleSelection sel = c->selectionController->GetSelectedSet(); 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 // Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above // purpose with or without fee is hereby granted, provided that the above
@ -20,22 +20,17 @@
#pragma once #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 "gl_wrap.h"
#include "selection_controller.h" #include "selection_controller.h"
#include "vector2d.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 AssDialogue;
class SubtitlesGrid; class SubtitlesGrid;
class VideoDisplay; class VideoDisplay;
@ -155,21 +150,9 @@ template<class FeatureType>
class VisualTool : public VisualToolBase { class VisualTool : public VisualToolBase {
protected: protected:
typedef FeatureType Feature; typedef FeatureType Feature;
typedef typename boost::container::list<FeatureType>::iterator feature_iterator; typedef agi::owning_intrusive_list<FeatureType> feature_list;
typedef typename boost::container::list<FeatureType>::const_iterator feature_const_iterator;
private: 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? bool sel_changed; /// Has the selection already been changed in the current click?
/// @brief Called when a hold is begun /// @brief Called when a hold is begun
@ -181,23 +164,22 @@ private:
/// @brief Called at the beginning of a drag /// @brief Called at the beginning of a drag
/// @param feature The visual feature clicked on /// @param feature The visual feature clicked on
/// @return Should the drag happen? /// @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 /// @brief Called on every mouse event during a drag
/// @param feature The current feature to process; not necessarily the one clicked on /// @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 /// @brief Draw stuff
virtual void Draw()=0; virtual void Draw()=0;
protected: protected:
std::set<feature_iterator, ltaddr> sel_features; ///< Currently selected visual features std::set<FeatureType *> sel_features; ///< Currently selected visual features
typedef typename std::set<feature_iterator, ltaddr>::const_iterator sel_iterator;
/// Topmost feature under the mouse; generally only valid during a drag /// 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 of features which are drawn and can be clicked on
/// List is used here for the iterator invalidation properties /// 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 /// Draw all of the features in the list
void DrawAllFeatures(); void DrawAllFeatures();
@ -205,11 +187,11 @@ protected:
/// @brief Remove a feature from the selection /// @brief Remove a feature from the selection
/// @param i Index in the feature list /// @param i Index in the feature list
/// Also deselects lines if all features for that line have been deselected /// 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 /// @brief Set the selection to a single feature, deselecting everything else
/// @param i Index in the feature list /// @param i Index in the feature list
void SetSelection(feature_iterator feat, bool clear); void SetSelection(FeatureType *feat, bool clear);
public: public:
/// @brief Handler for all mouse events /// @brief Handler for all mouse events

View File

@ -36,18 +36,11 @@ VisualToolClip::VisualToolClip(VideoDisplay *parent, agi::Context *context)
, cur_2(video_res) , cur_2(video_res)
, inverse(false) , 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]; ClipCorner *feats[4];
feature_iterator cur = features.begin(); for (size_t i = 0; i < 4; ++i) {
feats[0] = &*(cur++); feats[i] = new ClipCorner;
feats[1] = &*(cur++); features.push_back(*feats[i]);
feats[2] = &*(cur++); }
feats[3] = &*(cur++);
// Attach each feature to the two features it shares edges with // Attach each feature to the two features it shares edges with
// Top-left // Top-left
@ -123,11 +116,7 @@ void VisualToolClip::CommitHold() {
} }
} }
bool VisualToolClip::InitializeDrag(feature_iterator) { void VisualToolClip::UpdateDrag(ClipCorner *feature) {
return true;
}
void VisualToolClip::UpdateDrag(feature_iterator feature) {
// Update features which share an edge with the dragged one // Update features which share an edge with the dragged one
feature->horiz->pos = Vector2D(feature->horiz->pos, feature->pos); feature->horiz->pos = Vector2D(feature->horiz->pos, feature->pos);
feature->vert->pos = Vector2D(feature->pos, feature->vert->pos); feature->vert->pos = Vector2D(feature->pos, feature->vert->pos);
@ -139,7 +128,7 @@ void VisualToolClip::UpdateDrag(feature_iterator feature) {
} }
void VisualToolClip::SetFeaturePositions() { void VisualToolClip::SetFeaturePositions() {
feature_iterator it = features.begin(); auto it = features.begin();
(it++)->pos = cur_1; // Top-left (it++)->pos = cur_1; // Top-left
(it++)->pos = Vector2D(cur_2, cur_1); // Top-right (it++)->pos = Vector2D(cur_2, cur_1); // Top-right
(it++)->pos = Vector2D(cur_1, cur_2); // Bottom-left (it++)->pos = Vector2D(cur_1, cur_2); // Bottom-left

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -35,8 +35,8 @@ struct VisualToolVectorClipDraggableFeature : public VisualDraggableFeature {
int point; int point;
/// @brief Constructor /// @brief Constructor
VisualToolVectorClipDraggableFeature() VisualToolVectorClipDraggableFeature()
: VisualDraggableFeature() : VisualDraggableFeature()
, point(0) , point(0)
{ } { }
}; };
@ -59,8 +59,8 @@ class VisualToolVectorClip : public VisualTool<VisualToolVectorClipDraggableFeat
bool InitializeHold(); bool InitializeHold();
void UpdateHold(); void UpdateHold();
void UpdateDrag(feature_iterator feature); void UpdateDrag(Feature *feature);
bool InitializeDrag(feature_iterator feature); bool InitializeDrag(Feature *feature);
void DoRefresh(); void DoRefresh();
void Draw(); void Draw();