From 570321722b784a4847d3686a81b115c2c5ef616f Mon Sep 17 00:00:00 2001 From: Rodrigo Braz Monteiro Date: Thu, 5 Jul 2007 04:32:46 +0000 Subject: [PATCH] Barely function vector clip tool implemented Originally committed to SVN as r1364. --- aegisub/Makefile.am | 1 + aegisub/gl_wrap.cpp | 13 ++ aegisub/gl_wrap.h | 1 + aegisub/spline.cpp | 165 ++++++++++++++++++---- aegisub/spline.h | 3 +- aegisub/video_box.cpp | 13 +- aegisub/video_box.h | 3 + aegisub/video_display.cpp | 21 +-- aegisub/visual_feature.cpp | 13 +- aegisub/visual_feature.h | 2 +- aegisub/visual_tool.cpp | 36 +++++ aegisub/visual_tool.h | 1 + aegisub/visual_tool_clip.cpp | 2 +- aegisub/visual_tool_drag.cpp | 7 +- aegisub/visual_tool_vector_clip.cpp | 204 ++++++++++++++++++++++++++++ aegisub/visual_tool_vector_clip.h | 64 +++++++++ 16 files changed, 502 insertions(+), 47 deletions(-) create mode 100644 aegisub/visual_tool_vector_clip.cpp create mode 100644 aegisub/visual_tool_vector_clip.h diff --git a/aegisub/Makefile.am b/aegisub/Makefile.am index 50d516797..99836173e 100644 --- a/aegisub/Makefile.am +++ b/aegisub/Makefile.am @@ -222,6 +222,7 @@ aegisub_SOURCES = \ visual_tool_rotatexy.cpp \ visual_tool_rotatez.cpp \ visual_tool_scale.cpp \ + visual_tool_vector_clip.cpp \ MatroskaParser.c noinst_HEADERS = \ diff --git a/aegisub/gl_wrap.cpp b/aegisub/gl_wrap.cpp index eee180b8b..23388304e 100644 --- a/aegisub/gl_wrap.cpp +++ b/aegisub/gl_wrap.cpp @@ -101,6 +101,19 @@ void OpenGLWrapper::DrawLine(float x1,float y1,float x2,float y2) { } +///////////// +// Draw line +void OpenGLWrapper::DrawDashedLine(float x1,float y1,float x2,float y2,float step) { + float dist = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); + int steps = (dist-20)/step; + double stepx = double(x2-x1)/steps; + double stepy = double(y2-y1)/steps; + for (int i=0;i #include "spline.h" @@ -63,10 +64,10 @@ wxString Spline::EncodeToASS() { bool isFirst = true; // Insert each element - for (std::list::iterator cur = curves.begin();cur!=curves.end();cur++) { + for (std::list::iterator cur=curves.begin();cur!=curves.end();cur++) { // Start of spline if (isFirst) { - result = wxString::Format(_T("m %i %i"),cur->x1,cur->y1); + result = wxString::Format(_T("m %i %i "),cur->x1,cur->y1); lastCommand = 'm'; isFirst = false; } @@ -78,14 +79,14 @@ wxString Spline::EncodeToASS() { result += _T("l "); lastCommand = 'l'; } - result += wxString::Format(_T("%i %i"),cur->x2,cur->y2); + result += wxString::Format(_T("%i %i "),cur->x2,cur->y2); break; case CURVE_BICUBIC: if (lastCommand != 'b') { result += _T("b "); lastCommand = 'b'; } - result += wxString::Format(_T("%i %i %i %i"),cur->x2,cur->y2,cur->x3,cur->y3,cur->x4,cur->y4); + result += wxString::Format(_T("%i %i %i %i %i %i "),cur->x2,cur->y2,cur->x3,cur->y3,cur->x4,cur->y4); break; default: break; } @@ -99,6 +100,79 @@ wxString Spline::EncodeToASS() { void Spline::DecodeFromASS(wxString str) { // Clear current curves.clear(); + std::vector stack; + + // Prepare + char lastCommand = 'm'; + int x = 0; + int y = 0; + + // Tokenize the string + wxStringTokenizer tkn(str,_T(" ")); + while (tkn.HasMoreTokens()) { + wxString token = tkn.GetNextToken(); + + // Got a number + if (token.IsNumber()) { + long n; + token.ToLong(&n); + stack.push_back(n); + + // Move + if (stack.size() == 2 && lastCommand == 'm') { + x = stack[0]; + y = stack[1]; + stack.clear(); + } + + // Line + if (stack.size() == 2 && lastCommand == 'l') { + SplineCurve curve; + curve.x1 = x; + curve.y1 = y; + curve.x2 = stack[0]; + curve.y2 = stack[1]; + curve.type = CURVE_LINE; + x = curve.x2; + y = curve.y2; + stack.clear(); + AppendCurve(curve); + } + + // Bicubic + else if (stack.size() == 6 && lastCommand == 'b') { + SplineCurve curve; + curve.x1 = x; + curve.y1 = y; + curve.x2 = stack[0]; + curve.y2 = stack[1]; + curve.x3 = stack[2]; + curve.y3 = stack[3]; + curve.x4 = stack[4]; + curve.y4 = stack[5]; + curve.type = CURVE_BICUBIC; + x = curve.x4; + y = curve.y4; + stack.clear(); + AppendCurve(curve); + } + + // Close + else if (lastCommand == 'c') { + stack.clear(); + } + } + + // Got something else + else { + if (token == _T("m")) lastCommand = 'm'; + else if (token == _T("l")) lastCommand = 'l'; + else if (token == _T("b")) lastCommand = 'b'; + else if (token == _T("n")) lastCommand = 'n'; + else if (token == _T("s")) lastCommand = 's'; + else if (token == _T("c")) lastCommand = 'c'; + } + } } @@ -112,31 +186,63 @@ void Spline::AppendCurve(SplineCurve &curve) { //////////////////////////////////////// // Moves a specific point in the spline void Spline::MovePoint(int curveIndex,int point,wxPoint pos) { + // Curves int i = 0; + SplineCurve *c0 = NULL; + SplineCurve *c1 = NULL; + SplineCurve *c2 = NULL; + + // Indices + int size = curves.size(); + int i0 = curveIndex-1; + int i1 = curveIndex; + int i2 = curveIndex+1; + //if (i0 < 0) i0 = size-1; + //if (i2 >= size) i2 = 0; + + // Get the curves for (std::list::iterator cur = curves.begin();cur!=curves.end();cur++) { - if (i == curveIndex) { - switch (point) { - case 0: - cur->x1 = pos.x; - cur->y1 = pos.y; - break; - case 1: - cur->x2 = pos.x; - cur->y2 = pos.y; - break; - case 2: - cur->x3 = pos.x; - cur->y3 = pos.y; - break; - case 3: - cur->x4 = pos.x; - cur->y4 = pos.y; - break; - } - return; - } + if (i == i0) c0 = &(*cur); + if (i == i1) c1 = &(*cur); + if (i == i2) c2 = &(*cur); i++; } + + // Modify + if (point == 0) { + c1->x1 = pos.x; + c1->y1 = pos.y; + if (c0) { + if (c0->type == CURVE_BICUBIC) { + c0->x4 = pos.x; + c0->y4 = pos.y; + } + else { + c0->x2 = pos.x; + c0->y2 = pos.y; + } + } + } + else if (point == 1) { + c1->x2 = pos.x; + c1->y2 = pos.y; + if (c2 && c1->type != CURVE_BICUBIC) { + c2->x1 = pos.x; + c2->y1 = pos.y; + } + } + else if (point == 2) { + c1->x3 = pos.x; + c1->y3 = pos.y; + } + else if (point == 3) { + c1->x4 = pos.x; + c1->y4 = pos.y; + if (c2 && c1->type == CURVE_BICUBIC) { + c2->x1 = pos.x; + c2->y1 = pos.y; + } + } } @@ -176,8 +282,8 @@ void Spline::GetPointList(std::vector &points) { int y3 = cur->y3; int y4 = cur->y4; - // Hardcoded at 10 steps for now - int steps = 10; + // Hardcoded at 50 steps for now + int steps = 50; for (int i=0;i &points) { } } } + + // Insert a copy of the first point at the end + if (points.size()) { + points.push_back(points[0]); + } } diff --git a/aegisub/spline.h b/aegisub/spline.h index 5cc769ae2..e7fe26eed 100644 --- a/aegisub/spline.h +++ b/aegisub/spline.h @@ -71,10 +71,9 @@ public: ///////////////////////// // Spline managing class class Spline { -private: +public: std::list curves; -public: Spline(); wxString EncodeToASS(); diff --git a/aegisub/video_box.cpp b/aegisub/video_box.cpp index 62af4b88f..7450b9351 100644 --- a/aegisub/video_box.cpp +++ b/aegisub/video_box.cpp @@ -118,6 +118,8 @@ VideoBox::VideoBox(wxWindow *parent) scale->SetToolTip(_("Scale subtitles on X and Y axes.")); clip = new wxBitmapButton(videoPage,Video_Mode_Clip,wxBITMAP(visual_clip)); clip->SetToolTip(_("Clip subtitles to a rectangle.")); + vectorClip = new wxBitmapButton(videoPage,Video_Mode_Vector_Clip,wxBITMAP(visual_clip)); + vectorClip->SetToolTip(_("Clip subtitles to a vectorial area.")); realtime = new ToggleBitmap(videoPage,Video_Mode_Realtime,wxBITMAP(visual_realtime),wxSize(20,20)); realtime->SetToolTip(_("Toggle realtime display of changes.")); bool isRealtime = Options.AsBool(_T("Video Visual Realtime")); @@ -128,7 +130,8 @@ VideoBox::VideoBox(wxWindow *parent) typeSizer->Add(rotatez,0,wxEXPAND,0); typeSizer->Add(rotatexy,0,wxEXPAND,0); typeSizer->Add(scale,0,wxEXPAND,0); - typeSizer->Add(clip,0,wxEXPAND | wxBOTTOM,5); + typeSizer->Add(clip,0,wxEXPAND,0); + typeSizer->Add(vectorClip,0,wxEXPAND | wxBOTTOM,5); typeSizer->Add(realtime,0,wxEXPAND,0); typeSizer->AddStretchSpacer(1); @@ -175,6 +178,7 @@ BEGIN_EVENT_TABLE(VideoBox, wxPanel) EVT_BUTTON(Video_Mode_Rotate_XY, VideoBox::OnModeRotateXY) EVT_BUTTON(Video_Mode_Scale, VideoBox::OnModeScale) EVT_BUTTON(Video_Mode_Clip, VideoBox::OnModeClip) + EVT_BUTTON(Video_Mode_Vector_Clip, VideoBox::OnModeVectorClip) EVT_TOGGLEBUTTON(Video_Mode_Realtime, VideoBox::OnToggleRealtime) END_EVENT_TABLE() @@ -254,6 +258,13 @@ void VideoBox::OnModeClip(wxCommandEvent &event) { } +//////////////////// +// Vector clip mode +void VideoBox::OnModeVectorClip(wxCommandEvent &event) { + videoDisplay->SetVisualMode(6); +} + + /////////////////// // Realtime toggle void VideoBox::OnToggleRealtime(wxCommandEvent &event) { diff --git a/aegisub/video_box.h b/aegisub/video_box.h index 6faa37389..cecb3464d 100644 --- a/aegisub/video_box.h +++ b/aegisub/video_box.h @@ -62,6 +62,7 @@ private: wxButton *rotatexy; wxButton *scale; wxButton *clip; + wxButton *vectorClip; ToggleBitmap *realtime; void OnVideoPlay(wxCommandEvent &event); @@ -79,6 +80,7 @@ private: void OnModeRotateXY(wxCommandEvent &event); void OnModeScale(wxCommandEvent &event); void OnModeClip(wxCommandEvent &event); + void OnModeVectorClip(wxCommandEvent &event); void OnToggleRealtime(wxCommandEvent &event); public: @@ -113,6 +115,7 @@ enum { Video_Mode_Rotate_XY, Video_Mode_Scale, Video_Mode_Clip, + Video_Mode_Vector_Clip, Video_Mode_Realtime, Video_Tracker_Menu, diff --git a/aegisub/video_display.cpp b/aegisub/video_display.cpp index 3fbe7eecf..ca76f67dc 100644 --- a/aegisub/video_display.cpp +++ b/aegisub/video_display.cpp @@ -73,6 +73,7 @@ #include "visual_tool_rotatexy.h" #include "visual_tool_scale.h" #include "visual_tool_clip.h" +#include "visual_tool_vector_clip.h" #include "visual_tool_drag.h" @@ -106,7 +107,7 @@ END_EVENT_TABLE() ////////////// // Parameters -int attribList[3] = { WX_GL_RGBA , WX_GL_DOUBLEBUFFER, 0 }; +int attribList[] = { WX_GL_RGBA , WX_GL_DOUBLEBUFFER, WX_GL_STENCIL_SIZE, 8, 0 }; /////////////// // Constructor @@ -185,18 +186,20 @@ void VideoDisplay::Render() { wxASSERT(pw > 0); wxASSERT(ph > 0); + // Clear frame buffer + glClearColor(0,0,0,0); + if (glGetError()) throw _T("Error setting glClearColor()."); + glClearStencil(0); + if (glGetError()) throw _T("Error setting glClearStencil()."); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + if (glGetError()) throw _T("Error calling glClear()."); + // Freesized transform dx1 = 0; dy1 = 0; dx2 = w; dy2 = h; if (freeSize) { - // Clear frame buffer - glClearColor(0,0,0,0); - if (glGetError()) throw _T("Error setting glClearColor()."); - glClear(GL_COLOR_BUFFER_BIT); - if (glGetError()) throw _T("Error calling glClear()."); - // Set aspect ratio float thisAr = float(w)/float(h); float vidAr; @@ -482,7 +485,8 @@ void VideoDisplay::OnKey(wxKeyEvent &event) { if (event.GetKeyCode() == 'D') SetVisualMode(2); if (event.GetKeyCode() == 'F') SetVisualMode(3); if (event.GetKeyCode() == 'G') SetVisualMode(4); - if (event.GetKeyCode() == 'H') SetVisualMode(5); + if (event.GetKeyCode() == 'H') SetVisualMode(5); + if (event.GetKeyCode() == 'J') SetVisualMode(6); } @@ -664,6 +668,7 @@ void VideoDisplay::SetVisualMode(int mode) { case 3: visual = new VisualToolRotateXY(this); break; case 4: visual = new VisualToolScale(this); break; case 5: visual = new VisualToolClip(this); break; + case 6: visual = new VisualToolVectorClip(this); break; default: visual = NULL; } diff --git a/aegisub/visual_feature.cpp b/aegisub/visual_feature.cpp index 5e79786b5..fd1a14a3f 100644 --- a/aegisub/visual_feature.cpp +++ b/aegisub/visual_feature.cpp @@ -46,7 +46,7 @@ VisualDraggableFeature::VisualDraggableFeature() { type = DRAG_NONE; x = -1; y = -1; - value = 0; + value = value2 = 0; layer = 0; lineN = -1; line = NULL; @@ -80,6 +80,12 @@ bool VisualDraggableFeature::IsMouseOver(int mx,int my) { return (16*dx+9*dy < 0 && 16*dx-9*dy > 0); } + // Small square + else if (type == DRAG_SMALL_SQUARE) { + if (mx < x-4 || mx > x+4 || my < y-4 || my > y+4) return false; + return true; + } + // Small circle else if (type == DRAG_SMALL_CIRCLE) { int dx = mx-x; @@ -120,6 +126,11 @@ void VisualDraggableFeature::Draw(OpenGLWrapper *gl) { gl->DrawLine(x,y,x+14,y+8); } + // Square + else if (type == DRAG_SMALL_SQUARE) { + gl->DrawRectangle(x-4,y-4,x+4,y+4); + } + // Small circle else if (type == DRAG_SMALL_CIRCLE) { gl->DrawCircle(x,y,4); diff --git a/aegisub/visual_feature.h b/aegisub/visual_feature.h index 44d6d2aee..65c8ed257 100644 --- a/aegisub/visual_feature.h +++ b/aegisub/visual_feature.h @@ -66,7 +66,7 @@ public: DraggableFeatureType type; int x,y; int layer; // Higher = above - int value; + int value,value2; AssDialogue *line; int lineN; diff --git a/aegisub/visual_tool.cpp b/aegisub/visual_tool.cpp index 1e7a21b1d..ed2f8534f 100644 --- a/aegisub/visual_tool.cpp +++ b/aegisub/visual_tool.cpp @@ -581,6 +581,42 @@ void VisualTool::GetLineClip(AssDialogue *diag,int &x1,int &y1,int &x2,int &y2) } +////////////////////////////////////// +// Get line vector clip, if it exists +wxString VisualTool::GetLineVectorClip(AssDialogue *diag,int &scale) { + // Prepare overrides + wxString result; + scale = 1; + diag->ParseASSTags(); + AssDialogueBlockOverride *override; + AssOverrideTag *tag; + size_t blockn = diag->Blocks.size(); + if (blockn == 0) { + diag->ClearBlocks(); + return result; + } + + // Process override + override = AssDialogueBlock::GetAsOverride(diag->Blocks.at(0)); + if (override) { + for (size_t j=0;jTags.size();j++) { + tag = override->Tags.at(j); + if (tag->Name == _T("\\clip")) { + if (tag->Params.size() == 1) { + result = tag->Params[0]->AsText(); + } + if (tag->Params.size() == 2) { + scale = tag->Params[0]->AsInt(); + result = tag->Params[1]->AsText(); + } + } + } + } + diag->ClearBlocks(); + return result; +} + + //////////////// // Set override void VisualTool::SetOverride(wxString tag,wxString value) { diff --git a/aegisub/visual_tool.h b/aegisub/visual_tool.h index 5c0478289..215b6b07f 100644 --- a/aegisub/visual_tool.h +++ b/aegisub/visual_tool.h @@ -101,6 +101,7 @@ protected: void GetLineRotation(AssDialogue *diag,float &rx,float &ry,float &rz); void GetLineScale(AssDialogue *diag,float &scalX,float &scalY); void GetLineClip(AssDialogue *diag,int &x1,int &y1,int &x2,int &y2); + wxString GetLineVectorClip(AssDialogue *diag,int &scale); void FillPositionData(); void SetOverride(wxString tag,wxString value); diff --git a/aegisub/visual_tool_clip.cpp b/aegisub/visual_tool_clip.cpp index 0584b86c1..74d66894d 100644 --- a/aegisub/visual_tool_clip.cpp +++ b/aegisub/visual_tool_clip.cpp @@ -92,7 +92,7 @@ void VisualToolClip::Draw() { // Draw outside area SetLineColour(colour[3],0.0f); - SetFillColour(colour[3],0.3f); + SetFillColour(wxColour(0,0,0),0.5f); DrawRectangle(0,0,sw,dy1); DrawRectangle(0,dy2,sw,sh); DrawRectangle(0,dy1,dx1,dy2); diff --git a/aegisub/visual_tool_drag.cpp b/aegisub/visual_tool_drag.cpp index 73da0d3d3..e4dfdbf11 100644 --- a/aegisub/visual_tool_drag.cpp +++ b/aegisub/visual_tool_drag.cpp @@ -185,12 +185,7 @@ void VisualToolDrag::Draw() { // Draw dashed line else { SetLineColour(colour[3],0.5f,2); - int steps = (dist-20)/6; - double stepx = double(x2-x1)/steps; - double stepy = double(y2-y1)/steps; - for (int i=0;iRender(); +} + + +//////// +// Draw +void VisualToolVectorClip::Draw() { + // Get line + AssDialogue *line = GetActiveDialogueLine(); + if (!line) return; + + // Parse vector + std::vector points; + spline.GetPointList(points); + + // Draw lines + SetLineColour(colour[3]); + SetFillColour(colour[3],0.0f); + for (size_t i=0;i::iterator cur=spline.curves.begin();cur!=spline.curves.end();cur++) { + if (cur->type == CURVE_BICUBIC) { + DrawDashedLine(cur->x1,cur->y1,cur->x2,cur->y2,6); + DrawDashedLine(cur->x3,cur->y3,cur->x4,cur->y4,6); + } + } +} + + +///////////////////////// +// Populate feature list +void VisualToolVectorClip::PopulateFeatureList() { + // Clear + features.clear(); + VisualDraggableFeature feat; + + // Go through each curve + bool isFirst = true; + int i = 0; + for (std::list::iterator cur=spline.curves.begin();cur!=spline.curves.end();cur++,i++) { + // First point + if (isFirst) { + isFirst = false; + feat.x = cur->x1; + feat.y = cur->y1; + feat.type = DRAG_SMALL_CIRCLE; + feat.value = i; + feat.value2 = 0; + features.push_back(feat); + } + + // Line + if (cur->type == CURVE_LINE) { + feat.x = cur->x2; + feat.y = cur->y2; + feat.type = DRAG_SMALL_CIRCLE; + feat.value = i; + feat.value2 = 1; + features.push_back(feat); + } + + // Bicubic + if (cur->type == CURVE_BICUBIC) { + // Current size + int size = features.size(); + + // Control points + feat.x = cur->x2; + feat.y = cur->y2; + feat.value = i; + feat.value2 = 1; + feat.brother[0] = size-1; + feat.type = DRAG_SMALL_SQUARE; + features.push_back(feat); + feat.x = cur->x3; + feat.y = cur->y3; + feat.value2 = 2; + feat.brother[0] = size+2; + features.push_back(feat); + + // End point + feat.x = cur->x4; + feat.y = cur->y4; + feat.type = DRAG_SMALL_CIRCLE; + feat.value2 = 3; + features.push_back(feat); + } + } +} + + +////////// +// Update +void VisualToolVectorClip::UpdateDrag(VisualDraggableFeature &feature) { + spline.MovePoint(feature.value,feature.value2,wxPoint(feature.x,feature.y)); +} + + +////////// +// Commit +void VisualToolVectorClip::CommitDrag(VisualDraggableFeature &feature) { + SetOverride(_T("\\clip"),_T("(") + spline.EncodeToASS() + _T(")")); +} + + +/////////// +// Refresh +void VisualToolVectorClip::DoRefresh() { + if (!dragging) { + // Get line + AssDialogue *line = GetActiveDialogueLine(); + if (!line) return; + + // Get clip vector + wxString vect; + int scale; + vect = GetLineVectorClip(line,scale); + if (vect.IsEmpty()) return; + spline.DecodeFromASS(vect); + PopulateFeatureList(); + } +} diff --git a/aegisub/visual_tool_vector_clip.h b/aegisub/visual_tool_vector_clip.h new file mode 100644 index 000000000..ceb4f2dfb --- /dev/null +++ b/aegisub/visual_tool_vector_clip.h @@ -0,0 +1,64 @@ +// Copyright (c) 2007, Rodrigo Braz Monteiro +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Aegisub Group nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------------- +// +// AEGISUB +// +// Website: http://aegisub.cellosoft.com +// Contact: mailto:zeratul@cellosoft.com +// + + +#pragma once + + +/////////// +// Headers +#include "visual_tool.h" +#include "spline.h" + + +////////////////////////// +// Vector clip tool class +class VisualToolVectorClip : public VisualTool { +private: + Spline spline; + + bool CanDrag() { return true; } + void PopulateFeatureList(); + void UpdateDrag(VisualDraggableFeature &feature); + void CommitDrag(VisualDraggableFeature &feature); + + void DoRefresh(); + +public: + VisualToolVectorClip(VideoDisplay *parent); + + void Update(); + void Draw(); +};